Rewrite for Kirby

This commit is contained in:
Marcel Kapfer 2024-03-17 22:34:51 +01:00
parent 07201d05de
commit f854d60108
Signed by: mmk2410
GPG key ID: CADE6F0C09F21B09
116 changed files with 4156 additions and 8875 deletions

0
site/accounts/index.html Normal file
View file

View file

@ -0,0 +1,37 @@
title: Blog Article
num: '{{ page.date.toDate("YmdHi") }}'
create:
fields:
- date
columns:
main:
width: 2/3
sections:
fields:
type: fields
fields:
text:
type: textarea
size: huge
sidebar:
width: 1/3
sections:
meta:
type: fields
fields:
date:
type: date
label: Published on
time: true
required: true
categories:
type: tags
labe: Categories
tags:
type: tags
labe: Tags
files:
type: files

View file

@ -0,0 +1,21 @@
title: Blog Page
sections:
fields:
type: fields
fields:
text:
type: textarea
size: small
drafts:
extends: sections/articles
label: Drafts
status: draft
unlisted:
extends: sections/articles
label: In Review
status: unlisted
listed:
extends: sections/articles
label: Published
status: listed

View file

@ -0,0 +1,21 @@
title: Default Page
columns:
main:
width: 2/3
sections:
fields:
type: fields
fields:
text:
type: textarea
size: huge
sidebar:
width: 1/3
sections:
pages:
type: pages
template: default
files:
type: files

View file

@ -0,0 +1,46 @@
title: Home Page
columns:
main:
width: 2/3
sections:
fields:
type: fields
fields:
subtitle:
type: text
text:
type: textarea
size: medium
internal_menu:
type: structure
label: Internal Links
fields:
title:
type: text
label: Title
link:
type: link
label: Link
options:
- page
external_menu:
type: structure
label: External Links
fields:
title:
type: text
label: Title
link:
type: link
label: Link
options:
- url
sidebar:
width: 1/3
sections:
pages:
type: pages
template: default
files:
type: files

View file

@ -0,0 +1,29 @@
title: Blog Article
num: '{{ page.date.toDate("YmdHi") }}'
create:
fields:
- date
columns:
main:
width: 2/3
sections:
fields:
type: fields
fields:
text:
type: textarea
size: medium
sidebar:
width: 1/3
sections:
meta:
type: fields
fields:
date:
type: date
label: Published on
time: true
required: true

View file

@ -0,0 +1,21 @@
title: Quotes Page
sections:
fields:
type: fields
fields:
text:
type: textarea
size: small
quotes:
type: pages
search: true
image: false
template: quote
sortBy: date desc
empty: No quotes yet
layout: table
columns:
date:
label: Published on
width: 1/6

View file

@ -0,0 +1,17 @@
type: pages
search: true
image: false
template: article
sortBy: date desc
empty: No articles yet
layout: table
columns:
date:
label: Published on
width: 1/6
categories:
label: Categories
width: 1/6
tags:
label: Tags
width: 1/6

56
site/blueprints/site.yml Normal file
View file

@ -0,0 +1,56 @@
title: Site
columns:
main:
width: 2/3
sections:
pages:
type: pages
fields:
type: fields
fields:
menu:
type: structure
fields:
title:
type: text
label: Title
link:
type: link
label: Link
options:
- page
- url
sidebar:
width: 1/3
sections:
articles_draft:
extends: sections/articles
label: Draft Articles
status: draft
parent: site.find("blog")
layout: list
limit: 5
articles_review:
extends: sections/articles
label: In Review Articles
status: unlisted
parent: site.find("blog")
layout: list
limit: 5
articles_published:
extends: sections/articles
label: Published Articles
status: listed
parent: site.find("blog")
layout: list
limit: 5
metadata:
type: fields
fields:
author:
type: text
description:
type: text
keywords:
type: tags

0
site/cache/index.html vendored Normal file
View file

37
site/config/config.php Normal file
View file

@ -0,0 +1,37 @@
<?php
return [
'analytics' => [
'goatcounter' => 'stats.mmk2410.org'
],
'routes' => [
[
'pattern' => 'feed',
'action' => fn () => go('/blog.rss')
],
[
'pattern' => 'index.xml',
'action' => fn () => go('/blog.rss')
],
[
'pattern' => '(:num)/(:num)/(:num)/(:any)',
'action' => function ($year, $month, $day, $slug) {
$page = page('blog/' . $slug);
if (!$page) return site()->errorPage();
return site()->visit($page);
}
],
[
'pattern' => 'blog/category/(:any)',
'action' => function($category) {
return page('blog')->render(['category' => $category]);
}
],
[
'pattern' => 'blog/tag/(:any)',
'action' => function($tag) {
return page('blog')->render(['tag' => $tag]);
}
]
]
];

17
site/controllers/blog.php Normal file
View file

@ -0,0 +1,17 @@
<?php
return function ($page, $tag, $category) {
$articles = $page->children()->listed();
if ($tag) {
$articles = $articles->filterBy('tags', $tag, ',');
}
if ($category) {
$articles = $articles->filterBy('categories', $category, ',');
}
return [
'articles' => $articles->flip()->paginate(20)
];
};

View file

@ -0,0 +1,7 @@
<?php
return function ($page) {
return [
'quotes' => $page->children()->listed()->flip()->paginate(20)
];
};

30
site/models/article.php Normal file
View file

@ -0,0 +1,30 @@
<?php
class ArticlePage extends Page
{
public function url($options = null): string
{
$date = $this->date()->toDate('Y/m/d');
return '/' . $date .'/' . $this->slug();
}
public function readingTime() {
$doc = new DOMDocument();
$doc->loadHtml(
"<html><head><meta charset=\"UTF-8\"><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"></head><body>"
. $this->text()->kirbytext()
."</body></html>"
);
$pElems = $doc->getElementsByTagName('p');
$text = '';
foreach ($pElems as $pElem) {
$text .= $pElem->nodeValue . ' ';
}
$wordCount = count(explode(' ', $text));
$readingTime = (int)ceil($wordCount / 150);
return $wordCount . ' words, ~' . $readingTime . 'min reading time';
}
}

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-PRESENT Johann Schopplich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,135 @@
# Kirby Highlighter
Server-side code highlighting available as [custom block](https://getkirby.com/docs/reference/panel/fields/blocks) and for [KirbyText](https://getkirby.com/docs/guide/content/text-formatting#kirbytext).
Built upon [highlight.php](http://www.highlightjs.org) which itself is a port of [highlight.js](http://www.highlightjs.org).
## Key Features
- 🏗 Works with Kirby's [`code` block](https://getkirby.com/docs/reference/panel/blocks/code)
- 🏳️‍🌈 Supports 189 languages
- 💫 94 styles available
- ⛳️ Automatic language detection for KirbyText
## Requirements
- Kirby 3.8+
## Installation
### Composer
```
composer require johannschopplich/kirby-highlighter
```
### Download
Download and copy this repository to `/site/plugins/kirby-highlighter`.
## Usage
### With Kirby Blocks Field
This plugin overwrites Kirby's internal [`code` block](https://getkirby.com/docs/reference/panel/blocks/code). Thus, you won't have to change a thing.
Use the `code` block just like before, the output will be highlighted automatically:
```yaml
fields:
example:
label: Paste code here
type: blocks
fieldsets:
- code
```
### Within KirbyText
Create a code block in your KirbyText field and optionally set the code language:
<pre lang="no-highlight"><code>```css
.currentColor {
color: currentColor;
}
```
</code></pre>
Or use the new `code`-KirbyTag from this plugin with a base64 encoded code string:
```
(code: LmN1cnJlbnRDb2xvciB7CiAgY29sb3I6IGN1cnJlbnRDb2xvcjsKfQ== lang: css)
```
Which outputs:
```html
<pre class="hljs"><code><span class="hljs-selector-class">.currentColor</span> {
<span class="hljs-attribute">color</span>: currentColor;
}</code></pre>
```
The syntax highlighting functionality can be changed. You can choose between two highlighting modes:
1. Explicit mode (default)
2. Automatic language detection mode (opt-in)
#### Explicit Mode
In explicit mode, you have to define which language the code block is. Otherwise highlighting will be skipped.
#### Automatic Language Detection
Alternatively you can use the automatic detection mode, which highlights your code with the language the library thinks is best. It is highly recommended you explicitly choose the language or limit the number of languages to automatically detect from. This reduces the number of inaccuracies and skips this extremely inefficient selection process.
To enable automatic language detection, set:
- `johannschopplich.highlighter.autodetect` to `true`
- `johannschopplich.highlighter.languages` to an array of names from which languages should be chosen
## Options
| Option |  Default | Description |
| --------------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `johannschopplich.highlighter.class` | `hljs` | Style class for Highlight to be added to the `pre` element. |
| `johannschopplich.highlighter.autodetect` | `false` | Indicates if the library should define which language thinks is best. Only applies when no language was set on the KirbyText code block. |
| `johannschopplich.highlighter.languages` | `[]` | Array of language names to be auto-detected. If empty, every language will be auto-detectable. |
| `johannschopplich.highlighter.line-numbering` | `false` | Indicates if the library should split up the highlighted code on each new line and wrap it in a `<span>` element. |
| `johannschopplich.highlighter.line-numbering-class` | `hljs-code-line` | CSS class applied to highlighted code lines, respectively `<span>` elements. |
## Styling in the Frontend
Since this plugin handles highlighting code only and thus just wraps span's around code, you have to link styles in your frontend yourself. I recommend choosing one of the available themes directly from the highlight.js project: [highlight.js/src/styles/](https://github.com/highlightjs/highlight.js/tree/master/src/styles)
The CSS files over at the repository are maintained and new ones arrive from time to time, therefore it would be redundant to include a copy in this repository.
One of my favorite themes is [Night Owl by Sarah Drasner](https://github.com/highlightjs/highlight.js/blob/master/src/styles/night-owl.css).
For example you could download the CSS file and save it in your Kirby project under `assets/css/hljs-night-owl.css`. Now you just have to include it in your template `<?= css('assets/css/hljs-night-owl.css') ?>`. Alternatively, use a CSS bundler of your choice.
### Line Numbering
If you choose to activate the line numbering option, you will need to include additional CSS style to display line numbering.
A basic example using [pseudo-elements](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements):
```css
pre.hljs .hljs-code-line {
counter-increment: line;
}
pre.hljs .hljs-code-line::before {
content: counter(line);
display: inline-block;
margin-right: 1em;
opacity: 0.5;
}
```
## Credits
- Geert Bergman and contributors for the awesome [highlight.php](https://github.com/scrivo/highlight.php) port.
- Martin Folkers for his [Kirby Highlight](https://github.com/S1SYPHOS/kirby3-highlight) plugin which built the base of this package.
## License
[MIT](./LICENSE) License © 2020-PRESENT [Johann Schopplich](https://github.com/johannschopplich)

View file

@ -0,0 +1,74 @@
<?php
namespace JohannSchopplich;
use DOMDocument;
use DOMNode;
class HTML5DOMDocument extends DOMDocument
{
/**
* Name of temporary root element for the XML parser
*/
protected string $tempRoot = 'main';
/**
* Create a new HTML5-compatible document parser
*/
public function __construct(string $version = '1.0', string $encoding = 'UTF-8')
{
// Silence libxml errors with HTML5 elements
libxml_use_internal_errors(true);
// Call parent class
parent::__construct($version, $encoding);
}
/**
* Load HTML from string, make UTF-8 compatible and add temporary root element
*/
public function loadHTML(string $source, int $options = LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD): bool
{
// `loadHTML` will treat the string as being in ISO-8859-1 unless
// told otherwise, so translate anything above the ASCII range into
// its html entity equivalent
// @see https://stackoverflow.com/questions/39148170/utf-8-with-php-domdocument-loadhtml
$convertedSource = htmlspecialchars_decode(htmlentities($source, ENT_COMPAT, 'UTF-8'), ENT_QUOTES);
// Add fake root element for XML parser because it assumes that the
// first encountered tag is the root element
// @see https://stackoverflow.com/questions/39479994/php-domdocument-savehtml-breaks-format
return parent::loadHTML("<{$this->tempRoot}>" . $convertedSource . "</{$this->tempRoot}>", $options);
}
/**
* Strip the temporarily added root element
*/
private function unwrapTempRoot(string $output): string
{
if ($this->firstChild->nodeName === $this->tempRoot) {
return substr($output, strlen($this->tempRoot) + 2, -strlen($this->tempRoot) - 4);
}
return $output;
}
/**
* Dump the internal document into a HTML string
*/
#[\ReturnTypeWillChange]
public function saveHTML(DOMNode|null $node = null, bool $entities = false): string|false
{
$html = parent::saveHTML($node);
if ($entities === false) {
$html = html_entity_decode($html);
}
if ($node === null) {
$html = $this->unwrapTempRoot($html);
}
return $html;
}
}

View file

@ -0,0 +1,49 @@
{
"name": "johannschopplich/kirby-highlighter",
"description": "Server-side syntax highlighting for Kirby CMS",
"type": "kirby-plugin",
"version": "3.1.0",
"keywords": [
"kirby",
"highlight",
"highlighter",
"hljs"
],
"license": "MIT",
"homepage": "https://github.com/johannschopplich/kirby-highlighter#readme",
"authors": [
{
"name": "Johann Schopplich",
"email": "pkg@johannschopplich.com",
"homepage": "https://johannschopplich.com"
}
],
"require": {
"getkirby/composer-installer": "^1.2",
"scrivo/highlight.php": "^9.18"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "@stable",
"getkirby/cms": "^4",
"phpunit/phpunit": "^9.0"
},
"autoload": {
"psr-4": {
"JohannSchopplich\\": "classes/JohannSchopplich/"
}
},
"scripts": {
"fix": "php-cs-fixer fix",
"dist": "composer install --no-dev --optimize-autoloader",
"test": "phpunit"
},
"config": {
"optimize-autoloader": true,
"allow-plugins": {
"getkirby/composer-installer": true
}
},
"extra": {
"kirby-cms-path": "tests/fixtures/kirby"
}
}

View file

@ -0,0 +1,23 @@
<?php
if (!function_exists('is_base64_string_s')) {
// https://stackoverflow.com/a/51877882
function is_base64_string_s(string $str, $enc = ['UTF-8', 'ASCII'])
{
return !(($b = base64_decode($str, true)) === false) && in_array(mb_detect_encoding($b), $enc);
}
}
return [
'fromBase64' => function ($field) {
if ($field->isNotEmpty()) {
$value = trim((string)$field->value());
if (is_base64_string_s($value)) {
$field->value = base64_decode($value);
}
}
return $field;
},
];

View file

@ -0,0 +1,86 @@
<?php
use Highlight\Highlighter;
use JohannSchopplich\HTML5DOMDocument;
use Kirby\Cms\App;
return [
'kirbytext:after' => function (string|null $text) {
$kirby = App::instance();
// Parse KirbyText input as HTML document
$dom = new HTML5DOMDocument();
$dom->loadHTML($text);
// Retrieve all `pre` elements inside newly created HTML document
$preNodes = $dom->getElementsByTagName('pre');
// Bail if no `pre` elements have been found
if ($preNodes->length === 0) {
return $text;
}
// Loop through all `pre` elements
foreach ($preNodes as $preNode) {
// Ensure nothing but the `code` element exists
if ($preNode->childNodes->length !== 1) {
continue;
}
// Select direct `code` child element of `pre` block
$codeNode = $preNode->firstChild;
// Get language code if present
$language = $codeNode->getAttribute('class');
if (str_starts_with($language, 'language-')) {
$language = preg_replace('/^language-/', '', $language);
}
// Bail highlighting if language isn't set and auto detection is disabled
if (empty($language) && !$kirby->option('johannschopplich.highlighter.autodetect', false)) {
continue;
}
// Add `hljs` class to `pre` block
$preNode->setAttribute('class', $kirby->option('johannschopplich.highlighter.class', 'hljs'));
// Get raw code data to highlight
$code = $codeNode->nodeValue;
// Remove code element afterwards
$preNode->removeChild($codeNode);
// Initiate `Highlighter` and use pre-defined language code, fall
// back to language auto detection if enabled
$highlighter = new Highlighter();
// Highlight code
if (!empty($language)) {
$highlightedCode = $highlighter->highlight($language, $code);
} elseif ($kirby->option('johannschopplich.highlighter.autodetect', false)) {
$languageSubset = $kirby->option('johannschopplich.highlighter.languages', []);
if (!empty($languageSubset)) {
$highlighter->setAutodetectLanguages($languageSubset);
}
$highlightedCode = $highlighter->highlightAuto($code);
}
// Line numbering
if ($kirby->option('johannschopplich.highlighter.line-numbering', false)) {
$lines = preg_split('/\R/', $highlightedCode->value);
$lineClass = $kirby->option('johannschopplich.highlighter.line-numbering-class', 'hljs-code-line');
$highlightedCode->value = '<span class="' . $lineClass . '">' . implode("</span>\n<span class=\"$lineClass\">", $lines) . '</span>';
}
// Append highlighted wrapped in `code` block to parent `pre`
$codeNode = $dom->createDocumentFragment();
$codeNode->appendXML('<code data-language="' . $language . '">' . $highlightedCode->value . '</code>');
$preNode->appendChild($codeNode);
}
// Save all changes
$text = $dom->saveHTML(null, true);
return $text;
}
];

View file

@ -0,0 +1,27 @@
<?php
use Kirby\Cms\App;
return [
'code' => [
'attr' => [
'lang',
'language',
],
// TODO: Type as `\Kirby\Text\KirbyTag` for Kirby 4
'html' => function ($tag) {
$kirby = App::instance();
$code = $tag->value;
$language = $tag->lang ?? $tag->language;
$block = new \Kirby\Cms\Block([
'type' => 'code',
'content' => [
'language' => $language ?? 'plaintext',
'code' => is_base64_string_s($code) ? base64_decode($code) : $code,
]
]);
return $kirby->snippet('blocks/code', ['block' => $block], true);
}
]
];

View file

@ -0,0 +1,12 @@
<?php
@include_once __DIR__ . '/vendor/autoload.php';
\Kirby\Cms\App::plugin('johannschopplich/highlighter', [
'hooks' => require __DIR__ . '/extensions/hooks.php',
'fieldMethods' => require __DIR__ . '/extensions/fieldmethods.php',
'tags' => require __DIR__ . '/extensions/kirbytags.php',
'snippets' => [
'blocks/code' => __DIR__ . '/snippets/blocks/code.php'
]
]);

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
bootstrap="vendor/autoload.php"
colors="true"
verbose="true"
>
<testsuites>
<testsuite name="classes">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
</phpunit>

View file

@ -0,0 +1,22 @@
<?php
/** @var \Kirby\Cms\Block $block */
$highlighter = new \Highlight\Highlighter();
$language = $block->language()->value();
$code = $block->code()->fromBase64()->value();
if (empty($language) || !in_array($language, $highlighter->listRegisteredLanguages())) {
$language = 'plaintext';
}
$highlightedCode = $highlighter->highlight($language, $code)->value;
// Handle line numbering
if (option('johannschopplich.highlighter.line-numbering', false)) {
$lines = preg_split('/\R/', $highlightedCode);
$lineClass = option('johannschopplich.highlighter.line-numbering-class', 'hljs-code-line');
$highlightedCode = '<span class="' . $lineClass . '">' . implode("</span>\n<span class=\"$lineClass\">", $lines) . '</span>';
}
?>
<pre class="<?= option('johannschopplich.highlighter.class', 'hljs') ?>"><code data-language="<?= $language ?>"><?= $highlightedCode ?></code></pre>

0
site/sessions/index.html Normal file
View file

28
site/snippets/article.php Normal file
View file

@ -0,0 +1,28 @@
<article>
<h2><a href="<?= $article->url() ?>"><?= $article->title() ?></a></h2>
<p id="date"><?= $article->date()->toDate('Y-m-d') ?></p>
<p>
<div class="tagories">
<?php if ($article->categories()->isNotEmpty()): ?>
<span id="categories">
<?php foreach ($article->categories()->split() as $category): ?>
<a href="/blog/category/<?= $category ?>"><?= $category ?></a>
<?php endforeach ?>
</span>
<?php endif ?>
<?php if ($article->tags()->isNotEmpty()): ?>
<span id="tags">
<?php foreach ($article->tags()->split() as $tag): ?>
<a href="/blog/tag/<?= $tag ?>"><?= $tag ?></a>
<?php endforeach ?>
</span>
<?php endif ?>
</div>
</p>
<p>
<?= $article->text()->excerpt(300) ?>
</p>
<p><a href="<?= $article->url() ?>">Read more</a></p>
</article>

0
site/snippets/index.html Normal file
View file

54
site/snippets/layout.php Normal file
View file

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title>
<?= $site->title() ?>
<?php if (!$page->isHomePage()): ?>
- <?= $page->title() ?>
<?php endif ?>
</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="author" content="<?= $site->author() ?>" />
<meta name="description" content="<?= $site->description() ?>" />
<meta name="keywords" content="<?= $site->keywords() ?>" />
<?= css('assets/build/main.css') ?>
<link rel="alternate" type="application/rss+xml" href="<?= url('feed') ?>" title="<?= $site->title() ?>" />
<link rel="apple-touch-icon-precomposed" sizes="256x256" href="/assets/img/favicon/favicon-256.png">
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/assets/img/favicon/favicon-144.png">
<link rel="apple-touch-icon-precomposed" sizes="128x128" href="/assets/img/favicon/favicon-128.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/assets/img/favicon/favicon-72.png">
<link rel="apple-touch-icon-precomposed" href="/assets/img/favicon/favicon.png">
<link rel="shortcut icon" href="/assets/img/favicon/favicon.png">
<script data-goatcounter="https://<?= option('analytics.goatcounter') ?>/count" async src="https://<?= option('analytics.goatcounter') ?>/count.js"></script>
</head>
<body>
<header>
<a id="title" href="/"><?= $site->title() ?></a>
<label for="show-menu" class="show-menu">Menu</label>
<input type="checkbox" id="show-menu" role="button">
<nav>
<ul>
<?php foreach ($site->menu()->toStructure() as $item): ?>
<li>
<a href="<?= $item->link()->toUrl() ?>"><?= $item->title() ?></a>
</li>
<?php endforeach ?>
</ul>
</nav>
</header>
<main>
<?= $slot ?>
</main>
<footer>
<p><?= date('Y') ?> © <?= $site->author() ?></p>
<p>
<a href="<?= page('imprint')->url() ?>">Impressum und Datenschutz</a>
</p>
</footer>
</body>
</html>

16
site/templates/about.php Normal file
View file

@ -0,0 +1,16 @@
<?php snippet('layout', slots: true) ?>
<?php slot() ?>
<h1><?= $page->title() ?></h1>
<div style="text-align: center; margin: 20px 0;">
<img src="<?= $page->image('profile.png')->url() ?>" style="width: 300px" />
<div style="font-size: 40px">
<strong><?= $page->intro_title() ?></strong>
</div>
<div style="font-size: 25px">
<?= $page->intro_text() ?>
</div>
</div>
<?= $page->text()->kirbytext() ?>
<?php endslot() ?>

View file

@ -0,0 +1,33 @@
<?php snippet('layout', slots: true) ?>
<?php slot() ?>
<h1><?= $page->title() ?></h1>
<p id="date"><?= $page->date()->toDate('Y-m-d') ?></p>
<p id="readingtime"><?= $page->readingTime() ?></p>
<p>
<div class="tagories">
<?php if ($page->categories()->isNotEmpty()): ?>
<span id="categories">
<?php foreach ($page->categories()->split() as $category): ?>
<a href="/blog/category/<?= $category ?>"><?= $category ?></a>
<?php endforeach ?>
</span>
<?php endif ?>
<?php if ($page->tags()->isNotEmpty()): ?>
<span id="tags">
<?php foreach ($page->tags()->split() as $tag): ?>
<a href="/blog/tag/<?= $tag ?>"><?= $tag ?></a>
<?php endforeach ?>
</span>
<?php endif ?>
</div>
</p>
<?= $page->text()->kirbytext() ?>
<div class="comment">
<p>I would like to hear what you think about this post. Feel free to write me a mail!</p>
<a class="btn" href="mailto:comment@mmk2410.org?subject=Reply to: <?= $page->title()?>">Reply by mail</a>
</div>
<?php endslot() ?>

27
site/templates/blog.php Normal file
View file

@ -0,0 +1,27 @@
<?php snippet('layout', slots: true) ?>
<?php slot() ?>
<h1><?= $page->title() ?></h1>
<?= $page->text()->kirbytext() ?>
<?php foreach($articles as $article): ?>
<?php snippet('article', ['article' => $article]) ?>
<?php endforeach ?>
<?php if ($articles->pagination()->hasPages()): ?>
<nav class="pagination">
<?php if ($articles->pagination()->hasNextPage()): ?>
<a class="page-item next" href="<?= $articles->pagination()->nextPageURL() ?>">
older posts
</a>
<?php endif ?>
<?php if ($articles->pagination()->hasPrevPage()): ?>
<a class="page-item prev" href="<?= $articles->pagination()->prevPageURL() ?>">
newer posts
</a>
<?php endif ?>
</nav>
<?php endif ?>
<?php endslot() ?>

View file

@ -0,0 +1,31 @@
<?php
$feed = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"></rss>');
$feed->channel->title = $site->title()->toString();
$feed->channel->description = $site->description()->toString();
$feed->channel->link = url();
$feed->channel->language = 'en-us';
$feed->channel->lastBuildDate = date(DATE_RSS);
$feed->channel->generator = 'Kirby';
$atomLink = $feed->channel->addChild('atom:link', null, 'atom');
$atomLink->addAttribute('href', url('/feed'));
$atomLink->addAttribute('rel', 'self');
$atomLink->addAttribute('type', 'application/rss+xml');
$articles = $page->children()->template('article')->sortBy('date', 'desc')->limit(10);
foreach ($articles as $article) {
$xmlArticle = $feed->channel->addChild('item');
$xmlArticle->title = $article->title()->toString();
$xmlArticle->link = url($article->url());
$xmlArticle->description = Escape::xml($article->text()->kirbytext());
$xmlArticle->pubDate = $article->date()->toDate(DATE_RSS);
$xmlArticle->guid = url($article->url());
}
$kirby->response()->type('application/rss+xml');
echo $feed->asXML();

View file

@ -0,0 +1,6 @@
<?php snippet('layout', slots: true) ?>
<?php slot() ?>
<h1><?= $page->title() ?></h1>
<?= $page->text()->kirbytext() ?>
<?php endslot() ?>

29
site/templates/home.php Normal file
View file

@ -0,0 +1,29 @@
<?php snippet('layout', slots: true) ?>
<?php slot() ?>
<header>
<h1><?= $site->title() ?></h1>
<h3><?= $page->subtitle() ?></h3>
</header>
<?= $page->text()->kirbytext() ?>
<h4>Read more from me</h4>
<?php foreach ($page->internal_menu()->toStructure() as $item): ?>
<a class="btn" href="<?= $item->link()->toUrl() ?>"><?= $item->title() ?></a>
<?php endforeach ?>
<h4>Find me on other places</h4>
<?php foreach ($page->external_menu()->toStructure() as $item): ?>
<a class="btn" href="<?= $item->link()->toUrl() ?>"><?= $item->title() ?></a>
<?php endforeach ?>
<h2>Latest Posts</h2>
<?php foreach(page('blog')->children()->listed()->flip()->limit(3) as $article): ?>
<?php snippet('article', ['article' => $article]) ?>
<?php endforeach ?>
<a class="btn" href="<?= page('blog')->url() ?>">Read more posts</a>
<?php endslot() ?>

34
site/templates/quotes.php Normal file
View file

@ -0,0 +1,34 @@
<?php snippet('layout', slots: true) ?>
<?php slot() ?>
<h1><?= $page->title() ?></h1>
<?= $page->text()->kirbytext() ?>
<?php foreach($quotes as $quote): ?>
<article>
<h2><a href="<?= $quote->url() ?>"><?= $quote->title() ?></a></h2>
<p id="date"><?= $quote->date()->toDate('Y-m-d') ?></p>
<p>
<?= $quote->text()->kirbytext() ?>
</p>
</article>
<?php endforeach ?>
<?php if ($quotes->pagination()->hasPages()): ?>
<nav class="pagination">
<?php if ($quotes->pagination()->hasNextPage()): ?>
<a class="page-item next" href="<?= $quotes->pagination()->nextPageURL() ?>">
ältere Einträge
</a>
<?php endif ?>
<?php if ($quotes->pagination()->hasPrevPage()): ?>
<a class="page-item prev" href="<?= $quotes->pagination()->prevPageURL() ?>">
neuere Einträge
</a>
<?php endif ?>
</nav>
<?php endif ?>
<?php endslot() ?>