Commit 12259818 authored by Robin Sonnabend's avatar Robin Sonnabend
Browse files

Sending mail with attached protocol

parent 4f136efa
......@@ -2,6 +2,7 @@ from flask import render_template, send_file, url_for, redirect, flash, request
from datetime import datetime, time, date, timedelta
import math
from io import StringIO, BytesIO
from shared import db
from utils import random_string, url_manager, get_etherpad_url
......@@ -104,8 +105,6 @@ class ProtocolType(db.Model):
if protocoltype.has_private_view_right(user)
]
class Protocol(db.Model):
__tablename__ = "protocols"
id = db.Column(db.Integer, primary_key=True)
......@@ -283,6 +282,10 @@ class Document(db.Model):
def get_filename(self):
return os.path.join(config.DOCUMENTS_PATH, self.filename)
def as_file_like(self):
with open(self.get_filename(), "rb") as file:
return BytesIO(file.read())
@event.listens_for(Document, "before_delete")
def on_document_delete(mapper, connection, document):
if document.filename is not None:
......
......@@ -565,6 +565,19 @@ def update_protocol(protocol_id):
return redirect(request.args.get("next") or url_for("show_protocol", protocol_id=protocol.id))
return render_template("protocol-update.html", upload_form=upload_form, edit_form=edit_form, protocol=protocol)
@app.route("/prococol/send/<int:protocol_id>")
@login_required
def send_protocol(protocol_id):
user = current_user()
protocol = Protocol.query.filter_by(id=protocol_id).first()
if protocol is None or not protocol.protocoltype.has_modify_right(user):
flash("Invalides Protokoll oder keine Berechtigung.", "alert-error")
return redirect(request.args.get("next") or url_for("index"))
tasks.send_protocol(protocol)
flash("Das Protokoll wurde versandt.", "alert-success")
return redirect(request.args.get("next") or url_for("show_protocol", protocol_id=protocol.id))
@app.route("/protocol/tops/new/<int:protocol_id>", methods=["GET", "POST"])
@login_required
def new_top(protocol_id):
......@@ -738,9 +751,7 @@ def download_document(document_id):
and not document.protocol.protocoltype.has_public_view_right(user))):
flash("Keine Berechtigung.", "alert-error")
return redirect(request.args.get("next") or url_for("index"))
with open(document.get_filename(), "rb") as file:
file_like = BytesIO(file.read())
return send_file(file_like, cache_timeout=1, as_attachment=True, attachment_filename=document.name)
return send_file(document.as_file_like(), cache_timeout=1, as_attachment=True, attachment_filename=document.name)
@app.route("/document/upload/<int:protocol_id>", methods=["POST"])
@login_required
......
......@@ -199,7 +199,6 @@ def parse_protocol_async(protocol_id, encoded_kwargs):
content_private = render_template("protocol.txt", render_type=RenderType.plaintext, show_private=True, **render_kwargs)
content_public = render_template("protocol.txt", render_type=RenderType.plaintext, show_private=False, **render_kwargs)
if content_private != content_public:
print("different")
privacy_states.append(True)
protocol.content_private = content_private
protocol.content_public = content_public
......@@ -316,23 +315,40 @@ def send_reminder_async(reminder_id, protocol_id):
with app.app_context():
reminder = MeetingReminder.query.filter_by(id=reminder_id).first()
protocol = Protocol.query.filter_by(id=protocol_id).first()
reminder_text = render_template("reminder.txt", reminder=reminder, protocol=protocol)
reminder_text = render_template("reminder-mail.txt", reminder=reminder, protocol=protocol)
if reminder.send_public:
send_mail(protocol, protocol.protocoltype.public_mail, "Tagesordnung der {}".format(protocol.protocoltype.name), reminder_text)
if reminder.send_private:
send_mail(protocol, protocol.protocoltype.private_mail, "Tagesordnung der {}".format(protocol.protocoltype.name), reminder_text)
def send_mail(protocol, to_addr, subject, content):
def send_protocol(protocol):
send_protocol_async.delay(protocol.id, show_private=True)
send_protocol_async.delay(protocol.id, show_private=False)
@celery.task
def send_protocol_async(protocol_id, show_private):
with app.app_context():
protocol = Protocol.query.filter_by(id=protocol_id).first()
to_addr = protocol.protocoltype.private_mail if show_private else protocol.protocoltype.public_mail
subject = "{}{}-Protokoll vom {}".format("Internes " if show_private else "", protocol.protocoltype.short_name, date_filter(protocol.date))
mail_content = render_template("protocol-mail.txt", protocol=protocol)
appendix = [(document.name, document.as_file_like())
for document in protocol.documents
if show_private or not document.is_private
]
send_mail(protocol, to_addr, subject, mail_content, appendix)
def send_mail(protocol, to_addr, subject, content, appendix=None):
if to_addr is not None and len(to_addr.strip()) > 0:
send_mail_async.delay(protocol.id, to_addr, subject, content)
send_mail_async.delay(protocol.id, to_addr, subject, content, appendix)
@celery.task
def send_mail_async(protocol_id, to_addr, subject, content):
def send_mail_async(protocol_id, to_addr, subject, content, appendix):
with app.app_context():
protocol = Protocol.query.filter_by(id=protocol_id).first()
try:
print("sending {} to {}".format(subject, to_addr))
mail_manager.send(to_addr, subject, content)
mail_manager.send(to_addr, subject, content, appendix)
except Exception as exc:
error = protocol.create_error("Sending Mail", "Sending mail failed", str(exc))
db.session.add(error)
......
Protocol der {{protocol.name}} vom {{protocol.date|datify}}
Datum: {{protocol.date|datify_long}}
Zeit: von {{protocol.start_time|timify}} bis {{protocol.end_time|timify}}
Protokollant: {{protocol.author}}
Anwesende: {{protocol.participants}}
Die Tagesordnung ist:
{% if not protocol.has_nonplanned_tops() %}
{% for default_top in protocol.protocoltype.default_tops %}
{% if not default_top.is_at_end() %}
* {{default_top.name}}
{% endif %}
{% endfor %}
{% endif %}
{% for top in protocol.tops %}
* {{top.name }}
{% endfor %}
{% if not protocol.has_nonplanned_tops() %}
{% for default_top in protocol.protocoltype.default_tops %}
{% if default_top.is_at_end() %}
* {{default_top.name}}
{% endif %}
{% endfor %}
{% endif %}
Beschlüsse:
{% if protocol.decisions|length > 0 %}
{% for decision in protocol.decisions %}
* {{decision.content}}
{% endfor %}
{% else %}
* Keine Beschlüsse
{% endif %}
......@@ -20,6 +20,8 @@
{% if not protocol.is_done() %}
<a class="btn btn-default" href="{{url_for("get_protocol_template", protocol_id=protocol.id)}}">Vorlage</a>
<a class="btn btn-primary" href="{{url_for("etherpush_protocol", protocol_id=protocol.id)}}">In Etherpad</a>
{% else %}
<a class="btn btn-default" href="{{url_for("send_protocol", protocol_id=protocol.id)}}">Per Mail versenden</a>
{% endif %}
<a class="btn btn-default" href="{{protocol.get_etherpad_link()}}" target="_blank">Etherpad</a>
<a class="btn btn-default" href="{{url_for("show_type", type_id=protocol.protocoltype.id)}}">Typ</a>
......
......@@ -6,6 +6,7 @@ import regex
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from datetime import datetime, date, timedelta
import requests
from io import BytesIO
......@@ -68,18 +69,23 @@ class MailManager:
self.username = getattr(config, "MAIL_USER", "")
self.password = getattr(config, "MAIL_PASSWORD", "")
def send(self, to_addr, subject, content):
def send(self, to_addr, subject, content, appendix=None):
if (not self.active
or not self.hostname
or not self.username
or not self.password
or not self.from_addr):
return
msg = MIMEMultipart("alternative")
msg = MIMEMultipart("mixed") # todo: test if clients accept attachment-free mails set to multipart/mixed
msg["From"] = self.from_addr
msg["To"] = to_addr
msg["Subject"] = subject
msg.attach(MIMEText(content, _charset="utf-8"))
if appendix is not None:
for name, file_like in appendix:
part = MIMEApplication(file_like.read(), "octet-stream")
part["Content-Disposition"] = 'attachment; filename="{}"'.format(name)
msg.attach(part)
server = smtplib.SMTP_SSL(self.hostname)
server.login(self.username, self.password)
server.sendmail(self.from_addr, to_addr, msg.as_string())
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment