From 8cb13a4bca412009abe0497449ec711d3e58a19a Mon Sep 17 00:00:00 2001 From: Jake Charman Date: Thu, 2 Jan 2025 20:34:22 +0000 Subject: [PATCH 1/5] Begin porting code from NJR --- src/.gitattributes => .gitattributes | 0 Dockerfile | 8 +- config/httpd.conf | 440 ++++++++++++++++++ src/common.py | 11 + src/main.py | 12 + src/projects.py | 109 +++++ src/projects.wsgi | 6 + src/requirements.txt | 5 + .../fonts/fontawesome/css/all.min.css | 0 .../fontawesome/webfonts/fa-brands-400.ttf | Bin .../fontawesome/webfonts/fa-brands-400.woff2 | Bin .../fontawesome/webfonts/fa-regular-400.ttf | Bin .../fontawesome/webfonts/fa-regular-400.woff2 | Bin .../fontawesome/webfonts/fa-solid-900.ttf | Bin .../fontawesome/webfonts/fa-solid-900.woff2 | Bin .../webfonts/fa-v4compatibility.ttf | Bin .../webfonts/fa-v4compatibility.woff2 | Bin src/{ => static}/images/njr-code.png | 0 .../images/topfuel_startline.jpg.jpeg | 0 src/{ => static}/index.html | 0 src/{ => static}/style/desktop.css | 0 src/{ => static}/style/mobile.css | 0 src/templates/article.html | 2 + src/templates/error.html | 5 + src/templates/footer.html | 5 + src/templates/header.html | 19 + src/templates/projects.html | 2 + 27 files changed, 623 insertions(+), 1 deletion(-) rename src/.gitattributes => .gitattributes (100%) create mode 100644 config/httpd.conf create mode 100644 src/common.py create mode 100644 src/main.py create mode 100644 src/projects.py create mode 100644 src/projects.wsgi create mode 100644 src/requirements.txt rename src/{ => static}/fonts/fontawesome/css/all.min.css (100%) rename src/{ => static}/fonts/fontawesome/webfonts/fa-brands-400.ttf (100%) rename src/{ => static}/fonts/fontawesome/webfonts/fa-brands-400.woff2 (100%) rename src/{ => static}/fonts/fontawesome/webfonts/fa-regular-400.ttf (100%) rename src/{ => static}/fonts/fontawesome/webfonts/fa-regular-400.woff2 (100%) rename src/{ => static}/fonts/fontawesome/webfonts/fa-solid-900.ttf (100%) rename src/{ => static}/fonts/fontawesome/webfonts/fa-solid-900.woff2 (100%) rename src/{ => static}/fonts/fontawesome/webfonts/fa-v4compatibility.ttf (100%) rename src/{ => static}/fonts/fontawesome/webfonts/fa-v4compatibility.woff2 (100%) rename src/{ => static}/images/njr-code.png (100%) rename src/{ => static}/images/topfuel_startline.jpg.jpeg (100%) rename src/{ => static}/index.html (100%) rename src/{ => static}/style/desktop.css (100%) rename src/{ => static}/style/mobile.css (100%) create mode 100644 src/templates/article.html create mode 100644 src/templates/error.html create mode 100644 src/templates/footer.html create mode 100644 src/templates/header.html create mode 100644 src/templates/projects.html diff --git a/src/.gitattributes b/.gitattributes similarity index 100% rename from src/.gitattributes rename to .gitattributes diff --git a/Dockerfile b/Dockerfile index 6bc8406..ac9f62c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,2 +1,8 @@ FROM httpd:2.4 -COPY --chown=apache:apache src/ /usr/local/apache2/htdocs +RUN apt-get update +RUN apt-get -y install libapache2-mod-wsgi-py3 python3 python3-pip +COPY src/requirements.txt /var/www/jc/requirements.txt +RUN pip3 install -r /var/www/jc/requirements.txt || pip3 install --break-system-packages -r /var/www/jc/requirements.txt +COPY --chown=www-data:www-data config/httpd.conf /usr/local/apache2/conf/httpd.conf +COPY --chown=www-data:www-data src/ /var/www/jc +RUN httpd -t \ No newline at end of file diff --git a/config/httpd.conf b/config/httpd.conf new file mode 100644 index 0000000..5ad9e3c --- /dev/null +++ b/config/httpd.conf @@ -0,0 +1,440 @@ +# +# This is the main Apache HTTP server configuration file. It contains the +# configuration directives that give the server its instructions. +# See for detailed information. +# In particular, see +# +# for a discussion of each configuration directive. +# +# Do NOT simply read the instructions in here without understanding +# what they do. They're here only as hints or reminders. If you are unsure +# consult the online docs. You have been warned. +# +# Configuration and logfile names: If the filenames you specify for many +# of the server's control files begin with "/" (or "drive:/" for Win32), the +# server will use that explicit path. If the filenames do *not* begin +# with "/", the value of ServerRoot is prepended -- so "logs/access_log" +# with ServerRoot set to "/usr/local/apache2" will be interpreted by the +# server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log" +# will be interpreted as '/logs/access_log'. + +# +# ServerRoot: The top of the directory tree under which the server's +# configuration, error, and log files are kept. +# +# Do not add a slash at the end of the directory path. If you point +# ServerRoot at a non-local disk, be sure to specify a local disk on the +# Mutex directive, if file-based mutexes are used. If you wish to share the +# same ServerRoot for multiple httpd daemons, you will need to change at +# least PidFile. +# +ServerRoot "/usr/local/apache2" + +# +# Mutex: Allows you to set the mutex mechanism and mutex file directory +# for individual mutexes, or change the global defaults +# +# Uncomment and change the directory if mutexes are file-based and the default +# mutex file directory is not on a local disk or is not appropriate for some +# other reason. +# +# Mutex default:logs + +# +# Listen: Allows you to bind Apache to specific IP addresses and/or +# ports, instead of the default. See also the +# directive. +# +# Change this to Listen on specific IP addresses as shown below to +# prevent Apache from glomming onto all bound IP addresses. +# +#Listen 12.34.56.78:80 +Listen 80 + +# +# Dynamic Shared Object (DSO) Support +# +# To be able to use the functionality of a module which was built as a DSO you +# have to place corresponding `LoadModule' lines at this location so the +# directives contained in it are actually available _before_ they are used. +# Statically compiled modules (those listed by `httpd -l') do not need +# to be loaded here. +# +# Example: +# LoadModule foo_module modules/mod_foo.so +# +LoadModule mpm_event_module modules/mod_mpm_event.so +#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so +#LoadModule mpm_worker_module modules/mod_mpm_worker.so +LoadModule authn_file_module modules/mod_authn_file.so +#LoadModule authn_dbm_module modules/mod_authn_dbm.so +#LoadModule authn_anon_module modules/mod_authn_anon.so +#LoadModule authn_dbd_module modules/mod_authn_dbd.so +#LoadModule authn_socache_module modules/mod_authn_socache.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule authz_groupfile_module modules/mod_authz_groupfile.so +LoadModule authz_user_module modules/mod_authz_user.so +#LoadModule authz_dbm_module modules/mod_authz_dbm.so +#LoadModule authz_owner_module modules/mod_authz_owner.so +#LoadModule authz_dbd_module modules/mod_authz_dbd.so +LoadModule authz_core_module modules/mod_authz_core.so +#LoadModule authnz_ldap_module modules/mod_authnz_ldap.so +#LoadModule authnz_fcgi_module modules/mod_authnz_fcgi.so +LoadModule access_compat_module modules/mod_access_compat.so +LoadModule auth_basic_module modules/mod_auth_basic.so +#LoadModule auth_form_module modules/mod_auth_form.so +#LoadModule auth_digest_module modules/mod_auth_digest.so +#LoadModule allowmethods_module modules/mod_allowmethods.so +#LoadModule isapi_module modules/mod_isapi.so +#LoadModule file_cache_module modules/mod_file_cache.so +#LoadModule cache_module modules/mod_cache.so +#LoadModule cache_disk_module modules/mod_cache_disk.so +#LoadModule cache_socache_module modules/mod_cache_socache.so +#LoadModule socache_shmcb_module modules/mod_socache_shmcb.so +#LoadModule socache_dbm_module modules/mod_socache_dbm.so +#LoadModule socache_memcache_module modules/mod_socache_memcache.so +#LoadModule socache_redis_module modules/mod_socache_redis.so +#LoadModule watchdog_module modules/mod_watchdog.so +#LoadModule macro_module modules/mod_macro.so +#LoadModule dbd_module modules/mod_dbd.so +#LoadModule bucketeer_module modules/mod_bucketeer.so +#LoadModule dumpio_module modules/mod_dumpio.so +#LoadModule echo_module modules/mod_echo.so +#LoadModule example_hooks_module modules/mod_example_hooks.so +#LoadModule case_filter_module modules/mod_case_filter.so +#LoadModule case_filter_in_module modules/mod_case_filter_in.so +#LoadModule example_ipc_module modules/mod_example_ipc.so +#LoadModule buffer_module modules/mod_buffer.so +#LoadModule data_module modules/mod_data.so +#LoadModule ratelimit_module modules/mod_ratelimit.so +LoadModule reqtimeout_module modules/mod_reqtimeout.so +#LoadModule ext_filter_module modules/mod_ext_filter.so +#LoadModule request_module modules/mod_request.so +#LoadModule include_module modules/mod_include.so +LoadModule filter_module modules/mod_filter.so +#LoadModule reflector_module modules/mod_reflector.so +#LoadModule substitute_module modules/mod_substitute.so +#LoadModule sed_module modules/mod_sed.so +#LoadModule charset_lite_module modules/mod_charset_lite.so +LoadModule deflate_module modules/mod_deflate.so +#LoadModule xml2enc_module modules/mod_xml2enc.so +#LoadModule proxy_html_module modules/mod_proxy_html.so +#LoadModule brotli_module modules/mod_brotli.so +LoadModule mime_module modules/mod_mime.so +#LoadModule ldap_module modules/mod_ldap.so +LoadModule log_config_module modules/mod_log_config.so +#LoadModule log_debug_module modules/mod_log_debug.so +#LoadModule log_forensic_module modules/mod_log_forensic.so +#LoadModule logio_module modules/mod_logio.so +#LoadModule lua_module modules/mod_lua.so +LoadModule env_module modules/mod_env.so +#LoadModule mime_magic_module modules/mod_mime_magic.so +#LoadModule cern_meta_module modules/mod_cern_meta.so +#LoadModule expires_module modules/mod_expires.so +LoadModule headers_module modules/mod_headers.so +#LoadModule ident_module modules/mod_ident.so +#LoadModule usertrack_module modules/mod_usertrack.so +#LoadModule unique_id_module modules/mod_unique_id.so +LoadModule setenvif_module modules/mod_setenvif.so +LoadModule version_module modules/mod_version.so +#LoadModule remoteip_module modules/mod_remoteip.so +#LoadModule proxy_module modules/mod_proxy.so +#LoadModule proxy_connect_module modules/mod_proxy_connect.so +#LoadModule proxy_ftp_module modules/mod_proxy_ftp.so +#LoadModule proxy_http_module modules/mod_proxy_http.so +#LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so +#LoadModule proxy_scgi_module modules/mod_proxy_scgi.so +#LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so +#LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so +#LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so +#LoadModule proxy_ajp_module modules/mod_proxy_ajp.so +#LoadModule proxy_balancer_module modules/mod_proxy_balancer.so +#LoadModule proxy_express_module modules/mod_proxy_express.so +#LoadModule proxy_hcheck_module modules/mod_proxy_hcheck.so +#LoadModule session_module modules/mod_session.so +#LoadModule session_cookie_module modules/mod_session_cookie.so +#LoadModule session_crypto_module modules/mod_session_crypto.so +#LoadModule session_dbd_module modules/mod_session_dbd.so +#LoadModule slotmem_shm_module modules/mod_slotmem_shm.so +#LoadModule slotmem_plain_module modules/mod_slotmem_plain.so +#LoadModule ssl_module modules/mod_ssl.so +#LoadModule optional_hook_export_module modules/mod_optional_hook_export.so +#LoadModule optional_hook_import_module modules/mod_optional_hook_import.so +#LoadModule optional_fn_import_module modules/mod_optional_fn_import.so +#LoadModule optional_fn_export_module modules/mod_optional_fn_export.so +#LoadModule dialup_module modules/mod_dialup.so +#LoadModule http2_module modules/mod_http2.so +#LoadModule proxy_http2_module modules/mod_proxy_http2.so +#LoadModule md_module modules/mod_md.so +#LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so +#LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so +#LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so +#LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so +LoadModule unixd_module modules/mod_unixd.so +#LoadModule heartbeat_module modules/mod_heartbeat.so +#LoadModule heartmonitor_module modules/mod_heartmonitor.so +#LoadModule dav_module modules/mod_dav.so +LoadModule status_module modules/mod_status.so +LoadModule autoindex_module modules/mod_autoindex.so +#LoadModule asis_module modules/mod_asis.so +#LoadModule info_module modules/mod_info.so +#LoadModule suexec_module modules/mod_suexec.so + + #LoadModule cgid_module modules/mod_cgid.so + + + #LoadModule cgi_module modules/mod_cgi.so + +#LoadModule dav_fs_module modules/mod_dav_fs.so +#LoadModule dav_lock_module modules/mod_dav_lock.so +#LoadModule vhost_alias_module modules/mod_vhost_alias.so +#LoadModule negotiation_module modules/mod_negotiation.so +LoadModule dir_module modules/mod_dir.so +#LoadModule imagemap_module modules/mod_imagemap.so +#LoadModule actions_module modules/mod_actions.so +#LoadModule speling_module modules/mod_speling.so +#LoadModule userdir_module modules/mod_userdir.so +LoadModule alias_module modules/mod_alias.so +#LoadModule rewrite_module modules/mod_rewrite.somod_wsgi-express start-server + +LoadModule wsgi_module /usr/lib/apache2/modules/mod_wsgi.so + + + +# +# If you wish httpd to run as a different user or group, you must run +# httpd as root initially and it will switch. +# +# User/Group: The name (or #number) of the user/group to run httpd as. +# It is usually good practice to create a dedicated user and group for +# running httpd, as with most system services. +# +User www-data +Group www-data + + + +# 'Main' server configuration +# +# The directives in this section set up the values used by the 'main' +# server, which responds to any requests that aren't handled by a +# definition. These values also provide defaults for +# any containers you may define later in the file. +# +# All of these directives may appear inside containers, +# in which case these default settings will be overridden for the +# virtual host being defined. +# + +# +# ServerAdmin: Your address, where problems with the server should be +# e-mailed. This address appears on some server-generated pages, such +# as error documents. e.g. admin@your-domain.com +# +ServerAdmin jake@nitrousjunkie.com + +# +# ServerName gives the name and port that the server uses to identify itself. +# This can often be determined automatically, but we recommend you specify +# it explicitly to prevent problems during startup. +# +# If your host doesn't have a registered DNS name, enter its IP address here. +# +#ServerName www.example.com:80 + +# +# Deny access to the entirety of your server's filesystem. You must +# explicitly permit access to web content directories in other +# blocks below. +# + + AllowOverride none + Require all denied + + +# +# DirectoryIndex: sets the file that Apache will serve if a directory +# is requested. +# + + DirectoryIndex index.html + + +# +# The following lines prevent .htaccess and .htpasswd files from being +# viewed by Web clients. +# + + Require all denied + + +# +# ErrorLog: The location of the error log file. +# If you do not specify an ErrorLog directive within a +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# container, that host's errors will be logged there and not here. +# +ErrorLog /proc/self/fd/2 + +# +# LogLevel: Control the number of messages logged to the error_log. +# Possible values include: debug, info, notice, warn, error, crit, +# alert, emerg. +# +LogLevel warn + + + # + # The following directives define some format nicknames for use with + # a CustomLog directive (see below). + # + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined + LogFormat "%h %l %u %t \"%r\" %>s %b" common + + + # You need to enable mod_logio.c to use %I and %O + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio + + + # + # The location and format of the access logfile (Common Logfile Format). + # If you do not define any access logfiles within a + # container, they will be logged here. Contrariwise, if you *do* + # define per- access logfiles, transactions will be + # logged therein and *not* in this file. + # + CustomLog /proc/self/fd/1 common + + # + # If you prefer a logfile with access, agent, and referer information + # (Combined Logfile Format) you can use the following directive. + # + #CustomLog "logs/access_log" combined + + + + # + # Redirect: Allows you to tell clients about documents that used to + # exist in your server's namespace, but do not anymore. The client + # will make a new request for the document at its new location. + # Example: + # Redirect permanent /foo http://www.example.com/bar + + # + # Alias: Maps web paths into filesystem paths and is used to + # access content that does not live under the DocumentRoot. + # Example: + # Alias /webpath /full/filesystem/path + # + # If you include a trailing / on /webpath then the server will + # require it to be present in the URL. You will also likely + # need to provide a section to allow access to + # the filesystem path. + + # + # ScriptAlias: This controls which directories contain server scripts. + # ScriptAliases are essentially the same as Aliases, except that + # documents in the target directory are treated as applications and + # run by the server when requested rather than as documents sent to the + # client. The same rules about trailing "/" apply to ScriptAlias + # directives as to Alias. + # + ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/" + + + + + # + # ScriptSock: On threaded servers, designate the path to the UNIX + # socket used to communicate with the CGI daemon of mod_cgid. + # + #Scriptsock cgisock + + + + + # + # Avoid passing HTTP_PROXY environment to CGI's on this or any proxied + # backend servers which have lingering "httpoxy" defects. + # 'Proxy' request header is undefined by the IETF, not listed by IANA + # + RequestHeader unset Proxy early + + + + # + # TypesConfig points to the file containing the list of mappings from + # filename extension to MIME-type. + # + TypesConfig conf/mime.types + + # + # AddType allows you to add to or override the MIME configuration + # file specified in TypesConfig for specific file types. + # + #AddType application/x-gzip .tgz + # + # AddEncoding allows you to have certain browsers uncompress + # information on the fly. Note: Not all browsers support this. + # + #AddEncoding x-compress .Z + #AddEncoding x-gzip .gz .tgz + # + # If the AddEncoding directives above are commented-out, then you + # probably should define those extensions to indicate media types: + # + AddType application/x-compress .Z + AddType application/x-gzip .gz .tgz + + # + # AddHandler allows you to map certain file extensions to "handlers": + # actions unrelated to filetype. These can be either built into the server + # or added with the Action directive (see below) + # + # To use CGI scripts outside of ScriptAliased directories: + # (You will also need to add "ExecCGI" to the "Options" directive.) + # + #AddHandler cgi-script .cgi + + # For type maps (negotiated resources): + #AddHandler type-map var + + # + # Filters allow you to process content before it is sent to the client. + # + # To parse .shtml files for server-side includes (SSI): + # (You will also need to add "Includes" to the "Options" directive.) + # + #AddType text/html .shtml + #AddOutputFilter INCLUDES .shtml + + +AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript +DocumentRoot /var/www/jc/static +WSGIErrorOverride On +ErrorDocument 400 /error/400 +ErrorDocument 403 /error/403 +ErrorDocument 404 /error/404 +#ErrorDocument 418 /error/418 +ErrorDocument 500 /error/500 +ErrorDocument 503 /error/503 +ErrorDocument 505 /error/505 + +WSGISocketPrefix /var/run/wsgi + +WSGIDaemonProcess jc-wsgi user=www-data group=www-data threads=5 +WSGIProcessGroup jc-wsgi +WSGIScriptAlias /projects /var/www/jc/projects.wsgi + + + WSGIProcessGroup jc-wsgi + WSGIApplicationGroup %{GLOBAL} + Require all granted + + + + Require all granted + + diff --git a/src/common.py b/src/common.py new file mode 100644 index 0000000..43438eb --- /dev/null +++ b/src/common.py @@ -0,0 +1,11 @@ +#!/usr/bin/python3 + +import frontmatter +from glob import glob +from os import path +from main import app +from datetime import datetime +from bs4 import BeautifulSoup +from markdown import markdown + + diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..1939bcb --- /dev/null +++ b/src/main.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 + +from flask import Flask, render_template + +application = Flask(__name__) +app = application + +import projects +import common + +# Load the homepage. + diff --git a/src/projects.py b/src/projects.py new file mode 100644 index 0000000..c9841f9 --- /dev/null +++ b/src/projects.py @@ -0,0 +1,109 @@ +#!/usr/bin/python3 + +from main import application as app +import common +from os import path +import json +from flask import render_template, Response, send_from_directory +from markdown import markdown + +md_directory = path.join(path.realpath(path.dirname(__file__)), path.normpath('projects/')) + +@app.context_processor +def processor(): + def get_excerpt(post): + html = markdown(post.content) + post_soup = BeautifulSoup(html, 'html.parser') + all_text = ' '.join([x.get_text() for x in post_soup.findAll('p')]) + return ' '.join(all_text.split()[:200]) + return dict(get_excerpt=get_excerpt) + +@app.template_filter('human_date') +def human_date(iso_date: str) -> str: + try: + return datetime.fromisoformat(str(iso_date)).strftime('%A %d %B %Y') + except ValueError: + return iso_date + +@app.template_filter('to_html') +def to_html(content): + return markdown(content) + +def get_all_posts(directory: str) -> list: + abs_paths = [path.join(directory, x) for x in glob(f'{directory}/*.md')] + return [frontmatter.load(x) for x in abs_paths] + +def get_by_meta_key(directory: str, key: str, value: str): + return [x for x in get_all_posts(directory) if x.get(key) == value] + +@app.route('/') +def index(): + return "Hello, World!" + +@app.route('/error/') +def error(code): + error_definitions = { + 400: 'Bad Request', + 403: 'Forbidden', + 404: 'Page Not Found', + 418: 'I\'m a Teapot', + 500: 'Internal Server Error', + 503: 'Service Temporarily Unavailable', + 505: 'HTTP Version Not Supported' + } + error_desc = { + 400: 'Sorry, we didn\'t understand your request.', + 403: 'Sorry, you aren\'t allowed to view this page.', + 404: 'Sorry, that page doesn\'t exist.', + 418: 'I can\'t brew coffee as I am, in fact, a teapot.', + 500: 'Something went wrong on our end.', + 503: 'Our website is experiencing some issues and will be back shortly.', + 505: 'Your browser tried to use a HTTP version we don\'t support. Check it is up to date.' + } + errorText = f''' +
+

{code}: {error_definitions.get(int(code))}

+

{error_desc.get(int(code))}

+ Click here to return to our homepage +
+ ''' + return render_template('error.html', post=errorText) + +@app.route('//') +def category(category): + with open(path.join(md_directory, 'categories.json')) as categories_file: + categories = json.load(categories_file) + + the_category = next((x for x in categories if x.get('id') == category), None) + + if the_category is None: + return Response(status=404) + + articles_to_return = sorted( + common.get_by_meta_key( + md_directory, 'category', category), + key=lambda d: d.metadata.get('date'), + reverse=True + ) + + return render_template('projects.html', articles=articles_to_return, + title=the_category['title'], + description=the_category['long_description'], + pageName=f'{the_category["title"]} -') + +@app.route('//
') +def article(category, article): + articles = [x for x in common.get_by_meta_key(md_directory, 'id', article) if x.metadata.get('category') == category] + + if len(articles) == 0: + return Response(status=404) + if len(articles) > 1: + return Response(status=500) + + the_article = articles[0] + return render_template('article.html', post=markdown(the_article.content), metadata=the_article.metadata, + pageName=f'{the_article.metadata["title"]} - {the_article.metadata["author"]} -') + +@app.route('/image/') +def image(image): + return send_from_directory(path.join(md_directory, 'images'), image) diff --git a/src/projects.wsgi b/src/projects.wsgi new file mode 100644 index 0000000..2c855ac --- /dev/null +++ b/src/projects.wsgi @@ -0,0 +1,6 @@ +#!/usr/bin/python3 + +import sys +sys.path.append('/var/www/jc') + +from main import application diff --git a/src/requirements.txt b/src/requirements.txt new file mode 100644 index 0000000..e2d8dd1 --- /dev/null +++ b/src/requirements.txt @@ -0,0 +1,5 @@ +flask>=2.2.3 +flask-markdown>=0.3 +markdown>=3.4.1 +beautifulsoup4>=4.11.1 +python-frontmatter>=1.1.0 diff --git a/src/fonts/fontawesome/css/all.min.css b/src/static/fonts/fontawesome/css/all.min.css similarity index 100% rename from src/fonts/fontawesome/css/all.min.css rename to src/static/fonts/fontawesome/css/all.min.css diff --git a/src/fonts/fontawesome/webfonts/fa-brands-400.ttf b/src/static/fonts/fontawesome/webfonts/fa-brands-400.ttf similarity index 100% rename from src/fonts/fontawesome/webfonts/fa-brands-400.ttf rename to src/static/fonts/fontawesome/webfonts/fa-brands-400.ttf diff --git a/src/fonts/fontawesome/webfonts/fa-brands-400.woff2 b/src/static/fonts/fontawesome/webfonts/fa-brands-400.woff2 similarity index 100% rename from src/fonts/fontawesome/webfonts/fa-brands-400.woff2 rename to src/static/fonts/fontawesome/webfonts/fa-brands-400.woff2 diff --git a/src/fonts/fontawesome/webfonts/fa-regular-400.ttf b/src/static/fonts/fontawesome/webfonts/fa-regular-400.ttf similarity index 100% rename from src/fonts/fontawesome/webfonts/fa-regular-400.ttf rename to src/static/fonts/fontawesome/webfonts/fa-regular-400.ttf diff --git a/src/fonts/fontawesome/webfonts/fa-regular-400.woff2 b/src/static/fonts/fontawesome/webfonts/fa-regular-400.woff2 similarity index 100% rename from src/fonts/fontawesome/webfonts/fa-regular-400.woff2 rename to src/static/fonts/fontawesome/webfonts/fa-regular-400.woff2 diff --git a/src/fonts/fontawesome/webfonts/fa-solid-900.ttf b/src/static/fonts/fontawesome/webfonts/fa-solid-900.ttf similarity index 100% rename from src/fonts/fontawesome/webfonts/fa-solid-900.ttf rename to src/static/fonts/fontawesome/webfonts/fa-solid-900.ttf diff --git a/src/fonts/fontawesome/webfonts/fa-solid-900.woff2 b/src/static/fonts/fontawesome/webfonts/fa-solid-900.woff2 similarity index 100% rename from src/fonts/fontawesome/webfonts/fa-solid-900.woff2 rename to src/static/fonts/fontawesome/webfonts/fa-solid-900.woff2 diff --git a/src/fonts/fontawesome/webfonts/fa-v4compatibility.ttf b/src/static/fonts/fontawesome/webfonts/fa-v4compatibility.ttf similarity index 100% rename from src/fonts/fontawesome/webfonts/fa-v4compatibility.ttf rename to src/static/fonts/fontawesome/webfonts/fa-v4compatibility.ttf diff --git a/src/fonts/fontawesome/webfonts/fa-v4compatibility.woff2 b/src/static/fonts/fontawesome/webfonts/fa-v4compatibility.woff2 similarity index 100% rename from src/fonts/fontawesome/webfonts/fa-v4compatibility.woff2 rename to src/static/fonts/fontawesome/webfonts/fa-v4compatibility.woff2 diff --git a/src/images/njr-code.png b/src/static/images/njr-code.png similarity index 100% rename from src/images/njr-code.png rename to src/static/images/njr-code.png diff --git a/src/images/topfuel_startline.jpg.jpeg b/src/static/images/topfuel_startline.jpg.jpeg similarity index 100% rename from src/images/topfuel_startline.jpg.jpeg rename to src/static/images/topfuel_startline.jpg.jpeg diff --git a/src/index.html b/src/static/index.html similarity index 100% rename from src/index.html rename to src/static/index.html diff --git a/src/style/desktop.css b/src/static/style/desktop.css similarity index 100% rename from src/style/desktop.css rename to src/static/style/desktop.css diff --git a/src/style/mobile.css b/src/static/style/mobile.css similarity index 100% rename from src/style/mobile.css rename to src/static/style/mobile.css diff --git a/src/templates/article.html b/src/templates/article.html new file mode 100644 index 0000000..badd2e7 --- /dev/null +++ b/src/templates/article.html @@ -0,0 +1,2 @@ +{% include 'header.html' %} +{% include 'footer.html' %} \ No newline at end of file diff --git a/src/templates/error.html b/src/templates/error.html new file mode 100644 index 0000000..4d715f4 --- /dev/null +++ b/src/templates/error.html @@ -0,0 +1,5 @@ +{% include 'header.html' %} +
+ {{ post | safe }} +
+{% include 'footer.html' %} \ No newline at end of file diff --git a/src/templates/footer.html b/src/templates/footer.html new file mode 100644 index 0000000..56cdd8e --- /dev/null +++ b/src/templates/footer.html @@ -0,0 +1,5 @@ +
+

© 2024 Jake Charman. This site uses cookies.

+
+ + diff --git a/src/templates/header.html b/src/templates/header.html new file mode 100644 index 0000000..3c0d097 --- /dev/null +++ b/src/templates/header.html @@ -0,0 +1,19 @@ + + + + + Jake Charman + + + + + + + + + +
+ +
diff --git a/src/templates/projects.html b/src/templates/projects.html new file mode 100644 index 0000000..badd2e7 --- /dev/null +++ b/src/templates/projects.html @@ -0,0 +1,2 @@ +{% include 'header.html' %} +{% include 'footer.html' %} \ No newline at end of file From e0e60830be5e49e89a7a51c54607349ca45fa497 Mon Sep 17 00:00:00 2001 From: Jake Charman Date: Thu, 2 Jan 2025 22:33:21 +0000 Subject: [PATCH 2/5] Frontend work --- src/common.py | 11 ----------- src/main.py | 12 ------------ src/projects.py | 23 +++++++++++++++++------ src/projects.wsgi | 2 +- src/static/style/desktop.css | 13 +++++++++++++ src/static/style/mobile.css | 11 +++++++++++ src/templates/header.html | 6 +++--- src/templates/projects.html | 17 +++++++++++++++++ 8 files changed, 62 insertions(+), 33 deletions(-) delete mode 100644 src/common.py delete mode 100644 src/main.py diff --git a/src/common.py b/src/common.py deleted file mode 100644 index 43438eb..0000000 --- a/src/common.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/python3 - -import frontmatter -from glob import glob -from os import path -from main import app -from datetime import datetime -from bs4 import BeautifulSoup -from markdown import markdown - - diff --git a/src/main.py b/src/main.py deleted file mode 100644 index 1939bcb..0000000 --- a/src/main.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/python3 - -from flask import Flask, render_template - -application = Flask(__name__) -app = application - -import projects -import common - -# Load the homepage. - diff --git a/src/projects.py b/src/projects.py index c9841f9..819b76b 100644 --- a/src/projects.py +++ b/src/projects.py @@ -1,12 +1,16 @@ #!/usr/bin/python3 -from main import application as app -import common from os import path import json -from flask import render_template, Response, send_from_directory +from flask import Flask, render_template, Response, send_from_directory from markdown import markdown +import frontmatter +from glob import glob +from datetime import datetime + +application = Flask(__name__) +app = application md_directory = path.join(path.realpath(path.dirname(__file__)), path.normpath('projects/')) @app.context_processor @@ -38,7 +42,14 @@ def get_by_meta_key(directory: str, key: str, value: str): @app.route('/') def index(): - return "Hello, World!" + articles_to_return = sorted( + get_all_posts( + md_directory), + key=lambda d: d.metadata.get('date'), + reverse=True + ) + + return render_template('projects.html', articles=articles_to_return) @app.route('/error/') def error(code): @@ -80,7 +91,7 @@ def category(category): return Response(status=404) articles_to_return = sorted( - common.get_by_meta_key( + get_by_meta_key( md_directory, 'category', category), key=lambda d: d.metadata.get('date'), reverse=True @@ -93,7 +104,7 @@ def category(category): @app.route('//
') def article(category, article): - articles = [x for x in common.get_by_meta_key(md_directory, 'id', article) if x.metadata.get('category') == category] + articles = [x for x in get_by_meta_key(md_directory, 'id', article) if x.metadata.get('category') == category] if len(articles) == 0: return Response(status=404) diff --git a/src/projects.wsgi b/src/projects.wsgi index 2c855ac..92aa835 100644 --- a/src/projects.wsgi +++ b/src/projects.wsgi @@ -3,4 +3,4 @@ import sys sys.path.append('/var/www/jc') -from main import application +from projects import application diff --git a/src/static/style/desktop.css b/src/static/style/desktop.css index 17dd22f..5b09dc6 100644 --- a/src/static/style/desktop.css +++ b/src/static/style/desktop.css @@ -36,4 +36,17 @@ .gradient-right{ background-image: linear-gradient(to left, rgba(23, 22, 20, 1), rgba(23, 22, 20, 1), rgba(23, 22, 20, 0)); } + + #projects{ + align-items: center; + justify-content: center; + flex-direction: row; + flex-wrap: wrap; + } + + .project{ + width: 20vw; + height: 25vw; + margin: 10px; + } } \ No newline at end of file diff --git a/src/static/style/mobile.css b/src/static/style/mobile.css index 175d6e5..3b8ec82 100644 --- a/src/static/style/mobile.css +++ b/src/static/style/mobile.css @@ -90,4 +90,15 @@ footer h2, section h2{ a{ color: #e5e5e5; +} + +#projects{ + align-items: center; + display: flex; + flex-direction: column; +} + +.project{ + width: 80vw; + border: 2px solid rgba(23, 22, 20, 1); } \ No newline at end of file diff --git a/src/templates/header.html b/src/templates/header.html index 3c0d097..da80030 100644 --- a/src/templates/header.html +++ b/src/templates/header.html @@ -5,9 +5,9 @@ Jake Charman - - - + + + diff --git a/src/templates/projects.html b/src/templates/projects.html index badd2e7..a53de1a 100644 --- a/src/templates/projects.html +++ b/src/templates/projects.html @@ -1,2 +1,19 @@ {% include 'header.html' %} +
+

Projects

+

A selection of projects I've worked on.

+
+ {% for row in articles %} +
+ {% if row.get('link') is not none %} +

{{ row.title }}

+ {% else %} +

{{ row.title }}

+ {% endif %} +

{{ row.description }}

+ +
+ {% endfor %} +
+
{% include 'footer.html' %} \ No newline at end of file From 028103d32150068f3f74827a5d37e1c439dbd214 Mon Sep 17 00:00:00 2001 From: Jake Charman Date: Fri, 3 Jan 2025 22:08:04 +0000 Subject: [PATCH 3/5] Reworking of projects page --- config/httpd.conf | 4 +- src/index.py | 41 ++++++++++++++++ src/projects.py | 73 +++++++++++----------------- src/projects.wsgi | 2 +- src/projects/categories.json | 12 +++++ src/projects/test_01.md | 26 ++++++++++ src/projects/test_02.md | 26 ++++++++++ src/projects/test_03.md | 26 ++++++++++ src/static/js/filter_projects.js | 11 +++++ src/static/style/desktop.css | 1 - src/static/style/mobile.css | 14 +++++- src/templates/article.html | 8 +++ src/templates/error.html | 2 +- src/templates/header.html | 11 +++-- src/{static => templates}/index.html | 26 +--------- src/templates/projects.html | 21 ++++++-- 16 files changed, 220 insertions(+), 84 deletions(-) create mode 100644 src/index.py create mode 100644 src/projects/categories.json create mode 100644 src/projects/test_01.md create mode 100644 src/projects/test_02.md create mode 100644 src/projects/test_03.md create mode 100644 src/static/js/filter_projects.js rename src/{static => templates}/index.html (73%) diff --git a/config/httpd.conf b/config/httpd.conf index 5ad9e3c..ba14fcc 100644 --- a/config/httpd.conf +++ b/config/httpd.conf @@ -412,7 +412,7 @@ LogLevel warn AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript -DocumentRoot /var/www/jc/static +# DocumentRoot /var/www/jc/ WSGIErrorOverride On ErrorDocument 400 /error/400 ErrorDocument 403 /error/403 @@ -426,7 +426,7 @@ WSGISocketPrefix /var/run/wsgi WSGIDaemonProcess jc-wsgi user=www-data group=www-data threads=5 WSGIProcessGroup jc-wsgi -WSGIScriptAlias /projects /var/www/jc/projects.wsgi +WSGIScriptAlias / /var/www/jc/projects.wsgi WSGIProcessGroup jc-wsgi diff --git a/src/index.py b/src/index.py new file mode 100644 index 0000000..cbd2598 --- /dev/null +++ b/src/index.py @@ -0,0 +1,41 @@ +#!/usr/bin/python3 + +from flask import Flask, render_template, Response + +app = Flask(__name__) + +import projects + +@app.route('/') +def index(): + print('blah') + return render_template('index.html') + +@app.route('/error/') +def error(code): + error_definitions = { + 400: 'Bad Request', + 403: 'Forbidden', + 404: 'Page Not Found', + 418: 'I\'m a Teapot', + 500: 'Internal Server Error', + 503: 'Service Temporarily Unavailable', + 505: 'HTTP Version Not Supported' + } + error_desc = { + 400: 'Sorry, we didn\'t understand your request.', + 403: 'Sorry, you aren\'t allowed to view this page.', + 404: 'Sorry, that page doesn\'t exist.', + 418: 'I can\'t brew coffee as I am, in fact, a teapot.', + 500: 'Something went wrong on our end.', + 503: 'Our website is experiencing some issues and will be back shortly.', + 505: 'Your browser tried to use a HTTP version we don\'t support. Check it is up to date.' + } + errorText = f''' +
+

{code}: {error_definitions.get(int(code))}

+

{error_desc.get(int(code))}

+ Click here to return to our homepage +
+ ''' + return render_template('error.html', post=errorText) \ No newline at end of file diff --git a/src/projects.py b/src/projects.py index 819b76b..18877c8 100644 --- a/src/projects.py +++ b/src/projects.py @@ -7,10 +7,9 @@ from markdown import markdown import frontmatter from glob import glob from datetime import datetime +from index import app +from bs4 import BeautifulSoup - -application = Flask(__name__) -app = application md_directory = path.join(path.realpath(path.dirname(__file__)), path.normpath('projects/')) @app.context_processor @@ -22,6 +21,13 @@ def processor(): return ' '.join(all_text.split()[:200]) return dict(get_excerpt=get_excerpt) +@app.template_filter('category_title') +def category_title(category_id: str) -> str: + with open(path.join(md_directory, 'categories.json')) as categories_file: + categories = json.load(categories_file) + + return categories.get(category_id).get('title', '') + @app.template_filter('human_date') def human_date(iso_date: str) -> str: try: @@ -38,10 +44,10 @@ def get_all_posts(directory: str) -> list: return [frontmatter.load(x) for x in abs_paths] def get_by_meta_key(directory: str, key: str, value: str): - return [x for x in get_all_posts(directory) if x.get(key) == value] + return [x for x in get_all_posts(directory) if x.get(key) == value or type(x.get(key, [])) is list and value in x.get(key, [])] -@app.route('/') -def index(): +@app.route('/projects/') +def projects(): articles_to_return = sorted( get_all_posts( md_directory), @@ -49,50 +55,24 @@ def index(): reverse=True ) - return render_template('projects.html', articles=articles_to_return) + with open(path.join(md_directory, 'categories.json')) as categories_file: + categories = json.load(categories_file) -@app.route('/error/') -def error(code): - error_definitions = { - 400: 'Bad Request', - 403: 'Forbidden', - 404: 'Page Not Found', - 418: 'I\'m a Teapot', - 500: 'Internal Server Error', - 503: 'Service Temporarily Unavailable', - 505: 'HTTP Version Not Supported' - } - error_desc = { - 400: 'Sorry, we didn\'t understand your request.', - 403: 'Sorry, you aren\'t allowed to view this page.', - 404: 'Sorry, that page doesn\'t exist.', - 418: 'I can\'t brew coffee as I am, in fact, a teapot.', - 500: 'Something went wrong on our end.', - 503: 'Our website is experiencing some issues and will be back shortly.', - 505: 'Your browser tried to use a HTTP version we don\'t support. Check it is up to date.' - } - errorText = f''' -
-

{code}: {error_definitions.get(int(code))}

-

{error_desc.get(int(code))}

- Click here to return to our homepage -
- ''' - return render_template('error.html', post=errorText) + return render_template('projects.html', articles=articles_to_return, all_categories=categories) -@app.route('//') +@app.route('/projects/category//') def category(category): with open(path.join(md_directory, 'categories.json')) as categories_file: categories = json.load(categories_file) - the_category = next((x for x in categories if x.get('id') == category), None) + the_category = categories.get(category) if the_category is None: return Response(status=404) articles_to_return = sorted( get_by_meta_key( - md_directory, 'category', category), + md_directory, 'categories', category), key=lambda d: d.metadata.get('date'), reverse=True ) @@ -100,11 +80,16 @@ def category(category): return render_template('projects.html', articles=articles_to_return, title=the_category['title'], description=the_category['long_description'], - pageName=f'{the_category["title"]} -') + page_title=f'{the_category["title"]} - ', + all_categories=categories, + current_category=category) -@app.route('//
') -def article(category, article): - articles = [x for x in get_by_meta_key(md_directory, 'id', article) if x.metadata.get('category') == category] +@app.route('/projects/
') +def article(article): + articles = get_by_meta_key(md_directory, 'id', article) + print(articles) + for i in articles: + print(i.metadata) if len(articles) == 0: return Response(status=404) @@ -113,8 +98,8 @@ def article(category, article): the_article = articles[0] return render_template('article.html', post=markdown(the_article.content), metadata=the_article.metadata, - pageName=f'{the_article.metadata["title"]} - {the_article.metadata["author"]} -') + pageName=f'{the_article.metadata["title"]} - ') -@app.route('/image/') +@app.route('/projects/image/') def image(image): return send_from_directory(path.join(md_directory, 'images'), image) diff --git a/src/projects.wsgi b/src/projects.wsgi index 92aa835..02fed47 100644 --- a/src/projects.wsgi +++ b/src/projects.wsgi @@ -3,4 +3,4 @@ import sys sys.path.append('/var/www/jc') -from projects import application +from index import app as application diff --git a/src/projects/categories.json b/src/projects/categories.json new file mode 100644 index 0000000..583d527 --- /dev/null +++ b/src/projects/categories.json @@ -0,0 +1,12 @@ + { + "software": { + "title": "Software", + "description": "Projects involving software of some kind", + "long_description": "Projects involving software of some kind" + }, + "racing": { + "title": "Racing", + "description": "Projects to do with motorsport", + "long_description": "Projects to do with motorsport" + } + } \ No newline at end of file diff --git a/src/projects/test_01.md b/src/projects/test_01.md new file mode 100644 index 0000000..658978a --- /dev/null +++ b/src/projects/test_01.md @@ -0,0 +1,26 @@ +--- +id: software_and_racing +title: "Software and Racing" +date: 2000-07-13 +categories: [software, racing] +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eu tortor pellentesque, hendrerit elit feugiat, lobortis urna. Donec porttitor urna vitae augue scelerisque, sit amet maximus quam blandit. Quisque et diam ut ante tempus placerat. Ut pretium urna sollicitudin egestas mattis. Praesent eu arcu et ex maximus posuere pharetra bibendum dolor. Pellentesque id bibendum quam. Curabitur sapien tellus, vulputate a diam vitae, ultrices lobortis elit. Praesent sodales arcu magna, et commodo mi euismod in. + +Vivamus pellentesque molestie arcu, a varius risus cursus eget. Donec cursus massa quis pulvinar tincidunt. Fusce dignissim mi ac ultrices sagittis. Vestibulum aliquet nulla vel ex mattis porta. Praesent vitae lacus eu tortor tempor venenatis. Nam at ipsum in magna tristique tincidunt. Aliquam feugiat pharetra bibendum. Praesent vitae erat sed lorem congue scelerisque vel ut velit. Phasellus quis sapien ultricies, blandit neque nec, sollicitudin dolor. Aliquam ultricies iaculis dapibus. + +Quisque quis auctor tortor, tincidunt bibendum erat. Curabitur justo nulla, ultrices at velit vel, interdum iaculis libero. Vestibulum semper urna nibh, vitae iaculis dui mollis a. Quisque eget placerat massa. Vivamus vel nisl sit amet nibh ultricies egestas hendrerit a est. In finibus lacinia sapien. Etiam id erat dolor. + +Suspendisse tincidunt leo placerat, elementum nunc id, sagittis lorem. Praesent lectus mi, facilisis sed mauris et, imperdiet sagittis massa. Proin ultrices eros vel elit convallis, ut fermentum neque porta. Praesent sed ullamcorper leo, id dapibus mi. Pellentesque vitae mauris dolor. Suspendisse elementum, odio id tincidunt accumsan, ipsum sem fringilla metus, quis ultrices est est ac dolor. Integer tempus, sem rhoncus consectetur tristique, metus purus sodales leo, eget facilisis quam nunc sit amet neque. Nulla luctus mattis tempor. Sed varius sapien ac erat scelerisque sagittis. Ut augue nunc, vulputate vitae elementum eget, ornare vitae sem. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Maecenas magna neque, efficitur sed pharetra egestas, convallis vel augue. Quisque eu lacinia neque, id ultrices felis. Nunc semper vulputate metus, eget volutpat lectus volutpat eu. + +Praesent mauris sem, eleifend nec ultrices ut, pretium sed massa. Duis faucibus tortor ut dignissim sagittis. Sed odio massa, vulputate eu urna sit amet, tristique fringilla enim. Cras et augue sit amet tortor facilisis pharetra. Fusce eget malesuada sapien. Praesent ultrices sed augue id ornare. Donec eu vestibulum lectus. Ut sed tempor mi. Mauris cursus venenatis fermentum. + +Nunc vulputate auctor enim ut gravida. Proin a suscipit eros. Nunc convallis auctor purus, ut tincidunt lectus euismod et. Duis mattis id leo ac lacinia. Nam ullamcorper metus ullamcorper, commodo ipsum a, rutrum massa. Nam mattis maximus dolor a sagittis. Pellentesque tincidunt ligula nec tortor bibendum, et lobortis massa volutpat. Nulla faucibus aliquet orci, id semper arcu. Mauris facilisis tortor congue, hendrerit risus et, imperdiet ligula. Duis a ligula tempor quam volutpat commodo eu nec nisl. Cras feugiat ornare nisl vel aliquam. + +Quisque consectetur erat lorem, id condimentum mauris vehicula id. Nullam tempus bibendum arcu et tristique. Sed consequat, justo a efficitur feugiat, sapien diam pharetra eros, id commodo augue leo rutrum lectus. Duis aliquet mauris sed odio dapibus, a elementum ante tincidunt. Nulla dictum ultrices posuere. Praesent massa augue, placerat eleifend imperdiet vitae, consectetur non odio. Sed eget pulvinar est. Aenean id varius lorem. + +Vivamus eleifend tellus nec blandit volutpat. Maecenas tempor lorem vel nunc fringilla, eget commodo ipsum lacinia. Morbi elit diam, tincidunt sit amet risus et, eleifend convallis quam. Nunc tempor rhoncus posuere. Pellentesque odio turpis, efficitur eget felis sed, fringilla gravida magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer ipsum neque, varius ac tempor at, dapibus vel tortor. Sed a augue ultricies leo eleifend cursus nec a augue. Donec et elit velit. Mauris sem magna, ultrices sed pulvinar semper, vehicula hendrerit velit. Donec et lorem erat. Sed lobortis ligula vel massa malesuada blandit nec id mauris. + +Ut ullamcorper erat felis, vitae faucibus lorem tempus non. Donec ac nunc dapibus, vehicula turpis non, tincidunt velit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus quam nibh, feugiat non nunc eget, congue iaculis ex. In vulputate ligula tempus, rhoncus mi ac, scelerisque nibh. Donec nec libero vitae purus blandit egestas. Sed vitae metus ut massa posuere sollicitudin. Ut iaculis eu sem at accumsan. Ut sed tellus pretium, tincidunt dolor sed, pretium quam. Donec ultrices, risus ornare convallis facilisis, augue nibh maximus lacus, vel laoreet ligula diam sit amet tellus. Vestibulum eget nibh nec orci maximus volutpat quis vitae justo. Pellentesque vehicula, ante varius condimentum suscipit, ligula est placerat diam, elementum cursus orci tortor et nisl. Nulla ac rutrum nisi. Suspendisse nisi ipsum, bibendum nec sem quis, fermentum tincidunt libero. Ut ligula dolor, tincidunt a eros rhoncus, rhoncus faucibus risus. Sed tincidunt risus quis lorem mattis mattis. + +Pellentesque elementum, metus id interdum lobortis, libero tortor consequat turpis, quis aliquam nisi ligula a magna. Aenean nec aliquet lorem. Aliquam feugiat viverra tellus ut congue. Morbi a fermentum neque. Nulla facilisi. Maecenas consectetur tincidunt diam, non aliquet lorem tempor vestibulum. Morbi cursus, tellus eget congue ultricies, dolor dui porta ipsum, vel tincidunt nisi eros sed dolor. Fusce ut metus semper, malesuada erat a, tempus diam. Quisque ut pellentesque lorem, ut sodales augue. Proin bibendum consectetur ex, vitae lobortis erat sagittis nec. \ No newline at end of file diff --git a/src/projects/test_02.md b/src/projects/test_02.md new file mode 100644 index 0000000..64a880a --- /dev/null +++ b/src/projects/test_02.md @@ -0,0 +1,26 @@ +--- +id: racing +title: "Racing" +date: 2000-07-14 +categories: [racing] +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eu tortor pellentesque, hendrerit elit feugiat, lobortis urna. Donec porttitor urna vitae augue scelerisque, sit amet maximus quam blandit. Quisque et diam ut ante tempus placerat. Ut pretium urna sollicitudin egestas mattis. Praesent eu arcu et ex maximus posuere pharetra bibendum dolor. Pellentesque id bibendum quam. Curabitur sapien tellus, vulputate a diam vitae, ultrices lobortis elit. Praesent sodales arcu magna, et commodo mi euismod in. + +Vivamus pellentesque molestie arcu, a varius risus cursus eget. Donec cursus massa quis pulvinar tincidunt. Fusce dignissim mi ac ultrices sagittis. Vestibulum aliquet nulla vel ex mattis porta. Praesent vitae lacus eu tortor tempor venenatis. Nam at ipsum in magna tristique tincidunt. Aliquam feugiat pharetra bibendum. Praesent vitae erat sed lorem congue scelerisque vel ut velit. Phasellus quis sapien ultricies, blandit neque nec, sollicitudin dolor. Aliquam ultricies iaculis dapibus. + +Quisque quis auctor tortor, tincidunt bibendum erat. Curabitur justo nulla, ultrices at velit vel, interdum iaculis libero. Vestibulum semper urna nibh, vitae iaculis dui mollis a. Quisque eget placerat massa. Vivamus vel nisl sit amet nibh ultricies egestas hendrerit a est. In finibus lacinia sapien. Etiam id erat dolor. + +Suspendisse tincidunt leo placerat, elementum nunc id, sagittis lorem. Praesent lectus mi, facilisis sed mauris et, imperdiet sagittis massa. Proin ultrices eros vel elit convallis, ut fermentum neque porta. Praesent sed ullamcorper leo, id dapibus mi. Pellentesque vitae mauris dolor. Suspendisse elementum, odio id tincidunt accumsan, ipsum sem fringilla metus, quis ultrices est est ac dolor. Integer tempus, sem rhoncus consectetur tristique, metus purus sodales leo, eget facilisis quam nunc sit amet neque. Nulla luctus mattis tempor. Sed varius sapien ac erat scelerisque sagittis. Ut augue nunc, vulputate vitae elementum eget, ornare vitae sem. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Maecenas magna neque, efficitur sed pharetra egestas, convallis vel augue. Quisque eu lacinia neque, id ultrices felis. Nunc semper vulputate metus, eget volutpat lectus volutpat eu. + +Praesent mauris sem, eleifend nec ultrices ut, pretium sed massa. Duis faucibus tortor ut dignissim sagittis. Sed odio massa, vulputate eu urna sit amet, tristique fringilla enim. Cras et augue sit amet tortor facilisis pharetra. Fusce eget malesuada sapien. Praesent ultrices sed augue id ornare. Donec eu vestibulum lectus. Ut sed tempor mi. Mauris cursus venenatis fermentum. + +Nunc vulputate auctor enim ut gravida. Proin a suscipit eros. Nunc convallis auctor purus, ut tincidunt lectus euismod et. Duis mattis id leo ac lacinia. Nam ullamcorper metus ullamcorper, commodo ipsum a, rutrum massa. Nam mattis maximus dolor a sagittis. Pellentesque tincidunt ligula nec tortor bibendum, et lobortis massa volutpat. Nulla faucibus aliquet orci, id semper arcu. Mauris facilisis tortor congue, hendrerit risus et, imperdiet ligula. Duis a ligula tempor quam volutpat commodo eu nec nisl. Cras feugiat ornare nisl vel aliquam. + +Quisque consectetur erat lorem, id condimentum mauris vehicula id. Nullam tempus bibendum arcu et tristique. Sed consequat, justo a efficitur feugiat, sapien diam pharetra eros, id commodo augue leo rutrum lectus. Duis aliquet mauris sed odio dapibus, a elementum ante tincidunt. Nulla dictum ultrices posuere. Praesent massa augue, placerat eleifend imperdiet vitae, consectetur non odio. Sed eget pulvinar est. Aenean id varius lorem. + +Vivamus eleifend tellus nec blandit volutpat. Maecenas tempor lorem vel nunc fringilla, eget commodo ipsum lacinia. Morbi elit diam, tincidunt sit amet risus et, eleifend convallis quam. Nunc tempor rhoncus posuere. Pellentesque odio turpis, efficitur eget felis sed, fringilla gravida magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer ipsum neque, varius ac tempor at, dapibus vel tortor. Sed a augue ultricies leo eleifend cursus nec a augue. Donec et elit velit. Mauris sem magna, ultrices sed pulvinar semper, vehicula hendrerit velit. Donec et lorem erat. Sed lobortis ligula vel massa malesuada blandit nec id mauris. + +Ut ullamcorper erat felis, vitae faucibus lorem tempus non. Donec ac nunc dapibus, vehicula turpis non, tincidunt velit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus quam nibh, feugiat non nunc eget, congue iaculis ex. In vulputate ligula tempus, rhoncus mi ac, scelerisque nibh. Donec nec libero vitae purus blandit egestas. Sed vitae metus ut massa posuere sollicitudin. Ut iaculis eu sem at accumsan. Ut sed tellus pretium, tincidunt dolor sed, pretium quam. Donec ultrices, risus ornare convallis facilisis, augue nibh maximus lacus, vel laoreet ligula diam sit amet tellus. Vestibulum eget nibh nec orci maximus volutpat quis vitae justo. Pellentesque vehicula, ante varius condimentum suscipit, ligula est placerat diam, elementum cursus orci tortor et nisl. Nulla ac rutrum nisi. Suspendisse nisi ipsum, bibendum nec sem quis, fermentum tincidunt libero. Ut ligula dolor, tincidunt a eros rhoncus, rhoncus faucibus risus. Sed tincidunt risus quis lorem mattis mattis. + +Pellentesque elementum, metus id interdum lobortis, libero tortor consequat turpis, quis aliquam nisi ligula a magna. Aenean nec aliquet lorem. Aliquam feugiat viverra tellus ut congue. Morbi a fermentum neque. Nulla facilisi. Maecenas consectetur tincidunt diam, non aliquet lorem tempor vestibulum. Morbi cursus, tellus eget congue ultricies, dolor dui porta ipsum, vel tincidunt nisi eros sed dolor. Fusce ut metus semper, malesuada erat a, tempus diam. Quisque ut pellentesque lorem, ut sodales augue. Proin bibendum consectetur ex, vitae lobortis erat sagittis nec. \ No newline at end of file diff --git a/src/projects/test_03.md b/src/projects/test_03.md new file mode 100644 index 0000000..11ad411 --- /dev/null +++ b/src/projects/test_03.md @@ -0,0 +1,26 @@ +--- +id: software +title: "Software" +date: 2000-07-15 +categories: [software] +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eu tortor pellentesque, hendrerit elit feugiat, lobortis urna. Donec porttitor urna vitae augue scelerisque, sit amet maximus quam blandit. Quisque et diam ut ante tempus placerat. Ut pretium urna sollicitudin egestas mattis. Praesent eu arcu et ex maximus posuere pharetra bibendum dolor. Pellentesque id bibendum quam. Curabitur sapien tellus, vulputate a diam vitae, ultrices lobortis elit. Praesent sodales arcu magna, et commodo mi euismod in. + +Vivamus pellentesque molestie arcu, a varius risus cursus eget. Donec cursus massa quis pulvinar tincidunt. Fusce dignissim mi ac ultrices sagittis. Vestibulum aliquet nulla vel ex mattis porta. Praesent vitae lacus eu tortor tempor venenatis. Nam at ipsum in magna tristique tincidunt. Aliquam feugiat pharetra bibendum. Praesent vitae erat sed lorem congue scelerisque vel ut velit. Phasellus quis sapien ultricies, blandit neque nec, sollicitudin dolor. Aliquam ultricies iaculis dapibus. + +Quisque quis auctor tortor, tincidunt bibendum erat. Curabitur justo nulla, ultrices at velit vel, interdum iaculis libero. Vestibulum semper urna nibh, vitae iaculis dui mollis a. Quisque eget placerat massa. Vivamus vel nisl sit amet nibh ultricies egestas hendrerit a est. In finibus lacinia sapien. Etiam id erat dolor. + +Suspendisse tincidunt leo placerat, elementum nunc id, sagittis lorem. Praesent lectus mi, facilisis sed mauris et, imperdiet sagittis massa. Proin ultrices eros vel elit convallis, ut fermentum neque porta. Praesent sed ullamcorper leo, id dapibus mi. Pellentesque vitae mauris dolor. Suspendisse elementum, odio id tincidunt accumsan, ipsum sem fringilla metus, quis ultrices est est ac dolor. Integer tempus, sem rhoncus consectetur tristique, metus purus sodales leo, eget facilisis quam nunc sit amet neque. Nulla luctus mattis tempor. Sed varius sapien ac erat scelerisque sagittis. Ut augue nunc, vulputate vitae elementum eget, ornare vitae sem. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Maecenas magna neque, efficitur sed pharetra egestas, convallis vel augue. Quisque eu lacinia neque, id ultrices felis. Nunc semper vulputate metus, eget volutpat lectus volutpat eu. + +Praesent mauris sem, eleifend nec ultrices ut, pretium sed massa. Duis faucibus tortor ut dignissim sagittis. Sed odio massa, vulputate eu urna sit amet, tristique fringilla enim. Cras et augue sit amet tortor facilisis pharetra. Fusce eget malesuada sapien. Praesent ultrices sed augue id ornare. Donec eu vestibulum lectus. Ut sed tempor mi. Mauris cursus venenatis fermentum. + +Nunc vulputate auctor enim ut gravida. Proin a suscipit eros. Nunc convallis auctor purus, ut tincidunt lectus euismod et. Duis mattis id leo ac lacinia. Nam ullamcorper metus ullamcorper, commodo ipsum a, rutrum massa. Nam mattis maximus dolor a sagittis. Pellentesque tincidunt ligula nec tortor bibendum, et lobortis massa volutpat. Nulla faucibus aliquet orci, id semper arcu. Mauris facilisis tortor congue, hendrerit risus et, imperdiet ligula. Duis a ligula tempor quam volutpat commodo eu nec nisl. Cras feugiat ornare nisl vel aliquam. + +Quisque consectetur erat lorem, id condimentum mauris vehicula id. Nullam tempus bibendum arcu et tristique. Sed consequat, justo a efficitur feugiat, sapien diam pharetra eros, id commodo augue leo rutrum lectus. Duis aliquet mauris sed odio dapibus, a elementum ante tincidunt. Nulla dictum ultrices posuere. Praesent massa augue, placerat eleifend imperdiet vitae, consectetur non odio. Sed eget pulvinar est. Aenean id varius lorem. + +Vivamus eleifend tellus nec blandit volutpat. Maecenas tempor lorem vel nunc fringilla, eget commodo ipsum lacinia. Morbi elit diam, tincidunt sit amet risus et, eleifend convallis quam. Nunc tempor rhoncus posuere. Pellentesque odio turpis, efficitur eget felis sed, fringilla gravida magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer ipsum neque, varius ac tempor at, dapibus vel tortor. Sed a augue ultricies leo eleifend cursus nec a augue. Donec et elit velit. Mauris sem magna, ultrices sed pulvinar semper, vehicula hendrerit velit. Donec et lorem erat. Sed lobortis ligula vel massa malesuada blandit nec id mauris. + +Ut ullamcorper erat felis, vitae faucibus lorem tempus non. Donec ac nunc dapibus, vehicula turpis non, tincidunt velit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus quam nibh, feugiat non nunc eget, congue iaculis ex. In vulputate ligula tempus, rhoncus mi ac, scelerisque nibh. Donec nec libero vitae purus blandit egestas. Sed vitae metus ut massa posuere sollicitudin. Ut iaculis eu sem at accumsan. Ut sed tellus pretium, tincidunt dolor sed, pretium quam. Donec ultrices, risus ornare convallis facilisis, augue nibh maximus lacus, vel laoreet ligula diam sit amet tellus. Vestibulum eget nibh nec orci maximus volutpat quis vitae justo. Pellentesque vehicula, ante varius condimentum suscipit, ligula est placerat diam, elementum cursus orci tortor et nisl. Nulla ac rutrum nisi. Suspendisse nisi ipsum, bibendum nec sem quis, fermentum tincidunt libero. Ut ligula dolor, tincidunt a eros rhoncus, rhoncus faucibus risus. Sed tincidunt risus quis lorem mattis mattis. + +Pellentesque elementum, metus id interdum lobortis, libero tortor consequat turpis, quis aliquam nisi ligula a magna. Aenean nec aliquet lorem. Aliquam feugiat viverra tellus ut congue. Morbi a fermentum neque. Nulla facilisi. Maecenas consectetur tincidunt diam, non aliquet lorem tempor vestibulum. Morbi cursus, tellus eget congue ultricies, dolor dui porta ipsum, vel tincidunt nisi eros sed dolor. Fusce ut metus semper, malesuada erat a, tempus diam. Quisque ut pellentesque lorem, ut sodales augue. Proin bibendum consectetur ex, vitae lobortis erat sagittis nec. \ No newline at end of file diff --git a/src/static/js/filter_projects.js b/src/static/js/filter_projects.js new file mode 100644 index 0000000..1268f70 --- /dev/null +++ b/src/static/js/filter_projects.js @@ -0,0 +1,11 @@ +window.onload = function () { + var project_filter = document.getElementById("filter_category"); + project_filter.onchange = function () { + if (project_filter.value == 'all') { + window.location.href = '/projects'; + } + else { + window.location.href = '/projects/category/' + project_filter.value; + } + } +} \ No newline at end of file diff --git a/src/static/style/desktop.css b/src/static/style/desktop.css index 5b09dc6..ed53e09 100644 --- a/src/static/style/desktop.css +++ b/src/static/style/desktop.css @@ -47,6 +47,5 @@ .project{ width: 20vw; height: 25vw; - margin: 10px; } } \ No newline at end of file diff --git a/src/static/style/mobile.css b/src/static/style/mobile.css index 3b8ec82..05e0cbd 100644 --- a/src/static/style/mobile.css +++ b/src/static/style/mobile.css @@ -2,9 +2,14 @@ body{ margin: 0; color: #e5e5e5; font-family: "Noto Sans", sans-serif; + background-color: rgba(23, 22, 20, 1); } -h1, h2{ +main{ + padding: 0 10px 0 10px; +} + +h1, h2, h3{ font-family: "Orbitron", sans-serif; font-optical-sizing: auto; font-weight: 500; @@ -30,6 +35,10 @@ footer{ margin: 0 auto 0 auto; } +#logo>a{ + text-decoration: none; +} + footer h2, section h2{ margin: 0; } @@ -100,5 +109,6 @@ a{ .project{ width: 80vw; - border: 2px solid rgba(23, 22, 20, 1); + border: 2px solid #e5e5e5; + margin: 10px; } \ No newline at end of file diff --git a/src/templates/article.html b/src/templates/article.html index badd2e7..6e1524a 100644 --- a/src/templates/article.html +++ b/src/templates/article.html @@ -1,2 +1,10 @@ {% include 'header.html' %} +
+
+

{{ metadata.title}}

+

{{ metadata.date | human_date }}

+
+ {{post|safe}} +
+
{% include 'footer.html' %} \ No newline at end of file diff --git a/src/templates/error.html b/src/templates/error.html index 4d715f4..c47ef22 100644 --- a/src/templates/error.html +++ b/src/templates/error.html @@ -1,5 +1,5 @@ {% include 'header.html' %} -
+
{{ post | safe }}
{% include 'footer.html' %} \ No newline at end of file diff --git a/src/templates/header.html b/src/templates/header.html index da80030..b32347b 100644 --- a/src/templates/header.html +++ b/src/templates/header.html @@ -2,18 +2,19 @@ - Jake Charman + {{ page_title }}Jake Charman - - - + + + +
diff --git a/src/static/index.html b/src/templates/index.html similarity index 73% rename from src/static/index.html rename to src/templates/index.html index 5ec8744..9b82047 100644 --- a/src/static/index.html +++ b/src/templates/index.html @@ -1,22 +1,4 @@ - - - - - Jake Charman - - - - - - - - - -
- -
+{% include 'header.html' %}
@@ -60,8 +42,4 @@
-
-

© 2024 Jake Charman. This site uses cookies.

-
- - \ No newline at end of file +{% include 'footer.html' %} \ No newline at end of file diff --git a/src/templates/projects.html b/src/templates/projects.html index a53de1a..cb1709c 100644 --- a/src/templates/projects.html +++ b/src/templates/projects.html @@ -1,17 +1,30 @@ {% include 'header.html' %}
-

Projects

-

A selection of projects I've worked on.

+ +

{{ title }}

+

{{ description }}

{% for row in articles %}
+ {% if row.get('link') is not none %} -

{{ row.title }}

+

{{ row.title }}

{% else %} -

{{ row.title }}

+

{{ row.title }}

{% endif %}

{{ row.description }}

+ {% for category in row.categories %} + + {% endfor %}
{% endfor %}
From 032481d465f3dba67aa8bc15b6d97e12d3d6a6ab Mon Sep 17 00:00:00 2001 From: Jake Charman Date: Fri, 3 Jan 2025 23:25:38 +0000 Subject: [PATCH 4/5] Design work --- src/index.py | 12 +++---- src/projects.py | 4 +-- src/static/js/filter_projects.js | 17 +++++---- src/static/js/update_copyright.js | 5 +++ src/static/style/desktop.css | 7 ++++ src/static/style/mobile.css | 58 +++++++++++++++++++++++++------ src/templates/error.html | 12 +++++-- src/templates/footer.html | 2 +- src/templates/header.html | 11 ++++-- src/templates/projects.html | 4 +-- 10 files changed, 95 insertions(+), 37 deletions(-) create mode 100644 src/static/js/update_copyright.js diff --git a/src/index.py b/src/index.py index cbd2598..afda2a2 100644 --- a/src/index.py +++ b/src/index.py @@ -31,11 +31,7 @@ def error(code): 503: 'Our website is experiencing some issues and will be back shortly.', 505: 'Your browser tried to use a HTTP version we don\'t support. Check it is up to date.' } - errorText = f''' -
-

{code}: {error_definitions.get(int(code))}

-

{error_desc.get(int(code))}

- Click here to return to our homepage -
- ''' - return render_template('error.html', post=errorText) \ No newline at end of file + + return render_template('error.html', code=code, + description=error_definitions.get(int(code)), + long_description=error_desc.get(int(code))) \ No newline at end of file diff --git a/src/projects.py b/src/projects.py index 18877c8..394d2ef 100644 --- a/src/projects.py +++ b/src/projects.py @@ -58,13 +58,13 @@ def projects(): with open(path.join(md_directory, 'categories.json')) as categories_file: categories = json.load(categories_file) - return render_template('projects.html', articles=articles_to_return, all_categories=categories) + return render_template('projects.html', articles=articles_to_return, all_categories=categories, title='Projects', description='A selection of projects I\'ve been involved in') @app.route('/projects/category//') def category(category): with open(path.join(md_directory, 'categories.json')) as categories_file: categories = json.load(categories_file) - + print(categories) the_category = categories.get(category) if the_category is None: diff --git a/src/static/js/filter_projects.js b/src/static/js/filter_projects.js index 1268f70..6d772e0 100644 --- a/src/static/js/filter_projects.js +++ b/src/static/js/filter_projects.js @@ -1,11 +1,10 @@ -window.onload = function () { +function update_filter() { var project_filter = document.getElementById("filter_category"); - project_filter.onchange = function () { - if (project_filter.value == 'all') { - window.location.href = '/projects'; - } - else { - window.location.href = '/projects/category/' + project_filter.value; - } + console.log(project_filter.value) + if (project_filter.value == 'all') { + window.location.href = '/projects'; } -} \ No newline at end of file + else { + window.location.href = '/projects/category/' + project_filter.value; + } +} diff --git a/src/static/js/update_copyright.js b/src/static/js/update_copyright.js new file mode 100644 index 0000000..1ee3772 --- /dev/null +++ b/src/static/js/update_copyright.js @@ -0,0 +1,5 @@ +window.onload = function () { + var current_year = new Date().getFullYear(); + var copyright = document.getElementById('cr-year'); + copyright.textContent = current_year; +} \ No newline at end of file diff --git a/src/static/style/desktop.css b/src/static/style/desktop.css index ed53e09..7e89217 100644 --- a/src/static/style/desktop.css +++ b/src/static/style/desktop.css @@ -48,4 +48,11 @@ width: 20vw; height: 25vw; } + + #top-nav{ + float: right; + width: fit-content; + padding-top: 20px; + padding-right: 20px; + } } \ No newline at end of file diff --git a/src/static/style/mobile.css b/src/static/style/mobile.css index 05e0cbd..7c5ea9a 100644 --- a/src/static/style/mobile.css +++ b/src/static/style/mobile.css @@ -5,10 +5,6 @@ body{ background-color: rgba(23, 22, 20, 1); } -main{ - padding: 0 10px 0 10px; -} - h1, h2, h3{ font-family: "Orbitron", sans-serif; font-optical-sizing: auto; @@ -17,17 +13,23 @@ h1, h2, h3{ } header{ - width: 100%; - height: 25vh; - display: flex; - align-items: center; background-color: 4c4c4c; + height: 25vh; + width: 100%; } footer{ background-color: 4c4c4c; - padding: 30px; - text-align: center; + display: flex; + justify-content: center; + align-items: center; + height: 10vh; +} + +#logo-container{ + width: 100%; + justify-content: center; + display: flex; } #logo{ @@ -111,4 +113,40 @@ a{ width: 80vw; border: 2px solid #e5e5e5; margin: 10px; +} + +.project-thumb{ + max-width: 100%; + max-height: 100%; +} + +#filter { + float: right; +} + +#top-nav{ + width: 100%; + text-align: center; + padding-bottom: 30px; +} + +#top-nav>a{ + text-decoration: none; + padding: 0 10px 0 10px; + font-weight: bold; +} + +#top-nav>a:hover{ + text-decoration: underline; +} + +#error-container{ + display: flex; + align-items: center; + justify-content: center; + height: 65vh; +} + +#error{ + text-align: center; } \ No newline at end of file diff --git a/src/templates/error.html b/src/templates/error.html index c47ef22..4c405c0 100644 --- a/src/templates/error.html +++ b/src/templates/error.html @@ -1,5 +1,11 @@ {% include 'header.html' %} -
- {{ post | safe }} -
+
+
+
+

{{ code }}: {{ description }}

+

{{ long_description }}

+ Click here to return to our homepage +
+
+
{% include 'footer.html' %} \ No newline at end of file diff --git a/src/templates/footer.html b/src/templates/footer.html index 56cdd8e..4fb12d8 100644 --- a/src/templates/footer.html +++ b/src/templates/footer.html @@ -1,5 +1,5 @@
-

© 2024 Jake Charman. This site uses cookies.

+

© Jake Charman. This site uses cookies.

diff --git a/src/templates/header.html b/src/templates/header.html index b32347b..3939e7f 100644 --- a/src/templates/header.html +++ b/src/templates/header.html @@ -11,10 +11,17 @@ +
-
diff --git a/src/templates/projects.html b/src/templates/projects.html index cb1709c..001b5ed 100644 --- a/src/templates/projects.html +++ b/src/templates/projects.html @@ -2,7 +2,7 @@