Compare commits
No commits in common. "main" and "dev/ci-test" have entirely different histories.
main
...
dev/ci-tes
2
.gitignore
vendored
|
@ -1,3 +1 @@
|
|||
/vendor/
|
||||
|
||||
sftp.json
|
6
.htaccess
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
|
||||
RewriteCond %{REQUEST_URI} !^/?static/.*
|
||||
RewriteRule (.*) router.php
|
7
composer.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"require": {
|
||||
"twig/twig": "^3.0",
|
||||
"twig/markdown-extra": "^3.6",
|
||||
"league/commonmark": "^2.4"
|
||||
}
|
||||
}
|
792
www/composer.lock → composer.lock
generated
|
@ -1,7 +1,7 @@
|
|||
FROM composer
|
||||
WORKDIR /app
|
||||
|
||||
COPY www/composer.* .
|
||||
COPY composer.* .
|
||||
RUN composer install
|
||||
|
||||
FROM php:apache
|
||||
|
@ -12,9 +12,6 @@ RUN chmod -R a+r ./vendor
|
|||
|
||||
RUN a2enmod rewrite
|
||||
RUN a2enmod headers
|
||||
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
|
||||
|
||||
RUN mkdir raw
|
||||
|
||||
COPY www/ .
|
||||
RUN chmod -R a+rw $(ls -I vendor)
|
||||
COPY . .
|
||||
RUN chmod -R a+r $(ls -I vendor)
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
FROM mysql:8.0-debian
|
||||
|
||||
WORKDIR /docker-entrypoint-initdb.d
|
||||
COPY mysql_schema.sql ./
|
|
@ -5,44 +5,22 @@ 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: ../www/composer.*
|
||||
action: rebuild
|
||||
- path: ../www/
|
||||
- path: ../.
|
||||
action: sync
|
||||
target: /var/www/html
|
||||
ignore:
|
||||
- ../.git
|
||||
- mysql_schema.sql
|
||||
volumes:
|
||||
- website_datavolume:/var/www/html/raw
|
||||
|
||||
target: /usr/local/apache2/htdocs/
|
||||
mysql:
|
||||
build:
|
||||
dockerfile: MysqlDockerfile
|
||||
image: mysql:8.0-debian
|
||||
# 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: {}
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
|
||||
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.
|
||||
'
|
||||
);
|
43
router.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
$loader = new \Twig\Loader\FilesystemLoader(['./templates', './user_content']);
|
||||
|
||||
$twig = new \Twig\Environment($loader,['debug' => 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!"
|
||||
]);
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
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
|
|
@ -1,7 +1,5 @@
|
|||
Allow from all
|
||||
|
||||
Options +Indexes
|
||||
|
||||
<filesMatch ".(flv|gif|ico|jpg|jpeg|mp4|mpeg|png|svg|swf|webp)$">
|
||||
Header set Cache-Control "max-age=315360, public"
|
||||
</filesMatch>
|
69
static/banner.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
|
||||
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));
|
Before Width: | Height: | Size: 527 KiB After Width: | Height: | Size: 527 KiB |
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
134
static/dergstyle.css
Normal file
|
@ -0,0 +1,134 @@
|
|||
|
||||
* {
|
||||
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%;
|
||||
}
|
|
@ -1,12 +1,18 @@
|
|||
|
||||
|
||||
#main_content_wrapper figcaption h1 {
|
||||
article {
|
||||
margin-top: 0.8em;
|
||||
margin-left: 10%;
|
||||
margin-right: 10%;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
text-align: center;
|
||||
|
||||
border-bottom: solid 3px darkgrey;
|
||||
font-size: 1.8rem;
|
||||
font-size: 2em;
|
||||
|
||||
margin-bottom: 0.3rem;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
#gallery_image {
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
13
static/rrrorstyle.css
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
|
||||
#rrr_header {
|
||||
font-size: 2em;
|
||||
|
||||
border-bottom: 1px solid grey;
|
||||
padding-bottom: 0.2em;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
#rrr_code {
|
||||
font-size: 1.5em;
|
||||
}
|
13
templates/about.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
|
||||
{% extends "root.html" %}
|
||||
|
||||
{% block second_title %}
|
||||
<h2> Abouts </h2>
|
||||
{% endblock %}
|
||||
|
||||
{%block main_content%}
|
||||
<article>
|
||||
{{ include('about.md')|markdown_to_html }}
|
||||
</article>
|
||||
{%endblock%}
|
31
templates/index.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Lucidragons' Fire</title>
|
||||
<link rel="stylesheet" href="/dergstyle.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="big_title">The dergsite</h1>
|
||||
<div id="title_separator"></div>
|
||||
|
||||
<ul>
|
||||
<li><a href="/about"> About </a></li>
|
||||
<li><a href="/blog"> Blog </a></li>
|
||||
<li><a href="/projects"> Projects </a></li>
|
||||
<li><a href="/artwork"> Artworks </a></li>
|
||||
</ul>
|
||||
|
||||
<div id="main_content_wrapper">
|
||||
{% block content %}<h3>Soon there shall be content!</h3>{% endblock %}
|
||||
</div>
|
||||
|
||||
<div id="sidebar-wrapper">
|
||||
{%block sidebar %}
|
||||
<ul>
|
||||
<li> Test 1 </li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
18
templates/root.dergplate
Normal file
|
@ -0,0 +1,18 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>PHP Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
|
||||
echo '<p>Hello World</p>';
|
||||
|
||||
require 'src/templater.php';
|
||||
|
||||
$test = new TemplateFillout('test.dergplate');
|
||||
|
||||
$test->render();
|
||||
|
||||
?>
|
||||
</body>
|
||||
</html>
|
42
templates/root.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Lucidragons' Fire</title>
|
||||
<link rel="stylesheet" href="/static/dergstyle.css">
|
||||
<link rel="icon" type="image/x-icon" href="/static/icon.jpeg">
|
||||
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
{% block extra_head %}{% endblock %}
|
||||
|
||||
<script src="/static/banner.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<header id="main_header">
|
||||
<img id="main_banner_img"></img>
|
||||
<a id="main_banner_img_link" href="/gallery"> full picture</a>
|
||||
|
||||
<h1 id="big_title">{% block big_title %}The dergsite{%endblock%}</h1>
|
||||
{% block second_title %}{% endblock %}
|
||||
<div id="title_separator"></div>
|
||||
|
||||
<menu id="nav_bar">
|
||||
<li><a href="/about"> About </a></li>
|
||||
<li><a href="/blog"> Blog </a></li>
|
||||
<li><a href="/projects"> Projects </a></li>
|
||||
<li><a href="/artwork"> Artworks </a></li>
|
||||
</menu>
|
||||
</header>
|
||||
|
||||
<main id="main_content_wrapper">
|
||||
{% block main_content %}<h3>Soon there shall be content!</h3>{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer id="main_footer">
|
||||
{% block main_footer %}
|
||||
<span> test? </span>
|
||||
{% endblock %}
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
21
templates/rrror.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
{% extends "root.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="/static/rrrorstyle.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block second_title %}
|
||||
<h2> (Broken) </h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block main_content %}
|
||||
<article>
|
||||
<h1 id="rrr_header"> The Dergs are confused:</h2>
|
||||
<h2 id="rrr_code"> {{ error_code }}</h1>
|
||||
|
||||
<div>
|
||||
{{ error_description|markdown_to_html }}
|
||||
</div>
|
||||
</article>
|
||||
{% endblock %}
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
settings:
|
||||
colourscheme: fun
|
||||
post_style: generic
|
||||
banners:
|
||||
- src: /banner/0.png
|
||||
- src: /banner/1.png
|
||||
---
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
tags: [what]
|
||||
directory_data:
|
||||
type: text/markdown
|
||||
---
|
||||
|
||||
# The dergens
|
||||
|
||||
The
|
||||
|
||||
yes
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
---
|
||||
|
||||
# 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 :)
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
title: A cuddly image <3
|
||||
author: Shaky // Doggonaut
|
||||
---
|
||||
|
||||
# Cuddly dragons
|
||||
|
||||
A dear picture made by a dear friend. Shaky - we hope you are OK.
|
Before Width: | Height: | Size: 796 KiB |
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
directory:
|
||||
settings:
|
||||
banners:
|
||||
- src: /about/neira/Neira_Queen.png
|
||||
from: 0.5
|
||||
to: 0.95
|
||||
---
|
||||
|
||||
# She is soft~<3
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Oh, the pain
|
||||
---
|
||||
|
||||
# AAA
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
tags:
|
||||
- neira
|
||||
- comfy
|
||||
- testing
|
||||
title: Subtest, it's nice :>
|
||||
---
|
||||
|
||||
## Subtests, because it's nice
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
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:
|
||||
|
||||

|
Before Width: | Height: | Size: 2.4 MiB |
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
title: A cuddly image <3
|
||||
author: Shaky // Doggonaut
|
||||
---
|
||||
|
||||
# Cuddly dragons
|
||||
|
||||
A dear picture made by a dear friend. Shaky - we hope you are OK.
|
|
@ -1,17 +0,0 @@
|
|||
---
|
||||
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:
|
||||
|
||||

|
15
user_content/about.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# 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!
|
1
www/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
vendor/
|
|
@ -1,34 +1 @@
|
|||
|
||||
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
|
||||
|
||||
<filesMatch ".(flv|gif|ico|jpg|jpeg|mp4|mpeg|png|svg|swf|webp)$">
|
||||
Header set Cache-Control "max-age=315360, public"
|
||||
</filesMatch>
|
||||
|
||||
<filesMatch ".(js)$">
|
||||
Header set Cache-Control "max-age=315360, public"
|
||||
</filesMatch>
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
|
||||
|
||||
<?php
|
||||
|
||||
use Highlight\Highlighter;
|
||||
|
||||
class Dergdown extends ParsedownExtra
|
||||
{
|
||||
protected $highlighter;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->highlighter = new Highlighter();
|
||||
}
|
||||
|
||||
protected function blockFencedCodeComplete($block)
|
||||
{
|
||||
if (! isset($block['element']['text']['attributes'])) {
|
||||
return $block;
|
||||
}
|
||||
|
||||
$code = $block['element']['text']['text'];
|
||||
$languageClass = $block['element']['text']['attributes']['class'];
|
||||
$language = explode('-', $languageClass);
|
||||
|
||||
try {
|
||||
$highlighted = $this->highlighter->highlight($language[1], $code);
|
||||
$block['element']['text']['attributes']['class'] = vsprintf('%s hljs %s', [
|
||||
$languageClass,
|
||||
$highlighted->language,
|
||||
]);
|
||||
$block['element']['text']['rawHtml'] = $highlighted->value;
|
||||
unset($block['element']['text']['text']);
|
||||
} catch (DomainException $e) {
|
||||
}
|
||||
|
||||
return $block;
|
||||
}
|
||||
}
|
42
www/dergstyle.css
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
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;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
$FONT_AWESOME_ARRAY=[
|
||||
'markdown' => '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="20" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M593.8 59.1H46.2C20.7 59.1 0 79.8 0 105.2v301.5c0 25.5 20.7 46.2 46.2 46.2h547.7c25.5 0 46.2-20.7 46.1-46.1V105.2c0-25.4-20.7-46.1-46.2-46.1zM338.5 360.6H277v-120l-61.5 76.9-61.5-76.9v120H92.3V151.4h61.5l61.5 76.9 61.5-76.9h61.5v209.2zm135.3 3.1L381.5 256H443V151.4h61.5V256H566z"/></svg>',
|
||||
'image' => '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M0 96C0 60.7 28.7 32 64 32H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6h96 32H424c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>',
|
||||
'images' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M160 32c-35.3 0-64 28.7-64 64V320c0 35.3 28.7 64 64 64H512c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H160zM396 138.7l96 144c4.9 7.4 5.4 16.8 1.2 24.6S480.9 320 472 320H328 280 200c-9.2 0-17.6-5.3-21.6-13.6s-2.9-18.2 2.9-25.4l64-80c4.6-5.7 11.4-9 18.7-9s14.2 3.3 18.7 9l17.3 21.6 56-84C360.5 132 368 128 376 128s15.5 4 20 10.7zM192 128a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zM48 120c0-13.3-10.7-24-24-24S0 106.7 0 120V344c0 75.1 60.9 136 136 136H456c13.3 0 24-10.7 24-24s-10.7-24-24-24H136c-48.6 0-88-39.4-88-88V120z"/></svg>',
|
||||
'folder' => '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z"/></svg>',
|
||||
'rss' => '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM96 136c0-13.3 10.7-24 24-24c137 0 248 111 248 248c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-110.5-89.5-200-200-200c-13.3 0-24-10.7-24-24zm0 96c0-13.3 10.7-24 24-24c83.9 0 152 68.1 152 152c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-57.4-46.6-104-104-104c-13.3 0-24-10.7-24-24zm0 120a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>'
|
||||
];
|
||||
|
||||
?>
|
11
www/index.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
require_once '../vendor/autoload.php';
|
||||
|
||||
$loader = new \Twig\Loader\FilesystemLoader('../templates');
|
||||
$twig = new \Twig\Environment($loader);
|
||||
|
||||
echo $twig->render('index.html',[
|
||||
"a_variable" => "is very spicy. In a good way."
|
||||
]);
|
||||
?>
|
|
@ -1,431 +0,0 @@
|
|||
<?php
|
||||
|
||||
class MySQLAdapter {
|
||||
public $raw;
|
||||
|
||||
function __construct($SITE_CONFIG) {
|
||||
$this->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<br>';
|
||||
echo 'Error number: ' . mysqli_connect_errno() . '<br>';
|
||||
echo 'Error message: ' . mysqli_connect_error() . '<br>';
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,226 +0,0 @@
|
|||
<?php
|
||||
|
||||
require_once 'mysql_adapter.php';
|
||||
|
||||
use Spatie\YamlFrontMatter\YamlFrontMatter;
|
||||
use Laminas\Feed\Writer\Feed;
|
||||
|
||||
class PostHandler extends MySQLAdapter {
|
||||
public $data_directory;
|
||||
|
||||
function __construct($SITE_CONFIG) {
|
||||
parent::__construct($SITE_CONFIG);
|
||||
|
||||
$this->data_directory = 'raw/' . $this->SITE_CONFIG['HTTP_HOST'];
|
||||
}
|
||||
|
||||
function deduce_post_type($post_path) {
|
||||
$ext = pathinfo($post_path, PATHINFO_EXTENSION);
|
||||
|
||||
if(preg_match("/\.(\w+)\.md$/", $post_path, $ext_match)) {
|
||||
$ext = $ext_match[1];
|
||||
}
|
||||
|
||||
$ext_mapping = [
|
||||
'' => 'directory',
|
||||
'md' => 'text/markdown',
|
||||
'png' => 'image',
|
||||
'jpg' => 'image',
|
||||
'jpeg' => 'image'
|
||||
];
|
||||
|
||||
return $ext_mapping[$ext] ?? '?';
|
||||
}
|
||||
|
||||
function fill_in_post_meta($post_path, $meta) {
|
||||
$icon_mapping = [
|
||||
'' => 'question',
|
||||
'text/markdown' => 'markdown',
|
||||
'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);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,2 +0,0 @@
|
|||
User-agent: *
|
||||
Allow: /
|
268
www/router.php
|
@ -1,268 +0,0 @@
|
|||
<?php
|
||||
|
||||
$data_time_start = microtime(true);
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
require_once 'post_adapter.php';
|
||||
require_once 'fontawesome.php';
|
||||
require_once 'dergdown.php';
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
$SERVER_HOST = $_SERVER['HTTP_HOST'];
|
||||
if(!preg_match('/^[\w\.]+$/', $SERVER_HOST)) {
|
||||
die();
|
||||
}
|
||||
|
||||
$SERVER_PREFIX = "https://" . $SERVER_HOST;
|
||||
|
||||
$SITE_CONFIG = Yaml::parseFile('secrets/' . $SERVER_HOST . '.config.yml');
|
||||
$SITE_CONFIG['uri_prefix'] = $SERVER_PREFIX;
|
||||
$SITE_CONFIG['HTTP_HOST'] = $SERVER_HOST;
|
||||
|
||||
$adapter = new PostHandler($SITE_CONFIG);
|
||||
|
||||
$loader = new \Twig\Loader\FilesystemLoader(['./templates', './user_content']);
|
||||
|
||||
$twig = new \Twig\Environment($loader,[
|
||||
'debug' => true,
|
||||
'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);
|
||||
|
||||
?>
|
2
www/secrets/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
*.json
|
||||
api_admin_key
|
|
@ -1,2 +0,0 @@
|
|||
|
||||
Deny from all
|
|
@ -1,12 +0,0 @@
|
|||
|
||||
|
||||
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"
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
|
||||
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));
|
|
@ -1,273 +0,0 @@
|
|||
|
||||
|
||||
* {
|
||||
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%;
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
.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;
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
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;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
|
||||
|
||||
#rrr_header {
|
||||
font-size: 2rem;
|
||||
|
||||
border-bottom: 1px solid grey;
|
||||
padding-bottom: 0.2rem;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
#rrr_code {
|
||||
font-size: 1.5rem;
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
|
||||
Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull <sourdrums@gmail.com>
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #002b36;
|
||||
color: #839496;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #586e75;
|
||||
}
|
||||
|
||||
/* Solarized Green */
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-addition {
|
||||
color: #859900;
|
||||
}
|
||||
|
||||
/* Solarized Cyan */
|
||||
.hljs-number,
|
||||
.hljs-string,
|
||||
.hljs-meta .hljs-meta-string,
|
||||
.hljs-literal,
|
||||
.hljs-doctag,
|
||||
.hljs-regexp {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
/* Solarized Blue */
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
/* Solarized Yellow */
|
||||
.hljs-attribute,
|
||||
.hljs-attr,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-class .hljs-title,
|
||||
.hljs-type {
|
||||
color: #b58900;
|
||||
}
|
||||
|
||||
/* Solarized Orange */
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-subst,
|
||||
.hljs-meta,
|
||||
.hljs-meta .hljs-keyword,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-link {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
/* Solarized Red */
|
||||
.hljs-built_in,
|
||||
.hljs-deletion {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
.hljs-formula {
|
||||
background: #073642;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
<div id="post_file_bar">
|
||||
<menu id="post_file_titles">
|
||||
<li>
|
||||
{{ post.post_metadata.title }}
|
||||
</li>
|
||||
</menu>
|
||||
<menu id="post_file_path">
|
||||
{% set split_post = post.post_path |split('/') %}
|
||||
{% for i in range(0, split_post|length - 1) %}
|
||||
<li>
|
||||
{% if i != 0 %}
|
||||
<a href="{{split_post|slice(0,i+1)|join('/')}}">
|
||||
> {{ split_post[i] }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="/">root</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
<li class="hsmol_hide" style="margin-left: auto;">
|
||||
<a href="/raw{{post.post_path}}">raw</a>
|
||||
<a href="/api/posts{{post.post_path}}">api</a>
|
||||
</li>
|
||||
<li class="hsmol_hide">
|
||||
<a rel="alternate" type="application/rss+xml" target="_blank"
|
||||
style="padding-left: 0.3rem;" href="/feed/rss{{post.post_path}}">
|
||||
{{ fa['rss']|raw }}
|
||||
</a>
|
||||
</li>
|
||||
</menu>
|
||||
</div>
|
|
@ -1,29 +0,0 @@
|
|||
|
||||
|
||||
{% extends "root.html" %}
|
||||
|
||||
{% block feed_links %}
|
||||
|
||||
{{ parent() }}
|
||||
|
||||
<link rel="alternate" type="application/atom+xml" title="{{og.site_name}} Feed for {{post.post_path}}" href="{{site_config.uri_prefix}}/feed/atom{{post.post_path}}">
|
||||
{% endblock %}
|
||||
|
||||
{% block second_title %}
|
||||
<h2> {{ post.post_metadata.title }} </h2>
|
||||
{% endblock %}
|
||||
|
||||
{%block main_content%}
|
||||
|
||||
{{ include('fragments/filepath_bar.html') }}
|
||||
|
||||
<article>
|
||||
{%block content_article %}
|
||||
{%endblock%}
|
||||
|
||||
<span id="content_footer">
|
||||
This article was created on {{ post.post_create_time }}, last edited {{ post.post_update_time }}, and was viewed {{ post.post_access_count }} times~
|
||||
</span>
|
||||
</article>
|
||||
|
||||
{%endblock%}
|
|
@ -1,43 +0,0 @@
|
|||
|
||||
|
||||
{% extends "pathed_content.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="/static/directorystyle.css">
|
||||
{%endblock%}
|
||||
|
||||
{%block content_article%}
|
||||
{% if subposts|length > 0 %}
|
||||
<h3>Directory contents:</h3>
|
||||
<table class="directory">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="hsmol_hide">Name</th>
|
||||
<th>Title</th>
|
||||
<th class="entry_update_time hsmol_hide">Modified</th>
|
||||
</tr>
|
||||
{% for subpost in subposts %}
|
||||
<tr class="entry">
|
||||
<td>
|
||||
{{ fa[subpost.post_metadata.icon] | raw }}
|
||||
</td>
|
||||
<td class="hsmol_hide">
|
||||
<a href={{subpost.post_path}}>{{subpost.post_basename}}</a>
|
||||
</td>
|
||||
<td class="entry_title">
|
||||
<a href={{subpost.post_path}}>{{ subpost.post_metadata.title }}</a>
|
||||
</td>
|
||||
<td class="entry_update_time hsmol_hide">
|
||||
{{ subpost.post_update_time }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{%else%}
|
||||
<h4>This directory appears to be empty...</h4>
|
||||
<span>This shouldn't happen, sorry!</span>
|
||||
{% endif %}
|
||||
|
||||
{{ post.post_content|markdown_to_html }}
|
||||
|
||||
{%endblock%}
|
|
@ -1,59 +0,0 @@
|
|||
|
||||
|
||||
{% extends "pathed_content.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="/static/directorystyle.css">
|
||||
<link rel="stylesheet" href="/static/gallerystyle.css">
|
||||
{%endblock%}
|
||||
|
||||
{%block content_article%}
|
||||
{% if subposts|length > 0 %}
|
||||
<table class="directory">
|
||||
<h3>Art Subfolders:</h3>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="hsmol_hide">Name</th>
|
||||
<th>Title</th>
|
||||
<th class="entry_update_time hsmol_hide">Modified</th>
|
||||
</tr>
|
||||
{% for subpost in subposts %}
|
||||
<tr class="entry">
|
||||
<td>
|
||||
{{ fa[subpost.post_metadata.icon] | raw }}
|
||||
</td>
|
||||
<td class="hsmol_hide">
|
||||
<a href={{subpost.post_path}}>{{subpost.post_basename}}</a>
|
||||
</td>
|
||||
<td class="entry_title">
|
||||
<a href={{subpost.post_path}}>{{ subpost.post_metadata.title }}</a>
|
||||
</td>
|
||||
<td class="entry_update_time hsmol_hide">
|
||||
{{ subpost.post_update_time }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{{ content_html|raw }}
|
||||
|
||||
{% if gallery_images|length > 0 %}
|
||||
<ul class="gallery">
|
||||
{% for post in gallery_images %}
|
||||
<li class="entry">
|
||||
<a href={{post.post_path}}>
|
||||
<figure>
|
||||
<img src="{{post.post_metadata.media_file}}">
|
||||
<figcaption>
|
||||
{{ post.post_metadata.title }}
|
||||
</figcaption>
|
||||
</figure>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{%else%}
|
||||
<h4>How sad. There are no images yet... What a real shame :c </h4>
|
||||
{% endif %}
|
||||
{%endblock%}
|
|
@ -1,25 +0,0 @@
|
|||
|
||||
|
||||
{% extends "pathed_content.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="/static/imagestyle.css">
|
||||
{%endblock%}
|
||||
|
||||
{% block opengraph_tags %}
|
||||
{{ parent() }}
|
||||
|
||||
<meta property="og:type" content="image" />
|
||||
{%endblock %}
|
||||
|
||||
{%block content_article%}
|
||||
<figure>
|
||||
<a target="_blank" href="{{post.post_metadata.media_file}}">
|
||||
<img id="gallery_image" src="{{post.post_metadata.media_file}}"></img>
|
||||
</a>
|
||||
|
||||
<figcaption>
|
||||
{{ content_html|raw }}
|
||||
</figcaption>
|
||||
</figure>
|
||||
{%endblock%}
|
|
@ -1,13 +0,0 @@
|
|||
|
||||
|
||||
{% extends "pathed_content.html" %}
|
||||
|
||||
{% block opengraph_tags %}
|
||||
{{ parent() }}
|
||||
|
||||
<meta property="og:type" content="article" />
|
||||
{%endblock %}
|
||||
|
||||
{%block content_article%}
|
||||
{{ content_html|raw }}
|
||||
{% endblock %}
|
|
@ -1,19 +0,0 @@
|
|||
|
||||
{% extends "pathed_content.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="/static/rrrorstyle.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block second_title %}
|
||||
<h2> (Broken) </h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content_article %}
|
||||
<h1 id="rrr_header"> The Dergs are confused:</h2>
|
||||
<h2 id="rrr_code"> {{ error_code }}</h1>
|
||||
|
||||
<div>
|
||||
{{ error_description|markdown_to_html }}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,90 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{og.site_name}} - {{og.title}}</title>
|
||||
<link rel="stylesheet" href="/static/dergstyle.css">
|
||||
<link rel="stylesheet" href="/static/modest.css">
|
||||
<link rel="stylesheet" href="/static/solarized-dark.css">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/static/icon.jpeg">
|
||||
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
{% block feed_links %}
|
||||
<link rel="alternate" type="application/rss+xml" title="{{og.site_name}} Global Feed" href="{{site_config.uri_prefix}}/feed">
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_head %}{% endblock %}
|
||||
|
||||
{% block opengraph_tags %}
|
||||
<meta property="og:site_name" content="{{og.site_name}}">
|
||||
|
||||
<meta property="og:url" content="{{og.url}}" />
|
||||
|
||||
<meta property="og:title" content="{{ og.title|e }}" />
|
||||
<meta name="twitter:title" content="{{ og.title|e }}" />
|
||||
|
||||
<meta property="og:type" content="{{og.type}}" />
|
||||
|
||||
<meta property="og:description" content="{{ og.description|e }}" />
|
||||
<meta name="twitter:description" content="{{ og.description|e }}" />
|
||||
|
||||
<meta property="og:image" content="{{og.image}}" />
|
||||
<meta name="twitter:image" content="{{og.image}}" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="robots" content="max-image-preview:large">
|
||||
|
||||
<meta property="al:android:app_name" content="Medium" />
|
||||
{% endblock %}
|
||||
|
||||
<script type="text/javascript">
|
||||
window.dergBannerOptions = JSON.parse('{{banner|raw}}');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
{%if age_gate %}
|
||||
|
||||
<div id="age_gate_block">
|
||||
<div>
|
||||
<p>
|
||||
This website may contain content meant for an 18+ audience.
|
||||
</p>
|
||||
|
||||
<input type="button" onclick="confirmAge()" value="I understand and am over 18.">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/age_gate_check.js"></script>
|
||||
{% endif %}
|
||||
|
||||
<header id="main_header">
|
||||
<img id="main_banner_img"></img>
|
||||
<a id="main_banner_img_link" href="/gallery"> full picture</a>
|
||||
|
||||
<script src="/static/banner.js"></script>
|
||||
|
||||
<h1 id="big_title">{% block big_title %}{{og.site_name}}{%endblock%}</h1>
|
||||
{% block second_title %}{% endblock %}
|
||||
<div id="title_separator"></div>
|
||||
|
||||
<menu id="nav_bar">
|
||||
<li><a href="/about"> About </a></li>
|
||||
<li><a href="/blog"> Blog </a></li>
|
||||
<li><a href="/projects"> Projects </a></li>
|
||||
<li><a href="/artwork"> Artworks </a></li>
|
||||
</menu>
|
||||
</header>
|
||||
|
||||
<main id="main_content_wrapper">
|
||||
{% block main_content %}<h3>Soon there shall be content!</h3>{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer id="main_footer">
|
||||
{% block main_footer %}
|
||||
<span> test? </span>
|
||||
{% endblock %}
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,19 +0,0 @@
|
|||
|
||||
|
||||
{% extends "root.html" %}
|
||||
|
||||
{% block second_title %}
|
||||
<h2> UPLOAD CONTENT </h2>
|
||||
{% endblock %}
|
||||
|
||||
{%block main_content%}
|
||||
<article>
|
||||
<form method="post" enctype="multipart/form-data" action="/api/admin/upload">
|
||||
<input type="text" id="post_path" name="post_path"/>
|
||||
<input type="password" id="api_key" name="api_key"/>
|
||||
<input type="file" id="post_data" name="post_data"/>
|
||||
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
</article>
|
||||
{%endblock%}
|