parent
1463dca012
commit
a457f355d1
@ -0,0 +1,17 @@
|
|||||||
|
FROM python:3
|
||||||
|
|
||||||
|
MAINTAINER tiyn tiyn@mail-mk.eu
|
||||||
|
|
||||||
|
COPY src /blog
|
||||||
|
|
||||||
|
WORKDIR /blog
|
||||||
|
|
||||||
|
RUN pip3 install -r requirements.txt
|
||||||
|
|
||||||
|
VOLUME /blog/templates/entry
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
ENTRYPOINT [ "python3" ]
|
||||||
|
|
||||||
|
CMD [ "app.py" ]
|
@ -0,0 +1,68 @@
|
|||||||
|
# Container Critique
|
||||||
|
|
||||||
|
This is a blog based on Pythons Flask framework.
|
||||||
|
The blog is intended to be used to review and critique things.
|
||||||
|
|
||||||
|
## Features/To-Dos
|
||||||
|
|
||||||
|
- [ ] Plain text support for blog entries
|
||||||
|
- [ ] HTML files (.html)
|
||||||
|
- [ ] Markdown Files (.md)
|
||||||
|
- [ ] Infinite-scroll blog page
|
||||||
|
- [ ] Archive page
|
||||||
|
- [ ] Months as headings
|
||||||
|
- [ ] Links to scrolling blog page
|
||||||
|
- [ ] Links to standalone article
|
||||||
|
- [ ] Standalone article page
|
||||||
|
- [ ] Links to scrolling blog page
|
||||||
|
- [ ] RSS feed
|
||||||
|
- [ ] Navigation
|
||||||
|
- [ ] Header
|
||||||
|
- [ ] Footer
|
||||||
|
- [ ] Switchable CSS
|
||||||
|
- [ ] CSS dark-theme
|
||||||
|
- [ ] CSS light-theme
|
||||||
|
- [ ] Config file
|
||||||
|
- [ ] Docker installation
|
||||||
|
- [ ] Logo
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### PIP/Python
|
||||||
|
|
||||||
|
- `git clone https://github.com/tiyn/container-critique`
|
||||||
|
- `cd container-critique/src`
|
||||||
|
- edit the `config.py` file according to your needs
|
||||||
|
- `pip3install -r requirements.txt` - install depenencies
|
||||||
|
- run `python app.py`
|
||||||
|
- blog is available on port 5000
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
Make sure you copy an example `config.py` and edit it before running the container.
|
||||||
|
The `config.py` can be found in the `src` folder.
|
||||||
|
|
||||||
|
#### Volumes
|
||||||
|
|
||||||
|
Set the following volumes with the -v tag.
|
||||||
|
|
||||||
|
| Volume-Name | Container mount | Description |
|
||||||
|
| ------------- | --------------------------- | ------------------------------------------------------------ |
|
||||||
|
| `config-file` | `/blog/src/config.py` | Config file |
|
||||||
|
| `entries` | `/blog/src/templates/entry` | Directory for blog entries |
|
||||||
|
| `css` | `/blog/src/static/css` | (optional) Directory for css files |
|
||||||
|
| `html` | `/blog/src/templates` | (optional) Directory for templates (entry-volume not needed) |
|
||||||
|
|
||||||
|
#### Ports
|
||||||
|
|
||||||
|
Set the following ports with the -p tag.
|
||||||
|
|
||||||
|
| Container-Port | Recommended outside port | Protocol | Description |
|
||||||
|
| -------------- | ------------------------ | -------- | ----------- |
|
||||||
|
| `5000` | `80` | TCP | HTTP port |
|
||||||
|
|
||||||
|
#### Example run-command
|
||||||
|
|
||||||
|
An example run command is shown in `rebuild.sh`.
|
After Width: | Height: | Size: 3.6 KiB |
@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
docker stop container-critique
|
||||||
|
docker rm container-critique
|
||||||
|
docker build . -t tiyn/container-critique
|
||||||
|
docker run --name container-critique \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p "5000:5000" \
|
||||||
|
-e FLASK_ENV=development \
|
||||||
|
-d tiyn/container-critique
|
@ -0,0 +1 @@
|
|||||||
|
__pycache__/
|
@ -0,0 +1,90 @@
|
|||||||
|
from flask import Flask, flash, make_response, render_template, request, redirect, abort, url_for
|
||||||
|
from flask_login import current_user, login_user, LoginManager
|
||||||
|
|
||||||
|
import content as con_gen
|
||||||
|
import config
|
||||||
|
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, PasswordField, SubmitField, BooleanField
|
||||||
|
from wtforms.validators import DataRequired
|
||||||
|
|
||||||
|
class LoginForm(FlaskForm):
|
||||||
|
username = StringField('Username', validators=[DataRequired()])
|
||||||
|
password = PasswordField('Password', validators=[DataRequired()])
|
||||||
|
remember_me = BooleanField('Remember Me')
|
||||||
|
submit = SubmitField('Sign In')
|
||||||
|
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
login = LoginManager(app)
|
||||||
|
|
||||||
|
TITLE = config.TITLE
|
||||||
|
STYLE = config.STYLE
|
||||||
|
DESCRIPTION = config.DESCRIPTION
|
||||||
|
WEBSITE = config.WEBSITE
|
||||||
|
|
||||||
|
|
||||||
|
class Config(object):
|
||||||
|
SECRET_KEY = '123534'
|
||||||
|
|
||||||
|
app.config.from_object(Config)
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(404)
|
||||||
|
def page_not_found(e):
|
||||||
|
return render_template('error.html', title=TITLE, errorcode='404', style=STYLE), 404
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
@app.route('/index.html')
|
||||||
|
def index():
|
||||||
|
content = con_gen.gen_index_string()
|
||||||
|
return render_template('index.html', title=TITLE, content_string=content, style=STYLE)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/archive')
|
||||||
|
@app.route('/archive.html')
|
||||||
|
def blog_archive():
|
||||||
|
content = con_gen.gen_arch_string()
|
||||||
|
return render_template('archive.html', title=TITLE, content_string=content, style=STYLE)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/entry/<path>')
|
||||||
|
def entry(path):
|
||||||
|
content = con_gen.gen_stand_string(path)
|
||||||
|
if content != '':
|
||||||
|
return render_template('standalone.html', title=TITLE, content_string=content, style=STYLE)
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/feed.xml')
|
||||||
|
@app.route('/rss.xml')
|
||||||
|
def feed():
|
||||||
|
content = con_gen.get_rss_string()
|
||||||
|
rss_xml = render_template('rss.xml', content_string=content, title=TITLE,
|
||||||
|
description=DESCRIPTION, website=WEBSITE)
|
||||||
|
response = make_response(rss_xml)
|
||||||
|
response.headers['Content-Type'] = 'application/rss+xml'
|
||||||
|
return response
|
||||||
|
|
||||||
|
@login.user_loader
|
||||||
|
def load_user(id):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
|
def login():
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
form = LoginForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
user = User.query.filter_by(username=form.username.data).first()
|
||||||
|
if user is None or not user.check_password(form.password.data):
|
||||||
|
flash('Invalid username or password')
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
login_user(user, remember=form.remember_me.data)
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
return render_template('login.html', title='Sign In', form=form)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0')
|
@ -0,0 +1,11 @@
|
|||||||
|
# Name/title of your blog
|
||||||
|
TITLE = 'Container Critique'
|
||||||
|
|
||||||
|
# Description for RSS of your blog
|
||||||
|
DESCRIPTION = 'This is your personal Container Critique.'
|
||||||
|
|
||||||
|
# URL for your website: e.g. https://domain.tld
|
||||||
|
WEBSITE = 'localhost:5000'
|
||||||
|
|
||||||
|
# Theme for the blog: dark, light
|
||||||
|
STYLE = 'dark'
|
@ -0,0 +1,45 @@
|
|||||||
|
ENTRY_DIR = 'templates/entry'
|
||||||
|
|
||||||
|
def gen_arch_string():
|
||||||
|
"""
|
||||||
|
Creates and returns a archive string of every file in ENTRY_DIR.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: html-formatted archive-string
|
||||||
|
"""
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def gen_index_string():
|
||||||
|
"""
|
||||||
|
Create and returns a string including every file in the ENTRY_DIR as an index.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: html-formatted index string
|
||||||
|
"""
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def gen_stand_string(path_ex):
|
||||||
|
"""
|
||||||
|
Creates a html-string for a file.
|
||||||
|
If the file is markdown it will convert it.
|
||||||
|
This functions ensures upscaling for future formats.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
path_ex: path to a file.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: html-formatted string string equivalent to the file
|
||||||
|
"""
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def get_rss_string():
|
||||||
|
"""
|
||||||
|
Create a rss-string of the blog and return it.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: rss-string of everything that is in the ENTRY_DIR.
|
||||||
|
"""
|
||||||
|
return ""
|
@ -0,0 +1,4 @@
|
|||||||
|
Flask==2.1.2
|
||||||
|
flask_login==0.6.2
|
||||||
|
flask_wtf==1.0.1
|
||||||
|
WTForms==3.0.1
|
@ -0,0 +1,83 @@
|
|||||||
|
@import 'style.css';
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bg0: rgb(29,32,33);
|
||||||
|
--color0: rgb(220,120,0);
|
||||||
|
--error: rgb(255,0,0);
|
||||||
|
--footerbg0: rgb(29,32,33);
|
||||||
|
--link0: rgb(220, 120, 0);
|
||||||
|
--link1: rgb(255,255,255);
|
||||||
|
--menulink0: rgb(220, 120, 0);
|
||||||
|
--menulink1: rgb(255,255,255);
|
||||||
|
--menubg0: rgb(29,32,33);
|
||||||
|
--text0: rgb(235,219,178);
|
||||||
|
--text1: rgb(220, 120, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--link0);
|
||||||
|
transition: var(--transtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--link1);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--bg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background: var(--footerbg0);
|
||||||
|
color: var(--text0);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--text1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
color: var(--text0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container h1,
|
||||||
|
.container h2 {
|
||||||
|
color: var(--text1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container .flash {
|
||||||
|
background-color: var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-menu:hover,
|
||||||
|
.main-menu a:hover,
|
||||||
|
.show-menu:hover {
|
||||||
|
color: var(--menulink1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu a {
|
||||||
|
color: var(--menulink0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu-dropdown {
|
||||||
|
background: var(--menubg0);
|
||||||
|
color: var(--menulink0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width:800px) {
|
||||||
|
|
||||||
|
.main-menu {
|
||||||
|
background: var(--menubg0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry {
|
||||||
|
background: var(--bg0);
|
||||||
|
border-left: 10px solid var(--color0);
|
||||||
|
color: var(--text0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry h1,
|
||||||
|
.entry h2 {
|
||||||
|
color: var(--text1);
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
@import 'style.css';
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bg0: rgb(255,255,255);
|
||||||
|
--color0: rgb(0,0,120);
|
||||||
|
--error: rgb(255,0,0);
|
||||||
|
--footerbg0: rgb(192,192,192);
|
||||||
|
--link0: rgb(0,0,120);
|
||||||
|
--link1: rgb(255,255,255);
|
||||||
|
--menulink0: rgb(0,0,120);
|
||||||
|
--menulink1: rgb(255,255,255);
|
||||||
|
--menubg0: rgb(192,192,192);
|
||||||
|
--text0: rgb(0,0,0);
|
||||||
|
--text1: rgb(0,0,120);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--link0);
|
||||||
|
transition: var(--transtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--link1);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--bg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background: var(--footerbg0);
|
||||||
|
color: var(--text0);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--text1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
color: var(--text0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container h1,
|
||||||
|
.container h2 {
|
||||||
|
color: var(--text1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container .flash {
|
||||||
|
background-color: var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-menu:hover,
|
||||||
|
.main-menu a:hover,
|
||||||
|
.show-menu:hover {
|
||||||
|
color: var(--menulink1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu a {
|
||||||
|
color: var(--menulink0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu-dropdown {
|
||||||
|
background: var(--menubg0);
|
||||||
|
color: var(--menulink0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width:800px) {
|
||||||
|
|
||||||
|
.main-menu {
|
||||||
|
background: var(--menubg0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry {
|
||||||
|
background: var(--bg0);
|
||||||
|
border-left: 10px solid var(--color0);
|
||||||
|
color: var(--text0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry h1,
|
||||||
|
.entry h2 {
|
||||||
|
color: var(--text1);
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
:root {
|
||||||
|
--error: rgb(255,0,0);
|
||||||
|
--transtime: 0.7s;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
transition: var(--transtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
font-family: sans-serif;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
height: 100px;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
min-height: 100%;
|
||||||
|
padding-bottom: 50px;
|
||||||
|
padding-left: 10%;
|
||||||
|
padding-right: 10%;
|
||||||
|
padding-top: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container .flash {
|
||||||
|
padding: 10px;
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-menu,
|
||||||
|
.show-menu {
|
||||||
|
cursor: pointer;
|
||||||
|
display: none;
|
||||||
|
font-size: 30px;
|
||||||
|
transition: var(--transtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
.important {
|
||||||
|
font-size: xx-large;
|
||||||
|
padding-left: 25vw;
|
||||||
|
padding-right: 25vw;
|
||||||
|
padding-top: 30vh;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.important span {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height: 80px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu-dropdown span {
|
||||||
|
float: left;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 100px;
|
||||||
|
padding: 0 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: 0.7s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu {
|
||||||
|
float: right;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu a {
|
||||||
|
padding: 0 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: 0.7s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu-dropdown {
|
||||||
|
height: 100px;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-menu {
|
||||||
|
float: right;
|
||||||
|
line-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-menu-check {
|
||||||
|
position: absolute;
|
||||||
|
visibility: hidden;
|
||||||
|
z-index: -1111;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width:800px) {
|
||||||
|
.hide-menu {
|
||||||
|
position: absolute;
|
||||||
|
right: 40px;
|
||||||
|
top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-menu,
|
||||||
|
.show-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu {
|
||||||
|
height: 100vh;
|
||||||
|
line-height: normal;
|
||||||
|
padding: 80px 0;
|
||||||
|
position: fixed;
|
||||||
|
right: -100%;
|
||||||
|
text-align: center;
|
||||||
|
top: 0;
|
||||||
|
transition: var(--transtime);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.main-menu a {
|
||||||
|
display: block;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-menu-check:checked ~ .main-menu {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry {
|
||||||
|
border-radius: 0 10px 30px 0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry h1,
|
||||||
|
.entry h2 {
|
||||||
|
margin: 5px auto 2px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry ul {
|
||||||
|
padding-left: 20;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'template.html' %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="blogarchive">
|
||||||
|
<h1>Archive</h1><br>
|
||||||
|
{% autoescape off %}
|
||||||
|
{{ content_string }}
|
||||||
|
{% endautoescape %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,9 @@
|
|||||||
|
{% extends "template.html" %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="important">
|
||||||
|
Error<br>
|
||||||
|
<span>{{ errorcode }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,11 @@
|
|||||||
|
{% extends "template.html" %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="blog">
|
||||||
|
<h1>Index</h1><br>
|
||||||
|
{% autoescape off %}
|
||||||
|
{{ content_string }}
|
||||||
|
{% endautoescape %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
|
|
||||||
|
<channel>
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
<description>{{ description }}</description>
|
||||||
|
<language>en-us</language>
|
||||||
|
<link>{{ website }}/feed.xml</link>
|
||||||
|
<atom:link href="/feed.xml" rel="self" type="application/rss+xml" />
|
||||||
|
|
||||||
|
{% autoescape off %}
|
||||||
|
{{ content_string }}
|
||||||
|
{% endautoescape %}
|
||||||
|
|
||||||
|
</channel>
|
||||||
|
</rss>
|
@ -0,0 +1,10 @@
|
|||||||
|
{% extends 'template.html' %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="standalone">
|
||||||
|
{% autoescape off %}
|
||||||
|
{{ content_string }}
|
||||||
|
{% endautoescape %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,32 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
<link href="{{ url_for('static', filename='css/' + style + '.css') }}" rel="stylesheet" type="text/css">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width" initial-scale=1.0>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Menu -->
|
||||||
|
<div class="main-menu-dropdown">
|
||||||
|
<!-- <img class="logo" src="/static/images/logo.png"> -->
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
<input type="checkbox" id="main-menu-check">
|
||||||
|
<label for="main-menu-check" class="show-menu">☰</label>
|
||||||
|
<div class="main-menu">
|
||||||
|
<a href="/">Blog</a>
|
||||||
|
<a href="/archive">Archive</a>
|
||||||
|
<label for="main-menu-check" class="hide-menu">X</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Menu -->
|
||||||
|
<!-- Content -->
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
<!-- Content -->
|
||||||
|
<footer>
|
||||||
|
<div class="center">
|
||||||
|
Made with <a href="https://github.com/tiyn/container-critique">Container Critique </a>.
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in new issue