Add links
This commit is contained in:
@@ -1,19 +1,21 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
from os import environ
|
from os import environ, path
|
||||||
import threading
|
import threading
|
||||||
import logging
|
import logging
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from os import path
|
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urlsplit
|
||||||
from re import match
|
from re import match
|
||||||
import json
|
import json
|
||||||
|
from io import BytesIO
|
||||||
from requests import post
|
from requests import post
|
||||||
from flask import Flask, render_template, Response, url_for, request
|
from flask import Flask, render_template, Response, url_for, request, send_from_directory, make_response
|
||||||
|
from PIL import Image, UnidentifiedImageError
|
||||||
from .content import ContentArea
|
from .content import ContentArea
|
||||||
from .contact import ContactForm
|
from .contact import ContactForm
|
||||||
from .storage import LocalStorage
|
from .storage import LocalStorage
|
||||||
|
from .links import Links
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
@@ -26,6 +28,7 @@ projects = ContentArea(
|
|||||||
|
|
||||||
app.register_blueprint(projects, url_prefix='/projects')
|
app.register_blueprint(projects, url_prefix='/projects')
|
||||||
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')
|
||||||
|
|
||||||
class DiscordLogger(logging.Handler):
|
class DiscordLogger(logging.Handler):
|
||||||
''' Simple logging handler to send a message to Discord '''
|
''' Simple logging handler to send a message to Discord '''
|
||||||
@@ -145,3 +148,43 @@ def sitemap():
|
|||||||
ET.SubElement(url, 'lastmod').text = article.metadata['date'].strftime('%Y-%m-%d')
|
ET.SubElement(url, 'lastmod').text = article.metadata['date'].strftime('%Y-%m-%d')
|
||||||
|
|
||||||
return Response(ET.tostring(root, encoding='utf-8'), 200, {'content-type': 'application/xml'})
|
return Response(ET.tostring(root, encoding='utf-8'), 200, {'content-type': 'application/xml'})
|
||||||
|
|
||||||
|
@app.route('/image/<image_name>')
|
||||||
|
def image( image_name: str) -> Response:
|
||||||
|
''' Resize and return an image. '''
|
||||||
|
md_directory = LocalStorage(md_path)
|
||||||
|
|
||||||
|
w = int(request.args.get('w', 0))
|
||||||
|
h = int(request.args.get('h', 0))
|
||||||
|
|
||||||
|
if w == 0 and h == 0:
|
||||||
|
return send_from_directory(md_directory.uri, path.join('images', image_name))
|
||||||
|
try:
|
||||||
|
the_image = Image.open(path.join(md_directory.uri, 'images', image_name))
|
||||||
|
except FileNotFoundError:
|
||||||
|
return Response(status=404)
|
||||||
|
except UnidentifiedImageError:
|
||||||
|
return send_from_directory(md_directory.uri, path.join('images', image_name))
|
||||||
|
|
||||||
|
max_width, max_height = the_image.size
|
||||||
|
|
||||||
|
if (w >= max_width and h >= max_height):
|
||||||
|
return send_from_directory(md_directory.uri, path.join('images', image_name))
|
||||||
|
|
||||||
|
if path.exists(path.join('images', f'{w}-{h}-{image_name}')):
|
||||||
|
return send_from_directory(md_directory.uri, path.join('images', f'{w}-{h}-{image_name}'))
|
||||||
|
|
||||||
|
req_size = [max_width, max_height]
|
||||||
|
if w > 0:
|
||||||
|
req_size[0] = w
|
||||||
|
if h > 0:
|
||||||
|
req_size[1] = h
|
||||||
|
|
||||||
|
resized_img = BytesIO()
|
||||||
|
the_image.thumbnail(tuple(req_size))
|
||||||
|
the_image.save(resized_img, format=the_image.format)
|
||||||
|
the_image.save(path.join(md_directory.uri, 'images', f'{w}-{h}-{image_name}'), the_image.format)
|
||||||
|
|
||||||
|
response = make_response(resized_img.getvalue())
|
||||||
|
response.headers.set('Content-Type', f'image/{the_image.format}')
|
||||||
|
return response
|
||||||
|
|||||||
@@ -3,12 +3,10 @@
|
|||||||
from os import path
|
from os import path
|
||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from io import BytesIO
|
|
||||||
from PIL import Image, UnidentifiedImageError
|
|
||||||
import frontmatter
|
import frontmatter
|
||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from flask import render_template, Response, send_from_directory, request, make_response, Blueprint
|
from flask import render_template, Response, Blueprint
|
||||||
from .storage import LocalStorage
|
from .storage import LocalStorage
|
||||||
|
|
||||||
class ContentArea(Blueprint):
|
class ContentArea(Blueprint):
|
||||||
@@ -26,7 +24,7 @@ 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.add_url_rule('/image/<image_name>', view_func=self.image)
|
||||||
|
|
||||||
def processor(self) -> dict:
|
def processor(self) -> dict:
|
||||||
''' Jninja processors '''
|
''' Jninja processors '''
|
||||||
@@ -140,41 +138,4 @@ class ContentArea(Blueprint):
|
|||||||
the_article = articles[0]
|
the_article = articles[0]
|
||||||
return render_template('article.html', post=markdown(the_article.content),
|
return render_template('article.html', 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"]} - ')
|
||||||
|
|
||||||
def image(self, image_name: str) -> Response:
|
|
||||||
''' Resize and return an image. '''
|
|
||||||
w = int(request.args.get('w', 0))
|
|
||||||
h = int(request.args.get('h', 0))
|
|
||||||
|
|
||||||
if w == 0 and h == 0:
|
|
||||||
return send_from_directory(self.md_directory.uri, path.join('images', image_name))
|
|
||||||
try:
|
|
||||||
the_image = Image.open(path.join(self.md_directory.uri, 'images', image_name))
|
|
||||||
except FileNotFoundError:
|
|
||||||
return Response(status=404)
|
|
||||||
except UnidentifiedImageError:
|
|
||||||
return send_from_directory(self.md_directory.uri, path.join('images', image_name))
|
|
||||||
|
|
||||||
max_width, max_height = the_image.size
|
|
||||||
|
|
||||||
if (w >= max_width and h >= max_height):
|
|
||||||
return send_from_directory(self.md_directory.uri, path.join('images', image_name))
|
|
||||||
|
|
||||||
if path.exists(path.join('images', f'{w}-{h}-{image_name}')):
|
|
||||||
return send_from_directory(self.md_directory.uri, path.join('images', f'{w}-{h}-{image_name}'))
|
|
||||||
|
|
||||||
req_size = [max_width, max_height]
|
|
||||||
if w > 0:
|
|
||||||
req_size[0] = w
|
|
||||||
if h > 0:
|
|
||||||
req_size[1] = h
|
|
||||||
|
|
||||||
resized_img = BytesIO()
|
|
||||||
the_image.thumbnail(tuple(req_size))
|
|
||||||
the_image.save(resized_img, format=the_image.format)
|
|
||||||
the_image.save(path.join(self.md_directory.uri, 'images', f'{w}-{h}-{image_name}'), the_image.format)
|
|
||||||
|
|
||||||
response = make_response(resized_img.getvalue())
|
|
||||||
response.headers.set('Content-Type', f'image/{the_image.format}')
|
|
||||||
return response
|
|
||||||
13
src/jakecharman/links.py
Normal file
13
src/jakecharman/links.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from flask import Blueprint, render_template
|
||||||
|
import json
|
||||||
|
|
||||||
|
class Links(Blueprint):
|
||||||
|
def __init__(self, file, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.add_url_rule('/', view_func=self.links)
|
||||||
|
self.file = file
|
||||||
|
|
||||||
|
def links(self):
|
||||||
|
with open(self.file, encoding='utf8') as file:
|
||||||
|
links = json.load(file)
|
||||||
|
return render_template('links.html', links=links, page_title='Useful Links - ')
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
<a href="/">About</a>
|
<a href="/">About</a>
|
||||||
<a href="/projects/">Projects</a>
|
<a href="/projects/">Projects</a>
|
||||||
<a href="/contact/">Contact</a>
|
<a href="/contact/">Contact</a>
|
||||||
|
<a href="/links/">Links</a>
|
||||||
</nav>
|
</nav>
|
||||||
<div id="logo-container">
|
<div id="logo-container">
|
||||||
<div id="logo">
|
<div id="logo">
|
||||||
|
|||||||
28
src/jakecharman/templates/links.html
Normal file
28
src/jakecharman/templates/links.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{% include 'header.html' %}
|
||||||
|
<main id="links-main">
|
||||||
|
<h2>Useful Links</h2>
|
||||||
|
<section id="links">
|
||||||
|
{% for link in links %}
|
||||||
|
<div class="link">
|
||||||
|
{% if link.get('img') is not none %}
|
||||||
|
<a href="{{link.src}}" target="_blank"></a>
|
||||||
|
<img class="link-thumb"
|
||||||
|
srcset="
|
||||||
|
{% for i in range(200, 5100, 100) %}
|
||||||
|
/image/{{ link.img }}?w={{i}} {{i}}w{{"," if not loop.last}}
|
||||||
|
{% endfor %}
|
||||||
|
"
|
||||||
|
sizes="
|
||||||
|
(max-width: 999px) 80vw,
|
||||||
|
(min-width: 1000px) 20vw
|
||||||
|
"
|
||||||
|
src="/image/{{ link.img }}">
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{link.src}}" target="_blank"><h3>{{ link.title }}</h3></a>
|
||||||
|
<p>{{ link.description }}</p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
{% include 'footer.html' %}
|
||||||
@@ -24,14 +24,14 @@
|
|||||||
<img class="project-thumb"
|
<img class="project-thumb"
|
||||||
srcset="
|
srcset="
|
||||||
{% for i in range(200, 5100, 100) %}
|
{% for i in range(200, 5100, 100) %}
|
||||||
/projects/image/{{ row.image }}?w={{i}} {{i}}w{{"," if not loop.last}}
|
/image/{{ row.image }}?w={{i}} {{i}}w{{"," if not loop.last}}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
"
|
"
|
||||||
sizes="
|
sizes="
|
||||||
(max-width: 999px) 80vw,
|
(max-width: 999px) 80vw,
|
||||||
(min-width: 1000px) 20vw
|
(min-width: 1000px) 20vw
|
||||||
"
|
"
|
||||||
src="/projects/image/{{ row.image }}">
|
src="/image/{{ row.image }}">
|
||||||
</a>
|
</a>
|
||||||
<div class="project-text">
|
<div class="project-text">
|
||||||
{% if row.get('link') is not none %}
|
{% if row.get('link') is not none %}
|
||||||
|
|||||||
@@ -114,4 +114,12 @@
|
|||||||
.gallery>img {
|
.gallery>img {
|
||||||
width: calc(33% - 40px);
|
width: calc(33% - 40px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#links {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
.link>a>h3 {
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,4 +281,24 @@ pre{
|
|||||||
.gallery * {
|
.gallery * {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
max-width: 80vw;
|
max-width: 80vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
#links-main{
|
||||||
|
padding: 20px 10px 0 10px;
|
||||||
|
min-height: 65vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
border-top: 1px solid #e5e5e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link>img {
|
||||||
|
float: left;
|
||||||
|
padding-right: 20px;
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link>a>h3 {
|
||||||
|
padding-top: 10px;
|
||||||
|
margin: 0
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user