diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4407fe1..ce611a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,21 @@
# Matemat Changelog
+
+## Version 0.3.16
+
+Settings UI rework
+
+### Changes
+
+
+- fix: store notifications in the session so that they won't be served to other clients
+- feat: list all users and products in a table in the settings
+- feat: add back buttons to signup, password login and touchkey login pages
+- feat: if the tabfocus webextension is installed, use it to focus the tab when a barcode is scanned
+
+
+
+
## Version 0.3.15
diff --git a/matemat/__init__.py b/matemat/__init__.py
index 67cbcd2..b91143c 100644
--- a/matemat/__init__.py
+++ b/matemat/__init__.py
@@ -1,2 +1,2 @@
-__version__ = '0.3.15'
+__version__ = '0.3.16'
diff --git a/matemat/webserver/pagelets/buy.py b/matemat/webserver/pagelets/buy.py
index 84b7855..5ce27ba 100644
--- a/matemat/webserver/pagelets/buy.py
+++ b/matemat/webserver/pagelets/buy.py
@@ -3,6 +3,8 @@ from bottle import get, post, redirect, request
from matemat.db import MatematDatabase
from matemat.webserver import session
from matemat.webserver import config as c
+from matemat.webserver.template import Notification
+from matemat.util.currency_format import format_chf
@get('/buy')
@@ -35,9 +37,11 @@ def buy():
stock_provider = c.get_stock_provider()
if stock_provider.needs_update():
stock_provider.update_stock(product, -1)
+ # Show notification on next page load
+ Notification.success(
+ f'Purchased {product.name} for {format_chf(price)}', decay=True)
# Logout user if configured, logged in via touchkey and no price entry input was shown
if user.logout_after_purchase and authlevel < 2 and not product.custom_price:
- redirect(f'/logout?lastaction=buy&lastproduct={pid}&lastprice={price}')
- # Redirect to the main page (where this request should have come from)
- redirect(f'/?lastaction=buy&lastproduct={pid}&lastprice={price}')
+ redirect('/logout')
+ # Redirect to the main page (where this request should have come from)
redirect('/')
diff --git a/matemat/webserver/pagelets/deposit.py b/matemat/webserver/pagelets/deposit.py
index 291d0d2..cd19b26 100644
--- a/matemat/webserver/pagelets/deposit.py
+++ b/matemat/webserver/pagelets/deposit.py
@@ -3,6 +3,8 @@ from bottle import get, post, redirect, request
from matemat.db import MatematDatabase
from matemat.webserver import session
from matemat.webserver.config import get_app_config
+from matemat.webserver.template import Notification
+from matemat.util.currency_format import format_chf
@get('/deposit')
@@ -26,6 +28,7 @@ def deposit():
n = int(str(request.params.n))
# Write the deposit to the database
db.deposit(user, n)
+ # Show notification on next page load
+ Notification.success(f'Deposited {format_chf(n)}', decay=True)
# Redirect to the main page (where this request should have come from)
- redirect(f'/?lastaction=deposit&lastprice={n}')
redirect('/')
diff --git a/matemat/webserver/pagelets/main.py b/matemat/webserver/pagelets/main.py
index 7f9a255..a3ee069 100644
--- a/matemat/webserver/pagelets/main.py
+++ b/matemat/webserver/pagelets/main.py
@@ -20,12 +20,6 @@ def main_page():
with MatematDatabase(config['DatabaseFile']) as db:
# Fetch the list of products to display
products = db.list_products()
- lastprice = int(request.params.lastprice) if request.params.lastprice else None
- if request.params.lastaction == 'deposit' and lastprice:
- Notification.success(f'Deposited {format_chf(lastprice)}', decay=True)
- elif request.params.lastaction == 'buy' and lastprice and request.params.lastproduct:
- lastproduct = db.get_product(request.params.lastproduct)
- Notification.success(f'Purchased {lastproduct.name} for {format_chf(lastprice)}', decay=True)
buyproduct = None
if request.params.ean:
diff --git a/matemat/webserver/session/__init__.py b/matemat/webserver/session/__init__.py
index b8aaacf..2428aa3 100644
--- a/matemat/webserver/session/__init__.py
+++ b/matemat/webserver/session/__init__.py
@@ -1,2 +1,2 @@
-from .sessions import start, end, put, get, has, delete
+from .sessions import start, end, put, get, has, delete, setdefault
diff --git a/matemat/webserver/session/sessions.py b/matemat/webserver/session/sessions.py
index dc9f8a4..0837055 100644
--- a/matemat/webserver/session/sessions.py
+++ b/matemat/webserver/session/sessions.py
@@ -20,6 +20,9 @@ def start() -> str:
:return: The session ID.
"""
+ if hasattr(response, 'session_id'):
+ # A session has already been created while handling the same request
+ return response.session_id
# Reference date for session timeout
now = datetime.now(UTC)
# Read the client's session ID, if any
@@ -43,6 +46,9 @@ def start() -> str:
(now + timedelta(seconds=_SESSION_TIMEOUT), __session_vars[session_id][1])
# Return the session ID and timeout
response.set_cookie(_COOKIE_NAME, session_id, secret=__key)
+ # Piggy-back the session id onto the response object so that we don't create another session
+ # in subsequent calls to start() while handling the same request.
+ response.session_id = session_id
return session_id
@@ -61,10 +67,10 @@ def put(session_id: str, key: str, value: Any) -> None:
__session_vars[session_id][1][key] = value
-def get(session_id: str, key: str) -> Any:
+def get(session_id: str, key: str, default: Any = None) -> Any:
if session_id in __session_vars and key in __session_vars[session_id][1]:
return __session_vars[session_id][1][key]
- return None
+ return default
def delete(session_id: str, key: str) -> None:
@@ -74,3 +80,13 @@ def delete(session_id: str, key: str) -> None:
def has(session_id: str, key: str) -> bool:
return session_id in __session_vars and key in __session_vars[session_id][1]
+
+
+def setdefault(session_id: str, key: str, value: Any) -> Any:
+ if session_id in __session_vars:
+ if has(session_id, key):
+ return get(session_id, key)
+ else:
+ put(session_id, key, value)
+ return value
+ return None
diff --git a/matemat/webserver/template/notification.py b/matemat/webserver/template/notification.py
index a82690d..5e4ef33 100644
--- a/matemat/webserver/template/notification.py
+++ b/matemat/webserver/template/notification.py
@@ -1,7 +1,8 @@
+from matemat.webserver import session
+
class Notification:
- notifications = []
def __init__(self, msg: str, classes=None, decay: bool = False):
self.msg = msg
@@ -12,14 +13,18 @@ class Notification:
@classmethod
def render(cls):
- n = list(cls.notifications)
- cls.notifications.clear()
+ session_id: str = session.start()
+ sn = session.get(session_id, 'notifications', [])
+ n = list(sn)
+ sn.clear()
return n
@classmethod
def success(cls, msg: str, decay: bool = False):
- cls.notifications.append(cls(msg, classes=['success'], decay=decay))
+ session_id: str = session.start()
+ session.setdefault(session_id, 'notifications', []).append(cls(msg, classes=['success'], decay=decay))
@classmethod
def error(cls, msg: str, decay: bool = False):
- cls.notifications.append(cls(msg, classes=['error'], decay=decay))
+ session_id: str = session.start()
+ session.setdefault(session_id, 'notifications', []).append(cls(msg, classes=['error'], decay=decay))
diff --git a/templates/admin.html b/templates/admin.html
index f397c46..31d8d41 100644
--- a/templates/admin.html
+++ b/templates/admin.html
@@ -25,10 +25,8 @@
{% endblock %}
{% block eanwebsocket %}
- function (e) {
let eaninput = document.getElementById("admin-newproduct-ean");
eaninput.value = e.data;
eaninput.select();
eaninput.scrollIntoView();
- }
{% endblock %}
diff --git a/templates/admin_restricted.html b/templates/admin_restricted.html
index dc3056e..aa7656a 100644
--- a/templates/admin_restricted.html
+++ b/templates/admin_restricted.html
@@ -1,105 +1,85 @@
Create New User
+ Users
-Modify User
-
-
Create New Product
+ Products
-Restock Product
-
-
-Modify Product
-
-