Better fill codes
This commit is contained in:
Binary file not shown.
@@ -1138,70 +1138,83 @@ def init_routes(main_bp):
|
|||||||
@login_required
|
@login_required
|
||||||
def mails():
|
def mails():
|
||||||
if not current_user.is_admin:
|
if not current_user.is_admin:
|
||||||
flash('Only administrators can access mail logs.', 'error')
|
flash('You do not have permission to access settings.', 'error')
|
||||||
return redirect(url_for('main.dashboard'))
|
return redirect(url_for('main.index'))
|
||||||
|
|
||||||
# Get filter parameters
|
# Get filter parameters
|
||||||
status = request.args.get('status')
|
status = request.args.get('status', '')
|
||||||
date_range = request.args.get('date_range', '7d')
|
date_range = request.args.get('date_range', '7d')
|
||||||
user_id = request.args.get('user_id')
|
user_id = request.args.get('user_id', '')
|
||||||
|
template_id = request.args.get('template_id', '')
|
||||||
page = request.args.get('page', 1, type=int)
|
page = request.args.get('page', 1, type=int)
|
||||||
per_page = 10
|
per_page = 10
|
||||||
|
|
||||||
# Calculate date range
|
|
||||||
end_date = datetime.utcnow()
|
|
||||||
if date_range == '24h':
|
|
||||||
start_date = end_date - timedelta(days=1)
|
|
||||||
elif date_range == '7d':
|
|
||||||
start_date = end_date - timedelta(days=7)
|
|
||||||
elif date_range == '30d':
|
|
||||||
start_date = end_date - timedelta(days=30)
|
|
||||||
else:
|
|
||||||
start_date = None
|
|
||||||
|
|
||||||
# Build query
|
# Build query
|
||||||
query = Mail.query
|
query = Mail.query
|
||||||
|
|
||||||
|
# Apply filters
|
||||||
if status:
|
if status:
|
||||||
query = query.filter_by(status=status)
|
query = query.filter_by(status=status)
|
||||||
if start_date:
|
|
||||||
query = query.filter(Mail.created_at >= start_date)
|
|
||||||
if user_id:
|
if user_id:
|
||||||
query = query.filter(Mail.recipient == User.query.get(user_id).email)
|
query = query.filter_by(recipient=user_id)
|
||||||
|
if template_id:
|
||||||
|
query = query.filter_by(template_id=template_id)
|
||||||
|
if date_range:
|
||||||
|
if date_range == '24h':
|
||||||
|
cutoff = datetime.utcnow() - timedelta(hours=24)
|
||||||
|
elif date_range == '7d':
|
||||||
|
cutoff = datetime.utcnow() - timedelta(days=7)
|
||||||
|
elif date_range == '30d':
|
||||||
|
cutoff = datetime.utcnow() - timedelta(days=30)
|
||||||
|
else:
|
||||||
|
cutoff = None
|
||||||
|
if cutoff:
|
||||||
|
query = query.filter(Mail.created_at >= cutoff)
|
||||||
|
|
||||||
# Get total count for pagination
|
# Get paginated results
|
||||||
total_mails = query.count()
|
|
||||||
total_pages = (total_mails + per_page - 1) // per_page
|
|
||||||
|
|
||||||
# Get paginated mails
|
|
||||||
mails = query.order_by(Mail.created_at.desc()).paginate(page=page, per_page=per_page)
|
mails = query.order_by(Mail.created_at.desc()).paginate(page=page, per_page=per_page)
|
||||||
|
total_pages = mails.pages
|
||||||
|
current_page = mails.page
|
||||||
|
|
||||||
# Get all users for filter dropdown
|
# Get all users for the filter dropdown
|
||||||
users = User.query.order_by(User.username).all()
|
users = User.query.order_by(User.username).all()
|
||||||
|
|
||||||
|
# Get all email templates
|
||||||
|
email_templates = EmailTemplate.query.filter_by(is_active=True).all()
|
||||||
|
|
||||||
# Check if this is an AJAX request
|
# Check if this is an AJAX request
|
||||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
||||||
return render_template('settings/tabs/mails.html',
|
return render_template('settings/tabs/mails.html',
|
||||||
mails=mails.items,
|
mails=mails,
|
||||||
total_pages=total_pages,
|
total_pages=total_pages,
|
||||||
current_page=page,
|
current_page=page,
|
||||||
status=status,
|
status=status,
|
||||||
date_range=date_range,
|
date_range=date_range,
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
|
template_id=template_id,
|
||||||
users=users,
|
users=users,
|
||||||
|
email_templates=email_templates,
|
||||||
csrf_token=session.get('csrf_token'))
|
csrf_token=session.get('csrf_token'))
|
||||||
|
|
||||||
# For full page requests, render the full settings page
|
# For full page requests, render the full settings page
|
||||||
site_settings = SiteSettings.get_settings()
|
site_settings = SiteSettings.get_settings()
|
||||||
|
company_form = CompanySettingsForm()
|
||||||
|
|
||||||
return render_template('settings/settings.html',
|
return render_template('settings/settings.html',
|
||||||
primary_color=site_settings.primary_color,
|
primary_color=site_settings.primary_color,
|
||||||
secondary_color=site_settings.secondary_color,
|
secondary_color=site_settings.secondary_color,
|
||||||
active_tab='mails',
|
active_tab='mails',
|
||||||
site_settings=site_settings,
|
site_settings=site_settings,
|
||||||
mails=mails.items,
|
mails=mails,
|
||||||
total_pages=total_pages,
|
total_pages=total_pages,
|
||||||
current_page=page,
|
current_page=page,
|
||||||
|
status=status,
|
||||||
|
date_range=date_range,
|
||||||
|
user_id=user_id,
|
||||||
|
template_id=template_id,
|
||||||
users=users,
|
users=users,
|
||||||
|
email_templates=email_templates,
|
||||||
|
form=company_form,
|
||||||
csrf_token=session.get('csrf_token'))
|
csrf_token=session.get('csrf_token'))
|
||||||
|
|
||||||
@main_bp.route('/settings/mails/<int:mail_id>')
|
@main_bp.route('/settings/mails/<int:mail_id>')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{% macro mails_tab(mails, csrf_token, users, total_pages, current_page) %}
|
{% macro mails_tab(mails, csrf_token, users, total_pages, current_page, email_templates, status='', date_range='7d', user_id='', template_id='') %}
|
||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
@@ -6,20 +6,26 @@
|
|||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<select class="form-select form-select-sm" id="statusFilter" onchange="updateFilters()">
|
<select class="form-select form-select-sm" id="statusFilter" onchange="updateFilters()">
|
||||||
<option value="">All Statuses</option>
|
<option value="">All Statuses</option>
|
||||||
<option value="pending">Pending</option>
|
<option value="pending" {% if status == 'pending' %}selected{% endif %}>Pending</option>
|
||||||
<option value="sent">Sent</option>
|
<option value="sent" {% if status == 'sent' %}selected{% endif %}>Sent</option>
|
||||||
<option value="failed">Failed</option>
|
<option value="failed" {% if status == 'failed' %}selected{% endif %}>Failed</option>
|
||||||
</select>
|
</select>
|
||||||
<select class="form-select form-select-sm" id="dateRangeFilter" onchange="updateFilters()">
|
<select class="form-select form-select-sm" id="dateRangeFilter" onchange="updateFilters()">
|
||||||
<option value="24h">Last 24 Hours</option>
|
<option value="24h" {% if date_range == '24h' %}selected{% endif %}>Last 24 Hours</option>
|
||||||
<option value="7d" selected>Last 7 Days</option>
|
<option value="7d" {% if date_range == '7d' %}selected{% endif %}>Last 7 Days</option>
|
||||||
<option value="30d">Last 30 Days</option>
|
<option value="30d" {% if date_range == '30d' %}selected{% endif %}>Last 30 Days</option>
|
||||||
<option value="all">All Time</option>
|
<option value="all" {% if date_range == 'all' %}selected{% endif %}>All Time</option>
|
||||||
</select>
|
</select>
|
||||||
<select class="form-select form-select-sm" id="userFilter" onchange="updateFilters()">
|
<select class="form-select form-select-sm" id="userFilter" onchange="updateFilters()">
|
||||||
<option value="">All Recipients</option>
|
<option value="">All Recipients</option>
|
||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
<option value="{{ user.id }}">{{ user.username }} {{ user.last_name }}</option>
|
<option value="{{ user.id }}" {% if user_id|int == user.id %}selected{% endif %}>{{ user.username }} {{ user.last_name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<select class="form-select form-select-sm" id="templateFilter" onchange="updateFilters()">
|
||||||
|
<option value="">All Templates</option>
|
||||||
|
{% for template in email_templates %}
|
||||||
|
<option value="{{ template.id }}" {% if template_id|int == template.id %}selected{% endif %}>{{ template.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<button class="btn btn-secondary btn-sm" onclick="clearFilters()">
|
<button class="btn btn-secondary btn-sm" onclick="clearFilters()">
|
||||||
@@ -190,16 +196,18 @@ function downloadMailLog() {
|
|||||||
const status = document.getElementById('statusFilter').value;
|
const status = document.getElementById('statusFilter').value;
|
||||||
const dateRange = document.getElementById('dateRangeFilter').value;
|
const dateRange = document.getElementById('dateRangeFilter').value;
|
||||||
const userId = document.getElementById('userFilter').value;
|
const userId = document.getElementById('userFilter').value;
|
||||||
|
const templateId = document.getElementById('templateFilter').value;
|
||||||
|
|
||||||
window.location.href = `/settings/mails/download?status=${status}&date_range=${dateRange}&user_id=${userId}`;
|
window.location.href = `/settings/mails/download?status=${status}&date_range=${dateRange}&user_id=${userId}&template_id=${templateId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFilters() {
|
function updateFilters() {
|
||||||
const status = document.getElementById('statusFilter').value;
|
const status = document.getElementById('statusFilter').value;
|
||||||
const dateRange = document.getElementById('dateRangeFilter').value;
|
const dateRange = document.getElementById('dateRangeFilter').value;
|
||||||
const userId = document.getElementById('userFilter').value;
|
const userId = document.getElementById('userFilter').value;
|
||||||
|
const templateId = document.getElementById('templateFilter').value;
|
||||||
|
|
||||||
window.location.href = `/settings/mails?status=${status}&date_range=${dateRange}&user_id=${userId}`;
|
window.location.href = `/settings/mails?status=${status}&date_range=${dateRange}&user_id=${userId}&template_id=${templateId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearFilters() {
|
function clearFilters() {
|
||||||
@@ -210,8 +218,9 @@ function changePage(page) {
|
|||||||
const status = document.getElementById('statusFilter').value;
|
const status = document.getElementById('statusFilter').value;
|
||||||
const dateRange = document.getElementById('dateRangeFilter').value;
|
const dateRange = document.getElementById('dateRangeFilter').value;
|
||||||
const userId = document.getElementById('userFilter').value;
|
const userId = document.getElementById('userFilter').value;
|
||||||
|
const templateId = document.getElementById('templateFilter').value;
|
||||||
|
|
||||||
window.location.href = `/settings/mails?page=${page}&status=${status}&date_range=${dateRange}&user_id=${userId}`;
|
window.location.href = `/settings/mails?page=${page}&status=${status}&date_range=${dateRange}&user_id=${userId}&template_id=${templateId}`;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
Binary file not shown.
@@ -32,12 +32,62 @@ def generate_mail_from_notification(notif: Notif) -> Optional[Mail]:
|
|||||||
try:
|
try:
|
||||||
# Fill in the template with notification details
|
# Fill in the template with notification details
|
||||||
filled_body = template.body
|
filled_body = template.body
|
||||||
|
|
||||||
|
# Add user information
|
||||||
if notif.user:
|
if notif.user:
|
||||||
filled_body = filled_body.replace('{{ user.username }}', notif.user.username)
|
filled_body = filled_body.replace('{{ user.username }}', notif.user.username + ' ' + notif.user.last_name)
|
||||||
|
filled_body = filled_body.replace('{{ user.email }}', notif.user.email)
|
||||||
|
if hasattr(notif.user, 'company'):
|
||||||
|
filled_body = filled_body.replace('{{ user.company }}', notif.user.company or '')
|
||||||
|
if hasattr(notif.user, 'position'):
|
||||||
|
filled_body = filled_body.replace('{{ user.position }}', notif.user.position or '')
|
||||||
|
|
||||||
|
# Add sender information if available
|
||||||
|
if notif.sender:
|
||||||
|
filled_body = filled_body.replace('{{ sender.username }}', notif.sender.username + ' ' + notif.sender.last_name)
|
||||||
|
filled_body = filled_body.replace('{{ sender.email }}', notif.sender.email)
|
||||||
|
|
||||||
|
# Add site information
|
||||||
|
from models import SiteSettings
|
||||||
|
site_settings = SiteSettings.query.first()
|
||||||
|
if site_settings:
|
||||||
|
filled_body = filled_body.replace('{{ site.company_name }}', site_settings.company_name or '')
|
||||||
|
filled_body = filled_body.replace('{{ site.company_website }}', site_settings.company_website or '')
|
||||||
|
|
||||||
|
# Add notification details
|
||||||
if notif.details:
|
if notif.details:
|
||||||
for key, value in notif.details.items():
|
for key, value in notif.details.items():
|
||||||
|
# Handle nested keys (e.g., room.name -> room_name)
|
||||||
|
if '.' in key:
|
||||||
|
parts = key.split('.')
|
||||||
|
if len(parts) == 2:
|
||||||
|
obj_name, attr = parts
|
||||||
|
if obj_name in notif.details and isinstance(notif.details[obj_name], dict):
|
||||||
|
if attr in notif.details[obj_name]:
|
||||||
|
filled_body = filled_body.replace(f'{{{{ {key} }}}}', str(notif.details[obj_name][attr]))
|
||||||
|
else:
|
||||||
filled_body = filled_body.replace(f'{{{{ {key} }}}}', str(value))
|
filled_body = filled_body.replace(f'{{{{ {key} }}}}', str(value))
|
||||||
|
|
||||||
|
# Handle special URL variables
|
||||||
|
if 'room_link' in filled_body and 'room_id' in notif.details:
|
||||||
|
from flask import url_for
|
||||||
|
room_link = url_for('rooms.room', room_id=notif.details['room_id'], _external=True)
|
||||||
|
filled_body = filled_body.replace('{{ room_link }}', room_link)
|
||||||
|
|
||||||
|
if 'conversation_link' in filled_body and 'conversation_id' in notif.details:
|
||||||
|
from flask import url_for
|
||||||
|
conversation_link = url_for('conversations.conversation', conversation_id=notif.details['conversation_id'], _external=True)
|
||||||
|
filled_body = filled_body.replace('{{ conversation_link }}', conversation_link)
|
||||||
|
|
||||||
|
# Add timestamps
|
||||||
|
filled_body = filled_body.replace('{{ created_at }}', notif.timestamp.strftime('%Y-%m-%d %H:%M:%S'))
|
||||||
|
if 'updated_at' in filled_body:
|
||||||
|
filled_body = filled_body.replace('{{ updated_at }}', datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))
|
||||||
|
if 'deleted_at' in filled_body:
|
||||||
|
filled_body = filled_body.replace('{{ deleted_at }}', datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))
|
||||||
|
if 'removed_at' in filled_body:
|
||||||
|
filled_body = filled_body.replace('{{ removed_at }}', datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))
|
||||||
|
|
||||||
# Create a new Mail record
|
# Create a new Mail record
|
||||||
mail = Mail(
|
mail = Mail(
|
||||||
recipient=notif.user.email,
|
recipient=notif.user.email,
|
||||||
|
|||||||
Reference in New Issue
Block a user