commit2 permissions
This commit is contained in:
160
app.py
160
app.py
@@ -306,6 +306,7 @@ class ListPermission(db.Model):
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
__table_args__ = (db.UniqueConstraint("list_id", "user_id", name="uq_list_user"),)
|
||||
|
||||
|
||||
ShoppingList.permitted_users = db.relationship(
|
||||
"User",
|
||||
secondary="list_permission",
|
||||
@@ -467,7 +468,7 @@ def get_total_expense_for_list(list_id, start_date=None, end_date=None):
|
||||
|
||||
|
||||
def update_list_categories_from_form(shopping_list, form):
|
||||
raw_vals = form.getlist("categories")
|
||||
raw_vals = form.getlist("categories")
|
||||
candidate_ids = set()
|
||||
|
||||
for v in raw_vals:
|
||||
@@ -562,17 +563,22 @@ def redirect_with_flash(
|
||||
flash(message, category)
|
||||
return redirect(url_for(endpoint))
|
||||
|
||||
|
||||
def can_view_list(sl: ShoppingList) -> bool:
|
||||
if current_user.is_authenticated:
|
||||
if sl.owner_id == current_user.id:
|
||||
return True
|
||||
if sl.is_public:
|
||||
return True
|
||||
return db.session.query(ListPermission.id).filter_by(
|
||||
list_id=sl.id, user_id=current_user.id
|
||||
).first() is not None
|
||||
return (
|
||||
db.session.query(ListPermission.id)
|
||||
.filter_by(list_id=sl.id, user_id=current_user.id)
|
||||
.first()
|
||||
is not None
|
||||
)
|
||||
return bool(sl.is_public)
|
||||
|
||||
|
||||
def db_bucket(col, kind: str = "month"):
|
||||
name = db.engine.name # 'sqlite', 'mysql', 'mariadb', 'postgresql', ...
|
||||
kind = (kind or "month").lower()
|
||||
@@ -587,9 +593,9 @@ def db_bucket(col, kind: str = "month"):
|
||||
|
||||
if kind == "week":
|
||||
if name == "sqlite":
|
||||
return func.printf("%s-W%s",
|
||||
func.strftime("%Y", col),
|
||||
func.strftime("%W", col))
|
||||
return func.printf(
|
||||
"%s-W%s", func.strftime("%Y", col), func.strftime("%W", col)
|
||||
)
|
||||
elif name in ("mysql", "mariadb"):
|
||||
return func.date_format(col, "%x-W%v")
|
||||
else:
|
||||
@@ -630,6 +636,7 @@ def user_permission_subq(user_id):
|
||||
ListPermission.user_id == user_id
|
||||
)
|
||||
|
||||
|
||||
def admin_required(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
@@ -851,6 +858,7 @@ def category_to_color(name):
|
||||
r, g, b = colorsys.hls_to_rgb(hue, lightness, saturation)
|
||||
return f"#{int(r*255):02x}{int(g*255):02x}{int(b*255):02x}"
|
||||
|
||||
|
||||
def get_total_expenses_grouped_by_category(
|
||||
show_all, range_type, start_date, end_date, user_id, category_id=None
|
||||
):
|
||||
@@ -885,13 +893,13 @@ def get_total_expenses_grouped_by_category(
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
# ZAKRES: zawsze po created_at LISTY
|
||||
if start_date and end_date:
|
||||
try:
|
||||
dt_start = datetime.strptime(start_date, "%Y-%m-%d")
|
||||
dt_end = datetime.strptime(end_date, "%Y-%m-%d") + timedelta(days=1)
|
||||
lists_q = lists_q.filter(ShoppingList.created_at >= dt_start,
|
||||
ShoppingList.created_at < dt_end)
|
||||
dt_end = datetime.strptime(end_date, "%Y-%m-%d") + timedelta(days=1)
|
||||
lists_q = lists_q.filter(
|
||||
ShoppingList.created_at >= dt_start, ShoppingList.created_at < dt_end
|
||||
)
|
||||
except Exception:
|
||||
return {"error": "Błędne daty"}
|
||||
|
||||
@@ -899,7 +907,6 @@ def get_total_expenses_grouped_by_category(
|
||||
if not lists:
|
||||
return {"labels": [], "datasets": []}
|
||||
|
||||
# SUMY: po wszystkich wydatkach tych list (bez filtra dat po Expense)
|
||||
list_ids = [l.id for l in lists]
|
||||
totals = (
|
||||
db.session.query(
|
||||
@@ -912,7 +919,6 @@ def get_total_expenses_grouped_by_category(
|
||||
)
|
||||
expense_map = {lid: float(total or 0) for lid, total in totals}
|
||||
|
||||
# bucket wg created_at LISTY
|
||||
def bucket_from_dt(ts: datetime) -> str:
|
||||
if range_type == "daily":
|
||||
return ts.strftime("%Y-%m-%d")
|
||||
@@ -948,7 +954,7 @@ def get_total_expenses_grouped_by_category(
|
||||
data_map[key][c.name] += total_expense
|
||||
|
||||
labels = sorted(all_labels)
|
||||
cats = sorted({cat for b in data_map.values() for cat,v in b.items() if v > 0})
|
||||
cats = sorted({cat for b in data_map.values() for cat, v in b.items() if v > 0})
|
||||
|
||||
datasets = [
|
||||
{
|
||||
@@ -960,6 +966,7 @@ def get_total_expenses_grouped_by_category(
|
||||
]
|
||||
return {"labels": labels, "datasets": datasets}
|
||||
|
||||
|
||||
def get_total_expenses_grouped_by_list_created_at(
|
||||
user_only=False,
|
||||
admin=False,
|
||||
@@ -1006,13 +1013,13 @@ def get_total_expenses_grouped_by_list_created_at(
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
# ZAKRES: zawsze po created_at LISTY
|
||||
if start_date and end_date:
|
||||
try:
|
||||
dt_start = datetime.strptime(start_date, "%Y-%m-%d")
|
||||
dt_end = datetime.strptime(end_date, "%Y-%m-%d") + timedelta(days=1)
|
||||
lists_q = lists_q.filter(ShoppingList.created_at >= dt_start,
|
||||
ShoppingList.created_at < dt_end)
|
||||
dt_end = datetime.strptime(end_date, "%Y-%m-%d") + timedelta(days=1)
|
||||
lists_q = lists_q.filter(
|
||||
ShoppingList.created_at >= dt_start, ShoppingList.created_at < dt_end
|
||||
)
|
||||
except Exception:
|
||||
return {"error": "Błędne daty"}
|
||||
|
||||
@@ -1020,7 +1027,6 @@ def get_total_expenses_grouped_by_list_created_at(
|
||||
if not lists:
|
||||
return {"labels": [], "expenses": []}
|
||||
|
||||
# SUMY: po wszystkich wydatkach tych list (bez filtra dat po Expense)
|
||||
list_ids = [l.id for l in lists]
|
||||
totals = (
|
||||
db.session.query(
|
||||
@@ -1033,7 +1039,6 @@ def get_total_expenses_grouped_by_list_created_at(
|
||||
)
|
||||
expense_map = {lid: float(total or 0) for lid, total in totals}
|
||||
|
||||
# bucket wg created_at LISTY
|
||||
def bucket_from_dt(ts: datetime) -> str:
|
||||
if range_type == "daily":
|
||||
return ts.strftime("%Y-%m-%d")
|
||||
@@ -1056,6 +1061,7 @@ def get_total_expenses_grouped_by_list_created_at(
|
||||
expenses = [round(grouped[l], 2) for l in labels]
|
||||
return {"labels": labels, "expenses": expenses}
|
||||
|
||||
|
||||
def resolve_range(range_type: str):
|
||||
now = datetime.now(timezone.utc)
|
||||
sd = ed = None
|
||||
@@ -1069,7 +1075,7 @@ def resolve_range(range_type: str):
|
||||
elif rt in ("last30days", "last_30_days"):
|
||||
sd = (now - timedelta(days=30)).date().strftime("%Y-%m-%d")
|
||||
ed = now.date().strftime("%Y-%m-%d")
|
||||
bucket = "monthly"
|
||||
bucket = "monthly"
|
||||
elif rt in ("last90days", "last_90_days"):
|
||||
sd = (now - timedelta(days=90)).date().strftime("%Y-%m-%d")
|
||||
ed = now.date().strftime("%Y-%m-%d")
|
||||
@@ -1079,6 +1085,18 @@ def resolve_range(range_type: str):
|
||||
sd = first.date().strftime("%Y-%m-%d")
|
||||
ed = now.date().strftime("%Y-%m-%d")
|
||||
bucket = "monthly"
|
||||
elif rt in (
|
||||
"currentmonth",
|
||||
"thismonth",
|
||||
"this_month",
|
||||
"monthtodate",
|
||||
"month_to_date",
|
||||
"mtd",
|
||||
):
|
||||
first = datetime(now.year, now.month, 1, tzinfo=timezone.utc)
|
||||
sd = first.date().strftime("%Y-%m-%d")
|
||||
ed = now.date().strftime("%Y-%m-%d")
|
||||
bucket = "monthly"
|
||||
|
||||
return sd, ed, bucket
|
||||
|
||||
@@ -1493,9 +1511,7 @@ def favicon():
|
||||
@app.route("/")
|
||||
def main_page():
|
||||
perm_subq = (
|
||||
user_permission_subq(current_user.id)
|
||||
if current_user.is_authenticated
|
||||
else None
|
||||
user_permission_subq(current_user.id) if current_user.is_authenticated else None
|
||||
)
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
@@ -1604,8 +1620,12 @@ def main_page():
|
||||
db.session.query(
|
||||
Item.list_id,
|
||||
func.count(Item.id).label("total_count"),
|
||||
func.sum(case((Item.purchased == True, 1), else_=0)).label("purchased_count"),
|
||||
func.sum(case((Item.not_purchased == True, 1), else_=0)).label("not_purchased_count"),
|
||||
func.sum(case((Item.purchased == True, 1), else_=0)).label(
|
||||
"purchased_count"
|
||||
),
|
||||
func.sum(case((Item.not_purchased == True, 1), else_=0)).label(
|
||||
"not_purchased_count"
|
||||
),
|
||||
)
|
||||
.filter(Item.list_id.in_(all_ids))
|
||||
.group_by(Item.list_id)
|
||||
@@ -1630,12 +1650,17 @@ def main_page():
|
||||
)
|
||||
|
||||
for l in all_lists:
|
||||
total_count, purchased_count, not_purchased_count = stats_map.get(l.id, (0, 0, 0))
|
||||
total_count, purchased_count, not_purchased_count = stats_map.get(
|
||||
l.id, (0, 0, 0)
|
||||
)
|
||||
l.total_count = total_count
|
||||
l.purchased_count = purchased_count
|
||||
l.not_purchased_count = not_purchased_count
|
||||
l.total_expense = latest_expenses_map.get(l.id, 0)
|
||||
l.category_badges = [{"name": c.name, "color": category_to_color(c.name)} for c in l.categories]
|
||||
l.category_badges = [
|
||||
{"name": c.name, "color": category_to_color(c.name)}
|
||||
for c in l.categories
|
||||
]
|
||||
else:
|
||||
for l in all_lists:
|
||||
l.total_count = 0
|
||||
@@ -1987,9 +2012,9 @@ def view_list(list_id):
|
||||
@login_required
|
||||
def expenses():
|
||||
start_date_str = request.args.get("start_date")
|
||||
end_date_str = request.args.get("end_date")
|
||||
category_id = request.args.get("category_id", type=str)
|
||||
show_all = request.args.get("show_all", "true").lower() == "true"
|
||||
end_date_str = request.args.get("end_date")
|
||||
category_id = request.args.get("category_id", type=str)
|
||||
show_all = request.args.get("show_all", "true").lower() == "true"
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
@@ -2002,10 +2027,10 @@ def expenses():
|
||||
if start_date_str and end_date_str:
|
||||
try:
|
||||
start = datetime.strptime(start_date_str, "%Y-%m-%d")
|
||||
end = datetime.strptime(end_date_str, "%Y-%m-%d") + timedelta(days=1)
|
||||
end = datetime.strptime(end_date_str, "%Y-%m-%d") + timedelta(days=1)
|
||||
lists_q = lists_q.filter(
|
||||
ShoppingList.created_at >= start,
|
||||
ShoppingList.created_at < end,
|
||||
ShoppingList.created_at < end,
|
||||
)
|
||||
except ValueError:
|
||||
flash("Błędny zakres dat", "danger")
|
||||
@@ -2024,16 +2049,16 @@ def expenses():
|
||||
pass
|
||||
|
||||
lists_filtered = (
|
||||
lists_q
|
||||
.options(joinedload(ShoppingList.owner), joinedload(ShoppingList.categories))
|
||||
lists_q.options(
|
||||
joinedload(ShoppingList.owner), joinedload(ShoppingList.categories)
|
||||
)
|
||||
.order_by(ShoppingList.created_at.desc())
|
||||
.all()
|
||||
)
|
||||
list_ids = [l.id for l in lists_filtered] or [-1]
|
||||
|
||||
expenses = (
|
||||
Expense.query
|
||||
.options(
|
||||
Expense.query.options(
|
||||
joinedload(Expense.shopping_list).joinedload(ShoppingList.owner),
|
||||
joinedload(Expense.shopping_list).joinedload(ShoppingList.categories),
|
||||
)
|
||||
@@ -2056,9 +2081,12 @@ def expenses():
|
||||
totals_map = {row.lid: float(row.total_expense or 0) for row in totals_rows}
|
||||
|
||||
categories = (
|
||||
Category.query
|
||||
.join(shopping_list_category, shopping_list_category.c.category_id == Category.id)
|
||||
.join(ShoppingList, ShoppingList.id == shopping_list_category.c.shopping_list_id)
|
||||
Category.query.join(
|
||||
shopping_list_category, shopping_list_category.c.category_id == Category.id
|
||||
)
|
||||
.join(
|
||||
ShoppingList, ShoppingList.id == shopping_list_category.c.shopping_list_id
|
||||
)
|
||||
.filter(ShoppingList.id.in_(list_ids))
|
||||
.distinct()
|
||||
.order_by(Category.name.asc())
|
||||
@@ -2068,8 +2096,8 @@ def expenses():
|
||||
|
||||
expense_table = [
|
||||
{
|
||||
"title": (e.shopping_list.title if e.shopping_list else "Nieznana"),
|
||||
"amount": e.amount,
|
||||
"title": (e.shopping_list.title if e.shopping_list else "Nieznana"),
|
||||
"amount": e.amount,
|
||||
"added_at": e.added_at,
|
||||
}
|
||||
for e in expenses
|
||||
@@ -2102,8 +2130,8 @@ def expenses():
|
||||
def expenses_data():
|
||||
range_type = request.args.get("range", "monthly")
|
||||
start_date = request.args.get("start_date")
|
||||
end_date = request.args.get("end_date")
|
||||
show_all = request.args.get("show_all", "true").lower() == "true"
|
||||
end_date = request.args.get("end_date")
|
||||
show_all = request.args.get("show_all", "true").lower() == "true"
|
||||
category_id = request.args.get("category_id")
|
||||
by_category = request.args.get("by_category", "false").lower() == "true"
|
||||
|
||||
@@ -2111,7 +2139,7 @@ def expenses_data():
|
||||
sd, ed, bucket = resolve_range(range_type)
|
||||
if sd and ed:
|
||||
start_date = sd
|
||||
end_date = ed
|
||||
end_date = ed
|
||||
range_type = bucket
|
||||
|
||||
if by_category:
|
||||
@@ -2141,7 +2169,7 @@ def expenses_data():
|
||||
|
||||
|
||||
@app.route("/share/<token>")
|
||||
#@app.route("/guest-list/<int:list_id>")
|
||||
# @app.route("/guest-list/<int:list_id>")
|
||||
@app.route("/shared/<int:list_id>")
|
||||
def shared_list(token=None, list_id=None):
|
||||
now = datetime.now(timezone.utc)
|
||||
@@ -2150,7 +2178,11 @@ def shared_list(token=None, list_id=None):
|
||||
shopping_list = ShoppingList.query.filter_by(share_token=token).first_or_404()
|
||||
|
||||
# jeśli lista wygasła – zablokuj (spójne z resztą aplikacji)
|
||||
if shopping_list.is_temporary and shopping_list.expires_at and shopping_list.expires_at <= now:
|
||||
if (
|
||||
shopping_list.is_temporary
|
||||
and shopping_list.expires_at
|
||||
and shopping_list.expires_at <= now
|
||||
):
|
||||
flash("Link wygasł.", "warning")
|
||||
return redirect(url_for("main_page"))
|
||||
|
||||
@@ -2203,7 +2235,6 @@ def shared_list(token=None, list_id=None):
|
||||
)
|
||||
|
||||
|
||||
|
||||
@app.route("/copy/<int:list_id>")
|
||||
@login_required
|
||||
def copy_list(list_id):
|
||||
@@ -3490,15 +3521,11 @@ def add_suggestion():
|
||||
return redirect(url_for("list_products"))
|
||||
|
||||
|
||||
# ── Admin: zarządzanie dostępem do list ───────────────────────────────────────
|
||||
@app.route("/admin/lists-access", methods=["GET", "POST"])
|
||||
@login_required
|
||||
@admin_required
|
||||
def admin_lists_access():
|
||||
# Prosta autoryzacja admina – dostosuj do swojej aplikacji
|
||||
if not getattr(current_user, "is_admin", False):
|
||||
abort(403)
|
||||
|
||||
# Paginacja
|
||||
try:
|
||||
page = int(request.args.get("page", 1))
|
||||
except ValueError:
|
||||
@@ -3509,19 +3536,14 @@ def admin_lists_access():
|
||||
per_page = 25
|
||||
per_page = max(1, min(100, per_page))
|
||||
|
||||
# Filtrowanie bazowe (bez archiwalnych? – tutaj pokazujemy wszystkie)
|
||||
q = (
|
||||
ShoppingList.query
|
||||
.options(db.joinedload(ShoppingList.owner))
|
||||
.order_by(ShoppingList.created_at.desc())
|
||||
q = ShoppingList.query.options(db.joinedload(ShoppingList.owner)).order_by(
|
||||
ShoppingList.created_at.desc()
|
||||
)
|
||||
|
||||
# POST: grant/revoke per-wiersz + zbiorcza zmiana statusów
|
||||
if request.method == "POST":
|
||||
action = request.form.get("action")
|
||||
target_list_id = request.form.get("target_list_id", type=int)
|
||||
|
||||
# Grant pojedynczy
|
||||
if action == "grant" and target_list_id:
|
||||
login = (request.form.get("grant_username") or "").strip().lower()
|
||||
l = db.session.get(ShoppingList, target_list_id)
|
||||
@@ -3545,36 +3567,32 @@ def admin_lists_access():
|
||||
flash("Ten użytkownik już ma dostęp.", "info")
|
||||
return redirect(request.url)
|
||||
|
||||
# Revoke pojedynczy
|
||||
if action == "revoke" and target_list_id:
|
||||
uid = request.form.get("revoke_user_id", type=int)
|
||||
if uid:
|
||||
ListPermission.query.filter_by(list_id=target_list_id, user_id=uid).delete()
|
||||
ListPermission.query.filter_by(
|
||||
list_id=target_list_id, user_id=uid
|
||||
).delete()
|
||||
db.session.commit()
|
||||
flash("Odebrano dostęp użytkownikowi.", "success")
|
||||
return redirect(request.url)
|
||||
|
||||
# Zbiorcze zapisy statusów (checkboxy wierszy)
|
||||
if action == "save_changes":
|
||||
# Zaktualizuj pola is_public / is_temporary / is_archived na podstawie POST
|
||||
# Wysyłamy identyfikatory wszystkich list widocznych na stronie w ukrytym polu multiple
|
||||
ids = request.form.getlist("visible_ids", type=int)
|
||||
if ids:
|
||||
lists = ShoppingList.query.filter(ShoppingList.id.in_(ids)).all()
|
||||
posted = request.form
|
||||
for l in lists:
|
||||
l.is_public = (posted.get(f"is_public_{l.id}") is not None)
|
||||
l.is_temporary = (posted.get(f"is_temporary_{l.id}") is not None)
|
||||
l.is_archived = (posted.get(f"is_archived_{l.id}") is not None)
|
||||
l.is_public = posted.get(f"is_public_{l.id}") is not None
|
||||
l.is_temporary = posted.get(f"is_temporary_{l.id}") is not None
|
||||
l.is_archived = posted.get(f"is_archived_{l.id}") is not None
|
||||
db.session.commit()
|
||||
flash("Zapisano zmiany statusów.", "success")
|
||||
return redirect(request.url)
|
||||
|
||||
# Dane do tabeli
|
||||
pagination = q.paginate(page=page, per_page=per_page, error_out=False)
|
||||
lists = pagination.items
|
||||
|
||||
# Zbierz uprawnionych per lista (1 zapytanie)
|
||||
list_ids = [l.id for l in lists]
|
||||
perms = (
|
||||
db.session.query(
|
||||
@@ -3592,8 +3610,6 @@ def admin_lists_access():
|
||||
for lid, uid, uname in perms:
|
||||
permitted_by_list[lid].append({"id": uid, "username": uname})
|
||||
|
||||
|
||||
# Query-string do paginacji
|
||||
query_string = f"per_page={per_page}"
|
||||
|
||||
return render_template(
|
||||
|
@@ -23,7 +23,7 @@
|
||||
<th scope="col">Właściciel</th>
|
||||
<th scope="col">Utworzono</th>
|
||||
<th scope="col">Statusy</th>
|
||||
<th scope="col">Link share</th>
|
||||
<th scope="col">Udostępnianie</th>
|
||||
<th scope="col">Uprawnienia</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -65,17 +65,25 @@
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td style="min-width: 280px;">
|
||||
<td style="min-width: 320px;">
|
||||
{% if l.share_token %}
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="text" class="form-control bg-dark text-white border-secondary" readonly
|
||||
value="{{ url_for('shared_list', token=l.share_token, _external=True) }}">
|
||||
<a class="btn btn-outline-light"
|
||||
href="{{ url_for('shared_list', token=l.share_token) }}"
|
||||
target="_blank">Otwórz</a>
|
||||
{% set share_url = url_for('shared_list', token=l.share_token, _external=True) %}
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="badge bg-secondary">🔗 Link</span>
|
||||
<div class="flex-grow-1 text-truncate mono" title="{{ share_url }}">
|
||||
{{ share_url }}
|
||||
</div>
|
||||
<a class="btn btn-sm btn-outline-light"
|
||||
href="{{ url_for('shared_list', token=l.share_token) }}" target="_blank">
|
||||
👁️ Otwórz
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-info small">
|
||||
{% if l.is_public %}Widoczna publicznie{% else %}Dostępna przez link/uprawnienia{%
|
||||
endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted small">Brak tokenu</span>
|
||||
<div class="text-warning small">Brak tokenu</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
@@ -100,7 +108,7 @@
|
||||
{% endfor %}
|
||||
{% if permitted_by_list.get(l.id, [])|length == 0 %}
|
||||
<li class="list-group-item bg-dark text-white border-secondary">
|
||||
<div class="text-warning small">Brak dodatkowych uprawnień.</div>
|
||||
<div class="text-warning small">Brak dodanych uprawnień.</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
@@ -24,13 +24,13 @@
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="public" name="is_public" {% if list.is_public
|
||||
%}checked{% endif %}>
|
||||
<label class="form-check-label" for="public">🌐 Publiczna</label>
|
||||
<label class="form-check-label" for="public">🌐 Publiczna (czyli mogą zobaczyć goście)</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="temporary" name="is_temporary" {% if list.is_temporary
|
||||
%}checked{% endif %}>
|
||||
<label class="form-check-label" for="temporary">⏳ Tymczasowa</label>
|
||||
<label class="form-check-label" for="temporary">⏳ Tymczasowa (ustaw date wygasania)</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch">
|
||||
@@ -92,8 +92,8 @@
|
||||
|
||||
<!-- Link udostępniania -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">🔗 Link udostępniania (wejście przez link daje dostęp; zalogowani są
|
||||
zapisywani)</label>
|
||||
<label class="form-label">🔗 Link udostępniania (wejście przez link daje dostęp; zalogowani dostają
|
||||
uprawnienia na stałę po kliknięciu w link)</label>
|
||||
{% if list.share_token %}
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control bg-dark text-white border-secondary" readonly
|
||||
@@ -138,7 +138,7 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}<br>
|
||||
<div class="text-warning small">Brak dodatkowych uprawnień.</div>
|
||||
<div class="text-warning small">Brak dodanych uprawnień.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user