From 865769938819d48f6b0bbc98e1182e94d83fe9cd Mon Sep 17 00:00:00 2001 From: rimu <3310831+rimu@users.noreply.github.com> Date: Sat, 24 Feb 2024 20:01:38 +1300 Subject: [PATCH] admin can send email newsletters --- app/admin/forms.py | 8 +++++++ app/admin/routes.py | 23 +++++++++++++++++--- app/admin/util.py | 33 ++++++++++++++++++++++++++++- app/templates/admin/_nav.html | 1 + app/templates/admin/newsletter.html | 17 +++++++++++++++ app/templates/email/newsletter.html | 5 +++++ app/templates/email/newsletter.txt | 6 ++++++ app/templates/email/welcome.txt | 1 + 8 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 app/templates/admin/newsletter.html create mode 100644 app/templates/email/newsletter.html create mode 100644 app/templates/email/newsletter.txt diff --git a/app/admin/forms.py b/app/admin/forms.py index e933745c..68066e44 100644 --- a/app/admin/forms.py +++ b/app/admin/forms.py @@ -113,3 +113,11 @@ class EditUserForm(FlaskForm): indexable = BooleanField(_l('Allow search engines to index this profile')) manually_approves_followers = BooleanField(_l('Manually approve followers')) submit = SubmitField(_l('Save')) + + +class SendNewsletterForm(FlaskForm): + subject = StringField(_l('Subject'), validators=[DataRequired()]) + body_text = TextAreaField(_l('Body (text)'), render_kw={"rows": 10}, validators=[DataRequired()]) + body_html = TextAreaField(_l('Body (html)'), render_kw={"rows": 20}, validators=[DataRequired()]) + test = BooleanField(_l('Test mode'), render_kw={'checked': True}) + submit = SubmitField(_l('Send newsletter')) diff --git a/app/admin/routes.py b/app/admin/routes.py index f02862ef..3df9a6a6 100644 --- a/app/admin/routes.py +++ b/app/admin/routes.py @@ -11,8 +11,8 @@ from app.activitypub.routes import process_inbox_request, process_delete_request from app.activitypub.signature import post_request from app.activitypub.util import default_context from app.admin.forms import FederationForm, SiteMiscForm, SiteProfileForm, EditCommunityForm, EditUserForm, \ - EditTopicForm -from app.admin.util import unsubscribe_from_everything_then_delete, unsubscribe_from_community + EditTopicForm, SendNewsletterForm +from app.admin.util import unsubscribe_from_everything_then_delete, unsubscribe_from_community, send_newsletter from app.community.util import save_icon_file, save_banner_file from app.models import AllowedInstances, BannedInstances, ActivityPubLog, utcnow, Site, Community, CommunityMember, \ User, Instance, File, Report, Topic, UserRegistration @@ -596,4 +596,21 @@ def admin_reports(): moderating_communities=moderating_communities(current_user.get_id()), joined_communities=joined_communities(current_user.get_id()), site=g.site - ) \ No newline at end of file + ) + + +@bp.route('/newsletter', methods=['GET', 'POST']) +@login_required +@permission_required('administer all users') +def newsletter(): + form = SendNewsletterForm() + if form.validate_on_submit(): + send_newsletter(form) + flash('Newsletter sent') + return redirect(url_for('admin.newsletter')) + + return render_template("admin/newsletter.html", form=form, title=_('Send newsletter'), + moderating_communities=moderating_communities(current_user.get_id()), + joined_communities=joined_communities(current_user.get_id()), + site=g.site + ) diff --git a/app/admin/util.py b/app/admin/util.py index 62de8fc5..c076c222 100644 --- a/app/admin/util.py +++ b/app/admin/util.py @@ -1,4 +1,7 @@ -from flask import request, abort, g, current_app, json +from flask import request, abort, g, current_app, json, flash, render_template +from flask_login import current_user +from sqlalchemy import text, desc + from app import db, cache, celery from app.activitypub.signature import post_request from app.activitypub.util import default_context @@ -70,3 +73,31 @@ def unsubscribe_from_community(community, user): post_request(community.ap_inbox_url, undo, user.private_key, user.profile_id() + '#main-key') activity.result = 'success' db.session.commit() + + +def send_newsletter(form): + recipients = User.query.filter(User.newsletter == True, User.banned == False, User.ap_id == None).order_by(desc(User.id)).limit(40000) + + from app.email import send_email + + if recipients.count() == 0: + flash('No recipients', 'error') + + for recipient in recipients: + body_text = render_template('email/newsletter.txt', + recipient=recipient if not form.test.data else current_user, + content=form.body_text.data) + body_html = render_template('email/newsletter.html', + recipient=recipient if not form.test.data else current_user, + content=form.body_html.data, + domain=current_app.config['SERVER_NAME']) + if form.test.data: + to = current_user.email + else: + to = recipient.email + + send_email(subject=form.subject.data, sender=f'{g.site.name} ', recipients=[to], + text_body=body_text, html_body=body_html) + + if form.test.data: + break diff --git a/app/templates/admin/_nav.html b/app/templates/admin/_nav.html index 0df1acfe..f0048563 100644 --- a/app/templates/admin/_nav.html +++ b/app/templates/admin/_nav.html @@ -10,5 +10,6 @@ {% endif %} {{ _('Moderation') }} | {{ _('Federation') }} | + {{ _('Newsletter') }} | {{ _('Activities') }} diff --git a/app/templates/admin/newsletter.html b/app/templates/admin/newsletter.html new file mode 100644 index 00000000..0fe862c2 --- /dev/null +++ b/app/templates/admin/newsletter.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} +{% from 'bootstrap/form.html' import render_form %} + +{% block app_content %} + +
+
+ {% include 'admin/_nav.html' %} +
+
+ +
+
+ {{ render_form(form) }} +
+
+{% endblock %} \ No newline at end of file diff --git a/app/templates/email/newsletter.html b/app/templates/email/newsletter.html new file mode 100644 index 00000000..3450bfee --- /dev/null +++ b/app/templates/email/newsletter.html @@ -0,0 +1,5 @@ +

PieFed logo

Hello {{ recipient.display_name() }},

+{{ content|safe }} +

 

+

Unsubscribe from PieFed newsletter

+ diff --git a/app/templates/email/newsletter.txt b/app/templates/email/newsletter.txt new file mode 100644 index 00000000..b3c79eb3 --- /dev/null +++ b/app/templates/email/newsletter.txt @@ -0,0 +1,6 @@ +Hello {{ recipient.display_name() }},

+ +{{ content }} + + +Unsubscribe from PieFed newsletter at {{ url_for('user.user_newsletter_unsubscribe', user_id=recipient.id, token=recipient.verification_token, _external=True) }}. \ No newline at end of file diff --git a/app/templates/email/welcome.txt b/app/templates/email/welcome.txt index 559d49cf..1e95ebcb 100644 --- a/app/templates/email/welcome.txt +++ b/app/templates/email/welcome.txt @@ -17,3 +17,4 @@ Warm regards, Rimu Atkinson Founder +Unsubscribe from PieFed newsletter at {{ url_for('user.user_newsletter_unsubscribe', user_id=user.id, token=user.verification_token, _external=True) }}.