mirror of
https://github.com/simple-login/app.git
synced 2024-09-21 07:25:59 +08: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(
|
||||
or_(
|
||||
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>
|
||||
|
||||
<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="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>
|
||||
<span>
|
||||
<a href="{{ 'mailto:' + contact.website_send_to() }}"
|
||||
|
@ -72,26 +80,45 @@
|
|||
class="font-weight-bold">*************************</a>
|
||||
|
||||
<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() }}">
|
||||
Copy reverse-alias
|
||||
</span>
|
||||
</span>
|
||||
</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">
|
||||
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 %}
|
||||
Last email sent {{ contact.latest_reply | dt }}
|
||||
{% if email_log.is_reply %}
|
||||
<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 %}
|
||||
|
||||
<div>
|
||||
<span class="alias-activity">{{ contact_info.nb_forward }}</span> forwards,
|
||||
<span class="alias-activity">{{ contact_info.nb_reply }}</span> replies
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{{ url_for('dashboard.contact_detail_route', contact_id=contact.id) }}">Edit ➡</a>
|
||||
|
@ -137,7 +164,7 @@
|
|||
let that = $(this);
|
||||
|
||||
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: {
|
||||
confirm: {
|
||||
label: 'Yes, delete it',
|
||||
|
|
|
@ -246,7 +246,7 @@
|
|||
{{ email_log.created_at | dt }}
|
||||
{% else %}
|
||||
{{ 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 }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
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_wtf import FlaskForm
|
||||
from sqlalchemy import and_, func, case
|
||||
from wtforms import StringField, validators, ValidationError
|
||||
|
||||
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.extensions import db
|
||||
from app.log import LOG
|
||||
from app.models import Alias, Contact
|
||||
from app.models import Alias, Contact, EmailLog
|
||||
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"])
|
||||
@login_required
|
||||
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)
|
||||
)
|
||||
|
||||
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
|
||||
contacts = alias.get_contacts(page)
|
||||
contact_ids = [contact.id for contact in contacts]
|
||||
|
||||
last_page = len(contacts) < PAGE_LIMIT
|
||||
|
||||
contact_ids = [contact_info.contact.id for contact_info in contact_infos]
|
||||
if highlight_contact_id not in contact_ids:
|
||||
contact = Contact.get(highlight_contact_id)
|
||||
if contact and contact.alias_id == alias.id:
|
||||
contacts.insert(0, contact)
|
||||
contact_infos = (
|
||||
get_contact_infos(alias, contact_id=highlight_contact_id) + contact_infos
|
||||
)
|
||||
|
||||
return render_template(
|
||||
"dashboard/alias_contact_manager.html",
|
||||
contacts=contacts,
|
||||
contact_infos=contact_infos,
|
||||
alias=alias,
|
||||
new_contact_form=new_contact_form,
|
||||
highlight_contact_id=highlight_contact_id,
|
||||
|
|
|
@ -9,7 +9,7 @@ import bcrypt
|
|||
from arrow import Arrow
|
||||
from flask import url_for
|
||||
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 app import s3
|
||||
|
@ -915,30 +915,6 @@ class Alias(db.Model, ModelMixin):
|
|||
else:
|
||||
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):
|
||||
return f"<Alias {self.id} {self.email}>"
|
||||
|
||||
|
|
Loading…
Reference in a new issue