Start building a blog page
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
.vscode
|
.vscode
|
||||||
.buildinfo.json
|
.buildinfo.json
|
||||||
|
*.md
|
||||||
@@ -22,11 +22,23 @@ app = Flask(__name__)
|
|||||||
md_path = path.join(path.realpath(path.dirname(__file__)), path.normpath('../projects/'))
|
md_path = path.join(path.realpath(path.dirname(__file__)), path.normpath('../projects/'))
|
||||||
|
|
||||||
projects = ContentArea(
|
projects = ContentArea(
|
||||||
directory=LocalStorage(md_path),
|
directory=LocalStorage(path.join(md_path, 'projects')),
|
||||||
|
root_templ='projects.html',
|
||||||
|
post_templ='article.html',
|
||||||
name='projects',
|
name='projects',
|
||||||
import_name=__name__)
|
import_name=__name__
|
||||||
|
)
|
||||||
|
|
||||||
|
blog = ContentArea(
|
||||||
|
directory=LocalStorage(path.join(md_path, 'blog')),
|
||||||
|
root_templ='blog.html',
|
||||||
|
post_templ='post.html',
|
||||||
|
name='blog',
|
||||||
|
import_name=__name__
|
||||||
|
)
|
||||||
|
|
||||||
app.register_blueprint(projects, url_prefix='/projects')
|
app.register_blueprint(projects, url_prefix='/projects')
|
||||||
|
app.register_blueprint(blog, url_prefix='/blog')
|
||||||
app.register_blueprint(ContactForm('contact', __name__), url_prefix='/contact')
|
app.register_blueprint(ContactForm('contact', __name__), url_prefix='/contact')
|
||||||
app.register_blueprint(Links(path.join(md_path, 'links.json'), 'links', __name__), url_prefix='/links')
|
app.register_blueprint(Links(path.join(md_path, 'links.json'), 'links', __name__), url_prefix='/links')
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,11 @@ from flask import render_template, Response, Blueprint
|
|||||||
from .storage import LocalStorage
|
from .storage import LocalStorage
|
||||||
|
|
||||||
class ContentArea(Blueprint):
|
class ContentArea(Blueprint):
|
||||||
def __init__(self, directory: LocalStorage, *args, **kwargs):
|
def __init__(self, directory: LocalStorage, root_templ: str, post_templ: str, *args, **kwargs):
|
||||||
self.md_directory = directory
|
self.md_directory = directory
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.add_app_template_filter(self.category_title, 'category_title')
|
|
||||||
self.add_app_template_filter(self.human_date, 'human_date')
|
self.add_app_template_filter(self.human_date, 'human_date')
|
||||||
self.add_app_template_filter(self.to_html, 'to_html')
|
self.add_app_template_filter(self.to_html, 'to_html')
|
||||||
|
|
||||||
@@ -24,7 +23,9 @@ class ContentArea(Blueprint):
|
|||||||
self.add_url_rule('/', view_func=self.projects)
|
self.add_url_rule('/', view_func=self.projects)
|
||||||
self.add_url_rule('/category/<category_id>/', view_func=self.category)
|
self.add_url_rule('/category/<category_id>/', view_func=self.category)
|
||||||
self.add_url_rule('/<article_id>', view_func=self.article)
|
self.add_url_rule('/<article_id>', view_func=self.article)
|
||||||
#self.add_url_rule('/image/<image_name>', view_func=self.image)
|
|
||||||
|
self.root_templ = root_templ
|
||||||
|
self.post_templ = post_templ
|
||||||
|
|
||||||
def processor(self) -> dict:
|
def processor(self) -> dict:
|
||||||
''' Jninja processors '''
|
''' Jninja processors '''
|
||||||
@@ -35,13 +36,6 @@ class ContentArea(Blueprint):
|
|||||||
return ' '.join(all_text.split()[:200])
|
return ' '.join(all_text.split()[:200])
|
||||||
return dict(get_excerpt=get_excerpt)
|
return dict(get_excerpt=get_excerpt)
|
||||||
|
|
||||||
def category_title(self, category_id: str) -> str:
|
|
||||||
''' Jninja filter to get a category title by its ID '''
|
|
||||||
with self.md_directory.open('categories.json') as categories_file:
|
|
||||||
categories = json.load(categories_file)
|
|
||||||
|
|
||||||
return categories.get(category_id).get('title', '')
|
|
||||||
|
|
||||||
def human_date(self, iso_date: str) -> str:
|
def human_date(self, iso_date: str) -> str:
|
||||||
''' Jninja filter to convert an ISO date to human readable. '''
|
''' Jninja filter to convert an ISO date to human readable. '''
|
||||||
try:
|
try:
|
||||||
@@ -90,7 +84,7 @@ class ContentArea(Blueprint):
|
|||||||
error='There\'s nothing here... yet.',
|
error='There\'s nothing here... yet.',
|
||||||
description='I\'m still working on this page. Check back soon for some content.')
|
description='I\'m still working on this page. Check back soon for some content.')
|
||||||
|
|
||||||
return render_template('projects.html',
|
return render_template(self.root_templ,
|
||||||
articles=articles_to_return,
|
articles=articles_to_return,
|
||||||
all_categories=categories,
|
all_categories=categories,
|
||||||
title='Projects',
|
title='Projects',
|
||||||
@@ -123,7 +117,7 @@ class ContentArea(Blueprint):
|
|||||||
error='There\'s nothing here... yet.',
|
error='There\'s nothing here... yet.',
|
||||||
description='I\'m still working on this page. Check back soon for some content.')
|
description='I\'m still working on this page. Check back soon for some content.')
|
||||||
|
|
||||||
return render_template('projects.html', articles=articles_to_return,
|
return render_template(self.root_templ, articles=articles_to_return,
|
||||||
title=the_category['title'],
|
title=the_category['title'],
|
||||||
description=the_category['long_description'],
|
description=the_category['long_description'],
|
||||||
page_title=f'{the_category["title"]} - ',
|
page_title=f'{the_category["title"]} - ',
|
||||||
@@ -140,6 +134,6 @@ class ContentArea(Blueprint):
|
|||||||
return Response(status=500)
|
return Response(status=500)
|
||||||
|
|
||||||
the_article = articles[0]
|
the_article = articles[0]
|
||||||
return render_template('article.html', post=markdown(the_article.content),
|
return render_template(self.post_templ, post=markdown(the_article.content),
|
||||||
metadata=the_article.metadata,
|
metadata=the_article.metadata,
|
||||||
page_title=f'{the_article.metadata["title"]} - ')
|
page_title=f'{the_article.metadata["title"]} - ')
|
||||||
Executable
+57
@@ -0,0 +1,57 @@
|
|||||||
|
{% extends 'main.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<main id="blog-main">
|
||||||
|
<nav id="filter">
|
||||||
|
<label for="filter_category">Filter:</label>
|
||||||
|
<select id="filter_category" name="filter_category" onchange="update_filter()">
|
||||||
|
<option value="all">All Categories</option>
|
||||||
|
{% for category in all_categories %}
|
||||||
|
<option {{ 'selected' if current_category==category }} value="{{ category }}">
|
||||||
|
{{all_categories.get(category).get('title')}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</nav>
|
||||||
|
<h2>{{ title }}</h2>
|
||||||
|
<p>{{ description }}</p>
|
||||||
|
<p style="text-align: center; font-weight: bold; font-style: italic;">This page is relatively new. I'm adding projects as fast as I can but feel free to <a href="/contact">contact me</a> if you want to know about something in particular.</p>
|
||||||
|
<section id="blog">
|
||||||
|
{% for row in articles %}
|
||||||
|
<div class="blog-post">
|
||||||
|
{% if row.get('link') is not none %}
|
||||||
|
<a href="{{ row.link }}">
|
||||||
|
{% else %}
|
||||||
|
<a href="/blog/{{ row.id }}">
|
||||||
|
{% endif %}
|
||||||
|
<img class="blogpost-thumb"
|
||||||
|
alt="{{ row.alt }}"
|
||||||
|
srcset="
|
||||||
|
{% for i in range(200, 5100, 100) %}
|
||||||
|
/image/{{ row.image }}?w={{i}} {{i}}w{{"," if not loop.last}}
|
||||||
|
{% endfor %}
|
||||||
|
"
|
||||||
|
sizes="
|
||||||
|
(max-width: 999px) 80vw,
|
||||||
|
(min-width: 1000px) 20vw
|
||||||
|
"
|
||||||
|
src="/image/{{ row.image }}">
|
||||||
|
</a>
|
||||||
|
<div class="blogpost-text">
|
||||||
|
{% if row.get('link') is not none %}
|
||||||
|
<a href="{{ row.link }}">
|
||||||
|
{% else %}
|
||||||
|
<a href="/blog/{{ row.id }}">
|
||||||
|
{% endif %}
|
||||||
|
<h3>{{ row.title }}</h3>
|
||||||
|
</a>
|
||||||
|
<p class="blogpost-description">{{ row.description }}</p>
|
||||||
|
<p class="blogpost-date">{{ row.date | human_date }}</p>
|
||||||
|
{% for category in row.categories %}
|
||||||
|
<a class="blogpost-category" href="/projects/category/{{ category }}"> {{ all_categories.get(category, {}).get('title', '') }}{{ ", " if not loop.last }}</loop></a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
||||||
@@ -59,6 +59,7 @@
|
|||||||
<nav id="top-nav">
|
<nav id="top-nav">
|
||||||
<a href="/">About</a>
|
<a href="/">About</a>
|
||||||
<a href="/projects/">Projects</a>
|
<a href="/projects/">Projects</a>
|
||||||
|
<a href="/blog">Blog</a>
|
||||||
<a href="/contact/">Contact</a>
|
<a href="/contact/">Contact</a>
|
||||||
<a href="/links/">Links</a>
|
<a href="/links/">Links</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
Executable
+64
@@ -0,0 +1,64 @@
|
|||||||
|
{% extends 'main.html' %}
|
||||||
|
{% block description %}{{ metadata.description }}{% endblock %}
|
||||||
|
{% block head %}
|
||||||
|
{% if metadata.gallery %}
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded",function(){
|
||||||
|
post_gallery = new Viewer(document.getElementById('article'), {
|
||||||
|
'url': 'realsrc',
|
||||||
|
'title': false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "BlogPosting",
|
||||||
|
"headline": {{ metadata.title | tojson }},
|
||||||
|
"description": {{ metadata.description | tojson }},
|
||||||
|
"datePublished": "{{ metadata.date }}",
|
||||||
|
{% if metadata.updated %}
|
||||||
|
"dateModified": "{{ metadata.updated }}",
|
||||||
|
{% endif %}
|
||||||
|
"image": "https://{{ url }}/image/{{ metadata.image }}",
|
||||||
|
"author": {
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "Jake Charman",
|
||||||
|
"url": "https://{{ url }}/"
|
||||||
|
},
|
||||||
|
"publisher": {
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "Jake Charman",
|
||||||
|
"url": "https://{{ url }}/"
|
||||||
|
},
|
||||||
|
"mainEntityOfPage": {
|
||||||
|
"@type": "WebPage",
|
||||||
|
"@id": "https://{{ url }}/writing/{{ metadata.id }}"
|
||||||
|
},
|
||||||
|
"url": "https://{{ url }}/writing/{{ metadata.id }}",
|
||||||
|
"inLanguage": "en-GB"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
"itemListElement": [
|
||||||
|
{"@type": "ListItem", "position": 1, "name": "Projects", "item": "https://{{url}}/projects/"},
|
||||||
|
{"@type": "ListItem", "position": 2, "name": {{ metadata.title | tojson }}, "item": "https://{{url}}/projects/{{ metadata.id }}"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<main>
|
||||||
|
<section id="article">
|
||||||
|
<h1>{{ metadata.title}} </h1>
|
||||||
|
<p>{{ metadata.date | human_date }}</p>
|
||||||
|
<hr />
|
||||||
|
{{post|safe}}
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
<p class="article-description">{{ row.description }}</p>
|
<p class="article-description">{{ row.description }}</p>
|
||||||
<p class="article-date">{{ row.date | human_date }}</p>
|
<p class="article-date">{{ row.date | human_date }}</p>
|
||||||
{% for category in row.categories %}
|
{% for category in row.categories %}
|
||||||
<a class="article-category" href="/projects/category/{{ category }}">{{ category | category_title }}{{ ", " if not loop.last }}</loop></a>
|
<a class="article-category" href="/projects/category/{{ category }}"> {{ all_categories.get(category, {}).get('title', '') }}{{ ", " if not loop.last }}</loop></a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
function update_filter() {
|
function update_filter() {
|
||||||
|
var section = window.location.pathname.split('/')[1]
|
||||||
var project_filter = document.getElementById("filter_category");
|
var project_filter = document.getElementById("filter_category");
|
||||||
console.log(project_filter.value)
|
console.log(project_filter.value)
|
||||||
if (project_filter.value == 'all') {
|
if (project_filter.value == 'all') {
|
||||||
window.location.href = '/projects';
|
window.location.href = '/'+section;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
window.location.href = '/projects/category/' + project_filter.value;
|
window.location.href = '/'+section+'/category/' + project_filter.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,4 +129,8 @@
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
height: 14vw;
|
height: 14vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.blogpost-thumb {
|
||||||
|
width: 12.5vw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -338,3 +338,17 @@ pre{
|
|||||||
height: 31vw;
|
height: 31vw;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.blog-post {
|
||||||
|
border: 5px solid #e5e5e5;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px;
|
||||||
|
border-radius: 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blogpost-thumb {
|
||||||
|
float: left;
|
||||||
|
padding-right: 20px;
|
||||||
|
width: 25vw;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user