aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fietsboek/models/user.py5
-rw-r--r--fietsboek/static/fietsboek.js21
-rw-r--r--fietsboek/static/theme.css4
-rw-r--r--fietsboek/templates/home.jinja28
-rw-r--r--fietsboek/util.py16
-rw-r--r--fietsboek/views/detail.py2
-rw-r--r--fietsboek/views/upload.py2
7 files changed, 42 insertions, 16 deletions
diff --git a/fietsboek/models/user.py b/fietsboek/models/user.py
index fa6bd7f..83a36e0 100644
--- a/fietsboek/models/user.py
+++ b/fietsboek/models/user.py
@@ -2,7 +2,7 @@
import datetime
import enum
import uuid
-import os
+import secrets
from sqlalchemy import (
Column,
@@ -45,6 +45,7 @@ SCRYPT_PARAMETERS = {
'r': 8,
'p': 1,
}
+SALT_LENGTH = 32
friends_assoc = Table(
@@ -130,7 +131,7 @@ class User(Base):
:type new_password: str
"""
new_password = new_password.encode('utf-8')
- salt = os.urandom(16)
+ salt = secrets.token_bytes(SALT_LENGTH)
scrypt = Scrypt(salt=salt, **SCRYPT_PARAMETERS)
password = scrypt.derive(new_password)
self.salt = salt
diff --git a/fietsboek/static/fietsboek.js b/fietsboek/static/fietsboek.js
index bf402a8..2e88aa3 100644
--- a/fietsboek/static/fietsboek.js
+++ b/fietsboek/static/fietsboek.js
@@ -271,6 +271,27 @@ function saveImageDescriptionClicked(event) {
addHandler("#imageDescriptionModal button.btn-success", "click", saveImageDescriptionClicked);
+/**
+ * Handler to toggle (collapse/expand) the yearly/monthly summary.
+ *
+ * @param event - The triggering event.
+ */
+function toggleSummary(event) {
+ const chevron = event.target;
+ const containing = chevron.closest("a");
+ const summary = containing.nextElementSibling;
+ bootstrap.Collapse.getOrCreateInstance(summary).toggle();
+ if (chevron.classList.contains("bi-chevron-down")) {
+ chevron.classList.remove("bi-chevron-down");
+ chevron.classList.add("bi-chevron-right");
+ } else {
+ chevron.classList.remove("bi-chevron-right");
+ chevron.classList.add("bi-chevron-down");
+ }
+}
+
+addHandler(".summary-toggler", "click", toggleSummary);
+
/*
* Handler to enable the "Download archive button" ...
*/
diff --git a/fietsboek/static/theme.css b/fietsboek/static/theme.css
index 01ee06e..3f7f74d 100644
--- a/fietsboek/static/theme.css
+++ b/fietsboek/static/theme.css
@@ -81,6 +81,10 @@ strong {
max-width: 100%;
}
+.summary-toggler {
+ cursor: pointer;
+}
+
.admin-badge-list * {
margin-left: 5px;
margin-right: 5px;
diff --git a/fietsboek/templates/home.jinja2 b/fietsboek/templates/home.jinja2
index 6825c62..4a9c3ad 100644
--- a/fietsboek/templates/home.jinja2
+++ b/fietsboek/templates/home.jinja2
@@ -6,11 +6,11 @@
{% if summary %}
<div class="list-group list-group-root">
{% for year in summary %}
- <a class="list-group-item list-group-item-primary">{{ year.year }} &mdash; {{ (year.total_length / 1000) | round(2) | format_decimal }} km</a>
- <div class="list-group">
+ <a class="list-group-item list-group-item-primary"><i class="bi bi-chevron-down summary-toggler"></i> {{ year.year }} &mdash; {{ (year.total_length / 1000) | round(2) | format_decimal }} km</a>
+ <div class="list-group collapse show">
{% for month in year %}
- <a class="list-group-item list-group-item-secondary">{{ month_name(request, month.month) }} &mdash; {{ (month.total_length / 1000) | round(2) | format_decimal }} km</a>
- <div class="list-group">
+ <a class="list-group-item list-group-item-secondary"><i class="bi bi-chevron-down summary-toggler"></i> {{ month_name(request, month.month) }} &mdash; {{ (month.total_length / 1000) | round(2) | format_decimal }} km</a>
+ <div class="list-group collapse show">
{% for track in month %}
<span class="list-group-item">{{ track.date.day }}: <a href="{{ request.route_url('details', track_id=track.id) }}" data-bs-toggle="tooltip" data-bs-container="body" data-bs-html="true" title="{{ track.html_tooltip(request.localizer) }}">{{ track.title | default(track.date, true) }}</a></span>
{% endfor %}
diff --git a/fietsboek/util.py b/fietsboek/util.py
index ddc4832..71f5d16 100644
--- a/fietsboek/util.py
+++ b/fietsboek/util.py
@@ -1,10 +1,9 @@
"""Various utility functions."""
-import random
-import string
import datetime
import re
import os
import unicodedata
+import secrets
# Compat for Python < 3.9
import importlib_resources
@@ -213,16 +212,17 @@ def month_name(request, month):
return locale.months["stand-alone"]["wide"][month]
-def random_alphanum_string(length=20):
- """Draws a random string that consists of alphanumerical characters.
+def random_link_secret(nbytes=20):
+ """Safely generates a secret suitable for the link share.
- :param length: Length of the string.
- :type length: int
+ The returned string consists of characters that are safe to use in a URL.
+
+ :param nbytes: Number of random bytes to use.
+ :type nbytes: int
:return: A randomly drawn string.
:rtype: str
"""
- candidates = string.ascii_letters + string.digits
- return ''.join(random.choice(candidates) for _ in range(length))
+ return secrets.token_urlsafe(nbytes)
def retrieve_multiple(dbsession, model, params, name):
diff --git a/fietsboek/views/detail.py b/fietsboek/views/detail.py
index f213231..6eba3ce 100644
--- a/fietsboek/views/detail.py
+++ b/fietsboek/views/detail.py
@@ -73,7 +73,7 @@ def invalidate_share(request):
:rtype: pyramid.response.Response
"""
track = request.context
- track.link_secret = util.random_alphanum_string()
+ track.link_secret = util.random_link_secret()
return HTTPFound(request.route_url('details', track_id=track.id))
diff --git a/fietsboek/views/upload.py b/fietsboek/views/upload.py
index ea05c74..95008b4 100644
--- a/fietsboek/views/upload.py
+++ b/fietsboek/views/upload.py
@@ -155,7 +155,7 @@ def do_finish_upload(request):
visibility=Visibility[request.params["visibility"]],
description=request.params["description"],
badges=badges,
- link_secret=util.random_alphanum_string(),
+ link_secret=util.random_link_secret(),
tagged_people=tagged_people,
)
track.date = date