v1.4: group folder/photo limits and ad banners
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+120
-4
@@ -15,8 +15,8 @@ from app.deploy_utils import (
|
||||
get_deploy_status,
|
||||
is_deploy_enabled,
|
||||
)
|
||||
from app.models import Photo, User, UserGroup
|
||||
from app.quota_utils import get_user_storage_used
|
||||
from app.models import AdBanner, Photo, User, UserGroup
|
||||
from app.quota_utils import get_user_folder_count, get_user_photo_count, get_user_storage_used
|
||||
from app.settings_service import get_settings, update_settings_from_form
|
||||
from app.storage_service import delete_photo_file
|
||||
|
||||
@@ -131,6 +131,12 @@ def groups():
|
||||
if request.method == "POST":
|
||||
name = request.form.get("name", "").strip()
|
||||
quota_mb = request.form.get("disk_quota_mb", type=int) or 100
|
||||
max_folders = request.form.get("max_folders", type=int)
|
||||
max_photos = request.form.get("max_photos", type=int)
|
||||
if max_folders is None:
|
||||
max_folders = 10
|
||||
if max_photos is None:
|
||||
max_photos = 500
|
||||
|
||||
if len(name) < 2:
|
||||
flash("Название группы — минимум 2 символа", "error")
|
||||
@@ -144,7 +150,13 @@ def groups():
|
||||
slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
|
||||
group = UserGroup(name=name, slug=slug, disk_quota_mb=max(0, quota_mb))
|
||||
group = UserGroup(
|
||||
name=name,
|
||||
slug=slug,
|
||||
disk_quota_mb=max(0, quota_mb),
|
||||
max_folders=max(0, max_folders),
|
||||
max_photos=max(0, max_photos),
|
||||
)
|
||||
db.session.add(group)
|
||||
db.session.commit()
|
||||
flash(f"Группа «{name}» создана", "success")
|
||||
@@ -154,7 +166,14 @@ def groups():
|
||||
group_stats = []
|
||||
for group in all_groups:
|
||||
used = sum(get_user_storage_used(u.id) for u in group.users)
|
||||
group_stats.append({"group": group, "storage_used": used})
|
||||
photos = sum(get_user_photo_count(u.id) for u in group.users)
|
||||
folders = sum(get_user_folder_count(u.id) for u in group.users)
|
||||
group_stats.append({
|
||||
"group": group,
|
||||
"storage_used": used,
|
||||
"photo_count": photos,
|
||||
"folder_count": folders,
|
||||
})
|
||||
return render_template("admin/groups.html", group_stats=group_stats)
|
||||
|
||||
|
||||
@@ -164,6 +183,8 @@ def edit_group(group_id):
|
||||
group = UserGroup.query.get_or_404(group_id)
|
||||
name = request.form.get("name", "").strip()
|
||||
quota_mb = request.form.get("disk_quota_mb", type=int)
|
||||
max_folders = request.form.get("max_folders", type=int)
|
||||
max_photos = request.form.get("max_photos", type=int)
|
||||
|
||||
if len(name) < 2:
|
||||
flash("Название группы — минимум 2 символа", "error")
|
||||
@@ -177,6 +198,10 @@ def edit_group(group_id):
|
||||
group.name = name
|
||||
if quota_mb is not None:
|
||||
group.disk_quota_mb = max(0, quota_mb)
|
||||
if max_folders is not None:
|
||||
group.max_folders = max(0, max_folders)
|
||||
if max_photos is not None:
|
||||
group.max_photos = max(0, max_photos)
|
||||
db.session.commit()
|
||||
flash(f"Группа «{group.name}» обновлена", "success")
|
||||
return redirect(url_for("admin.groups"))
|
||||
@@ -202,6 +227,97 @@ def delete_group(group_id):
|
||||
return redirect(url_for("admin.groups"))
|
||||
|
||||
|
||||
@bp.route("/banners", methods=["GET", "POST"])
|
||||
@admin_required
|
||||
def banners():
|
||||
if request.method == "POST":
|
||||
title = request.form.get("title", "").strip()
|
||||
image_url = request.form.get("image_url", "").strip()
|
||||
link_url = request.form.get("link_url", "").strip() or None
|
||||
alt_text = request.form.get("alt_text", "").strip() or None
|
||||
position = request.form.get("position", "main").strip()
|
||||
sort_order = request.form.get("sort_order", type=int) or 0
|
||||
is_active = request.form.get("is_active") == "on"
|
||||
|
||||
if len(title) < 2:
|
||||
flash("Название баннера — минимум 2 символа", "error")
|
||||
elif not image_url:
|
||||
flash("Укажите URL изображения", "error")
|
||||
elif position not in AdBanner.POSITIONS:
|
||||
flash("Неверная позиция баннера", "error")
|
||||
else:
|
||||
banner = AdBanner(
|
||||
title=title,
|
||||
image_url=image_url,
|
||||
link_url=link_url,
|
||||
alt_text=alt_text or title,
|
||||
position=position,
|
||||
sort_order=sort_order,
|
||||
is_active=is_active,
|
||||
)
|
||||
db.session.add(banner)
|
||||
db.session.commit()
|
||||
flash(f"Баннер «{title}» добавлен", "success")
|
||||
return redirect(url_for("admin.banners"))
|
||||
|
||||
all_banners = AdBanner.query.order_by(AdBanner.position, AdBanner.sort_order, AdBanner.id).all()
|
||||
return render_template("admin/banners.html", banners=all_banners, positions=AdBanner.POSITIONS)
|
||||
|
||||
|
||||
@bp.route("/banners/<int:banner_id>/edit", methods=["POST"])
|
||||
@admin_required
|
||||
def edit_banner(banner_id):
|
||||
banner = AdBanner.query.get_or_404(banner_id)
|
||||
title = request.form.get("title", "").strip()
|
||||
image_url = request.form.get("image_url", "").strip()
|
||||
link_url = request.form.get("link_url", "").strip() or None
|
||||
alt_text = request.form.get("alt_text", "").strip() or None
|
||||
position = request.form.get("position", banner.position).strip()
|
||||
sort_order = request.form.get("sort_order", type=int)
|
||||
is_active = request.form.get("is_active") == "on"
|
||||
|
||||
if len(title) < 2:
|
||||
flash("Название баннера — минимум 2 символа", "error")
|
||||
elif not image_url:
|
||||
flash("Укажите URL изображения", "error")
|
||||
elif position not in AdBanner.POSITIONS:
|
||||
flash("Неверная позиция баннера", "error")
|
||||
else:
|
||||
banner.title = title
|
||||
banner.image_url = image_url
|
||||
banner.link_url = link_url
|
||||
banner.alt_text = alt_text or title
|
||||
banner.position = position
|
||||
if sort_order is not None:
|
||||
banner.sort_order = sort_order
|
||||
banner.is_active = is_active
|
||||
db.session.commit()
|
||||
flash(f"Баннер «{banner.title}» обновлён", "success")
|
||||
|
||||
return redirect(url_for("admin.banners"))
|
||||
|
||||
|
||||
@bp.route("/banners/<int:banner_id>/delete", methods=["POST"])
|
||||
@admin_required
|
||||
def delete_banner(banner_id):
|
||||
banner = AdBanner.query.get_or_404(banner_id)
|
||||
db.session.delete(banner)
|
||||
db.session.commit()
|
||||
flash("Баннер удалён", "success")
|
||||
return redirect(url_for("admin.banners"))
|
||||
|
||||
|
||||
@bp.route("/banners/<int:banner_id>/toggle", methods=["POST"])
|
||||
@admin_required
|
||||
def toggle_banner(banner_id):
|
||||
banner = AdBanner.query.get_or_404(banner_id)
|
||||
banner.is_active = not banner.is_active
|
||||
db.session.commit()
|
||||
state = "включён" if banner.is_active else "выключен"
|
||||
flash(f"Баннер «{banner.title}» {state}", "success")
|
||||
return redirect(url_for("admin.banners"))
|
||||
|
||||
|
||||
@bp.route("/photos")
|
||||
@admin_required
|
||||
def photos():
|
||||
|
||||
Reference in New Issue
Block a user