Merge remote-tracking branch 'gitlab/quote-styling'
This commit is contained in:
commit
253c0f5f3f
17 changed files with 67449 additions and 23 deletions
13
.editorconfig
Normal file
13
.editorconfig
Normal file
|
@ -0,0 +1,13 @@
|
|||
# https://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{html,css}]
|
||||
indent_size = 2
|
15
app/app.py
15
app/app.py
|
@ -187,7 +187,20 @@ def random_quote():
|
|||
|
||||
@app.route('/robots.txt', methods=['GET'])
|
||||
def get_robots():
|
||||
return send_file('templates/robots.txt')
|
||||
return send_file('static/robots.txt')
|
||||
|
||||
|
||||
@app.route('/fonts/<filename>', methods=['GET'])
|
||||
def get_font(filename):
|
||||
if not re.fullmatch(r'[a-zA-Z0-9][a-zA-Z0-9._-]*\.(?:otf|svg|woff2?)', filename):
|
||||
return abort(404)
|
||||
|
||||
return send_file('static/fonts/' + filename)
|
||||
|
||||
|
||||
@app.route('/quotes.css', methods=['GET'])
|
||||
def get_quote_css():
|
||||
return send_file('static/quotes.css')
|
||||
|
||||
|
||||
@app.route('/quotes.html', methods=['GET'])
|
||||
|
|
|
@ -3,6 +3,10 @@ from datetime import datetime
|
|||
import re
|
||||
|
||||
|
||||
MONTHS = ["januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september",
|
||||
"oktober", "november", "december"]
|
||||
|
||||
|
||||
class User(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(255), unique=True, nullable=False)
|
||||
|
@ -46,3 +50,16 @@ class Quote(db.Model):
|
|||
# Experimentally try to find quoted user
|
||||
quotee_match = Quote.QUOTEE_REGEX.search(quote)
|
||||
self.quotee = quotee_match.group(1) if quotee_match is not None else None
|
||||
|
||||
def slur(self):
|
||||
return self.created_at.strftime("%Y-%m-%d_%H:%M:%S")
|
||||
|
||||
def created_at_machine(self):
|
||||
return self.created_at.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
|
||||
def created_at_human(self):
|
||||
c = self.created_at
|
||||
return "{} {} {:04}, {}:{:02}".format(
|
||||
c.day, MONTHS[c.month - 1], c.year,
|
||||
c.hour, c.minute
|
||||
)
|
||||
|
|
BIN
app/static/fonts/FiraSans-Bold.otf
Normal file
BIN
app/static/fonts/FiraSans-Bold.otf
Normal file
Binary file not shown.
33771
app/static/fonts/FiraSans-Bold.svg
Normal file
33771
app/static/fonts/FiraSans-Bold.svg
Normal file
File diff suppressed because it is too large
Load diff
After Width: | Height: | Size: 2.7 MiB |
BIN
app/static/fonts/FiraSans-Bold.woff
Normal file
BIN
app/static/fonts/FiraSans-Bold.woff
Normal file
Binary file not shown.
BIN
app/static/fonts/FiraSans-Bold.woff2
Normal file
BIN
app/static/fonts/FiraSans-Bold.woff2
Normal file
Binary file not shown.
BIN
app/static/fonts/FiraSans-Regular.otf
Normal file
BIN
app/static/fonts/FiraSans-Regular.otf
Normal file
Binary file not shown.
33448
app/static/fonts/FiraSans-Regular.svg
Normal file
33448
app/static/fonts/FiraSans-Regular.svg
Normal file
File diff suppressed because it is too large
Load diff
After Width: | Height: | Size: 2.6 MiB |
BIN
app/static/fonts/FiraSans-Regular.woff
Normal file
BIN
app/static/fonts/FiraSans-Regular.woff
Normal file
Binary file not shown.
BIN
app/static/fonts/FiraSans-Regular.woff2
Normal file
BIN
app/static/fonts/FiraSans-Regular.woff2
Normal file
Binary file not shown.
147
app/static/quotes.css
Normal file
147
app/static/quotes.css
Normal file
|
@ -0,0 +1,147 @@
|
|||
@font-face {
|
||||
font-family: "Fira Sans";
|
||||
font-weight: normal;
|
||||
src: local("Fira Sans"), local("Fira Sans Regular"),
|
||||
url(/fonts/FiraSans-Regular.otf) format("opentype"),
|
||||
url(/fonts/FiraSans-Regular.woff2) format("woff2"),
|
||||
url(/fonts/FiraSans-Regular.woff) format("woff"),
|
||||
url(/fonts/FiraSans-Regular.svg) format("svg");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Sans";
|
||||
font-weight: bold;
|
||||
src: local("Fira Sans"), local("Fira Sans Bold"),
|
||||
url(/fonts/FiraSans-Bold.otf) format("opentype"),
|
||||
url(/fonts/FiraSans-Bold.woff2) format("woff2"),
|
||||
url(/fonts/FiraSans-Bold.woff) format("woff"),
|
||||
url(/fonts/FiraSans-Bold.svg) format("svg");
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Fira Sans", sans-serif;
|
||||
color: #333;
|
||||
background-color: #eee;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
@media (min-width: 500px) {
|
||||
body {
|
||||
font-size: 150%;
|
||||
}
|
||||
}
|
||||
@media (min-width: 1000px) {
|
||||
body {
|
||||
font-size: 200%;
|
||||
}
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
h1 a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
}
|
||||
main {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 0.5em;
|
||||
}
|
||||
blockquote {
|
||||
position: relative;
|
||||
margin: 0 0.2em 2em 1em;
|
||||
padding: 1em 1em 0.9em 1.7em;
|
||||
background-color: #f8f8f8;
|
||||
box-shadow: 0 0.5em 1em rgba(0, 0, 0, 0.05);
|
||||
transition: box-shadow 0.2s, background-color 0.2s;
|
||||
}
|
||||
blockquote .quote {
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
hyphens: auto;
|
||||
}
|
||||
blockquote .quote p {
|
||||
margin: 0 0 0.3em;
|
||||
}
|
||||
blockquote .quote a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
blockquote .quote a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
blockquote:target, blockquote:hover {
|
||||
background-color: #fefeff;
|
||||
box-shadow: 0 0.5em 1em rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
blockquote::before {
|
||||
content: "“";
|
||||
font-size: 600%;
|
||||
position: absolute;
|
||||
color: #ccc;
|
||||
top: -0.1em;
|
||||
left: -0.2em;
|
||||
z-index: 1;
|
||||
}
|
||||
blockquote .attribution {
|
||||
display: block;
|
||||
position: relative;
|
||||
margin-top: 0.7em;
|
||||
color: #777;
|
||||
vertical-align: middle;
|
||||
}
|
||||
blockquote .attribution .quoted-by {
|
||||
color: #aaa;
|
||||
display: block;
|
||||
font-size: 10pt;
|
||||
text-align: right;
|
||||
}
|
||||
@media (min-width: 500px) {
|
||||
blockquote .attribution {
|
||||
display: flex;
|
||||
font-size: 75%;
|
||||
}
|
||||
blockquote .attribution > span {
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
}
|
||||
blockquote .attribution .author-time {
|
||||
align-self: flex-start;
|
||||
}
|
||||
blockquote .attribution .quoted-by {
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
blockquote .attribution a {
|
||||
color: inherit;
|
||||
font-weight: bold;
|
||||
font-size: 90%;
|
||||
text-decoration: none;
|
||||
}
|
||||
blockquote .attribution a.time {
|
||||
font-weight: normal;
|
||||
font-size: 75%;
|
||||
}
|
||||
blockquote .attribution a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
blockquote .attribution time {
|
||||
white-space: nowrap;
|
||||
}
|
||||
blockquote .attribution a[role=author] {
|
||||
color: #444;
|
||||
}
|
||||
blockquote .attribution span[role=author] {
|
||||
color: #222;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
border-top: 1px solid #ddd;
|
||||
background-color: #e5e5e5;
|
||||
color: #555;
|
||||
font-size: 1rem;
|
||||
padding: 3vh 1em 3vh;
|
||||
}
|
|
@ -1,24 +1,39 @@
|
|||
<html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="nl">
|
||||
<head>
|
||||
<title>Zeus WPI Quotes</title>
|
||||
<meta charset="utf-8"/>
|
||||
|
||||
<title>Zeus WPI-quotes</title>
|
||||
<meta name="author" content="Zeus WPI"/>
|
||||
<meta name="description" content="Epische quotes van Zeus WPI-leden"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="quotes.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Zeus WPI Quotes</h1>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Quoter</th>
|
||||
<th>Quotee</th>
|
||||
<th>When</th>
|
||||
<th>Quote</th>
|
||||
</tr>
|
||||
<main>
|
||||
<h1><a href="#">Zeus WPI-quotes</a></h1>
|
||||
|
||||
{% for quote in quotes %}
|
||||
<tr>
|
||||
<td>{{ quote.quoter }}</td>
|
||||
<td>{{ quote.quotee }}</td>
|
||||
<td>{{ quote.created_at }}</td>
|
||||
<td>{{ quote.quote }}</td>
|
||||
</tr>
|
||||
<blockquote id="{{ quote.slur() }}">
|
||||
<span class="quote">{{ quote.quote }}</span>
|
||||
|
||||
<span class="attribution">
|
||||
<span class="author-time">
|
||||
{% if quote.quotee %}
|
||||
<span role="author">{{ quote.quotee }}</span>,
|
||||
{% endif %}
|
||||
<a href="#{{ quote.slur() }}" class="time">
|
||||
<time datetime="{{ quote.created_at_machine() }}">{{ quote.created_at_human() }}</time>
|
||||
</a>
|
||||
</span>
|
||||
{% if quote.quoter %}
|
||||
<span class="quoted-by">
|
||||
(gequotet door <span role="quoter">{{ quote.quoter }}</span>)
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</blockquote>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
|
2
import_quotes.py
Normal file → Executable file
2
import_quotes.py
Normal file → Executable file
|
@ -1,3 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from flask import Flask, request, Response, abort, render_template
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_migrate import Migrate
|
||||
|
|
0
run_dev.py
Normal file → Executable file
0
run_dev.py
Normal file → Executable file
0
setup_database.py
Normal file → Executable file
0
setup_database.py
Normal file → Executable file
Loading…
Reference in a new issue