mirror of
https://github.com/simple-login/app.git
synced 2024-09-29 21:21:29 +02:00
display nb-reply, nb-forward on alias contact page
This commit is contained in:
parent
a1206d212f
commit
f213469e9f
@ -244,7 +244,7 @@ def get_alias_infos_with_pagination_v3(
|
|||||||
.filter(
|
.filter(
|
||||||
or_(
|
or_(
|
||||||
EmailLog.created_at == sub.c.max_created_at,
|
EmailLog.created_at == sub.c.max_created_at,
|
||||||
sub.c.max_created_at == None,
|
sub.c.max_created_at == None, # no email log yet for this alias
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -61,9 +61,17 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% for contact in contacts %}
|
{% for contact_info in contact_infos %}
|
||||||
|
{% set contact = contact_info.contact %}
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="my-2 p-2 card {% if contact.id == highlight_contact_id %} highlight-row {% endif %}">
|
<div class="my-2 p-2 card {% if contact.id == highlight_contact_id %} highlight-row {% endif %}">
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="font-weight-bold">{{ contact.website_email }}</span>
|
||||||
|
{% if contact.pgp_finger_print %}
|
||||||
|
<span class="cursor" data-toggle="tooltip" data-original-title="PGP Enabled">🗝</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
<a href="{{ 'mailto:' + contact.website_send_to() }}"
|
<a href="{{ 'mailto:' + contact.website_send_to() }}"
|
||||||
@ -72,26 +80,45 @@
|
|||||||
class="font-weight-bold">*************************</a>
|
class="font-weight-bold">*************************</a>
|
||||||
|
|
||||||
<span class="clipboard btn btn-sm btn-success copy-btn" data-toggle="tooltip"
|
<span class="clipboard btn btn-sm btn-success copy-btn" data-toggle="tooltip"
|
||||||
title="Copy to clipboard"
|
title="Copy the reverse-alias to clipboard"
|
||||||
data-clipboard-text="{{ contact.website_send_to() }}">
|
data-clipboard-text="{{ contact.website_send_to() }}">
|
||||||
Copy reverse-alias
|
Copy reverse-alias
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
Contact <b>{{ contact.website_email }}</b>
|
|
||||||
{% if contact.pgp_finger_print %}
|
|
||||||
<span class="cursor" data-toggle="tooltip" data-original-title="PGP Enabled">🗝</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-2 text-muted small-text">
|
<div class="mb-2 text-muted small-text">
|
||||||
Created {{ contact.created_at | dt }} <br>
|
{% if contact_info.latest_email_log != None %}
|
||||||
|
{% set email_log = contact_info.latest_email_log %}
|
||||||
|
|
||||||
{% if contact.latest_reply %}
|
{% if email_log.is_reply %}
|
||||||
Last email sent {{ contact.latest_reply | dt }}
|
<i class="fa fa-reply mr-2" data-toggle="tooltip" title="Email reply/sent from alias"></i>
|
||||||
|
{{ email_log.created_at | dt }}
|
||||||
|
{% elif email_log.bounced %}
|
||||||
|
<span class="text-danger">
|
||||||
|
<i class="fa fa-warning mr-2" data-toggle="tooltip"
|
||||||
|
title="Email bounced and cannot be forwarded to your mailbox"></i>
|
||||||
|
{{ email_log.created_at | dt }}
|
||||||
|
</span>
|
||||||
|
{% elif email_log.blocked %}
|
||||||
|
<i class="fa fa-ban mr-2 text-danger" data-toggle="tooltip" title="Email blocked"></i>
|
||||||
|
{{ email_log.created_at | dt }}
|
||||||
|
{% else %}
|
||||||
|
<i class="fa fa-paper-plane mr-2" data-toggle="tooltip" title="Email sent to alias"></i>
|
||||||
|
{{ email_log.created_at | dt }}
|
||||||
|
{% endif %}
|
||||||
|
<br>
|
||||||
|
Contact created {{ contact.created_at | dt }}
|
||||||
|
{% else %}
|
||||||
|
No Activity. Contact created {{ contact.created_at | dt }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span class="alias-activity">{{ contact_info.nb_forward }}</span> forwards,
|
||||||
|
<span class="alias-activity">{{ contact_info.nb_reply }}</span> replies
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a href="{{ url_for('dashboard.contact_detail_route', contact_id=contact.id) }}">Edit ➡</a>
|
<a href="{{ url_for('dashboard.contact_detail_route', contact_id=contact.id) }}">Edit ➡</a>
|
||||||
@ -137,7 +164,7 @@
|
|||||||
let that = $(this);
|
let that = $(this);
|
||||||
|
|
||||||
bootbox.confirm({
|
bootbox.confirm({
|
||||||
message: "All activities associated with this reverse-alias will also be deleted, please confirm.",
|
message: "All activities associated with this contact will also be deleted, please confirm.",
|
||||||
buttons: {
|
buttons: {
|
||||||
confirm: {
|
confirm: {
|
||||||
label: 'Yes, delete it',
|
label: 'Yes, delete it',
|
||||||
|
@ -246,7 +246,7 @@
|
|||||||
{{ email_log.created_at | dt }}
|
{{ email_log.created_at | dt }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ contact.website_email }}
|
{{ contact.website_email }}
|
||||||
<i class="fa fa-paper-plane mr-2" data-toggle="tooltip" title="Email forwarded to alias"></i>
|
<i class="fa fa-paper-plane mr-2" data-toggle="tooltip" title="Email sent to alias"></i>
|
||||||
{{ email_log.created_at | dt }}
|
{{ email_log.created_at | dt }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
import re
|
import re
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from operator import or_
|
||||||
|
|
||||||
from flask import render_template, request, redirect, url_for, flash
|
from flask import render_template, request, redirect, flash
|
||||||
|
from flask import url_for
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
|
from sqlalchemy import and_, func, case
|
||||||
from wtforms import StringField, validators, ValidationError
|
from wtforms import StringField, validators, ValidationError
|
||||||
|
|
||||||
from app.config import EMAIL_DOMAIN, PAGE_LIMIT
|
from app.config import EMAIL_DOMAIN, PAGE_LIMIT
|
||||||
@ -10,7 +14,7 @@ from app.dashboard.base import dashboard_bp
|
|||||||
from app.email_utils import parseaddr_unicode
|
from app.email_utils import parseaddr_unicode
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.log import LOG
|
from app.log import LOG
|
||||||
from app.models import Alias, Contact
|
from app.models import Alias, Contact, EmailLog
|
||||||
from app.utils import random_string
|
from app.utils import random_string
|
||||||
|
|
||||||
|
|
||||||
@ -45,6 +49,82 @@ class NewContactForm(FlaskForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ContactInfo(object):
|
||||||
|
contact: Contact
|
||||||
|
|
||||||
|
nb_forward: int
|
||||||
|
nb_reply: int
|
||||||
|
|
||||||
|
latest_email_log: EmailLog
|
||||||
|
|
||||||
|
|
||||||
|
def get_contact_infos(alias: Alias, page=0, contact_id=None) -> [ContactInfo]:
|
||||||
|
"""if contact_id is set, only return the contact info for this contact"""
|
||||||
|
sub = (
|
||||||
|
db.session.query(
|
||||||
|
Contact.id,
|
||||||
|
func.sum(case([(EmailLog.is_reply, 1)], else_=0)).label("nb_reply"),
|
||||||
|
func.sum(
|
||||||
|
case(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
and_(
|
||||||
|
EmailLog.is_reply == False, EmailLog.blocked == False,
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
else_=0,
|
||||||
|
)
|
||||||
|
).label("nb_forward"),
|
||||||
|
func.max(EmailLog.created_at).label("max_email_log_created_at"),
|
||||||
|
)
|
||||||
|
.join(EmailLog, EmailLog.contact_id == Contact.id, isouter=True,)
|
||||||
|
.filter(Contact.alias_id == alias.id)
|
||||||
|
.group_by(Contact.id)
|
||||||
|
.subquery()
|
||||||
|
)
|
||||||
|
|
||||||
|
q = (
|
||||||
|
db.session.query(Contact, EmailLog, sub.c.nb_reply, sub.c.nb_forward,)
|
||||||
|
.join(EmailLog, EmailLog.contact_id == Contact.id, isouter=True,)
|
||||||
|
.filter(Contact.alias_id == alias.id)
|
||||||
|
.filter(Contact.id == sub.c.id)
|
||||||
|
.filter(
|
||||||
|
or_(
|
||||||
|
EmailLog.created_at == sub.c.max_email_log_created_at,
|
||||||
|
# no email log yet for this contact
|
||||||
|
sub.c.max_email_log_created_at == None,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if contact_id:
|
||||||
|
q = q.filter(Contact.id == contact_id)
|
||||||
|
|
||||||
|
latest_activity = case(
|
||||||
|
[
|
||||||
|
(EmailLog.created_at > Contact.created_at, EmailLog.created_at),
|
||||||
|
(EmailLog.created_at < Contact.created_at, Contact.created_at),
|
||||||
|
],
|
||||||
|
else_=Contact.created_at,
|
||||||
|
)
|
||||||
|
q = q.order_by(latest_activity.desc()).limit(PAGE_LIMIT).offset(page * PAGE_LIMIT)
|
||||||
|
|
||||||
|
ret = []
|
||||||
|
for contact, latest_email_log, nb_reply, nb_forward in q:
|
||||||
|
contact_info = ContactInfo(
|
||||||
|
contact=contact,
|
||||||
|
nb_forward=nb_forward,
|
||||||
|
nb_reply=nb_reply,
|
||||||
|
latest_email_log=latest_email_log,
|
||||||
|
)
|
||||||
|
ret.append(contact_info)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@dashboard_bp.route("/alias_contact_manager/<alias_id>/", methods=["GET", "POST"])
|
@dashboard_bp.route("/alias_contact_manager/<alias_id>/", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def alias_contact_manager(alias_id):
|
def alias_contact_manager(alias_id):
|
||||||
@ -149,20 +229,20 @@ def alias_contact_manager(alias_id):
|
|||||||
url_for("dashboard.alias_contact_manager", alias_id=alias_id)
|
url_for("dashboard.alias_contact_manager", alias_id=alias_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
contact_infos = get_contact_infos(alias, page)
|
||||||
|
last_page = len(contact_infos) < PAGE_LIMIT
|
||||||
|
|
||||||
|
# if highlighted contact isn't included, fetch it
|
||||||
# make sure highlighted contact is at array start
|
# make sure highlighted contact is at array start
|
||||||
contacts = alias.get_contacts(page)
|
contact_ids = [contact_info.contact.id for contact_info in contact_infos]
|
||||||
contact_ids = [contact.id for contact in contacts]
|
|
||||||
|
|
||||||
last_page = len(contacts) < PAGE_LIMIT
|
|
||||||
|
|
||||||
if highlight_contact_id not in contact_ids:
|
if highlight_contact_id not in contact_ids:
|
||||||
contact = Contact.get(highlight_contact_id)
|
contact_infos = (
|
||||||
if contact and contact.alias_id == alias.id:
|
get_contact_infos(alias, contact_id=highlight_contact_id) + contact_infos
|
||||||
contacts.insert(0, contact)
|
)
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"dashboard/alias_contact_manager.html",
|
"dashboard/alias_contact_manager.html",
|
||||||
contacts=contacts,
|
contact_infos=contact_infos,
|
||||||
alias=alias,
|
alias=alias,
|
||||||
new_contact_form=new_contact_form,
|
new_contact_form=new_contact_form,
|
||||||
highlight_contact_id=highlight_contact_id,
|
highlight_contact_id=highlight_contact_id,
|
||||||
|
@ -9,7 +9,7 @@ import bcrypt
|
|||||||
from arrow import Arrow
|
from arrow import Arrow
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
from flask_login import UserMixin
|
from flask_login import UserMixin
|
||||||
from sqlalchemy import text, desc, CheckConstraint, and_, func
|
from sqlalchemy import text, desc, CheckConstraint, and_, func, case
|
||||||
from sqlalchemy_utils import ArrowType
|
from sqlalchemy_utils import ArrowType
|
||||||
|
|
||||||
from app import s3
|
from app import s3
|
||||||
@ -915,30 +915,6 @@ class Alias(db.Model, ModelMixin):
|
|||||||
else:
|
else:
|
||||||
return self.user.email
|
return self.user.email
|
||||||
|
|
||||||
def get_contacts(self, page=0):
|
|
||||||
latest_reply = func.max(EmailLog.created_at)
|
|
||||||
q = (
|
|
||||||
db.session.query(Contact, latest_reply)
|
|
||||||
.join(
|
|
||||||
EmailLog,
|
|
||||||
and_(EmailLog.contact_id == Contact.id, EmailLog.is_reply),
|
|
||||||
isouter=True,
|
|
||||||
)
|
|
||||||
.filter(Contact.alias_id == self.id)
|
|
||||||
.group_by(Contact.id)
|
|
||||||
.order_by(Contact.created_at.desc())
|
|
||||||
.limit(PAGE_LIMIT)
|
|
||||||
.offset(page * PAGE_LIMIT)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
|
|
||||||
contacts = []
|
|
||||||
for contact, l in q:
|
|
||||||
contact.latest_reply = l
|
|
||||||
contacts.append(contact)
|
|
||||||
|
|
||||||
return contacts
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<Alias {self.id} {self.email}>"
|
return f"<Alias {self.id} {self.email}>"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user