├── .gitignore ├── LICENSE ├── README.md ├── app.py ├── config_public.py ├── freeze.py ├── models ├── forms.py └── paginate.py ├── pages ├── about.md ├── another-post.md ├── dolor-sin-amet.md ├── flaskblog.md ├── hello-world.md ├── lorem-ipsum.md ├── posting-like-crazy.md ├── so-many-posts.md └── third-post.md ├── requirements.txt ├── screenshot.png ├── static ├── css │ ├── milligram.min.css │ └── style.css ├── img │ └── logo.png └── pictures │ └── 20181212-1.jpeg └── templates ├── 404.html ├── _list.html ├── base.html ├── contact.html ├── index.html ├── page.html ├── post.html └── tags.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Tarballs 2 | *.gz 3 | *.xz 4 | *.zip 5 | 6 | # Backup files 7 | *.swp 8 | *.un~ 9 | *.pyc 10 | 11 | # Flask dir 12 | venv 13 | __pycache__ 14 | flask_session 15 | build 16 | 17 | # Config 18 | config.py 19 | .travis.yml 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 insomnux 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flask Blog 2 | 3 | A very simple blog with [Flask](http://flask.pocoo.org/) and [Flask-Flatpages](https://flask-flatpages.readthedocs.io/) 4 | 5 | Demo at [https://insomnux.pythonanywhere.com/](http://insomnux.pythonanywhere.com/) 6 | 7 | ## Features: 8 | 9 | Posts are added in `markdown` format in the `pages` directory. Posts must have [YAML metadata](http://www.yaml.org/), followed by a blank line and an `---` delimiter. Followed by the page or post body. 10 | 11 | Example: 12 | 13 | ``` 14 | title: My post 15 | date: 2018-12-12 16 | descr: A new awesome post I wrote 17 | tags: [post, new, awesome] 18 | img: cutecat.jpg 19 | imgalt: Photo of my cute cat 20 | 21 | --- 22 | 23 | # Lorem Ipsum 24 | ``` 25 | 26 | Metadata tags used: 27 | 28 | | tag | used for | 29 | |--------|--------------------------------------------------------------| 30 | | title | post or page title | 31 | | date | publication date - mandatory for posts | 32 | | descr | page or post description | 33 | | tags | tags for the post | 34 | | img | filename of a picture uploaded in `static/pictures` | 35 | | imgalt | alt property for picture (required) | 36 | | static | `static: True` signifies that an article is a post, not page | 37 | 38 | 39 | ![Screenshot of Flask Blog](./screenshot.png) 40 | 41 | + Displays post list by post date in `$HOSTNAME/articles/$POSTNAME` - ex: [insomnux.pythonanywhere.com/articles/dolor-sin-amet/](http://insomnux.pythonanywhere.com/articles/dolor-sin-amet/) 42 | + Static pages (yaml matter: `static: True`) in `$HOSTNAME/$PAGENAME` - ex: [insomnux.pythonanywhere.com/about](http://insomnux.pythonanywhere.com/about/) 43 | + Tags support 44 | + Contact form with [Flask-WTF](https://flask-wtf.readthedocs.io/en/stable/) and [Flask-Mail](https://pythonhosted.org/Flask-Mail/) 45 | + Code highlighting with [Pygments](http://pygments.org/) 46 | 47 | ## Credits: 48 | 49 | - Tutorials: 50 | + [Dead easy yet powerful static website generator with Flask](https://nicolas.perriault.net/code/2012/dead-easy-yet-powerful-static-website-generator-with-flask/) • [archive.org copy](https://web.archive.org/web/20190222172546/https://nicolas.perriault.net/code/2012/dead-easy-yet-powerful-static-website-generator-with-flask/) 51 | + [Using Flask to build static blog builder](http://ju.outofmemory.cn/entry/152919) • [archive.org copy](https://web.archive.org/web/20180809050908/http://ju.outofmemory.cn/entry/152919) 52 | + [The Flask Mega-Tutorial](https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world) • [archive.org copy](https://web.archive.org/web/20200223231125/https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world) 53 | - `paginate.py`: [Using Flask to build static blog builder](http://ju.outofmemory.cn/entry/152919) • [archive.org copy](https://web.archive.org/web/20180809050908/http://ju.outofmemory.cn/entry/152919) 54 | - Logo: [pigment/fake-logos](https://github.com/pigment/fake-logos) 55 | - CSS framework: [Milligram - A minimalist CSS framework](https://milligram.io/) 56 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import pygments, markdown 2 | from flask import Flask, flash, redirect, render_template, render_template_string, request, url_for 3 | from flask_flatpages import FlatPages, pygmented_markdown, pygments_style_defs 4 | from flask_mail import Mail, Message 5 | from models.paginate import Paginate as Paginate 6 | from models.forms import ContactForm as ContactForm 7 | 8 | def my_markdown(text): 9 | markdown_text = render_template_string(text) 10 | pygmented_text = markdown.markdown(markdown_text, extensions=["codehilite", "fenced_code", "tables"]) 11 | return pygmented_text 12 | 13 | app = Flask(__name__) 14 | #app.config.from_object("config") 15 | app.config.from_object("config_public") 16 | app.config["FLATPAGES_HTML_RENDERER"] = my_markdown 17 | pages = FlatPages(app) 18 | mail = Mail(app) 19 | 20 | 21 | # 404 22 | @app.errorhandler(404) 23 | def page_not_found(e): 24 | return render_template("404.html"), 404 25 | 26 | @app.route('/pygments.css') 27 | def pygments_css(): 28 | return pygments_style_defs("monokai"), 200, {"Content-Type":"text/css"} 29 | 30 | @app.route("/") 31 | def index(num = 0): 32 | posts = [p for p in pages if "date" in p.meta] 33 | sorted_pages=sorted(posts, reverse=True, key=lambda page: page.meta["date"]) 34 | ppaginate=Paginate(app.config["PAGES_NUMBER_PER_PAGE"],sorted_pages) 35 | if (num >= ppaginate.get_total_number()): 36 | return redirect(url_for("index_extend")) 37 | 38 | return render_template("index.html",num=num,pages=ppaginate.get_number_pages(num),config=app.config,current_number=num,total_num=ppaginate.get_total_number()- 1) 39 | 40 | @app.route("/index/.html") 41 | def index_extend(num): 42 | num=int(num) 43 | posts = [p for p in pages if "date" in p.meta] 44 | sorted_pages=sorted(posts, reverse=True, key=lambda page: page.meta["date"]) 45 | ppaginate=Paginate(app.config["PAGES_NUMBER_PER_PAGE"],sorted_pages) 46 | if (num >= ppaginate.get_total_number()): 47 | num = 0 48 | 49 | return render_template("index.html",num=num,pages=ppaginate.get_number_pages(num),config=app.config,current_number=num,total_num=ppaginate.get_total_number()- 1) 50 | 51 | @app.route("//") 52 | def staticpage(path): 53 | p = pages.get_or_404(path) 54 | staticpage = p if "static" in p.meta else None 55 | if page == None: 56 | return page_not_found(404) 57 | 58 | return render_template("page.html", page=staticpage) 59 | 60 | @app.route("/articles//") 61 | def page(path): 62 | p = pages.get_or_404(path) 63 | page = p if "date" in p.meta else None 64 | if page == None: 65 | return page_not_found(404) 66 | return render_template("post.html", page=page) 67 | 68 | @app.route("/tag//") 69 | def tag(tag): 70 | tagged = [p for p in pages if tag in p.meta.get("tags", [])] 71 | return render_template("tags.html", pages=tagged, tag=tag) 72 | 73 | @app.route("/contact/", methods=("GET", "POST")) 74 | def contact(): 75 | form = ContactForm() 76 | error = None 77 | 78 | if request.method == "POST": 79 | if form.validate() == False: 80 | error = "Please fill in all fields" 81 | else: 82 | msg = Message( 83 | "Message from " + form.name.data + "," + form.email.data, 84 | sender="mail@example.net", 85 | recipients=["mail@example.net"]) 86 | msg.body = """ 87 | From: %s <%s>, 88 | %s 89 | """ % (form.name.data, form.email.data, form.message.data) 90 | mail.send(msg) 91 | flash("Message sent.") 92 | return redirect( url_for("contact") ) 93 | 94 | return render_template("contact.html", form=form, error=error) 95 | 96 | if __name__ == "__main__": 97 | app.run() 98 | -------------------------------------------------------------------------------- /config_public.py: -------------------------------------------------------------------------------- 1 | DEBUG = True 2 | 3 | # Flatpages 4 | FLATPAGES_AUTO_RELOAD = DEBUG 5 | FLATPAGES_EXTENSION = [".md", ".markdown"] 6 | FLATPAGES_MARKDOWN_EXTENSION =["codehilite", "fenced_code", "tables"] 7 | PAGES_NUMBER_PER_PAGE = 5 8 | 9 | # Mail server 10 | MAIL_SERVER = "smtp.server.com" 11 | MAIL_PORT = 465 12 | MAIL_USE_SSL = True 13 | MAIL_USERNAME = "user@example.net" 14 | MAIL_PASSWORD = "password" 15 | 16 | # Protection for Cross-site Request Forgery (CSRF) 17 | CSRF_ENABLED = True 18 | CSRF_SESSION_KEY = "secretcsrf" 19 | 20 | # Secret key for signing cookies 21 | SECRET_KEY = "secret" 22 | -------------------------------------------------------------------------------- /freeze.py: -------------------------------------------------------------------------------- 1 | from flask_frozen import Freezer 2 | from app import app 3 | 4 | freezer = Freezer(app) 5 | 6 | ''' 7 | For urls that are not linked with url_for 8 | @freezer.register_generator 9 | def static_urls(): 10 | yield "/my-url-1/" 11 | yield "/my-url-2/" 12 | ''' 13 | 14 | if __name__ == '__main__': 15 | freezer.freeze() 16 | -------------------------------------------------------------------------------- /models/forms.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import StringField, TextAreaField, SubmitField, validators 3 | 4 | class ContactForm(FlaskForm): 5 | name = StringField("Your Name:", [validators.DataRequired()]) 6 | email = StringField("Your e-mail address:", [validators.DataRequired(), validators.Email("your@email.com")]) 7 | message = TextAreaField("Your message:", [validators.DataRequired()]) 8 | submit = SubmitField("Send Message") 9 | -------------------------------------------------------------------------------- /models/paginate.py: -------------------------------------------------------------------------------- 1 | # taken from: http://ju.outofmemory.cn/entry/152919 2 | class Paginate(): 3 | def __init__(self,PAGES_NUMBER_PER_PAGE,pages): 4 | "global pages" 5 | count=0 6 | self.list_page1 = [] 7 | for page in pages: 8 | if count % PAGES_NUMBER_PER_PAGE == 0 : 9 | self.list_page1.append([]) 10 | self.list_page1[int(count /PAGES_NUMBER_PER_PAGE )].append(page) 11 | count +=1 12 | def get_total_number(self): 13 | return len(self.list_page1) 14 | 15 | def get_number_pages(self,num): 16 | return self.list_page1[num] 17 | -------------------------------------------------------------------------------- /pages/about.md: -------------------------------------------------------------------------------- 1 | title: About 2 | static: True 3 | 4 | --- 5 | 6 | ## Terrae blanditiis servat 7 | 8 | Lorem markdownum inveniunt deme. Volucris illis *silvis* paverunt nunc in 9 | inmissa plangore quis! Ea placido: ipsa missus inbelle ut fixerat sit ripis me Attis, suo non, dea. 10 | Ibat lecto pater omnia nec Thestius adsueta mixta! **Et in belua** constitit 11 | quoque totidem, mihi auget missus ripas. 12 | 13 | 1. Optet admoto movet verba deum ferumque in 14 | 2. Patri indicere dixerat cladis deum quisque rostro 15 | 3. Montis contraria sequitur ut ipsum lacerum 16 | 4. Ipse Italico 17 | -------------------------------------------------------------------------------- /pages/another-post.md: -------------------------------------------------------------------------------- 1 | title: Another post 2 | date: 2018-11-10 3 | descr: Another awesome blog post 4 | tags: [general, awesome] 5 | 6 | --- 7 | 8 | # Rursus frustra traxit caput quamquam tulit 9 | 10 | ## Occulta ferro priora tecta 11 | 12 | Lorem markdownum, liceret *nomen*. Locumque mariti? Falsosque et perosus et 13 | [mihi praesensque mors](http://www.umbris.org/anhelos-limosi) mediis, 14 | [Polydegmona quique et](http://cernimus.io/) umbrosa, committat ora **arces 15 | mare**. Notam per peracta verumque crura sub tangere innocuos **vivum** sacra 16 | putetis; modo superi Gigantas; deo. 17 | 18 | ```bash 19 | for i in *.jpeg; do 20 | new=$(printf "%04d.jpeg" "$a"); 21 | mv -- "$i" "$new"; 22 | let a=a+1; 23 | done 24 | ``` 25 | 26 | ## Iugo forcipe 27 | 28 | Membra coeperunt multis montibus. Tibi fui viri maeonis te vulnera cornua. 29 | Pallados concipit cecidit ad adhuc modo inpia consiliis rex arcitenens gemma 30 | monstri paratus. Lumina liquidi petita ubi inferre terras, vestras Pylius non, 31 | totoque Echo? Pro lapidis Antaeo colores in dabimus est, elice tetigit inmemores 32 | invidiae pigris, aut odisse vel? 33 | 34 | Valet abit novo! Dum caelatus Panopesque de Ceycis? Pereuntem hastam sceleris 35 | perire! 36 | -------------------------------------------------------------------------------- /pages/dolor-sin-amet.md: -------------------------------------------------------------------------------- 1 | title: Dolor sin amet 2 | date: 2018-11-28 3 | descr: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam viverra laoreet ante, eu ultricies dolor pulvinar non. 4 | tags: [general, lorem, ipsum] 5 | 6 | --- 7 | 8 | # Fretum in Iliaden fretumque quamvis ferar cuius 9 | 10 | ## Tellus poposcerit mensas 11 | 12 | Lorem markdownum, parabam et quam. Circaea haut. Puer ad quorum et canna deus 13 | vitisque iamque. Valetque spatioque possis propioris, Pleuron et equum coniunx 14 | exstat Iuppiter. Iovem mea ego Almo ille capere nata. 15 | 16 | 1. Nato hoc nova fallunt morti 17 | 2. Incepti crescitque bacaque admovet Nec quoque pertimui 18 | 3. Carpit et saucia plenissima quoque de rediere 19 | 4. Poenam pallor statuit urbem incaluisse haec superare 20 | 5. Auctor et negat 21 | 6. Nomen currere offensa haud cadunt 22 | 23 | Fulvis alios rumoribus iunxit cognovit legit factis: ita his uterum *contra* 24 | nebulasque, infantia cernis, visus derigere. Satis gravitate regale affectibus 25 | pendentis lactentis agam; audes novissima neque, unda. 26 | 27 | > Vivacis in telum parce quaesitisque tanti, Erycina! Ausis utque feras caveo 28 | > derexit! 29 | -------------------------------------------------------------------------------- /pages/flaskblog.md: -------------------------------------------------------------------------------- 1 | title: About Flask Blog 2 | date: 2018-12-12 3 | descr: Flaskblog is a very simple blog made with Flask and Flask-Flatpages 4 | tags: [flask, python, general] 5 | img: 20181212-1.jpeg 6 | imgalt: Screenshot of flaskblog 7 | 8 | --- 9 | 10 | Flaskblog on GitHub: [github.com/insomnux/flaskblog](https://github.com/insomnux/flaskblog). 11 | 12 | Demo on [insomnux.pythonanywhere.com/](https://insomnux.pythonanywhere.com/) 13 | 14 | ## Features: 15 | 16 | Posts are added in `markdown` format in the `pages` directory. Posts must have [YAML metadata](http://www.yaml.org/), followed by a blank line and an `---` delimiter. Followed by the page or post body. 17 | 18 | Example: 19 | 20 | ``` 21 | title: My post 22 | date: 2018-12-12 23 | descr: A new awesome post I wrote 24 | tags: [post, new, awesome] 25 | img: cutecat.jpg 26 | imgalt: Photo of my cute cat 27 | 28 | --- 29 | 30 | # Lorem Ipsum 31 | ``` 32 | 33 | Metadata tags used: 34 | 35 | tag | used for 36 | --------|-------------------------------------------------------------- 37 | title | post or page title 38 | date | publication date - mandatory for posts 39 | descr | page or post description 40 | tags | tags for the post 41 | img | filename of a picture uploaded in `static/pictures` 42 | imgalt | alt property for picture (required) 43 | static | `static: True` signifies that an article is a post, not page 44 | 45 | + Displays post list by post date in `$HOSTNAME/articles/$POSTNAME` - ex: [insomnux.pythonanywhere.com/articles/dolor-sin-amet/](http://insomnux.pythonanywhere.com/articles/dolor-sin-amet/) 46 | + Static pages (yaml matter: `static: True`) in `$HOSTNAME/$PAGENAME` - ex: [insomnux.pythonanywhere.com/about](http://insomnux.pythonanywhere.com/about/) 47 | + Tags support 48 | + Contact form with [Flask-WTF](https://flask-wtf.readthedocs.io/en/stable/) and [Flask-Mail](https://pythonhosted.org/Flask-Mail/) 49 | + Code highlighting with [Pygments](http://pygments.org/) 50 | 51 | ## Credits: 52 | 53 | - Tutorials: 54 | + [Dead easy yet powerful static website generator with Flask](https://nicolas.perriault.net/code/2012/dead-easy-yet-powerful-static-website-generator-with-flask/) 55 | + [Using Flask to build static blog builder](http://ju.outofmemory.cn/entry/152919) 56 | + [The Flask Mega-Tutorial](https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world) 57 | - `paginate.py`: [Using Flask to build static blog builder](http://ju.outofmemory.cn/entry/152919) 58 | - Logo: [pigment/fake-logos](https://github.com/pigment/fake-logos) 59 | - CSS framework: [Milligram - A minimalist CSS framework](https://milligram.io/) 60 | -------------------------------------------------------------------------------- /pages/hello-world.md: -------------------------------------------------------------------------------- 1 | title: hello-world 2 | date: 2018-10-30 3 | descr: First blog post 4 | tags: [general, awesome, stuff] 5 | 6 | --- 7 | 8 | ## Lumina nec 9 | 10 | Lorem markdownum minor **te** si trux. Illa viam vitiis! Nimia que, essemus 11 | ingenio tenet enim erravisse cadunt: quoque Troianaque diversa, ebiberant. 12 | 13 | - Agros iam notissima umida vota si Sisyphio 14 | - Exercent dolet Tydidae nobis nescio 15 | - Ab Iuppiter horum 16 | - Coniugis avitum 17 | - Conscia latus tractum poterat puer 18 | 19 | ## Malo fratrisque in veni veniebat 20 | 21 | Coniecto tamen te tamen vixque nobis utinam, ego vetat. Latiis perpetiar 22 | quisquis coniunx manu mitis, et **leto populus cuius**. 23 | 24 | -------------------------------------------------------------------------------- /pages/lorem-ipsum.md: -------------------------------------------------------------------------------- 1 | title: Lorem Ipsum 2 | date: 2018-11-02 3 | descr: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis eu ullamcorper mi. 4 | tags: [general, lorem, ipsum] 5 | 6 | --- 7 | 8 | # Ithacis sacris 9 | 10 | ## Sine nec quotiens laesit credo latet aeternus 11 | 12 | Lorem markdownum de auras meritis quoque procul festo mite tamen. Fortes 13 | rigidos, non et gramen nec **inponis ditissimus medio**; suae 14 | [simul](http://www.progeniespotae.org/). 15 | 16 | 1. Ora nec aras quod Pergama 17 | 2. Dracones ille pinguesque quae ablatus altera sumit 18 | 3. Ter secus 19 | 4. Deus caede 20 | 21 | Cura tenuit quia picea mirantia mentes! Tectus pro, in interdictae omnis evicit 22 | tandem. Iungere una erravit habenti: dare umquam molle nigra cadet; pars timor 23 | Dryopen et summa. 24 | 25 | ## Inspicitur aquas iaculatus duritiam tellus est rigore 26 | 27 | Omnia tamen namque Cerberei voverat et dicite erat dummodo alvum siqua ne atria 28 | tenues nunc agreste. Nec nemorum vasto. Laetos nomen spretis solumque virgo rapi 29 | **attigit quo** traxit pacalibus. Ausis colebat fratres finierat concolor oscula 30 | [caeruleus](http://melampus-amictae.org/) voce sua tabe interdum ipsa, spatium. 31 | 32 | > Propositum iacentes vestigia, mox quae viderent: pax penates quoque uti 33 | > vellere. Vidit ardere serpentibus sine quosque sui; terra manu Graecia. 34 | -------------------------------------------------------------------------------- /pages/posting-like-crazy.md: -------------------------------------------------------------------------------- 1 | title: Posting like crazy 2 | date: 2018-11-15 3 | descr: I can't stop posting, posting is awesome! 4 | tags: [post, awesome, important] 5 | 6 | --- 7 | 8 | # Patris arvis huic portare expellere Dymantida manus 9 | 10 | ## Quid suo vocatur tecta fluctus 11 | 12 | Lorem markdownum illo. Deus minus numen comitatur vocabat spiritus stolidae 13 | foedoque aequales quae, in. Dextra dubitare pastoribus ceperat flexerat ictu 14 | iacentes, nostra; mihi. Volenti vestigia lapsus, praeter 15 | ieiunia? Hiems forsitan furtim nempe. 16 | 17 | ```python 18 | # Get three test score 19 | round1 = int(raw_input("Enter score for round 1: ")) 20 | round2 = int(raw_input("Enter score for round 2: ")) 21 | round3 = int(raw_input("Enter score for round 3: ")) 22 | 23 | # Calculate the average 24 | average = (round1 + round2 + round3) / 3 25 | 26 | # Print out the test score 27 | print "the average score is: ", average 28 | ``` 29 | 30 | ## Et semper ulli rerum hastam linguae abiit 31 | 32 | Phineu onerataque adit: *super* et domus vetustas, desinat quod auro locus 33 | Vulcania domosque. Arvis *aetas* adfuit pictam atros. Modo aptato cremet Ioles, 34 | spectant conveniet violari discordibus ducem probatis corpora morer aequore et 35 | unam ubi lugubris **teste** latet. Procul exsanguemque pressum ne Helles ingens 36 | quaesitisque everti **rectorque** suadet caedis indice pariter. 37 | 38 | ## Furores illo dei magno Herse 39 | 40 | Sic et illa Mareoticaque amanti fugit! Auraeque 41 | vocibus. 42 | -------------------------------------------------------------------------------- /pages/so-many-posts.md: -------------------------------------------------------------------------------- 1 | title: So many posts 2 | date: 2018-11-18 3 | descr: This is yet another post, this blog is filled with posts 4 | tags: [important, awesome] 5 | 6 | --- 7 | 8 | # Cornua aristas 9 | 10 | ## Praeponere habuere plangore hac 11 | 12 | Lorem markdownum corpora, ad colebat ille. Desuetas **in** undis fecundique 13 | remos; tempora adhuc contento ingentia forsitan et ventris. Nefas ille 14 | **virtute**. 15 | 16 | 1. Et mixtae culmen quae ea inque dixit 17 | 2. Collo irata siccis 18 | 3. Suprema a 19 | 4. Vices mihi ubi 20 | 5. Oriens nec alma nisi ex iamque Macareus 21 | 22 | ```css 23 | /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ 24 | html {line-height: 1.15; -ms-text-size-adjust: 100%; 25 | -webkit-text-size-adjust: 100%;} 26 | body {margin: 0;} 27 | article, aside, footer, header, nav, section {display: block;} 28 | h1 {font-size: 2em; margin: 0.67em 0;} 29 | figcaption, figure, main {display: block;} 30 | figure {margin: 1em 40px;} 31 | hr {box-sizing: content-box; height: 0; overflow: visible;} 32 | pre {font-family: monospace, monospace; font-size: 1em;} 33 | a {background-color: transparent; -webkit-text-decoration-skip: objects;} 34 | abbr[title] {border-bottom: none; text-decoration: underline; text-decoration: underline dotted;} 35 | b, strong {font-weight: inherit;} 36 | b, strong {font-weight: bolder;} 37 | code, kbd, samp {font-family: monospace, monospace; font-size: 1em;} 38 | dfn {font-style: italic;} 39 | mark {background-color: #ff0; color: #000;} 40 | small {font-size: 80%;} 41 | sub, sup {font-size: 75%; line-height: 0; position: relative; vertical-align: baseline;} 42 | sub {bottom: -0.25em;} 43 | sup {top: -0.5em;} 44 | audio, video {display: inline-block;} 45 | audio:not([controls]) {display: none; height: 0;} 46 | img {border-style: none;} 47 | svg:not(:root) {overflow: hidden;} 48 | button, input, optgroup, select, textarea {font-family: sans-serif; font-size: 100%; line-height: 1.15; margin: 0;} 49 | button, input {overflow: visible;} 50 | button, select {text-transform: none;} 51 | button, html [type="button"], [type="reset"], [type="submit"] {-webkit-appearance: button;} 52 | button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner {border-style: none; padding: 0;} 53 | button:-moz-focusring, [type="button"]:-moz-focusring, [type="reset"]:-moz-focusring, [type="submit"]:-moz-focusring {outline: 1px dotted ButtonText;} 54 | fieldset {padding: 0.35em 0.75em 0.625em;} 55 | legend {box-sizing: border-box; color: inherit; display: table; max-width: 100%; padding: 0; white-space: normal;} 56 | progress {display: inline-block; vertical-align: baseline;} 57 | textarea {overflow: auto;} 58 | [type="checkbox"], [type="radio"] {box-sizing: border-box; padding: 0;} 59 | [type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button {height: auto;} 60 | [type="search"] {-webkit-appearance: textfield; outline-offset: -2px;} 61 | [type="search"]::-webkit-search-cancel-button, [type="search"]::-webkit-search-decoration {-webkit-appearance: none;} 62 | ::-webkit-file-upload-button {-webkit-appearance: button; font: inherit;} 63 | details, menu {display: block;} 64 | summary {display: list-item;} 65 | canvas {display: inline-block;} 66 | template {display: none;} 67 | [hidden] {display: none;} 68 | ``` 69 | 70 | **Telamon silva**; vacca quae pro ulla animorum, quasi neque exclamat. Unde opes 71 | Hesioneque vultus hinc aures suis, que ripas eluditque *ubi lugebat* quae. Quae 72 | iam afueram fecit, lumina digna, odoriferae socii circumdata perque Hector 73 | arcum, carinis, manibus? Moenia honorem tener! 74 | -------------------------------------------------------------------------------- /pages/third-post.md: -------------------------------------------------------------------------------- 1 | title: Third post 2 | date: 2018-11-12 3 | descr: Another awesome blog post 4 | tags: [awesome, lakes] 5 | 6 | --- 7 | 8 | # Domus si nescit videre amantem exposcere perde 9 | 10 | ## Perseus rapere 11 | 12 | Lorem markdownum passis. Latrare socerque sic esse cum alumnae Troiam antris tua 13 | aevo, attonitus, haesit tumidus legit portabat at templo. Tandemque nixus 14 | maledictaque miseri. Arbore non trepidus fuge terras mutatur: frondis vindicat 15 | habes *vocem*, illa exit ac. Potuit de sonos equidem per illo sinistra utque, 16 | contraxit ille dictae. 17 | 18 | Est servo tamen erit sed membra, pugnes me quondam pignus, in, latens micat, 19 | **Calydonius calet**, queruntur! Et nova foramina non indicat qua: **a hoc 20 | membra** plenissima auguror; sunt tura dederit! Curvique Ceyca suspenditque 21 | magna meritum. Vulnere **manu**: ut signa scilicet fugias. 22 | 23 | - Vino Saturnia vates ius 24 | - An hostem Venus 25 | - Athamantis monte moles neque viri ultima 26 | - Aliis finditque uritur tellus blanditur odere canibus 27 | - E defendit 28 | - Illis latuit caelestibus stet deus 29 | 30 | ## Victus et albi reserabo reparabat laborum bracchia 31 | 32 | Squamea **Orpheus** et miscent patris cum. Aper actis nunc altior frenataque ut 33 | tamen: dixit, fecitque, crescentesque patre tremoribus clipei; canam. Habet 34 | promissi horret! Utile fas, osculaque clamor? 35 | 36 | 1. Miserabilis quod conviciaque contigit faciunt fore 37 | 2. Fata licebit 38 | 3. Infelix de Marte 39 | 4. Domus que nam putat miseram Peleus 40 | 5. Me deserto carmina 41 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | blinker>=1.4 2 | Click>=7.0 3 | Flask>=1.0.2 4 | Flask-FlatPages>=0.8.1 5 | Flask-Mail>=0.9.1 6 | Flask-WTF>=0.14.2 7 | Frozen-Flask>=0.15 8 | itsdangerous>=1.1.0 9 | Jinja2>=2.10.1 10 | Markdown>=3.0.1 11 | MarkupSafe>=1.1.0 12 | Pygments>=2.3.0 13 | PyYAML>=4.2b1 14 | Werkzeug>=0.15.3 15 | WTForms>=2.2.1 16 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insomnux/flaskblog/fbe5c382ee873bf700c1b23d90361b0bac632883/screenshot.png -------------------------------------------------------------------------------- /static/css/milligram.min.css: -------------------------------------------------------------------------------- 1 | *,*:after,*:before{box-sizing:inherit}html{box-sizing:border-box;font-size:62.5%}body{color:#606c76;font-family:'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;letter-spacing:.01em;line-height:1.6}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#9b4dca;border:0.1rem solid #9b4dca;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#9b4dca;border-color:#9b4dca}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#9b4dca}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#9b4dca}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#9b4dca}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#9b4dca}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #9b4dca;overflow-y:hidden}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='email'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],input[type='color'],input[type='date'],input[type='month'],input[type='week'],input[type='datetime'],input[type='datetime-local'],input:not([type]),textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem;width:100%}input[type='email']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,input[type='color']:focus,input[type='date']:focus,input[type='month']:focus,input[type='week']:focus,input[type='datetime']:focus,input[type='datetime-local']:focus,input:not([type]):focus,textarea:focus,select:focus{border-color:#9b4dca;outline:0}select{background:url('data:image/svg+xml;utf8,') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,')}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.container{margin:0 auto;max-width:112.0rem;padding:0 2.0rem;position:relative;width:100%}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1 1 auto;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{-ms-grid-row-align:center;align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#9b4dca;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{border-spacing:0;width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem;text-align:left}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.6rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:2.8rem;line-height:1.3}h4{font-size:2.2rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right} 2 | 3 | /*# sourceMappingURL=milligram.min.css.map */ -------------------------------------------------------------------------------- /static/css/style.css: -------------------------------------------------------------------------------- 1 | /* === General === */ 2 | 3 | body{ 4 | background-color: #f2f2f2; 5 | color: #2e3338; 6 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 7 | font-size: 1.6em; 8 | font-weight: 300; 9 | letter-spacing: .02em; 10 | line-height: 1.75; 11 | padding: 1rem 5px; 12 | } 13 | 14 | pre { 15 | background: #515151; 16 | padding: 5px 10px; 17 | } 18 | 19 | code { 20 | background: inherit; 21 | font-size: initial; 22 | } 23 | 24 | /* === Wrapper === */ 25 | 26 | #wrapper { 27 | border-radius: 8px; 28 | margin: 1rem auto 3rem auto; 29 | width: 100%; 30 | } 31 | 32 | #wrapper main section { 33 | padding: 5em 3em 3em 3em ; 34 | } 35 | 36 | @media screen and (min-width: 480px) { 37 | #wrapper { 38 | width: 95%; 39 | } 40 | #wrapper > .main { 41 | padding: 3em 3em 1em 3em ; 42 | } 43 | } 44 | @media screen and (min-width: 736px) { 45 | #wrapper { 46 | width: 80%; 47 | } 48 | } 49 | @media screen and (min-width: 980px) { 50 | #wrapper { 51 | width: 75%; 52 | } 53 | } 54 | 55 | /* === Header === */ 56 | 57 | #wrapper #header { 58 | text-align: center; 59 | } 60 | 61 | #header > header { 62 | max-width: 400px; 63 | margin: 0 auto -8.5px auto; 64 | padding: 3rem 0; 65 | } 66 | #header > header > a { 67 | border-bottom: none; 68 | } 69 | 70 | 71 | /* === Topnav === */ 72 | 73 | #nav > ul { 74 | display: flex; 75 | flex-wrap: wrap; 76 | justify-content: center; 77 | list-style: none; 78 | margin-top: 0; 79 | margin-bottom: 0; 80 | padding: 0; 81 | } 82 | 83 | #nav > ul li { 84 | line-height: 5.6rem; 85 | padding: 0; 86 | } 87 | 88 | #nav > ul li a { 89 | border: 0; 90 | display: block; 91 | font-size: 0.9em; 92 | padding: 0 2em; 93 | text-decoration: none; 94 | text-indent: 0.125em; 95 | transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out; 96 | } 97 | 98 | #nav > ul li a:hover { 99 | background: rgba(207, 201, 214, 0.035); 100 | } 101 | 102 | /* Toggle menu */ 103 | #menutoggle, .labeltoggle { 104 | display: none; 105 | } 106 | 107 | @media screen and (max-width: 480px) { 108 | #nav > ul { 109 | flex-direction: column; 110 | opacity: 0; 111 | height: 1px; 112 | } 113 | .labeltoggle { 114 | display: block; 115 | } 116 | #nav #menutoggle:checked ~ ul { 117 | height: 100%; 118 | opacity: 1; 119 | height: auto; 120 | } 121 | #nav > ul li { 122 | line-height: initial; 123 | } 124 | } 125 | 126 | /* === Main === */ 127 | main { 128 | } 129 | 130 | .content { 131 | display: flex; 132 | flex-direction: column; 133 | } 134 | 135 | .content > footer { 136 | align-self: center; 137 | border-top: 1px solid rgba(152, 146, 158, .5); 138 | border-bottom: 1px solid rgba(152, 146, 158, .5); 139 | display: inline-block; 140 | padding: 1rem; 141 | } 142 | 143 | /* === Footer === */ 144 | footer { 145 | border-top: 1px inset rgba(152, 146, 158, .5); 146 | text-align: center; 147 | } 148 | 149 | /* ~ Copyright ~ */ 150 | #copyright { 151 | margin-bottom: 6em; 152 | text-align: center; 153 | } 154 | 155 | /* ~ Footer icons ~ */ 156 | 157 | .icon { 158 | display: inline-block; 159 | font-family: "FontAwesome"; 160 | } 161 | 162 | 163 | footer .icons { 164 | display: flex; 165 | justify-content: center; 166 | list-style: none; 167 | padding: 2em 0; 168 | } 169 | 170 | ul.icons li { 171 | display: inline-block; 172 | padding: 0 1.25em 0 0; 173 | } 174 | 175 | ul.icons li:last-child { 176 | padding-right: 0 !important; 177 | } 178 | 179 | ul.icons li > .icon { 180 | border-radius: 100%; 181 | border: solid 1px #4c4b4d; 182 | height: 3em; 183 | letter-spacing: 0; 184 | line-height: 3em; 185 | transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out; 186 | width: 3em; 187 | } 188 | ul.icons li .icon:before { 189 | font-size: 1em; 190 | } 191 | ul.icons li .icon:hover { 192 | background: rgba(207, 201, 214, 0.035); 193 | color: inherit; 194 | } 195 | .icon > .label { 196 | display: none; 197 | } 198 | 199 | /* === Pages === */ 200 | 201 | .content > header { 202 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 203 | margin-bottom: 3.2rem; 204 | text-align: center; 205 | } 206 | 207 | .content > header > p { 208 | font-size: 1.1em; 209 | } 210 | 211 | /* === Misc === */ 212 | .success { 213 | background-color: #d4edda; 214 | border: 1px solid #c3e6cb; 215 | color: #155724; 216 | font-size: 1.5em; 217 | padding: .5rem; 218 | } 219 | -------------------------------------------------------------------------------- /static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insomnux/flaskblog/fbe5c382ee873bf700c1b23d90361b0bac632883/static/img/logo.png -------------------------------------------------------------------------------- /static/pictures/20181212-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insomnux/flaskblog/fbe5c382ee873bf700c1b23d90361b0bac632883/static/pictures/20181212-1.jpeg -------------------------------------------------------------------------------- /templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Page Not Found{% endblock %} 3 | {% block content %} 4 |

Page Not Found

5 |

Return to index

6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /templates/_list.html: -------------------------------------------------------------------------------- 1 |
    2 | {% for page in pages %} 3 |
  • 4 | {{ page.title }}
    5 | Published: {{ page.date }} 6 |
  • 7 | {% else %} 8 |
  • No articles found for this tag
  • 9 | {% endfor %} 10 |
11 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% block title %}{% endblock %} - FlaskBlog 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 41 | 42 |
43 | {% block content %}{% endblock %} 44 |
45 | 46 | 47 | 53 | 54 |
55 | 56 | 57 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /templates/contact.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Contact me{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 |
9 |

Contact me

10 | {% with message = get_flashed_messages() %} 11 | {% if message %} 12 | {% for m in message %} 13 |

{{ m }}

14 | {% endfor %} 15 | {% endif %} 16 | {% endwith %} 17 |
18 | 19 |
20 |
21 | {{ form.hidden_tag() }} 22 |

23 | {{ form.name.label }} 24 | {{ form.name }} 25 | {% for error in form.name.errors %} 26 | [ {{ error }} ] 27 | {% endfor %} 28 |

29 |

30 | {{ form.email.label }} 31 | {{ form.email }} 32 | {% for error in form.email.errors %} 33 | [ {{ error }} ] 34 | {% endfor %} 35 | 36 |

37 |

38 | {{ form.message.label }} 39 | {{ form.message }} 40 | {% for error in form.message.errors %} 41 | [ {{ error }} ] 42 | {% endfor %} 43 |

44 |

45 | {{ form.submit }} 46 |

47 |
48 |
49 | 50 | {% endblock %} 51 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Home{% endblock %} 4 | 5 | {% block content %} 6 | 7 | 8 |
9 |

Latest articles

10 |
    11 | {% for page in pages %} 12 |
  • 13 | {{ page.title }} 14 |
  • 15 | {% else %} 16 |
  • No articles found
  • 17 | {% endfor %} 18 |
19 | {% if num > 0 %} 20 | {% set prevnum = num - 1 %} 21 | Previous 22 | {% endif %} 23 | {% if num < total_num %} 24 | {% set nextnum = num + 1 %} 25 | Next 26 | {% endif %} 27 |
28 | 29 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /templates/page.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}{{ page.title }}{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 |
9 |

{{ page.title }}

10 | {% if page.descr %} 11 |

{{ page.descr }}

12 | {% endif %} 13 |
14 | 15 |
16 | {{ page.html|safe }} 17 |
18 |
19 | 20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /templates/post.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}{{ page.title }}{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 |
9 |

{{ page.title }}

10 | {% if page.descr %} 11 |

{{ page.descr }}

12 | {% endif %} 13 | {% if page.img and page.imgalt %} 14 | {% set imgfile="pictures/" + page.meta.img %} 15 | {{ page.imgalt }} 16 | {% endif %} 17 |
18 | 19 |
20 | {{ page.html|safe }} 21 |
22 | 23 |
24 |
25 |   {{ page.date }} 26 | {% if page.meta.tags|length %} 27 | 28 | {% for tags in page.meta.tags %} 29 | #{{ tags }} 30 | {% endfor %} 31 | 32 | {% endif %} 33 |
34 |
35 |
36 | 37 | {% endblock %} 38 | -------------------------------------------------------------------------------- /templates/tags.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Tags{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 |

List of stuff tagged {{ tag }}

9 | {% with posts=posts %} 10 | {% include "_list.html" %} 11 | {% endwith %} 12 |
13 | {% endblock content %} 14 | --------------------------------------------------------------------------------