aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fietsboek/locale/de/LC_MESSAGES/messages.mobin10209 -> 11214 bytes
-rw-r--r--fietsboek/locale/de/LC_MESSAGES/messages.po119
-rw-r--r--fietsboek/locale/en/LC_MESSAGES/messages.mobin9612 -> 10549 bytes
-rw-r--r--fietsboek/locale/en/LC_MESSAGES/messages.po118
-rw-r--r--fietsboek/locale/fietslog.pot118
-rw-r--r--fietsboek/static/fietsboek.js15
-rw-r--r--fietsboek/templates/browse.jinja298
-rw-r--r--fietsboek/views/browse.py111
8 files changed, 485 insertions, 94 deletions
diff --git a/fietsboek/locale/de/LC_MESSAGES/messages.mo b/fietsboek/locale/de/LC_MESSAGES/messages.mo
index 7d7af8d..97ee495 100644
--- a/fietsboek/locale/de/LC_MESSAGES/messages.mo
+++ b/fietsboek/locale/de/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/fietsboek/locale/de/LC_MESSAGES/messages.po b/fietsboek/locale/de/LC_MESSAGES/messages.po
index 35a6bd4..9c93bb4 100644
--- a/fietsboek/locale/de/LC_MESSAGES/messages.po
+++ b/fietsboek/locale/de/LC_MESSAGES/messages.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2022-07-21 23:49+0200\n"
+"POT-Creation-Date: 2022-08-02 11:57+0200\n"
"PO-Revision-Date: 2022-07-02 17:35+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
@@ -18,11 +18,11 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.10.3\n"
-#: fietsboek/util.py:273
+#: fietsboek/util.py:282
msgid "password_constraint.mismatch"
msgstr "Passwörter stimmen nicht überein"
-#: fietsboek/util.py:275
+#: fietsboek/util.py:284
msgid "password_constraint.length"
msgstr "Passwort zu kurz"
@@ -86,51 +86,108 @@ msgstr "Hinzufügen"
msgid "page.browse.title"
msgstr "Stöbern"
-#: fietsboek/templates/browse.jinja2:19 fietsboek/templates/details.jinja2:86
+#: fietsboek/templates/browse.jinja2:11
+msgid "page.browse.filter.search_terms"
+msgstr "Suchbegriffe"
+
+#: fietsboek/templates/browse.jinja2:20
+msgid "page.browse.filter.tags"
+msgstr "Schlagwörter"
+
+#: fietsboek/templates/browse.jinja2:27
+msgid "page.browse.filter.tagged_person"
+msgstr "Markierte Personen"
+
+#: fietsboek/templates/browse.jinja2:34
+msgid "page.browse.filter.length_minimum"
+msgstr "Minimale Länge"
+
+#: fietsboek/templates/browse.jinja2:40
+msgid "page.browse.filter.length_boundaries"
+msgstr "Längenbeschränkungen"
+
+#: fietsboek/templates/browse.jinja2:46
+msgid "page.browse.filter.length_maximum"
+msgstr "Maximale Länge"
+
+#: fietsboek/templates/browse.jinja2:59
+msgid "page.browse.filter.date_boundaries"
+msgstr "Datumsgrenzen"
+
+#: fietsboek/templates/browse.jinja2:77
+msgid "page.browse.filter.my_tracks.only"
+msgstr "Zeige nur eigene Strecken"
+
+#: fietsboek/templates/browse.jinja2:78
+msgid "page.browse.filter.friends_tracks_only"
+msgstr "Zeige nur Strecken von Freunden"
+
+#: fietsboek/templates/browse.jinja2:79
+msgid "page.browse.filter.me_tagged_only"
+msgstr "Zeige nur Strecken, in denen ich markiert bin"
+
+#: fietsboek/templates/browse.jinja2:88
+msgid "page.browse.filters.apply"
+msgstr "Filter anwenden"
+
+#: fietsboek/templates/browse.jinja2:92
+msgid "page.browse.filters.clear_all"
+msgstr "Filter zurücksetzen"
+
+#: fietsboek/templates/browse.jinja2:95
+msgid "page.browse.filters.expand_advanced"
+msgstr "Erweitert"
+
+#: fietsboek/templates/browse.jinja2:115 fietsboek/templates/details.jinja2:86
msgid "page.details.date"
msgstr "Datum"
-#: fietsboek/templates/browse.jinja2:21 fietsboek/templates/details.jinja2:98
+#: fietsboek/templates/browse.jinja2:117 fietsboek/templates/details.jinja2:98
msgid "page.details.length"
msgstr "Länge"
-#: fietsboek/templates/browse.jinja2:25 fietsboek/templates/details.jinja2:90
+#: fietsboek/templates/browse.jinja2:121 fietsboek/templates/details.jinja2:90
msgid "page.details.start_time"
msgstr "Startzeit"
-#: fietsboek/templates/browse.jinja2:27 fietsboek/templates/details.jinja2:94
+#: fietsboek/templates/browse.jinja2:123 fietsboek/templates/details.jinja2:94
msgid "page.details.end_time"
msgstr "Endzeit"
-#: fietsboek/templates/browse.jinja2:31 fietsboek/templates/details.jinja2:102
+#: fietsboek/templates/browse.jinja2:127 fietsboek/templates/details.jinja2:102
msgid "page.details.uphill"
msgstr "Bergauf"
-#: fietsboek/templates/browse.jinja2:33 fietsboek/templates/details.jinja2:106
+#: fietsboek/templates/browse.jinja2:129 fietsboek/templates/details.jinja2:106
msgid "page.details.downhill"
msgstr "Bergab"
-#: fietsboek/templates/browse.jinja2:37 fietsboek/templates/details.jinja2:110
+#: fietsboek/templates/browse.jinja2:133 fietsboek/templates/details.jinja2:110
msgid "page.details.moving_time"
msgstr "Fahrzeit"
-#: fietsboek/templates/browse.jinja2:39 fietsboek/templates/details.jinja2:114
+#: fietsboek/templates/browse.jinja2:135 fietsboek/templates/details.jinja2:114
msgid "page.details.stopped_time"
msgstr "Haltezeit"
-#: fietsboek/templates/browse.jinja2:43 fietsboek/templates/details.jinja2:118
+#: fietsboek/templates/browse.jinja2:139 fietsboek/templates/details.jinja2:118
msgid "page.details.max_speed"
msgstr "maximale Geschwindigkeit"
-#: fietsboek/templates/browse.jinja2:45 fietsboek/templates/details.jinja2:122
+#: fietsboek/templates/browse.jinja2:141 fietsboek/templates/details.jinja2:122
msgid "page.details.avg_speed"
msgstr "durchschnittliche Geschwindigkeit"
-#: fietsboek/templates/browse.jinja2:60
+#: fietsboek/templates/browse.jinja2:156
msgid "page.browse.download_multiple"
msgstr "ausgewählte Herunterladen"
-#: fietsboek/templates/browse.jinja2:62
+#: fietsboek/templates/browse.jinja2:158
+msgid "page.browse.no_results"
+msgstr ""
+"Es wurden keine Strecken gefunden, die den Filtern entsprechen."
+
+#: fietsboek/templates/browse.jinja2:160
msgid "page.browse.no_tracks"
msgstr ""
"Es wurden keine Strecken gefunden, auf die Du Zugriff hast. Versuche, "
@@ -376,47 +433,47 @@ msgstr "Startseite"
msgid "page.home.total"
msgstr "Gesamt"
-#: fietsboek/templates/layout.jinja2:31
+#: fietsboek/templates/layout.jinja2:40
msgid "page.navbar.toggle"
msgstr "Navigation umschalten"
-#: fietsboek/templates/layout.jinja2:37
+#: fietsboek/templates/layout.jinja2:46
msgid "page.navbar.home"
msgstr "Startseite"
-#: fietsboek/templates/layout.jinja2:40
+#: fietsboek/templates/layout.jinja2:49
msgid "page.navbar.browse"
msgstr "Stöbern"
-#: fietsboek/templates/layout.jinja2:44
+#: fietsboek/templates/layout.jinja2:53
msgid "page.navbar.upload"
msgstr "Hochladen"
-#: fietsboek/templates/layout.jinja2:48
+#: fietsboek/templates/layout.jinja2:57
msgid "page.navbar.user"
msgstr "Nutzer"
-#: fietsboek/templates/layout.jinja2:52
+#: fietsboek/templates/layout.jinja2:61
msgid "page.navbar.welcome_user"
msgstr "Willkommen, {}!"
-#: fietsboek/templates/layout.jinja2:55
+#: fietsboek/templates/layout.jinja2:64
msgid "page.navbar.logout"
msgstr "Abmelden"
-#: fietsboek/templates/layout.jinja2:58
+#: fietsboek/templates/layout.jinja2:67
msgid "page.navbar.profile"
msgstr "Profil"
-#: fietsboek/templates/layout.jinja2:62
+#: fietsboek/templates/layout.jinja2:71
msgid "page.navbar.admin"
msgstr "Admin"
-#: fietsboek/templates/layout.jinja2:68
+#: fietsboek/templates/layout.jinja2:77
msgid "page.navbar.login"
msgstr "Anmelden"
-#: fietsboek/templates/layout.jinja2:72
+#: fietsboek/templates/layout.jinja2:81
msgid "page.navbar.create_account"
msgstr "Konto Erstellen"
@@ -617,7 +674,7 @@ msgstr "E-Mail-Adresse bestätigt"
msgid "flash.password_updated"
msgstr "Passwort aktualisiert"
-#: fietsboek/views/detail.py:88
+#: fietsboek/views/detail.py:93
msgid "flash.track_deleted"
msgstr "Strecke gelöscht"
@@ -641,19 +698,19 @@ msgstr "Freund hinzugefügt"
msgid "flash.friend_request_sent"
msgstr "Freundschaftsanfrage gesendet"
-#: fietsboek/views/upload.py:51
+#: fietsboek/views/upload.py:52
msgid "flash.no_file_selected"
msgstr "Keine Datei ausgewählt"
-#: fietsboek/views/upload.py:61
+#: fietsboek/views/upload.py:62
msgid "flash.invalid_file"
msgstr "Ungültige GPX-Datei gesendet"
-#: fietsboek/views/upload.py:178
+#: fietsboek/views/upload.py:177
msgid "flash.upload_success"
msgstr "Hochladen erfolgreich"
-#: fietsboek/views/upload.py:194
+#: fietsboek/views/upload.py:193
msgid "flash.upload_cancelled"
msgstr "Hochladen abgebrochen"
diff --git a/fietsboek/locale/en/LC_MESSAGES/messages.mo b/fietsboek/locale/en/LC_MESSAGES/messages.mo
index 8261149..8887d3d 100644
--- a/fietsboek/locale/en/LC_MESSAGES/messages.mo
+++ b/fietsboek/locale/en/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/fietsboek/locale/en/LC_MESSAGES/messages.po b/fietsboek/locale/en/LC_MESSAGES/messages.po
index c19184c..2ac9f98 100644
--- a/fietsboek/locale/en/LC_MESSAGES/messages.po
+++ b/fietsboek/locale/en/LC_MESSAGES/messages.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2022-07-21 23:49+0200\n"
+"POT-Creation-Date: 2022-08-02 11:57+0200\n"
"PO-Revision-Date: 2022-06-28 13:11+0200\n"
"Last-Translator: \n"
"Language: en\n"
@@ -18,11 +18,11 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.10.3\n"
-#: fietsboek/util.py:273
+#: fietsboek/util.py:282
msgid "password_constraint.mismatch"
msgstr "Passwords don't match"
-#: fietsboek/util.py:275
+#: fietsboek/util.py:284
msgid "password_constraint.length"
msgstr "Password not long enough"
@@ -86,51 +86,107 @@ msgstr "Add Badge"
msgid "page.browse.title"
msgstr "Browse"
-#: fietsboek/templates/browse.jinja2:19 fietsboek/templates/details.jinja2:86
+#: fietsboek/templates/browse.jinja2:11
+msgid "page.browse.filter.search_terms"
+msgstr "Search terms"
+
+#: fietsboek/templates/browse.jinja2:20
+msgid "page.browse.filter.tags"
+msgstr "Tags"
+
+#: fietsboek/templates/browse.jinja2:27
+msgid "page.browse.filter.tagged_person"
+msgstr "Tagged People"
+
+#: fietsboek/templates/browse.jinja2:34
+msgid "page.browse.filter.length_minimum"
+msgstr "Minimum length"
+
+#: fietsboek/templates/browse.jinja2:40
+msgid "page.browse.filter.length_boundaries"
+msgstr "Length limits"
+
+#: fietsboek/templates/browse.jinja2:46
+msgid "page.browse.filter.length_maximum"
+msgstr "Maximum length"
+
+#: fietsboek/templates/browse.jinja2:59
+msgid "page.browse.filter.date_boundaries"
+msgstr "Date limits"
+
+#: fietsboek/templates/browse.jinja2:77
+msgid "page.browse.filter.my_tracks.only"
+msgstr "Show only my own tracks"
+
+#: fietsboek/templates/browse.jinja2:78
+msgid "page.browse.filter.friends_tracks_only"
+msgstr "Show only tracks of my friends"
+
+#: fietsboek/templates/browse.jinja2:79
+msgid "page.browse.filter.me_tagged_only"
+msgstr "Show only tracks in which I'm tagged"
+
+#: fietsboek/templates/browse.jinja2:88
+msgid "page.browse.filters.apply"
+msgstr "Apply filters"
+
+#: fietsboek/templates/browse.jinja2:92
+msgid "page.browse.filters.clear_all"
+msgstr "Remove filters"
+
+#: fietsboek/templates/browse.jinja2:95
+msgid "page.browse.filters.expand_advanced"
+msgstr "Advanced"
+
+#: fietsboek/templates/browse.jinja2:115 fietsboek/templates/details.jinja2:86
msgid "page.details.date"
msgstr "Date"
-#: fietsboek/templates/browse.jinja2:21 fietsboek/templates/details.jinja2:98
+#: fietsboek/templates/browse.jinja2:117 fietsboek/templates/details.jinja2:98
msgid "page.details.length"
msgstr "Length"
-#: fietsboek/templates/browse.jinja2:25 fietsboek/templates/details.jinja2:90
+#: fietsboek/templates/browse.jinja2:121 fietsboek/templates/details.jinja2:90
msgid "page.details.start_time"
msgstr "Record Start"
-#: fietsboek/templates/browse.jinja2:27 fietsboek/templates/details.jinja2:94
+#: fietsboek/templates/browse.jinja2:123 fietsboek/templates/details.jinja2:94
msgid "page.details.end_time"
msgstr "Record End"
-#: fietsboek/templates/browse.jinja2:31 fietsboek/templates/details.jinja2:102
+#: fietsboek/templates/browse.jinja2:127 fietsboek/templates/details.jinja2:102
msgid "page.details.uphill"
msgstr "Uphill"
-#: fietsboek/templates/browse.jinja2:33 fietsboek/templates/details.jinja2:106
+#: fietsboek/templates/browse.jinja2:129 fietsboek/templates/details.jinja2:106
msgid "page.details.downhill"
msgstr "Downhill"
-#: fietsboek/templates/browse.jinja2:37 fietsboek/templates/details.jinja2:110
+#: fietsboek/templates/browse.jinja2:133 fietsboek/templates/details.jinja2:110
msgid "page.details.moving_time"
msgstr "Moving Time"
-#: fietsboek/templates/browse.jinja2:39 fietsboek/templates/details.jinja2:114
+#: fietsboek/templates/browse.jinja2:135 fietsboek/templates/details.jinja2:114
msgid "page.details.stopped_time"
msgstr "Stopped Time"
-#: fietsboek/templates/browse.jinja2:43 fietsboek/templates/details.jinja2:118
+#: fietsboek/templates/browse.jinja2:139 fietsboek/templates/details.jinja2:118
msgid "page.details.max_speed"
msgstr "Max Speed"
-#: fietsboek/templates/browse.jinja2:45 fietsboek/templates/details.jinja2:122
+#: fietsboek/templates/browse.jinja2:141 fietsboek/templates/details.jinja2:122
msgid "page.details.avg_speed"
msgstr "Average Speed"
-#: fietsboek/templates/browse.jinja2:60
+#: fietsboek/templates/browse.jinja2:156
msgid "page.browse.download_multiple"
msgstr "Download selected"
-#: fietsboek/templates/browse.jinja2:62
+#: fietsboek/templates/browse.jinja2:158
+msgid "page.browse.no_results"
+msgstr "No results matching the filters were found."
+
+#: fietsboek/templates/browse.jinja2:160
msgid "page.browse.no_tracks"
msgstr "You currently do not have access to any tracks. Try logging in."
@@ -372,47 +428,47 @@ msgstr "Home"
msgid "page.home.total"
msgstr "Total"
-#: fietsboek/templates/layout.jinja2:31
+#: fietsboek/templates/layout.jinja2:40
msgid "page.navbar.toggle"
msgstr "Toggle navigation"
-#: fietsboek/templates/layout.jinja2:37
+#: fietsboek/templates/layout.jinja2:46
msgid "page.navbar.home"
msgstr "Home"
-#: fietsboek/templates/layout.jinja2:40
+#: fietsboek/templates/layout.jinja2:49
msgid "page.navbar.browse"
msgstr "Browse"
-#: fietsboek/templates/layout.jinja2:44
+#: fietsboek/templates/layout.jinja2:53
msgid "page.navbar.upload"
msgstr "Upload"
-#: fietsboek/templates/layout.jinja2:48
+#: fietsboek/templates/layout.jinja2:57
msgid "page.navbar.user"
msgstr "User"
-#: fietsboek/templates/layout.jinja2:52
+#: fietsboek/templates/layout.jinja2:61
msgid "page.navbar.welcome_user"
msgstr "Welcome, {}!"
-#: fietsboek/templates/layout.jinja2:55
+#: fietsboek/templates/layout.jinja2:64
msgid "page.navbar.logout"
msgstr "Logout"
-#: fietsboek/templates/layout.jinja2:58
+#: fietsboek/templates/layout.jinja2:67
msgid "page.navbar.profile"
msgstr "Profile"
-#: fietsboek/templates/layout.jinja2:62
+#: fietsboek/templates/layout.jinja2:71
msgid "page.navbar.admin"
msgstr "Admin"
-#: fietsboek/templates/layout.jinja2:68
+#: fietsboek/templates/layout.jinja2:77
msgid "page.navbar.login"
msgstr "Login"
-#: fietsboek/templates/layout.jinja2:72
+#: fietsboek/templates/layout.jinja2:81
msgid "page.navbar.create_account"
msgstr "Create Account"
@@ -612,7 +668,7 @@ msgstr "Your email address has been verified"
msgid "flash.password_updated"
msgstr "Password has been updated"
-#: fietsboek/views/detail.py:88
+#: fietsboek/views/detail.py:93
msgid "flash.track_deleted"
msgstr "Track has been deleted"
@@ -636,19 +692,19 @@ msgstr "Friend has been added"
msgid "flash.friend_request_sent"
msgstr "Friend request sent"
-#: fietsboek/views/upload.py:51
+#: fietsboek/views/upload.py:52
msgid "flash.no_file_selected"
msgstr "No file selected"
-#: fietsboek/views/upload.py:61
+#: fietsboek/views/upload.py:62
msgid "flash.invalid_file"
msgstr "Invalid GPX file selected"
-#: fietsboek/views/upload.py:178
+#: fietsboek/views/upload.py:177
msgid "flash.upload_success"
msgstr "Upload successful"
-#: fietsboek/views/upload.py:194
+#: fietsboek/views/upload.py:193
msgid "flash.upload_cancelled"
msgstr "Upload cancelled"
diff --git a/fietsboek/locale/fietslog.pot b/fietsboek/locale/fietslog.pot
index a30c79c..2c25251 100644
--- a/fietsboek/locale/fietslog.pot
+++ b/fietsboek/locale/fietslog.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2022-07-21 23:49+0200\n"
+"POT-Creation-Date: 2022-08-02 11:57+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,11 +17,11 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.10.3\n"
-#: fietsboek/util.py:273
+#: fietsboek/util.py:282
msgid "password_constraint.mismatch"
msgstr ""
-#: fietsboek/util.py:275
+#: fietsboek/util.py:284
msgid "password_constraint.length"
msgstr ""
@@ -85,51 +85,107 @@ msgstr ""
msgid "page.browse.title"
msgstr ""
-#: fietsboek/templates/browse.jinja2:19 fietsboek/templates/details.jinja2:86
+#: fietsboek/templates/browse.jinja2:11
+msgid "page.browse.filter.search_terms"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:20
+msgid "page.browse.filter.tags"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:27
+msgid "page.browse.filter.tagged_person"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:34
+msgid "page.browse.filter.length_minimum"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:40
+msgid "page.browse.filter.length_boundaries"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:46
+msgid "page.browse.filter.length_maximum"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:59
+msgid "page.browse.filter.date_boundaries"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:77
+msgid "page.browse.filter.my_tracks.only"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:78
+msgid "page.browse.filter.friends_tracks_only"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:79
+msgid "page.browse.filter.me_tagged_only"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:88
+msgid "page.browse.filters.apply"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:92
+msgid "page.browse.filters.clear_all"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:95
+msgid "page.browse.filters.expand_advanced"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:115 fietsboek/templates/details.jinja2:86
msgid "page.details.date"
msgstr ""
-#: fietsboek/templates/browse.jinja2:21 fietsboek/templates/details.jinja2:98
+#: fietsboek/templates/browse.jinja2:117 fietsboek/templates/details.jinja2:98
msgid "page.details.length"
msgstr ""
-#: fietsboek/templates/browse.jinja2:25 fietsboek/templates/details.jinja2:90
+#: fietsboek/templates/browse.jinja2:121 fietsboek/templates/details.jinja2:90
msgid "page.details.start_time"
msgstr ""
-#: fietsboek/templates/browse.jinja2:27 fietsboek/templates/details.jinja2:94
+#: fietsboek/templates/browse.jinja2:123 fietsboek/templates/details.jinja2:94
msgid "page.details.end_time"
msgstr ""
-#: fietsboek/templates/browse.jinja2:31 fietsboek/templates/details.jinja2:102
+#: fietsboek/templates/browse.jinja2:127 fietsboek/templates/details.jinja2:102
msgid "page.details.uphill"
msgstr ""
-#: fietsboek/templates/browse.jinja2:33 fietsboek/templates/details.jinja2:106
+#: fietsboek/templates/browse.jinja2:129 fietsboek/templates/details.jinja2:106
msgid "page.details.downhill"
msgstr ""
-#: fietsboek/templates/browse.jinja2:37 fietsboek/templates/details.jinja2:110
+#: fietsboek/templates/browse.jinja2:133 fietsboek/templates/details.jinja2:110
msgid "page.details.moving_time"
msgstr ""
-#: fietsboek/templates/browse.jinja2:39 fietsboek/templates/details.jinja2:114
+#: fietsboek/templates/browse.jinja2:135 fietsboek/templates/details.jinja2:114
msgid "page.details.stopped_time"
msgstr ""
-#: fietsboek/templates/browse.jinja2:43 fietsboek/templates/details.jinja2:118
+#: fietsboek/templates/browse.jinja2:139 fietsboek/templates/details.jinja2:118
msgid "page.details.max_speed"
msgstr ""
-#: fietsboek/templates/browse.jinja2:45 fietsboek/templates/details.jinja2:122
+#: fietsboek/templates/browse.jinja2:141 fietsboek/templates/details.jinja2:122
msgid "page.details.avg_speed"
msgstr ""
-#: fietsboek/templates/browse.jinja2:60
+#: fietsboek/templates/browse.jinja2:156
msgid "page.browse.download_multiple"
msgstr ""
-#: fietsboek/templates/browse.jinja2:62
+#: fietsboek/templates/browse.jinja2:158
+msgid "page.browse.no_results"
+msgstr ""
+
+#: fietsboek/templates/browse.jinja2:160
msgid "page.browse.no_tracks"
msgstr ""
@@ -369,47 +425,47 @@ msgstr ""
msgid "page.home.total"
msgstr ""
-#: fietsboek/templates/layout.jinja2:31
+#: fietsboek/templates/layout.jinja2:40
msgid "page.navbar.toggle"
msgstr ""
-#: fietsboek/templates/layout.jinja2:37
+#: fietsboek/templates/layout.jinja2:46
msgid "page.navbar.home"
msgstr ""
-#: fietsboek/templates/layout.jinja2:40
+#: fietsboek/templates/layout.jinja2:49
msgid "page.navbar.browse"
msgstr ""
-#: fietsboek/templates/layout.jinja2:44
+#: fietsboek/templates/layout.jinja2:53
msgid "page.navbar.upload"
msgstr ""
-#: fietsboek/templates/layout.jinja2:48
+#: fietsboek/templates/layout.jinja2:57
msgid "page.navbar.user"
msgstr ""
-#: fietsboek/templates/layout.jinja2:52
+#: fietsboek/templates/layout.jinja2:61
msgid "page.navbar.welcome_user"
msgstr ""
-#: fietsboek/templates/layout.jinja2:55
+#: fietsboek/templates/layout.jinja2:64
msgid "page.navbar.logout"
msgstr ""
-#: fietsboek/templates/layout.jinja2:58
+#: fietsboek/templates/layout.jinja2:67
msgid "page.navbar.profile"
msgstr ""
-#: fietsboek/templates/layout.jinja2:62
+#: fietsboek/templates/layout.jinja2:71
msgid "page.navbar.admin"
msgstr ""
-#: fietsboek/templates/layout.jinja2:68
+#: fietsboek/templates/layout.jinja2:77
msgid "page.navbar.login"
msgstr ""
-#: fietsboek/templates/layout.jinja2:72
+#: fietsboek/templates/layout.jinja2:81
msgid "page.navbar.create_account"
msgstr ""
@@ -601,7 +657,7 @@ msgstr ""
msgid "flash.password_updated"
msgstr ""
-#: fietsboek/views/detail.py:88
+#: fietsboek/views/detail.py:93
msgid "flash.track_deleted"
msgstr ""
@@ -625,19 +681,19 @@ msgstr ""
msgid "flash.friend_request_sent"
msgstr ""
-#: fietsboek/views/upload.py:51
+#: fietsboek/views/upload.py:52
msgid "flash.no_file_selected"
msgstr ""
-#: fietsboek/views/upload.py:61
+#: fietsboek/views/upload.py:62
msgid "flash.invalid_file"
msgstr ""
-#: fietsboek/views/upload.py:178
+#: fietsboek/views/upload.py:177
msgid "flash.upload_success"
msgstr ""
-#: fietsboek/views/upload.py:194
+#: fietsboek/views/upload.py:193
msgid "flash.upload_cancelled"
msgstr ""
diff --git a/fietsboek/static/fietsboek.js b/fietsboek/static/fietsboek.js
index f99a531..2e88aa3 100644
--- a/fietsboek/static/fietsboek.js
+++ b/fietsboek/static/fietsboek.js
@@ -311,6 +311,21 @@ addHandler(".archive-checkbox", "change", () => {
document.querySelector("#archiveDownloadButton").disabled = (checked.length == 0);
});
+/**
+ * Handler to clear the input when a .button-clear-input is pressed.
+ *
+ * The button must be in an input-group with the input.
+ *
+ * @param event - The triggering event.
+ */
+function clearInputButtonClicked(event) {
+ const input = event.target.closest(".input-group").querySelector("input");
+ input.value = "";
+}
+
+addHandler(".button-clear-input", "click", clearInputButtonClicked);
+
+
document.addEventListener('DOMContentLoaded', function() {
window.fietsboekImageIndex = 0;
diff --git a/fietsboek/templates/browse.jinja2 b/fietsboek/templates/browse.jinja2
index 2732984..0f98192 100644
--- a/fietsboek/templates/browse.jinja2
+++ b/fietsboek/templates/browse.jinja2
@@ -2,6 +2,102 @@
{% block content %}
<div class="container">
<h1>{{ _("page.browse.title") }}</h1>
+ <div class="mb-3">
+ <form id="browseFilter">
+ <div class="row g-3 mb-3">
+ <div class="col-12">
+ <div class="input-group">
+ <button type="button" class="btn btn-outline-secondary button-clear-input"><i class="bi bi-eraser-fill"></i></button>
+ <input name="search-terms" type="text" class="form-control" placeholder="{{ _("page.browse.filter.search_terms") }}" value="{{ request.params.get('search-terms', '') }}">
+ </div>
+ </div>
+ </div>
+
+ <div class="collapse row g-3 mb-3" id="advancedSearch">
+ <div class="col-md-6">
+ <div class="input-group">
+ <button type="button" class="btn btn-outline-secondary button-clear-input"><i class="bi bi-eraser-fill"></i></button>
+ <input name="tags" type="text" class="form-control" placeholder="{{ _("page.browse.filter.tags") }}" value="{{ request.params.get('tags', '') }}">
+ </div>
+ </div>
+
+ <div class="col-md-6">
+ <div class="input-group">
+ <button type="button" class="btn btn-outline-secondary button-clear-input"><i class="bi bi-eraser-fill"></i></button>
+ <input name="tagged-person" type="text" class="form-control" placeholder="{{ _("page.browse.filter.tagged_person") }}" value="{{ request.params.get('tagged-person', '') }}">
+ </div>
+ </div>
+
+ <div class="col-md-4">
+ <div class="input-group">
+ <button type="button" class="btn btn-outline-secondary button-clear-input"><i class="bi bi-eraser-fill"></i></button>
+ <input name="min-length" type="number" class="form-control" placeholder="{{ _("page.browse.filter.length_minimum") }}" value="{{ request.params.get('min-length', '') }}">
+ <span class="input-group-text">km</span>
+ </div>
+ </div>
+
+ <div class="col-md-4 fs-5 d-flex align-items-center justify-content-center">
+ {{ _("page.browse.filter.length_boundaries") }}
+ </div>
+
+ <div class="col-md-4">
+ <div class="input-group">
+ <button type="button" class="btn btn-outline-secondary button-clear-input"><i class="bi bi-eraser-fill"></i></button>
+ <input name="max-length" type="number" class="form-control" placeholder="{{ _("page.browse.filter.length_maximum") }}" value="{{ request.params.get('max-length', '') }}">
+ <span class="input-group-text">km</span>
+ </div>
+ </div>
+
+ <div class="col-md-4">
+ <div class="input-group">
+ <button type="button" class="btn btn-outline-secondary button-clear-input"><i class="bi bi-eraser-fill"></i></button>
+ <input name="min-date" type="date" class="form-control" value="{{ request.params.get('min-date', '') }}">
+ </div>
+ </div>
+
+ <div class="col-md-4 fs-5 d-flex align-items-center justify-content-center">
+ {{ _("page.browse.filter.date_boundaries") }}
+ </div>
+
+ <div class="col-md-4">
+ <div class="input-group">
+ <button type="button" class="btn btn-outline-secondary button-clear-input"><i class="bi bi-eraser-fill"></i></button>
+ <input name="max-date" type="date" class="form-control" value="{{ request.params.get('max-date', '') }}">
+ </div>
+ </div>
+
+ {% if request.identity %}
+ <div class="col-12">
+ {% macro render_switch(id, name, value, text) %}
+ <div class="form-check form-switch">
+ <input class="form-check-input" type="checkbox" role="switch" id="{{ id }}" name="{{ name }}" value="{{ value }}" {% if value in request.params.getall(name) %}checked{% endif %}>
+ <label class="form-check-label" for="{{ id }}">{{ text }}</label>
+ </div>
+ {% endmacro %}
+ {{ render_switch("switchOnlyMyTracks", "show-only[]", "mine", _("page.browse.filter.my_tracks.only")) }}
+ {{ render_switch("switchOnlyFriendsTracks", "show-only[]", "friends", _("page.browse.filter.friends_tracks_only")) }}
+ {{ render_switch("switchOnlyMeTagged", "user-tagged", "on", _("page.browse.filter.me_tagged_only")) }}
+ </div>
+ {% endif %}
+ </div>
+
+ <div class="row g-3 mb-3">
+ <div class="col">
+ <button type="submit" class="btn btn-primary">
+ <i class="bi bi-funnel-fill"></i>
+ {{ _("page.browse.filters.apply") }}
+ </button>
+ <a class="btn btn-info" href="{{ request.route_path('browse') }}">
+ <i class="bi bi-x-octagon"></i>
+ {{ _("page.browse.filters.clear_all") }}
+ </a>
+ <button class="btn btn-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#advancedSearch" aria-expanded="false" aria-controls="advancedSearch">
+ {{ _("page.browse.filters.expand_advanced") }}
+ </button>
+ </div>
+ </div>
+ </form>
+ </div>
{% if tracks %}
{% for track in tracks %}
<div class="card mb-3">
@@ -58,6 +154,8 @@
</div>
{% endfor %}
<button type="button" class="btn btn-primary" id="archiveDownloadButton" disabled><i class="bi bi-file-earmark-zip"></i> {{ _("page.browse.download_multiple") }}</button>
+ {% elif used_filters %}
+ <p>{{ _("page.browse.no_results") }}</p>
{% else %}
<p>{{ _("page.browse.no_tracks") }}</p>
{% endif %}
diff --git a/fietsboek/views/browse.py b/fietsboek/views/browse.py
index 17dd45a..7120b0d 100644
--- a/fietsboek/views/browse.py
+++ b/fietsboek/views/browse.py
@@ -1,9 +1,10 @@
"""Views for browsing all tracks."""
+import datetime
from io import RawIOBase
from zipfile import ZipFile, ZIP_DEFLATED
from pyramid.view import view_config
-from pyramid.httpexceptions import HTTPForbidden, HTTPNotFound
+from pyramid.httpexceptions import HTTPForbidden, HTTPNotFound, HTTPBadRequest
from pyramid.response import Response
from sqlalchemy import select
@@ -32,6 +33,111 @@ class Stream(RawIOBase):
return b"".join(buf)
+def _get_int(request, name):
+ try:
+ return int(request.params.get(name))
+ except ValueError as exc:
+ raise HTTPBadRequest(f'Invalid integer in {name!r}') from exc
+
+
+def _get_date(request, name):
+ try:
+ return datetime.date.fromisoformat(request.params.get(name))
+ except ValueError as exc:
+ raise HTTPBadRequest(f'Invalid date in {name!r}') from exc
+
+
+class TrackFilters:
+ """A filter that applies user-given filters to a track."""
+ # pylint: disable=fixme
+ # TODO: We should also do some of those in SQL, if possible.
+
+ def __init__(self, filters):
+ self._filters = filters
+
+ def __bool__(self):
+ return bool(self._filters)
+
+ def apply(self, track):
+ """Apply the filters to the track.
+
+ :param track: The track.
+ :type track: fietsboek.models.track.Track
+ :return: Whether the track matches the filters.
+ :rtype: bool
+ """
+ return all(f(track) for f in self._filters)
+
+ @classmethod
+ def parse(cls, request):
+ """Parse the filters from the given request.
+
+ :raises HTTPBadRequest: If the filters are malformed.
+ :param request: The request.
+ :type request: pyramid.request.Request
+ :return: The parsed filter.
+ :rtype: TrackFilters
+ """
+ filters = []
+ if request.params.get('search-terms'):
+ term = request.params.get('search-terms').strip()
+ filters.append(lambda track: term.lower() in track.title.lower())
+
+ if request.params.get('tags'):
+ tags = [tag.strip() for tag in request.params.get('tags').split('&&')]
+ tags = list(filter(bool, tags))
+
+ def has_tags(track):
+ lower_tags = {tag.lower() for tag in track.text_tags()}
+ return all(tag.lower() in lower_tags for tag in tags)
+
+ filters.append(has_tags)
+
+ if request.params.get('tagged-person'):
+ names = [name.strip() for name in request.params.get('tagged-person').split('&&')]
+ names = list(filter(bool, names))
+
+ def has_people(track):
+ peoples_names = [person.name for person in track.tagged_people]
+ peoples_names.append(track.owner.name)
+ peoples_names = set(map(str.lower, peoples_names))
+ print(peoples_names)
+ return all(name.lower() in peoples_names for name in names)
+
+ filters.append(has_people)
+
+ if request.params.get('min-length'):
+ # Value is given in km, so convert it to m
+ min_length = _get_int(request, "min-length") * 1000
+ filters.append(lambda track: track.length >= min_length)
+
+ if request.params.get('max-length'):
+ max_length = _get_int(request, "max-length") * 1000
+ filters.append(lambda track: track.length <= max_length)
+
+ if request.params.get('min-date'):
+ min_date = _get_date(request, "min-date")
+ filters.append(lambda track: track.date.date() >= min_date)
+
+ if request.params.get('max-date'):
+ max_date = _get_date(request, "max-date")
+ filters.append(lambda track: track.date.date() <= max_date)
+
+ if "mine" in request.params.getall('show-only[]'):
+ filters.append(lambda track: track.owner == request.identity)
+
+ if "friends" in request.params.getall('show-only[]'):
+ filters.append(lambda track: request.identity and
+ track.owner in request.identity.get_friends())
+
+ if request.params.get('user-tagged'):
+ filters.append(lambda track: request.identity and
+ (track.owner == request.identity or
+ request.identity in track.tagged_people))
+
+ return TrackFilters(filters)
+
+
def visible_tracks(dbsession, user):
"""Returns all visible tracks for the given user.
@@ -64,10 +170,13 @@ def browse(request):
:return: The HTTP response.
:rtype: pyramid.response.Response
"""
+ filters = TrackFilters.parse(request)
tracks = visible_tracks(request.dbsession, request.identity)
+ tracks = [track for track in tracks if filters.apply(track)]
return {
'tracks': tracks,
'mps_to_kph': util.mps_to_kph,
+ 'used_filters': bool(filters),
}