commit d8ea9e6bd16c79853d401258ba8463b506584cd7 Author: Ed Date: Sun Dec 18 17:40:39 2022 +0100 Initial commit diff --git a/app.py b/app.py new file mode 100644 index 0000000..606b609 --- /dev/null +++ b/app.py @@ -0,0 +1,113 @@ +from sanic import Sanic, response, exceptions +from sanic.response import text, html, redirect, raw +from jinja2 import Environment, FileSystemLoader +from time import time +import httpx +import re +import json +from ext import * +from config import * + +app = Sanic(__name__) +app.static("/res", "res/") + +app.ext.add_dependency(Order, get_order) +app.ext.add_dependency(Quotas, get_quotas) + +from room import bp as room_bp + +app.blueprint([room_bp,]) + +@app.exception(exceptions.SanicException) +async def clear_session(request, exception): + tpl = app.ctx.tpl.get_template('error.html') + response = html(tpl.render(exception=exception)) + + if exception.status_code == 403: + del response.cookies["foxo_code"] + del response.cookies["foxo_secret"] + return response + +@app.before_server_start +async def main_start(*_): + print(">>>>>> main_start <<<<<<") + app.ctx.tpl = Environment(loader=FileSystemLoader("tpl"), autoescape=True) + app.ctx.tpl.globals.update(time=time) + app.ctx.tpl.globals.update(int=int) + app.ctx.tpl.globals.update(len=len) + +@app.route("/furizon/beyond/order///open/") +async def redirect_explore(request, code, secret, order: Order, secret2=None): + + response = redirect(app.url_for("welcome")) + if order and order.code != code: order = None + + if not order: + async with httpx.AsyncClient() as client: + res = await client.get(f"https://reg.furizon.net/api/v1/organizers/furizon/events/beyond/orders/{code}/", headers=headers) + if res.status_code != 200: + raise exceptions.NotFound("This order code does not exist. Check that your order wasn't deleted, or the link is correct.") + + res = res.json() + if secret != res['secret']: + raise exceptions.Forbidden("The secret part of the url is not correct. Check your E-Mail for the correct link, or contact support!") + response.cookies['foxo_code'] = code + response.cookies['foxo_secret'] = secret + return response + +@app.route("/manage/welcome") +async def welcome(request, order: Order, quota: Quotas): + + if not order: + raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!") + + pending_roommates = [] + if order.pending_roommates: + print('Oleee') + print(order.ans('pending_roommates')) + for pr in order.pending_roommates: + if not pr: continue + print(pr) + pending_roommates.append(await get_order(code=pr, insecure=True)) + + print(pending_roommates) + + room_members = [] + if order.room_id: + if order.room_id != order.code: + room_owner = await get_order(code=order.room_id, insecure=True) + else: + room_owner = order + + room_members.append(room_owner) + + for member_id in room_owner.ans('room_members').split(','): + if member_id == room_owner.code: continue + if member_id == order.code: + room_members.append(order) + else: + room_members.append(await get_order(code=member_id, insecure=True)) + + tpl = app.ctx.tpl.get_template('welcome.html') + return html(tpl.render(order=order, quota=quota, room_members=room_members, pending_roommates=pending_roommates)) + + +@app.route("/manage/download_ticket") +async def download_ticket(request, order: Order, quota: Quotas): + + if not order: + raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!") + + if not order.status != 'confirmed': + raise exceptions.Forbidden("You are not allowed to download this ticket.") + + async with httpx.AsyncClient() as client: + res = await client.get(f"https://reg.furizon.net/api/v1/organizers/furizon/events/beyond/orders/{order.code}/download/pdf/", headers=headers) + if res.status_code != 200: + raise exceptions.FileNotFound("Your ticket hasn't been generated yet. Please try later!") + + return raw(res.content, content_type='application/pdf') + print(res.content) + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=8188, dev=True) diff --git a/config.py b/config.py new file mode 100644 index 0000000..17fa113 --- /dev/null +++ b/config.py @@ -0,0 +1 @@ +headers = {'Authorization': 'Token 2q5h7a2z9tqfz8ogu0qpriclwufelswc7obapw1mq2l1b4zaos7ebpz424zvz5gv'} diff --git a/ext.py b/ext.py new file mode 100644 index 0000000..8cfc1ec --- /dev/null +++ b/ext.py @@ -0,0 +1,126 @@ +from dataclasses import dataclass +from sanic import Request, exceptions +import httpx +import re +from config import * + +@dataclass +class Order: + def __init__(self, data): + self.data = data + self.status = {'n': 'pending', 'p': 'paid', 'e': 'expired', 'c': 'canceled'}[self.data['status']] + self.code = data['code'] + + for p in self.data['positions']: + if p['item'] not in [16, 38]: + continue + + self.position_id = p['id'] + self.position_positionid = p['positionid'] + self.answers = p['answers'] + + self.name = self.ans('fursona_name') + self.room_id = self.ans('room_id') + self.room_confirmed = self.ans('room_confirmed') + self.pending_room = self.ans('pending_room') + self.pending_roommates = self.ans('pending_roommates').split(',') if self.ans('pending_roommates') else [] + self.room_members = self.ans('room_members').split(',') if self.ans('room_members') else [] + self.room_owner = (self.code == self.room_id) + self.room_secret = self.ans('room_secret') + + def __getitem__(self, var): + return self.data[var] + + def ans(self, name): + for a in self.answers: + if a['question_identifier'] == name: + if a['answer'] in ['True', 'False']: + return bool(a['answer'] == 'True') + return a['answer'] + return None + + async def edit_answer(self, name, new_answer): + found = False + for key in range(len(self.answers)): + if self.answers[key]['question_identifier'] == name: + if new_answer != None: + print('EXISTING ANSWER UPDATE', name, '=>', new_answer) + self.answers[key]['answer'] = new_answer + found = True + else: + print('DEL ANSWER', name, '=>', new_answer) + del self.answers[key] + + break + + if (not found) and (new_answer is not None): + + async with httpx.AsyncClient() as client: + res = await client.get('https://reg.furizon.net/api/v1/organizers/furizon/events/beyond/questions/', headers=headers) + res = res.json() + + for r in res['results']: + if r['identifier'] != name: continue + + print('ANSWER UPDATE', name, '=>', new_answer) + self.answers.append({ + 'question': r['id'], + 'answer': new_answer, + 'question_identifier': r['identifier'], + 'options': r['options'] + }) + + async def send_answers(self): + async with httpx.AsyncClient() as client: + res = await client.patch(f'https://reg.furizon.net/api/v1/organizers/furizon/events/beyond/orderpositions/{self.position_id}/', headers=headers, json={'answers': self.answers}) + +@dataclass +class Quotas: + def __init__(self, data): + self.data = data + self.capacity_mapping = { + 1: 16, + 2: 17, + 3: 18, + 4: 19, + 5: 20 + } + + def get_left(self, capacity): + for quota in self.data['results']: + if quota['id'] == self.capacity_mapping[capacity]: + return quota['available_number'] + +async def get_quotas(request: Request=None): + async with httpx.AsyncClient() as client: + res = await client.get(f"https://reg.furizon.net/api/v1/organizers/furizon/events/beyond/quotas/?order=id&with_availability=true", headers=headers) + res = res.json() + + return Quotas(res) + +async def get_order(request: Request=None, code=None, secret=None, insecure=False): + if request: + await request.receive_body() + code = request.cookies.get("foxo_code") + secret = request.cookies.get("foxo_secret") + + if re.match('^[A-Z0-9]{5}$', code or '') and (secret is None or re.match('^[a-z0-9]{16,}$', secret)): + print('Trying to get a session from stored', code, secret) + async with httpx.AsyncClient() as client: + res = await client.get(f"https://reg.furizon.net/api/v1/organizers/furizon/events/beyond/orders/{code}/", headers=headers) + if res.status_code != 200: + if request: + raise exceptions.Forbidden("Your session has expired due to order deletion or change! Please check your E-Mail for more info.") + + res = res.json() + + if res['status'] in ['c', 'e']: + if request: + raise exceptions.Forbidden(f"Your order has been deleted. Contact support with your order identifier ({res['code']}) for further info.") + + if secret == res['secret'] or insecure: + return Order(res) + else: + if request: + raise exceptions.Forbidden("Your session has expired due to a token change. Please check your E-Mail for an updated link!") + return None diff --git a/res/avatar.jpg b/res/avatar.jpg new file mode 100644 index 0000000..79716d6 Binary files /dev/null and b/res/avatar.jpg differ diff --git a/res/new.png b/res/new.png new file mode 100644 index 0000000..9fe8e46 Binary files /dev/null and b/res/new.png differ diff --git a/room.py b/room.py new file mode 100644 index 0000000..a1efb2e --- /dev/null +++ b/room.py @@ -0,0 +1,279 @@ +from sanic.response import html, redirect, text +from sanic import Blueprint, exceptions +from random import choice +from ext import * +from config import headers + +bp = Blueprint("room", url_prefix="/manage/room") + +@bp.post("/create") +async def room_create_post(request, order: Order): + if not order: raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!") + + error = None + name = request.form.get('name') + + if len(name) > 64 or len(name) < 4: + error = "Your room name is invalid. Please try another one." + + if order.room_id: + error = "You are already in another room. You need to delete (if it's yours) or leave it before creating another." + + if not error: + await order.edit_answer('room_name', name) + await order.edit_answer('room_id', order.code) + await order.edit_answer('room_members', order.code) + await order.edit_answer('room_secret', ''.join(choice('0123456789') for _ in range(6))) + await order.send_answers() + return redirect('/manage/welcome') + + tpl = request.app.ctx.tpl.get_template('create_room.html') + return html(tpl.render(order=order, error=error)) + +@bp.route("/create") +async def room_create(request, order: Order): + + if not order: raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!") + + tpl = request.app.ctx.tpl.get_template('create_room.html') + return html(tpl.render(order=order)) + +@bp.route("/delete") +async def delete_room(request, order: Order): + if not order: + raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!") + + if order.room_id != order.code: + raise exceptions.BadRequest("You are not allowed to delete room of others.") + + if order.ans('room_confirmed'): + raise exceptions.BadRequest("You are not allowed to change your room after it has been confirmed.") + + if len(order.room_members) > 1: + raise exceptions.BadRequest("You can only delete a room once there is nobody else inside.") + + await order.edit_answer('room_name', None) + await order.edit_answer('room_id', None) + await order.edit_answer('room_members', None) + await order.edit_answer('room_secret', None) + await order.send_answers() + return redirect('/manage/welcome') + +@bp.post("/join") +async def join_room(request, order: Order): + + if not order: + raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!") + + if order.pending_room: + raise exceptions.BadRequest("There is already a pending join request. Wait for the room owner to accept or refuse it.") + + if order.room_id: + raise exceptions.BadRequest("You are in another room already. Why would you join another?") + + code = request.form.get('code').strip() + room_secret = request.form.get('room_secret').strip() + + if (not code) or (not room_secret): + raise exceptions.BadRequest("The code or pin you provided are not valid.") + + room_owner = await get_order(code=code, insecure=True) + + if not room_owner: + raise exceptions.BadRequest("The code you provided is not valid.") + + if room_owner.room_secret != room_secret: + raise exceptions.BadRequest("The code or pin you provided is not valid.") + + if room_owner.room_confirmed: + raise exceptions.BadRequest("The room you're trying to join has been confirmed already") + + #if room_owner.pending_roommates and (order.code in room_owner.pending_roommates): + #raise exceptions.BadRequest("What? You should never reach this check, but whatever...") + + await order.edit_answer('pending_room', code) + await order.send_answers() + + pending_roommates = room_owner.pending_roommates + if not order.code in pending_roommates: + pending_roommates.append(order.code) + + await room_owner.edit_answer('pending_roommates', ','.join(pending_roommates)) + await room_owner.send_answers() + + return redirect('/manage/welcome') + +@bp.route("/kick/") +async def kick_member(request, code, order: Order): + if not order: + raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!") + + if order.room_confirmed: + raise exceptions.BadRequest("You cannot kick people out of confirmed rooms") + + if not order.room_owner: + raise exceptions.BadRequest("You cannot kick people if you're not the room owner") + + to_kick = await get_order(code=code, insecure=True) + if to_kick.room_id != order.code: + raise exceptions.BadRequest("You cannot kick people of other rooms") + + await to_kick.edit_answer('room_id', None) + await order.edit_answer('room_members', ','.join([x for x in order.room_members if x != to_kick.code]) or None) + + await order.send_answers() + await to_kick.send_answers() + + return redirect('/manage/welcome') + +@bp.route("/renew_secret") +async def renew_secret(request, order: Order): + if not order: + raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!") + + if not order.room_id: + raise exceptions.BadRequest("What room were you even trying to renew?") + + if not order.room_owner: + raise exceptions.BadRequest("You are not allowed to renew rooms of others.") + + await order.edit_answer('room_secret', ''.join(choice('0123456789') for _ in range(6))) + await order.send_answers() + return redirect('/manage/welcome') + +@bp.route("/cancel_request") +async def renew_secret(request, order: Order): + if not order: + raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!") + + if not order.pending_room: + raise exceptions.BadRequest("There is no pending room.") + + room_owner = await get_order(code=order.pending_room, insecure=True) + pending_roommates = room_owner.pending_roommates + if order.code in pending_roommates: + pending_roommates.remove(order.code) + await room_owner.edit_answer('pending_roommates', ','.join(pending_roommates) if pending_roommates else None) + await room_owner.send_answers() + + await order.edit_answer('pending_room', None) + await order.send_answers() + return redirect('/manage/welcome') + +@bp.route("/approve/") +async def reject_roomreq(request, code, order: Order): + if not order: + raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!") + + if not code in order.pending_roommates: + raise exceptions.BadRequest("You cannot accept people that didn't request to join your room") + + if order.room_confirmed: + raise exceptions.BadRequest("You cannot accept people to a confirmed room.") + + pending_member = await get_order(code=code, insecure=True) + + if pending_member.room_id: + raise exceptions.BadRequest("You cannot accept people who are in a room.") + + if pending_member.pending_room != order.code: + raise exceptions.BadRequest("You cannot accept people who are in another room or waiting to accept another request.") + + await pending_member.edit_answer('room_id', order.code) + await pending_member.edit_answer('pending_room', None) + + await order.edit_answer('room_members', ','.join([*order.room_members, pending_member.code])) + await order.edit_answer('pending_roommates', (','.join([x for x in order.pending_roommates if x != pending_member.code]) or None)) + + await pending_member.send_answers() + await order.send_answers() + + return redirect('/manage/welcome') + +@bp.route("/reject/") +async def reject_roomreq(request, code, order: Order): + if not order: + raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!") + + if not code in order.pending_roommates: + raise exceptions.BadRequest("You cannot reject people that didn't request to join your room") + + if order.room_confirmed: + raise exceptions.BadRequest("You cannot reject people to a confirmed room.") + + pending_member = await get_order(code=code, insecure=True) + + if pending_member.room_id: + raise exceptions.BadRequest("You cannot reject people who are in a room.") + + if pending_member.pending_room != order.code: + raise exceptions.BadRequest("You cannot reject people who are in another room or waiting to accept another request.") + + await pending_member.edit_answer('pending_room', None) + await order.edit_answer('pending_roommates', (','.join([x for x in order.pending_roommates if x != pending_member.code]) or None)) + + await pending_member.send_answers() + await order.send_answers() + + return redirect('/manage/welcome') + +@bp.route("/confirm") +async def confirm_room(request, order: Order, quotas: Quotas): + if not order: + raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!") + + if not order.room_id: + raise exceptions.BadRequest("Try joining a room before confirming it.") + + if order.room_id != order.code: + raise exceptions.BadRequest("You are not allowed to confirm rooms of others.") + + if quotas.get_left(len(order.room_members)) == 0: + raise exceptions.BadRequest("There are no more rooms of this size to reserve.") + + room_members = [] + for m in order.room_members: + if m == order.code: + res = order + else: + res = await get_order(code=m, insecure=True) + + if res.room_id != order.code: + raise exceptions.BadRequest("Please contact support: some of the members in your room are actually somewhere else") + + if res.status != 'paid': + raise exceptions.BadRequest("Somebody hasn't paid.") + + room_members.append(res) + + for rm in room_members: + await rm.edit_answer('room_id', order.code) + await rm.edit_answer('room_confirmed', "True") + await rm.edit_answer('pending_roommates', None) + await rm.edit_answer('pending_room', None) + + print(room_members) + print(len(room_members)) + + thing = { + 'order': order.code, + 'addon_to': order.position_positionid, + 'item': 39, + 'variation': ([None, 16, 17, 18, 19, 20])[len(room_members)] + } + + async with httpx.AsyncClient() as client: + res = await client.post("https://reg.furizon.net/api/v1/organizers/furizon/events/beyond/orderpositions/", headers=headers, json=thing) + + print(thing) + print(res.json()) + + print(res.status_code) + + if res.status_code != 201: + raise exceptions.BadRequest("Something has gone wrong! Please contact support immediately") + + for rm in room_members: + await rm.send_answers() + + return redirect('/manage/welcome') diff --git a/tpl/base.html b/tpl/base.html new file mode 100644 index 0000000..1a2a971 --- /dev/null +++ b/tpl/base.html @@ -0,0 +1,21 @@ + + + + + {% block title %}{% endblock %} + + + + + {% block main %}{% endblock %} + + diff --git a/tpl/create_room.html b/tpl/create_room.html new file mode 100644 index 0000000..8b3ee3e --- /dev/null +++ b/tpl/create_room.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% block title %}Welcome!{% endblock %} +{% block main %} + +
+
+

Create a new room

+
+ {% if error %} +

{{error}}

+ {% endif %} +

Give a name to your room. This name will be public and shown in the room list, so nothing offensive! :)

+
+ + + +
+
+ +{% endblock %} diff --git a/tpl/error.html b/tpl/error.html new file mode 100644 index 0000000..d75a946 --- /dev/null +++ b/tpl/error.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} +{% block title %}Error {{exception.status_code}}{% endblock %} +{% block main %} +
+

Oh no! Error {{exception.status_code}}

+

{{exception}}

+
+{% endblock %} diff --git a/tpl/manage.html b/tpl/manage.html new file mode 100644 index 0000000..8773d66 --- /dev/null +++ b/tpl/manage.html @@ -0,0 +1,97 @@ +{% extends "base.html" %} +{% block title %}Your Booking{% endblock %} +{% block main %} +
+
+

{{order.ans('fursona_name')}}'s booking

+
+ {% if order.status() == 'pending' %} +

⚠️ Your order is still pending. You will not be able to reserve a room. Check your payment status

+ {% endif %} + {% if not order.ans('propic') %} +

⚠️ You still haven't uploaded a profile pic. Upload one now

+ {% endif %} + +
+

Room Status: + {% if order.ans('room_confirmed') %} + {{order.ans('room_confirmed')}} + room confirmed

+

Your room is confirmed! Enjoy the convention :)

+ {% elif order.ans('room_members') %} + pending room +

You have a room, but it is not confirmed! Make sure all room owners have paid, then use the button below to reserve the room for two people.

+ +
Your room
+
+ {% set room = namespace(forbidden=false) %} + {% for person in roommate_orders %} +
+ +

{{person.ans('fursona_name')}}

+

{{person.ans('staff_title') if person.ans('staff_title') else ''}}{{' · Fursuiter' if person.ans('is_fursuiter') != 'No'}}

+

{{('UNPAID' if person.status() == 'pending')|safe}}

+
+ {% if person.status() != 'paid' %} + {% set room.forbidden = True %} + {% endif %} + {% endfor %} +
+ +
Available rooms
+ + + + + + + + + + + + + + + + + + + + + +
Single room{{room_qty[1]}} available
Double room{{room_qty[2]}} available
Triple room{{room_qty[3]}} available
Quadruple room{{room_qty[4]}} available
Quintuple room{{room_qty[5]}} available
+ + {% if room.forbidden %} +

Since at least one of your roommates still have a pending reservation, you will not be able to reserve a room for now.

+ + {% else %} + + {% endif %} + {% else %} + no room +

You currently don't have any room. If you don't create or join one within 42 days, 10 hours, 32 minutes, 13 seconds, we will assign you to a random one.

+
+
+ + +
+
+ {% endif %} + + +
+
+ + + +{% endblock %} diff --git a/tpl/welcome.html b/tpl/welcome.html new file mode 100644 index 0000000..fd57179 --- /dev/null +++ b/tpl/welcome.html @@ -0,0 +1,228 @@ +{% extends "base.html" %} +{% block title %}{{order.name}}'s Booking{% endblock %} +{% block main %} + +
+
+

{{order.name}}'s Booking

+
+ + +
+

Payment

+ {% if order.status == 'pending' %} +

⚠️ Your order is still pending due to incomplete payment. You will not be able to reserve a room for now. However, you will be able to create one with your friends and confirm it once all attendants have completed the order!

+

If you wish to change payment method, check payment instructions or complete a failed payment please access the payment area.

+ + {% elif order.status == 'paid' %} +

✅ Your order has been completed and approved! See you at furizon!

+ {% endif %} + + + + + + + + + +
Reference ID{{order.code}}
Order total{{order.data['total']}}€ by {{'Credit card' if order.data['payment_provider'] == 'stripe' else 'Bank Transfer'}}
+ {% if order.status == 'paid' %} +

Download ticket

+ {% else %} + + {% endif %} +
+ + +
+

Your room {% if room_members %}- {{room_members[0].ans('room_name')}}{% endif %}

+ + {# Show alert if room owner has wrong people inside #} + {% if order.room_owner and quota.get_left(len(room_members)) == 0 and (not order.room_confirmed) %} +

⚠️ Your room contains {{len(room_members)}} people inside, but sadly there are 0 rooms of this size available. Add or remove people until you reach the size of an available room.

+ {% endif %} + + {# Show alert if room was not confirmed #} + {% if order.room_id and not order.room_confirmed %} +

⚠️ Your room hasn't been confirmed yet. Unconfirmed rooms are subject to changes by the staff as we optimize for hotel capacity.

+ {% endif %} + + {# Show notice if the room is confirmed #} + {% if order.room_confirmed %} +

✅ Your room has been confirmed

+ {% endif %} + + {# Show roommates if room is set #} + {% if order.room_id %} +
+ {% set room = namespace(forbidden=false) %} + {% for person in room_members %} +
+ +

{{person.ans('fursona_name')}}

+ {% if person.code == order.room_id %}

ROOM OWNER

{% endif %} +

{{person.ans('staff_title') if person.ans('staff_title') else ''}}{{' · Fursuiter' if person.ans('is_fursuiter') != 'No'}}

+ {% if person.status == 'pending' %} +

UNPAID

+ {% else %} +

PAID

+ {% endif %} + {% if order.room_owner and person.code != order.code %}KICK{% endif %} +
+ + {% if person.status != 'paid' %} + {% set room.forbidden = True %} + {% endif %} + {% endfor %} + + {% if order.room_id == order.code and not order.room_confirmed and len(room_members) < 5 %} + + {% endif %} +
+ {% elif order.pending_room %} +

You have have asked to join the room of another member. Wait for them to confirm or reject your request.

+ Cancel pending join request + {% else %} +

🎲 If you don't join a room or create your one within the room deadline, we will randomly put you into a room with free spots.

+

To join a room, ask somebody to send you their room code.

+

+ Create a room + Join a room +

+ {% endif %} + + {% if order.room_owner %} + {% if quota.get_left(len(room_members)) == 0 %} +

⚠️ There are no more {{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}}, therefore you will not be able to confirm this room. Please add or remove people until you reach an available room.

+ {% elif room.forbidden %} +

⚠️ There are roommates who still didn't pay for the order, therefore you will not be able to confirm this room. Please ask them to pay or kick them out from your room.

+ {% endif %} + {% endif %} + + {% if order.room_owner %} +

+ {% if len(room_members) == 1 %} + Delete room + {% endif %} + + {% if not order.room_confirmed %} + 0 %}href="javascript:document.getElementById('modal-roomconfirm').setAttribute('open', 'true');"{% endif %}>Confirm {{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}} room + {% endif %} +

+ {% endif %} + + {# Pending roommates #} + {% if pending_roommates %} +

Pending roommates

+

These people have asked to join your room.

+ + {% for person in pending_roommates %} + + + + {% if person.status == 'pending' %} + + {% else %} + + {% endif %} + + + + + + {% if person.status != 'paid' %} + {% set room.forbidden = True %} + {% endif %} + {% endfor %} +
{{person.name}}UNPAIDPAIDApproveReject
+ {% endif %} + + {# Room availability is always shown #} +

Room availability

+ + {% for q in quota.data['results'] if 'Room' in q['name'] %} + + + + + {% endfor %} +
{{q['name']}}{{q['available_number']}} left
+
+ + {% if order.room_owner and not order.room_confirmed %} + + +
+ +

Invite your friends!

+ + + + +

Send your Ticket ID and room PIN to other attendants you want in your room.

+

If you want to change the room PIN, use the "Reset PIN" button to change the secret code.

+ +
+
+ + +
+ +

Confirm this room

+

Confirming the room is the only way to guarantee that you will stay with your friends.

+

Confirmed room cannot be changed. You will not be able to add or remove roommates, or change to another size.

+

In case somebody from your room decides to not participate, they will be replaced with a random person, or your room size will be changed.

+ +

Your room

+ + + + + + + + + +
Room type{{[None,'Single','Double','Triple','Quadruple','Quintuple'][len(room_members)]}} Room
Rooms left of this type{{quota.get_left(len(room_members))}}
+ +
+
+ + + {% endif %} + + + {% if not order.room_id %} +
+ +
+ +

Join a room!

+ + + + + +
+
+
+ {% endif %} + +{% endblock %}