Fix article editor

This commit is contained in:
2026-03-26 17:34:10 -04:00
parent 3687003935
commit bc9000aad9
7 changed files with 390 additions and 26 deletions
+78 -4
View File
@@ -1,6 +1,7 @@
import logging
import os
import uuid
from datetime import datetime, timedelta
from functools import wraps
from flask import Blueprint, render_template, redirect, url_for, flash, request, abort, jsonify, current_app, send_from_directory
from flask_login import login_required, current_user
@@ -226,8 +227,47 @@ def all_tickets():
@login_required
@it_required
def kb_list():
articles = KnowledgeBase.query.order_by(KnowledgeBase.created_at.desc()).all()
return render_template('admin/kb_list.html', articles=articles)
q = request.args.get('q', '').strip()
category = request.args.get('category', '')
author_id = request.args.get('author_id', '', type=str)
published = request.args.get('published', '') # 'yes' | 'no' | ''
query = KnowledgeBase.query
if q:
query = query.filter(
KnowledgeBase.title.ilike(f'%{q}%') |
KnowledgeBase.tags.ilike(f'%{q}%')
)
if category:
query = query.filter_by(category=category)
if author_id:
query = query.filter_by(author_id=int(author_id))
if published == 'yes':
query = query.filter_by(is_published=True)
elif published == 'no':
query = query.filter_by(is_published=False)
articles = query.order_by(KnowledgeBase.created_at.desc()).all()
# Build filter option lists from existing data
categories = sorted({a.category for a in KnowledgeBase.query.with_entities(KnowledgeBase.category).distinct() if a.category})
authors = User.query.filter(
User.id.in_(
db.session.query(KnowledgeBase.author_id).distinct()
),
User.is_active == True,
).order_by(User.full_name).all()
return render_template('admin/kb_list.html',
articles = articles,
categories = categories,
authors = authors,
q = q,
sel_category = category,
sel_author_id = author_id,
sel_published = published,
)
# ── Helpers ──────────────────────────────────────────────────────────────────
@@ -452,14 +492,48 @@ def kb_delete(article_id):
# ─── Activity Log ─────────────────────────────────────────────────────────────
@admin_bp.route('/logs')
@admin_bp.route('/logs', methods=['GET', 'POST'])
@login_required
@admin_required
def activity_logs():
# ── Cleanup action ────────────────────────────────────────────────────────
if request.method == 'POST':
days = request.form.get('days', type=int)
if days not in (7, 30, 60, 90):
flash('Invalid retention period selected.', 'danger')
return redirect(url_for('admin.activity_logs'))
cutoff = datetime.utcnow() - timedelta(days=days)
deleted = ActivityLog.query.filter(ActivityLog.created_at < cutoff).delete()
db.session.commit()
log_action(current_user.id, 'activity_log_cleanup', 'activity_log', None,
f'deleted={deleted} older_than={days}_days cutoff={cutoff.strftime("%Y-%m-%d")}')
db.session.commit()
logger.info(f'[ACTIVITY LOG CLEANUP] deleted={deleted} days={days} by user_id={current_user.id}')
flash(f'Deleted {deleted:,} log entr{"y" if deleted == 1 else "ies"} older than {days} days.', 'success')
return redirect(url_for('admin.activity_logs'))
# ── Stats for the summary bar ─────────────────────────────────────────────
from sqlalchemy import func
total = ActivityLog.query.count()
oldest = db.session.query(func.min(ActivityLog.created_at)).scalar()
counts_by_retention = {}
for days in (7, 30, 60, 90):
cutoff = datetime.utcnow() - timedelta(days=days)
counts_by_retention[days] = ActivityLog.query.filter(
ActivityLog.created_at < cutoff
).count()
# ── Paginated log listing ─────────────────────────────────────────────────
page = request.args.get('page', 1, type=int)
logs = ActivityLog.query.order_by(ActivityLog.created_at.desc()).paginate(
page=page, per_page=50)
return render_template('admin/activity_logs.html', logs=logs)
return render_template('admin/activity_logs.html',
logs = logs,
total = total,
oldest = oldest,
counts_by_retention= counts_by_retention,
)
def _roles():