Refactored blog home template
This commit is contained in:
parent
d81abaa984
commit
b8df9f73a0
8 changed files with 286 additions and 113 deletions
|
@ -1,11 +1,12 @@
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from glob import glob
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from markdown import markdown
|
|
||||||
|
|
||||||
from flask import Flask, abort, send_from_directory, render_template
|
from flask import Flask, abort, send_from_directory, render_template
|
||||||
|
from markdown import markdown
|
||||||
|
|
||||||
basedir = os.path.abspath(os.path.join(os.path.realpath(__file__), '..', '..'))
|
basedir = os.path.abspath(os.path.join(os.path.realpath(__file__), '..', '..'))
|
||||||
templates_dir = os.path.join(basedir, 'templates')
|
templates_dir = os.path.join(basedir, 'templates')
|
||||||
|
@ -33,6 +34,8 @@ def get_page_metadata(page: str) -> dict:
|
||||||
|
|
||||||
metadata = {}
|
metadata = {}
|
||||||
with open(os.path.join(pages_dir, page), 'r') as f:
|
with open(os.path.join(pages_dir, page), 'r') as f:
|
||||||
|
metadata['uri'] = '/article/' + page[:-3]
|
||||||
|
|
||||||
for line in f.readlines():
|
for line in f.readlines():
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
|
@ -40,6 +43,9 @@ def get_page_metadata(page: str) -> dict:
|
||||||
if not (m := re.match(r'^\[//]: # \(([^:]+):\s*([^)]+)\)\s*$', line)):
|
if not (m := re.match(r'^\[//]: # \(([^:]+):\s*([^)]+)\)\s*$', line)):
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if m.group(1) == 'published':
|
||||||
|
metadata[m.group(1)] = datetime.date.fromisoformat(m.group(2))
|
||||||
|
else:
|
||||||
metadata[m.group(1)] = m.group(2)
|
metadata[m.group(1)] = m.group(2)
|
||||||
|
|
||||||
return metadata
|
return metadata
|
||||||
|
@ -52,15 +58,27 @@ def get_page(page: str, title: Optional[str] = None):
|
||||||
metadata = get_page_metadata(page)
|
metadata = get_page_metadata(page)
|
||||||
with open(os.path.join(pages_dir, page), 'r') as f:
|
with open(os.path.join(pages_dir, page), 'r') as f:
|
||||||
return render_template('article.html',
|
return render_template('article.html',
|
||||||
title=title if title else metadata.get('title', 'Platypush Blog'),
|
title=title if title else metadata.get('title', 'Platypush blog'),
|
||||||
published=(datetime.date.fromisoformat(metadata['published']).strftime('%b %d, %Y')
|
image=metadata.get('image'),
|
||||||
|
description=metadata.get('description'),
|
||||||
|
published=(metadata['published'].strftime('%b %d, %Y')
|
||||||
if metadata.get('published') else None),
|
if metadata.get('published') else None),
|
||||||
content=markdown(f.read(),extensions=['fenced_code', 'codehilite']))
|
content=markdown(f.read(),extensions=['fenced_code', 'codehilite']))
|
||||||
|
|
||||||
|
|
||||||
|
def get_pages() -> list:
|
||||||
|
return sorted([
|
||||||
|
{
|
||||||
|
'path': path,
|
||||||
|
**get_page_metadata(os.path.basename(path)),
|
||||||
|
}
|
||||||
|
for path in glob(os.path.join(pages_dir, '*.md'))
|
||||||
|
], key=lambda page: page.get('published'), reverse=True)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/', methods=['GET'])
|
@app.route('/', methods=['GET'])
|
||||||
def home_route():
|
def home_route():
|
||||||
return get_page('Home', title='Platypush Blog')
|
return render_template('index.html', pages=get_pages())
|
||||||
|
|
||||||
|
|
||||||
@app.route('/favicon.ico', methods=['GET'])
|
@app.route('/favicon.ico', methods=['GET'])
|
||||||
|
@ -78,9 +96,6 @@ def css_route(style: str):
|
||||||
return send_from_directory(css_dir, style)
|
return send_from_directory(css_dir, style)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/<path:path>', methods=['GET'])
|
@app.route('/article/<article>', methods=['GET'])
|
||||||
def catch_all(path: str):
|
def article_route(article: str):
|
||||||
if path.endswith('.md'):
|
return get_page(article)
|
||||||
return get_page(path)
|
|
||||||
|
|
||||||
abort(404)
|
|
||||||
|
|
|
@ -1,72 +1,3 @@
|
||||||
html {
|
|
||||||
font-size: calc(1em + 1vw);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1024px) {
|
|
||||||
html {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, a:visited {
|
|
||||||
color: #555;
|
|
||||||
border-bottom: 1px dashed #999;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 3em;
|
|
||||||
padding: 0 .5em;
|
|
||||||
box-shadow: 1px 3px 3px 0 #bbb;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 767px) {
|
|
||||||
header {
|
|
||||||
height: 4em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
header > a {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .icon {
|
|
||||||
background: url(/img/icon.png);
|
|
||||||
background-size: 40px;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
display: inline-flex;
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .title {
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
height: calc(100% - 3em);
|
|
||||||
overflow: auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
font-family: Avenir, Palatino, Georgia, Verdana, Helvetica, Arial, sans-serif;
|
|
||||||
padding: 0 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
main .content {
|
main .content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -79,31 +10,26 @@ main .content code, .codehilite {
|
||||||
font-size: .85em;
|
font-size: .85em;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
.description h3 {
|
||||||
font-size: 2em;
|
font-weight: normal;
|
||||||
line-height: 1.2em;
|
opacity: 0.6;
|
||||||
}
|
margin: -.5em auto .5em auto;
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.5em;
|
|
||||||
line-height: 1.1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.published-date {
|
.published-date {
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
opacity: .75;
|
opacity: .75;
|
||||||
margin-top: -1em;
|
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 1024px) {
|
@media screen and (max-width: 1024px) {
|
||||||
main .content, main .title {
|
main .container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1024px) {
|
@media screen and (min-width: 1024px) {
|
||||||
main .content, main .title {
|
main .container {
|
||||||
max-width: 768px;
|
max-width: 768px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
78
static/css/common.css
Normal file
78
static/css/common.css
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
html {
|
||||||
|
font-size: calc(1em + 1vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1024px) {
|
||||||
|
html {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
a, a:visited {
|
||||||
|
color: #555;
|
||||||
|
border-bottom: 1px dashed #999;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 3em;
|
||||||
|
padding: 0 .5em;
|
||||||
|
box-shadow: 1px 3px 3px 0 #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
header {
|
||||||
|
height: 4em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header > a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .icon {
|
||||||
|
background: url(/img/icon.png);
|
||||||
|
background-size: 40px;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: inline-flex;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .title {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
height: calc(100% - 3em);
|
||||||
|
overflow: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
font-family: Avenir, Palatino, Georgia, Verdana, Helvetica, Arial, sans-serif;
|
||||||
|
padding: 0 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
line-height: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
line-height: 1.1em;
|
||||||
|
}
|
94
static/css/home.css
Normal file
94
static/css/home.css
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
main {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.articles {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article {
|
||||||
|
display: block;
|
||||||
|
box-shadow: 0 1px 3px 1px #ddd;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-height: 30em;
|
||||||
|
color: black !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article:hover {
|
||||||
|
box-shadow: 0 1px 4px 2px #bcbcbc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.article {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) and (max-width: 990px) {
|
||||||
|
.article {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 990px) and (max-width: 1023px) {
|
||||||
|
.article {
|
||||||
|
width: 33%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1024px) and (max-width: 1279px) {
|
||||||
|
.article {
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1280px) {
|
||||||
|
.article {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.article .container {
|
||||||
|
height: 100%;
|
||||||
|
padding: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
height: 35%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article .title {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: .4em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: rgba(0, 0, 0, 0.7);
|
||||||
|
font-size: .9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.published-date {
|
||||||
|
font-size: .8em;
|
||||||
|
color: rgba(0, 0, 0, 0.4);
|
||||||
|
margin: .5em 0 2em 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 40px 0 0;
|
||||||
|
}
|
||||||
|
|
BIN
static/img/dashboard-1.png
Normal file
BIN
static/img/dashboard-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 727 KiB |
|
@ -1,4 +1,6 @@
|
||||||
[//]: # (title: Ultimate self-hosted automation with Platypush)
|
[//]: # (title: Ultimate self-hosted automation with Platypush)
|
||||||
|
[//]: # (description: Get started with Platypush to automate your smart home and beyond)
|
||||||
|
[//]: # (image: /img/dashboard-1.png)
|
||||||
[//]: # (published: 2019-07-28)
|
[//]: # (published: 2019-07-28)
|
||||||
|
|
||||||
In the last few years we have experienced a terrific spike of products and solutions targeting home automation and
|
In the last few years we have experienced a terrific spike of products and solutions targeting home automation and
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/common.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/blog.css">
|
<link rel="stylesheet" type="text/css" href="/css/blog.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/code.css">
|
<link rel="stylesheet" type="text/css" href="/css/code.css">
|
||||||
<title>{{ title }}</title>
|
<title>{{ title }}</title>
|
||||||
|
@ -9,23 +10,32 @@
|
||||||
<header>
|
<header>
|
||||||
<a href="/">
|
<a href="/">
|
||||||
<div class="icon"></div>
|
<div class="icon"></div>
|
||||||
<div class="title">Platypush Blog</div>
|
<div class="title">Platypush blog</div>
|
||||||
</a>
|
</a>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
<div class="container">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<h1>{{ title }}</h1>
|
<h1>{{ title }}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if description %}
|
||||||
|
<div class="description">
|
||||||
|
<h3>{{ description }}</h3>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if published %}
|
{% if published %}
|
||||||
<div class="published-date">
|
<div class="published-date">
|
||||||
Published on {{ published }}
|
Published on {{ published }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{{ content | safe }}
|
{{ content | safe }}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
48
templates/index.html
Normal file
48
templates/index.html
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/common.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/home.css">
|
||||||
|
<title>Platypush blog</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<a href="/">
|
||||||
|
<div class="icon"></div>
|
||||||
|
<div class="title">Platypush blog</div>
|
||||||
|
</a>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="articles">
|
||||||
|
{% for page in pages %}
|
||||||
|
<a class="article" href="{{ page['uri'] }}">
|
||||||
|
<div class="container">
|
||||||
|
{% if page['image'] %}
|
||||||
|
<div class="image">
|
||||||
|
<img src="{{ page['image'] }}" alt="">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="title">
|
||||||
|
{{ page['title'] }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if page['published'] %}
|
||||||
|
<div class="published-date">
|
||||||
|
{{ page['published'].strftime('%b %d, %Y') }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if page['description'] %}
|
||||||
|
<div class="description">
|
||||||
|
{{ page['description'] }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue