]> littlesong.place Git - littlesongplace.git/commitdiff
Add new event page, refactor jam stuff
authorChris Fulljames <christianfulljames@gmail.com>
Sat, 12 Apr 2025 15:24:08 +0000 (11:24 -0400)
committerChris Fulljames <christianfulljames@gmail.com>
Sat, 12 Apr 2025 15:24:08 +0000 (11:24 -0400)
src/littlesongplace/jams.py
src/littlesongplace/templates/jam-event-list.html [new file with mode: 0644]
src/littlesongplace/templates/jam-event.html [new file with mode: 0644]
src/littlesongplace/templates/jam.html
src/littlesongplace/templates/jams-main.html
test/test_jams.py

index 771340e35d4313397adfc7a5b5030340612dac2a..75a143c81bfec72b857efff4add6e647d46c3542 100644 (file)
@@ -1,3 +1,4 @@
+import functools
 from dataclasses import dataclass
 from datetime import datetime, timezone
 
@@ -9,7 +10,20 @@ from .sanitize import sanitize_user_text
 bp = Blueprint("jams", __name__, url_prefix="/jams")
 
 
-@bp.get("/")
+def jam_owner_only(f):
+    @functools.wraps(f)
+    def _wrapper(jamid, *args, **kwargs):
+        row = db.query(
+                "SELECT * FROM jams WHERE jamid = ?", [jamid], expect_one=True)
+
+        if row["ownerid"] != g.userid:
+            abort(403)  # Forbidden; cannot modify other user's jam
+
+        return f(jamid, *args, **kwargs)
+    return _wrapper
+
+
+@bp.get("")
 def jams():
     # Show a list of all jams: ongoing, upcoming, previous
     rows = db.query(
@@ -55,6 +69,7 @@ def jam(jamid):
 
 @bp.post("/<int:jamid>/update")
 @auth.requires_login
+@jam_owner_only
 def update(jamid):
     # Update a jam with the new form data, redirect to view page
     title = request.form["title"]
@@ -73,6 +88,7 @@ def update(jamid):
 
 @bp.get("/<int:jamid>/delete")
 @auth.requires_login
+@jam_owner_only
 def delete(jamid):
     # Delete a jam, redirect to the jams list
     row = db.query(
@@ -83,35 +99,32 @@ def delete(jamid):
     return redirect(url_for("jams.jams"))
 
 
-@bp.get("/<int:jamid>/events")
-def events(jamid):
-    # Show a list of all events for the jam (current, upcoming, previous)
-    ...
-
-
 @bp.get("/<int:jamid>/events/create")
 @auth.requires_login
-def events_create():
+@jam_owner_only
+def events_create(jamid):
     # Create a new event and redirect to the edit form
     ...
 
 
 @bp.get("/<int:jamid>/events/<int:eventid>")
-def events_view(eventid):
+def events_view(jamid, eventid):
     # Show the event page
     ...
 
 
 @bp.post("/<int:jamid>/events/<int:eventid>/update")
 @auth.requires_login
-def events_update(jamid):
+@jam_owner_only
+def events_update(jamid, eventid):
     # Update an event with the new form data
     ...
 
 
 @bp.get("/<int:jamid>/events/<int:eventid>/delete")
 @auth.requires_login
-def events_delete(jamid):
+@jam_owner_only
+def events_delete(jamid, eventid):
     # Delete an event, redirect to list of all events
     ...
 
@@ -130,7 +143,24 @@ class Jam:
 
     @classmethod
     def from_row(cls, row):
-        event_rows = db.query("SELECT * FROM jam_events WHERE jamid = ?", [row["jamid"]])
+        event_rows = db.query(
+                """
+                SELECT
+                    e.eventid,
+                    e.jamid,
+                    e.title,
+                    e.threadid,
+                    e.created,
+                    e.startdate,
+                    e.enddate,
+                    e.description,
+                    j.title as jam_title,
+                    u.username as jam_ownername
+                FROM jam_events as e
+                INNER JOIN jams as j on e.jamid = j.jamid
+                INNER JOIN users as u on j.ownerid = u.userid
+                WHERE e.jamid = ?
+                """, [row["jamid"]])
         events = [JamEvent.from_row(r) for r in event_rows]
         return cls(
                 jamid=row["jamid"],
@@ -153,6 +183,8 @@ class JamEvent:
     startdate: datetime
     enddate: datetime
     description: str
+    jam_title: str
+    jam_ownername: str
     # TODO: Comment object?
     comments: list
 
@@ -168,6 +200,8 @@ class JamEvent:
                 startdate=datetime.fromisoformat(row["startdate"]),
                 enddate=datetime.fromisoformat(row["enddate"]),
                 description=sanitize_user_text(row["description"] or ""),
+                jam_title=row["jam_title"],
+                jam_ownername=row["jam_ownername"],
                 # TODO: Comment object?
                 comments=comments,
         )
diff --git a/src/littlesongplace/templates/jam-event-list.html b/src/littlesongplace/templates/jam-event-list.html
new file mode 100644 (file)
index 0000000..0e6a802
--- /dev/null
@@ -0,0 +1,21 @@
+{% macro jam_event_list(list_title, events) %}
+{% if events %}
+<h2>{{ list_title }}</h2>
+<div class="jam-event-list">
+    {% for event in events %}
+    <div class="jam-event-list-entry">
+        <span class="jam-event-list-title">
+            <a href="/jams/{{ event.jamid }}/events/{{ event.eventid }}">{{ event.title }}</a>
+        </span>
+        -
+        <span class="jam-event-list-jam-title">
+            <a href="/jams/{{ event.jamid }}">{{ event.jam_title }}</a>
+        </span>
+        <span class="jam-event-list-owner">
+            Hosted by <a href="/users/{{ event.jam_ownername }}" class="profile-link">{{ event.jam_ownername }}</a>
+        </span>
+    </div>
+    {% endfor %}
+</div>
+{% endif %}
+{% endmacro %}
diff --git a/src/littlesongplace/templates/jam-event.html b/src/littlesongplace/templates/jam-event.html
new file mode 100644 (file)
index 0000000..06c4ca1
--- /dev/null
@@ -0,0 +1,36 @@
+{% extends "base.html" %}
+
+{% block title %}{{ event.title }} - {{ jam.title }}{% endblock %}
+
+{% block body %}
+
+<h1>{{ event.title }} - {{ jam.title }}</h1>
+
+<div>
+    <strong>Host:</strong>
+        <a href="/users/{{ jam.ownername }}" class="profile-link">{{ jam.ownername }}</a>
+        <br/>
+    <strong>Start Date:</strong>
+        {{ event.startdate.aslocaltime().strftime("%Y-%m-%d @ %H:%M") }}
+        <br/>
+    <strong>End Date:</strong>
+        {{ event.enddate.aslocaltime().strftime("%Y-%m-%d @ %H:%M") }}
+</div>
+
+<div class="jam-description">
+{{ event.description }}
+</div>
+
+<h2>About the Jam</h2>
+<div class="jam-description">
+{{ jam.description }}
+</div>
+
+<h2>Submissions</h2>
+<button class="button">Submit a Song</button>
+{% include "song-list.html" %}
+
+<h2>Comments</h2>
+{{ comment_thread(event.threadid, session['userid'], jam.ownerid, event.comments) }}
+
+{% endblock %}
index 4704f54eca64dfa2ea9595925336c29be4623ed9..776d7b53cbafe5fd3892ff364a30fabc2365f965 100644 (file)
@@ -7,12 +7,18 @@
 <h1>{{ jam.title }}</h1>
 
 <div>
-    <span><strong>Jam Master:</strong></span> {{ jam.username }}
+    <strong>Host:</strong>
+    <a href="/users/{{ jam.ownername }}" class="profile-link">{{ jam.ownername }}</a>
 </div>
 
-<div>
 <h2>Description</h2>
+<div>
 {{ jam.description }}
 </div>
 
+{% from "jam-event-list.html" import jam_event_list %}
+{{ jam_event_list("Ongoing Events", ongoing) }}
+{{ jam_event_list("Upcoming Events", upcoming) }}
+{{ jam_event_list("Past Events", past) }}
+
 {% endblock %}
index 7144d4db30a71e65525bcbf2db3aa460893e822d..1bcf7c4bc58e70c32bb0e6d798a5d21d146512f4 100644 (file)
@@ -6,9 +6,12 @@
 
 <h1>Jams</h1>
 
-{% macro jam_list(list_title, jams) %}
-{% if jams %}
-<h2>{{ list_title }}</h2>
+{% from "jam-event-list.html" import jam_event_list %}
+{{ jam_event_list("Ongoing Events", ongoing) }}
+{{ jam_event_list("Upcoming Events", upcoming) }}
+{{ jam_event_list("Recent Events", recent) }}
+
+<h2>All Jams</h2>
 <div class="jam-list">
     {% for jam in jams %}
     <div class="jam-list-entry">
     </div>
     {% endfor %}
 </div>
-{% endif %}
-{% endmacro %}
-
-{{ jam_list("Ongoing Jams", ongoing) }}
-{{ jam_list("Upcoming Jams", upcoming) }}
-{{ jam_list("Past Jams", past) }}
 
 {% endblock %}
index 7fa1d417279110d077f9e0780bca53ecb6dfdfc0..e415101379e4f8384a05762173b2e73315da08df 100644 (file)
@@ -23,7 +23,7 @@ def test_create_jam(client, user):
     assert b"New Jam" in response.data
 
 def test_jams_list(client, user, jam):
-    response = client.get("/jams/")
+    response = client.get("/jams")
     assert response.status_code == 200
     assert b"New Jam" in response.data
 
@@ -44,9 +44,16 @@ def test_update_invalid_jam(client, user):
             data={"title": "Coolest Jam", "description": "pb and jam"})
     assert response.status_code == 404
 
+def test_update_other_users_jam(client, user, jam):
+    create_user(client, "otheruser", login=True)
+    response = client.post(
+            f"/jams/{jam}/update",
+            data={"title": "Coolest Jam", "description": "pb and jam"})
+    assert response.status_code == 403
+
 def test_delete_jam(client, user, jam):
     response = client.get(f"/jams/{jam}/delete", follow_redirects=True)
-    assert response.request.path == "/jams/"
+    assert response.request.path == "/jams"
     assert b"New Jam" not in response.data
 
     response = client.get(f"/jams/{jam}")
@@ -55,3 +62,8 @@ def test_delete_jam(client, user, jam):
 def test_delete_invalid_jam(client, user):
     response = client.get("/jams/1/delete")
     assert response.status_code == 404
+
+def test_delete_other_users_jam(client, user, jam):
+    create_user(client, "otheruser", login=True)
+    response = client.get(f"/jams/{jam}/delete")
+    assert response.status_code == 403