diff --git a/app/email.py b/app/email.py index f2d8164d8c..226429df24 100644 --- a/app/email.py +++ b/app/email.py @@ -10,10 +10,21 @@ def send_async_email(app, msg): mail.send(msg) -def send_email(subject, sender, recipients, text_body, html_body): +def send_email( + subject: str, sender: str, recipients: list, text_body: str, html_body="" +): + """ + Send an email + If html_body is specified, build a multipart message with HTML content, + else send a plain text email. + """ msg = Message(subject, sender=sender, recipients=recipients) msg.body = text_body msg.html = html_body + send_message(msg) + + +def send_message(msg): Thread( target=send_async_email, args=(current_app._get_current_object(), msg) ).start() diff --git a/app/scodoc/notes_log.py b/app/scodoc/notes_log.py index 0781373405..6f91ec1220 100644 --- a/app/scodoc/notes_log.py +++ b/app/scodoc/notes_log.py @@ -4,12 +4,11 @@ import os import time import traceback -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from email.header import Header from flask import g, current_app +from app import email + """Simple & stupid file logguer, used only to debug (logging to SQL is done in scolog) """ @@ -19,7 +18,7 @@ DEFAULT_LOG_DIR = ( "/opt/scodoc-data/log" # clients should call set_log_directory to change this ) -ALARM_DESTINATION = "emmanuel.viennet@gmail.com" # XXX a mettre en preference +ALARM_DESTINATION = "emmanuel@scodoc.org" class _logguer(object): @@ -66,20 +65,11 @@ log = _logguer() # Alarms by email: -def sendAlarm(subj, txt): - from . import sco_utils - from . import sco_emails - from . import sco_preferences +def sendAlarm(subject, txt): + from app.scodoc import sco_preferences - msg = MIMEMultipart() - subj = Header(subj, sco_utils.SCO_ENCODING) - msg["Subject"] = subj - msg["From"] = sco_preferences.get_preference("email_from_addr") - msg["To"] = ALARM_DESTINATION - msg.epilogue = "" - txt = MIMEText(txt, "plain", sco_utils.SCO_ENCODING) - msg.attach(txt) - sco_emails.sendEmail(msg) + sender = sco_preferences.get_preference("email_from_addr") + email.send_email(subject, sender, [ALARM_DESTINATION], txt) # Debug: log call stack diff --git a/app/scodoc/sco_abs_notification.py b/app/scodoc/sco_abs_notification.py index c397c73f0a..5ac4315ef8 100644 --- a/app/scodoc/sco_abs_notification.py +++ b/app/scodoc/sco_abs_notification.py @@ -33,13 +33,9 @@ Il suffit d'appeler abs_notify() après chaque ajout d'absence. """ import datetime -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from email.header import Header - from flask import g, url_for +from flask_mail import Message -from app.scodoc import sco_emails import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu from app.scodoc.notes_log import log @@ -48,6 +44,7 @@ from app.scodoc import sco_etud from app.scodoc import sco_formsemestre from app.scodoc import sco_preferences from app.scodoc import sco_users +from app import email def abs_notify(etudid, date): @@ -108,12 +105,11 @@ def abs_notify_send(destinations, etudid, msg, nbabs, nbabsjust, formsemestre_id cnx = ndb.GetDBConnexion() log("abs_notify: sending notification to %s" % destinations) cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) - for email in destinations: - del msg["To"] - msg["To"] = email - sco_emails.sendEmail(msg) + for dest_addr in destinations: + msg.recipients = [dest_addr] + email.send_message(msg) ndb.SimpleQuery( - """insert into absences_notifications + """INSERT into absences_notifications (etudid, email, nbabs, nbabsjust, formsemestre_id) VALUES (%(etudid)s, %(email)s, %(nbabs)s, %(nbabsjust)s, %(formsemestre_id)s) """, @@ -229,7 +225,8 @@ def user_nbdays_since_last_notif(email_addr, etudid): def abs_notification_message(sem, prefs, etudid, nbabs, nbabsjust): """Mime notification message based on template. - returns None if sending should be canceled (emplty template). + returns a Message instance + or None if sending should be canceled (empty template). """ from app.scodoc import sco_bulletins @@ -252,14 +249,9 @@ def abs_notification_message(sem, prefs, etudid, nbabs, nbabsjust): log("abs_notification_message: empty template, not sending message") return None - subject = """Trop d'absences pour %(nomprenom)s""" % etud - # - msg = MIMEMultipart() - subj = Header("[ScoDoc] " + subject, scu.SCO_ENCODING) - msg["Subject"] = subj - msg["From"] = prefs["email_from_addr"] - txt = MIMEText(txt, "plain", scu.SCO_ENCODING) - msg.attach(txt) + subject = """[ScoDoc] Trop d'absences pour %(nomprenom)s""" % etud + msg = Message(subject, sender=prefs["email_from_addr"]) + msg.body = txt return msg diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py index 9cbff15581..c1ce03ef62 100644 --- a/app/scodoc/sco_bulletins.py +++ b/app/scodoc/sco_bulletins.py @@ -42,8 +42,8 @@ import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error from flask import g from flask import url_for from flask_login import current_user +from flask_mail import Message -from app.scodoc import sco_emails import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app.scodoc.notes_log import log @@ -68,7 +68,7 @@ from app.scodoc import sco_photos from app.scodoc import sco_preferences from app.scodoc import sco_pvjury from app.scodoc import sco_users - +from app import email # ----- CLASSES DE BULLETINS DE NOTES from app.scodoc import sco_bulletins_standard @@ -983,28 +983,21 @@ def mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr): etud["etudid"], with_evals=False, format="text" ) - msg = MIMEMultipart() - subj = Header("Relevé de notes de %s" % etud["nomprenom"], scu.SCO_ENCODING) + subject = "Relevé de notes de %s" % etud["nomprenom"] recipients = [recipient_addr] - msg["Subject"] = subj - msg["From"] = sco_preferences.get_preference("email_from_addr", formsemestre_id) - msg["To"] = " ,".join(recipients) + sender = sco_preferences.get_preference("email_from_addr", formsemestre_id) if copy_addr: - msg["Bcc"] = copy_addr.strip() - # Guarantees the message ends in a newline - msg.epilogue = "" - # Text - txt = MIMEText(hea, "plain", scu.SCO_ENCODING) - # log('hea:\n' + hea) - msg.attach(txt) + bcc = copy_addr.strip() + else: + bcc = "" + msg = Message(subject, sender=sender, recipients=recipients, bcc=bcc) + msg.body = hea + # Attach pdf - att = MIMEBase("application", "pdf") - att.add_header("Content-Disposition", "attachment", filename=filename) - att.set_payload(pdfdata) - email.encoders.encode_base64(att) - msg.attach(att) - log("mail bulletin a %s" % msg["To"]) - sco_emails.sendEmail(msg) + msg.attach(filename, scu.PDF_MIMETYPE, pdfdata) + + log("mail bulletin a %s" % recipient_addr) + email.send_message(msg) def _formsemestre_bulletinetud_header_html( diff --git a/app/scodoc/sco_emails.py b/app/scodoc/sco_emails.py deleted file mode 100644 index a50db77f9d..0000000000 --- a/app/scodoc/sco_emails.py +++ /dev/null @@ -1,146 +0,0 @@ -# -*- mode: python -*- -# -*- coding: utf-8 -*- - -############################################################################## -# -# Gestion scolarite IUT -# -# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# Emmanuel Viennet emmanuel.viennet@viennet.net -# -############################################################################## - -"""Gestion des emails -""" - -from flask import request - - -# XXX WIP: à ré-écrire pour ScoDoc 8 (étaient des méthodes de ZScoDoc) -import os -import time - -# from email.MIMEMultipart import ( # pylint: disable=no-name-in-module,import-error -# MIMEMultipart, -# ) -# from email.MIMEText import MIMEText # pylint: disable=no-name-in-module,import-error -# from email.MIMEBase import MIMEBase # pylint: disable=no-name-in-module,import-error -# from email.Header import Header # pylint: disable=no-name-in-module,import-error -# from email import Encoders # pylint: disable=no-name-in-module,import-error - -import app.scodoc.sco_utils as scu -from app.scodoc.notes_log import log -import sco_version - - -def sendEmail(msg): # TODO A REECRIRE ScoDoc8 - """Send an email to the address using the mailhost, if there is one.""" - raise NotImplementedError() - try: - mail_host = xxx.MailHost - except: - log("warning: sendEmail: no MailHost found !") - return - # a failed notification shouldn't cause a Zope error on a site. - try: - mail_host.send(msg.as_string()) - log("sendEmail: ok") - except Exception as e: - log("sendEmail: exception while sending message") - log(e) - pass - - -def sendEmailFromException(msg): - # Send email by hand, as it seems to be not possible to use Zope Mail Host - # from an exception handler (see https://bugs.launchpad.net/zope2/+bug/246748) - log("sendEmailFromException") - try: - p = os.popen("sendmail -t", "w") # old brute force method - p.write(msg.as_string()) - exitcode = p.close() - if exitcode: - log("sendmail exit code: %s" % exitcode) - except: - log("an exception occurred sending mail") - - -def send_debug_alert(txt, REQUEST=None): - """Send an alert email (bug report) to ScoDoc developpers""" - if not scu.SCO_EXC_MAIL: - log("send_debug_alert: email disabled") - return - if REQUEST: - txt = _report_request(REQUEST) + txt - URL = REQUEST.URL - else: - URL = "send_debug_alert" - msg = MIMEMultipart() - subj = Header("[scodoc] exc %s" % URL, scu.SCO_ENCODING) - msg["Subject"] = subj - recipients = [scu.SCO_EXC_MAIL] - msg["To"] = " ,".join(recipients) - msg["From"] = "scodoc-alert" - msg.epilogue = "" - msg.attach(MIMEText(txt, "plain", scu.SCO_ENCODING)) - sendEmailFromException(msg) - log("Sent mail alert:\n" + txt) - - -def _report_request(REQUEST, fmt="txt"): - """string describing current request for bug reports""" - QUERY_STRING = REQUEST.QUERY_STRING - if QUERY_STRING: - QUERY_STRING = "?" + QUERY_STRING - if fmt == "txt": - REFERER = request.referrer - HTTP_USER_AGENT = request.user_agent - else: - REFERER = "na" - HTTP_USER_AGENT = "na" - - params = dict( - AUTHENTICATED_USER=REQUEST.AUTHENTICATED_USER, - dt=time.asctime(), - URL=REQUEST.URL, - QUERY_STRING=QUERY_STRING, - METHOD=request.method, - REFERER=REFERER, - HTTP_USER_AGENT=HTTP_USER_AGENT, - form=REQUEST.form, - HTTP_X_FORWARDED_FOR="?", - SCOVERSION=sco_version.SCOVERSION, - ) - txt = ( - """ -Version: %(SCOVERSION)s -User: %(AUTHENTICATED_USER)s -Date: %(dt)s -URL: %(URL)s%(QUERY_STRING)s -Method: %(METHOD)s - -REFERER: %(REFERER)s -Form: %(form)s -Origin: %(HTTP_X_FORWARDED_FOR)s -Agent: %(HTTP_USER_AGENT)s -""" - % params - ) - if fmt == "html": - txt = txt.replace("\n", "
") - return txt \ No newline at end of file diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index 0d8682a5e3..7915b0af6d 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -40,7 +40,6 @@ from email.header import Header from email.mime.base import MIMEBase from operator import itemgetter -from app.scodoc import sco_emails import app.scodoc.sco_utils as scu from app.scodoc.sco_utils import SCO_ENCODING import app.scodoc.notesdb as ndb @@ -51,6 +50,8 @@ from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc import safehtml from app.scodoc import sco_preferences from app.scodoc.scolog import logdb +from flask_mail import Message +from app import mail MONTH_NAMES_ABBREV = [ "Jan ", @@ -454,14 +455,9 @@ def notify_etud_change(email_addr, etud, before, after, subject): log("notify_etud_change: sending notification to %s" % email_addr) log("notify_etud_change: subject: %s" % subject) log(txt) - msg = MIMEMultipart() - subj = Header("[ScoDoc] " + subject, SCO_ENCODING) - msg["Subject"] = subj - msg["From"] = sco_preferences.get_preference("email_from_addr") - msg["To"] = email_addr - mime_txt = MIMEText(txt, "plain", SCO_ENCODING) - msg.attach(mime_txt) - sco_emails.sendEmail(msg) + mail.send_email( + subject, sco_preferences.get_preference("email_from_addr"), [email_addr], txt + ) return txt diff --git a/app/scodoc/sco_import_users.py b/app/scodoc/sco_import_users.py index 81ba3a4eac..3a46257c3f 100644 --- a/app/scodoc/sco_import_users.py +++ b/app/scodoc/sco_import_users.py @@ -35,7 +35,6 @@ from email.mime.text import MIMEText from email.header import Header from app import db, Departement -from app.scodoc import sco_emails import app.scodoc.sco_utils as scu from app.scodoc.notes_log import log from app.scodoc.sco_exceptions import AccessDenied, ScoValueError, ScoException @@ -47,6 +46,9 @@ from flask import g from flask_login import current_user from app.auth.models import User, UserRole +from app import email + + TITLES = ("user_name", "nom", "prenom", "email", "roles", "dept") COMMENTS = ( """user_name: @@ -335,12 +337,8 @@ Pour plus d'informations sur ce logiciel, voir %s ) msg = MIMEMultipart() if reset: - msg["Subject"] = Header("Mot de passe ScoDoc", scu.SCO_ENCODING) + subject = "Mot de passe ScoDoc" else: - msg["Subject"] = Header("Votre accès ScoDoc", scu.SCO_ENCODING) - msg["From"] = sco_preferences.get_preference("email_from_addr") - msg["To"] = u["email"] - msg.epilogue = "" - txt = MIMEText(txt, "plain", scu.SCO_ENCODING) - msg.attach(txt) - # sco_emails.sendEmail(msg) # TODO ScoDoc9 pending function + subject = "Votre accès ScoDoc" + sender = sco_preferences.get_preference("email_from_addr") + email.send_email(subject, sender, [u["email"]], txt) diff --git a/app/scodoc/sco_news.py b/app/scodoc/sco_news.py index a564bfd8c8..22d50fae3d 100644 --- a/app/scodoc/sco_news.py +++ b/app/scodoc/sco_news.py @@ -27,13 +27,10 @@ """Gestion des "nouvelles" """ -import datetime import re import time -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from email.header import Header + from operator import itemgetter from flask import g @@ -42,13 +39,12 @@ from flask_login import current_user import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app.scodoc.notes_log import log -from app.scodoc import safehtml -from app.scodoc import sco_emails -from app.scodoc.sco_utils import SCO_ENCODING, SCO_ANNONCES_WEBSITE from app.scodoc import sco_formsemestre from app.scodoc import sco_moduleimpl from app.scodoc import sco_preferences from app.scodoc import sco_users +from app import email + _scolar_news_editor = ndb.EditableTable( "scolar_news", @@ -225,7 +221,7 @@ def scolar_news_summary_html(n=5): abonner à la liste de diffusion. """ - % SCO_ANNONCES_WEBSITE + % scu.SCO_ANNONCES_WEBSITE ) H.append("") @@ -270,15 +266,7 @@ def _send_news_by_mail(n): # (si on veut des messages non html) txt = re.sub(r'(.*?)', r"\2: \1", txt) - msg = MIMEMultipart() - msg["Subject"] = Header("[ScoDoc] " + NEWS_MAP.get(n["type"], "?"), SCO_ENCODING) - msg["From"] = prefs["email_from_addr"] - txt = MIMEText(txt, "plain", SCO_ENCODING) - msg.attach(txt) + subject = "[ScoDoc] " + NEWS_MAP.get(n["type"], "?") + sender = prefs["email_from_addr"] - for email_addr in destinations: - if email_addr: - del msg["To"] - msg["To"] = email_addr - # log('xxx mail: %s' % msg) - sco_emails.sendEmail(msg) + email.send_email(subject, sender, destinations, txt) diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index 72aba2be28..1a54104488 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -561,7 +561,7 @@ def sendResult(REQUEST, data, name=None, format=None, force_outer_xml_tag=True): def get_scodoc_version(): "return a string identifying ScoDoc version" - return os.popen("cd %s; ./get_scodoc_version.sh -s" % SCO_TOOLS_DIR).read().strip() + return sco_version.SCOVERSION def check_scodoc7_password(scodoc7_hash, password): diff --git a/app/static/icons/scologo_img.png b/app/static/icons/scologo_img.png index 2c55de0f05..830e126684 100644 Binary files a/app/static/icons/scologo_img.png and b/app/static/icons/scologo_img.png differ diff --git a/sco_version.py b/sco_version.py index b07e5945ac..c696a09d48 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.2" +SCOVERSION = "9.0.3" SCONAME = "ScoDoc" diff --git a/tools/build_release.sh b/tools/build_release.sh index 13b0564a3a..35c2e3793e 100644 --- a/tools/build_release.sh +++ b/tools/build_release.sh @@ -2,6 +2,8 @@ # Préparation d'une release ScoDoc: # Utilise jq sur Debian 11 VM +apt-get install jq + # Le répertoire de ce script: .../scodoc/tools SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" @@ -15,7 +17,7 @@ SCODOC_RELEASE=$(grep SCOVERSION "$SCRIPT_DIR/../sco_version.py" | awk '{ print # Dernière release GITEA_RELEASE_URL="https://scodoc.org/git/api/v1/repos/viennet/ScoDoc/releases?pre-release=true" -LAST_RELEASE_TAG=$(curl "$GITEA_RELEASE_URL" | jq ".[].tag_name" | sort | tail -1 | awk '{ print substr($1, 1, length($1)) }') +LAST_RELEASE_TAG=$(curl "$GITEA_RELEASE_URL" | jq ".[].tag_name" | sort | tail -1 | awk '{ print substr($1, 2, length($1)-2) }') echo echo "Version détectée dans le source: $SCODOC_RELEASE" @@ -75,7 +77,7 @@ SCODOC_DIR="$optdir"/scodoc [ -d "$SCODOC_DIR" ] || die "die Erreur: $SCODOC_DIR inexistant" # Inject version (eg 9.0.2) in debian:control -sed -i.bak "s/Version: x.y.z/Version: $PACKAGE_VERSION/g" /tmp/control +sed -i.bak "s/Version: x.y.z/Version: $PACKAGE_VERSION/g" "$SCODOC_DIR/tools/debian/control" # and double check v=$(grep Version "$SCODOC_DIR/tools/debian/control" | awk '{ print $2 }') if [ "$v" != "$PACKAGE_VERSION" ] @@ -106,7 +108,20 @@ chmod 755 "$slash"/DEBIAN/*inst || die "can't chmod debian scripts" # -------- THE END echo "Terminé." -echo "Après vérification, construire le paquet .deb avec:" -echo " dpkg-deb --build --root-owner-group $DEST_DIR" + +echo -n "Voulez-vous poursuivre et construire le .deb ? (y/n) [y] " +read -r ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo "ok" +else + echo "arrêt." + exit 0 +fi + +dpkg-deb --build --root-owner-group $DEST_DIR +DEB_FILE="$DEST_DIR".deb +echo "paquet construit: $DEB_FILE" + diff --git a/tools/configure-scodoc9.sh b/tools/configure-scodoc9.sh index 0f2f694f4b..2c60d1563f 100755 --- a/tools/configure-scodoc9.sh +++ b/tools/configure-scodoc9.sh @@ -111,8 +111,8 @@ change_scodoc_file_ownership # ------------ CREATION BASE DE DONNEES echo echo "Voulez-vous créer la base SQL SCODOC ?" -echo "répondre O sauf si vous avez déjà une base existante" -echo "que vous souhaitez conserver." +echo "répondre oui sauf si vous avez déjà une base existante" +echo "que vous souhaitez conserver (mais pour les migrations, répondre oui)." echo -n 'Créer la base de données SCODOC ? (y/n) [y] ' read -r ans if [ "$(norm_ans "$ans")" != 'N' ] diff --git a/tools/get_scodoc_version.sh b/tools/get_scodoc_version.sh index 767eec454c..9c06bfcad9 100755 --- a/tools/get_scodoc_version.sh +++ b/tools/get_scodoc_version.sh @@ -1,5 +1,7 @@ #!/bin/bash +# Script non utilisé + # Get version information # Use VERSION.py, VERSION, last commit, diff, and last upstream commit date @@ -10,11 +12,10 @@ source "$SCRIPT_DIR/config.sh" source "$SCRIPT_DIR/utils.sh" # Source code version: -x=$(grep SCOVERSION "$SCODOC_DIR/app/scodoc/VERSION.py") || terminate "can't access VERSION.py" 1 -x=${x#*\"} -src_version=${x%\"*} +src_version=$(grep SCOVERSION "$SCRIPT_DIR/../sco_version.py" | awk '{ print substr($3, 2, length($3)-2) }') -release_version=$(cat "$SCODOC_DIR/VERSION") + +release_version="" git status >& /dev/null if [ $? = 0 ]