]> git.ipfire.org Git - ipfire.org.git/commitdiff
blog: Send out emails for posts
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 29 Nov 2019 18:19:31 +0000 (18:19 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 29 Nov 2019 18:19:31 +0000 (18:19 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/backend/base.py
src/backend/blog.py
src/backend/messages.py
src/crontab/ipfire
src/templates/blog/messages/announcement.html [new file with mode: 0644]
src/templates/blog/messages/announcement.txt [new file with mode: 0644]
src/templates/messages/main.scss

index c9610d3be729a3db544c69825c24feb1a1bfe123..a5f6e265a978b187e678725c9e60fefe664de4dc 100644 (file)
@@ -152,6 +152,12 @@ templates_blog_DATA = \
 
 templates_blogdir = $(templatesdir)/blog
 
+templates_blog_messages_DATA = \
+       src/templates/blog/messages/announcement.html \
+       src/templates/blog/messages/announcement.txt
+
+templates_blog_messagesdir = $(templates_blogdir)/messages
+
 templates_blog_modules_DATA = \
        src/templates/blog/modules/history-navigation.html \
        src/templates/blog/modules/list.html \
index 1578a01544f9a44078f0a744b0e85c2b7f9682eb..6bda25abd9821e4d7ed09cdc43064b765e99afc6 100644 (file)
@@ -96,18 +96,19 @@ class Backend(object):
 
        async def run_task(self, task, *args, **kwargs):
                tasks = {
-                       "check-mirrors"     : self.mirrors.check_all,
-                       "check-spam"        : self.accounts.check_spam,
-                       "cleanup"           : self.cleanup,
-                       "launch-campaigns"  : self.campaigns.launch_manually,
-                       "run-campaigns"     : self.campaigns.run,
-                       "scan-files"        : self.releases.scan_files,
-                       "send-message"      : self.messages.send_cli,
-                       "send-all-messages" : self.messages.queue.send_all,
-                       "test-blacklist"    : self.geoip.test_blacklist,
-                       "test-ldap"         : self.accounts.test_ldap,
-                       "tweet"             : self.tweets.tweet,
-                       "update-blog-feeds" : self.blog.update_feeds,
+                       "announce-blog-posts" : self.blog.announce,
+                       "check-mirrors"       : self.mirrors.check_all,
+                       "check-spam"          : self.accounts.check_spam,
+                       "cleanup"             : self.cleanup,
+                       "launch-campaigns"    : self.campaigns.launch_manually,
+                       "run-campaigns"       : self.campaigns.run,
+                       "scan-files"          : self.releases.scan_files,
+                       "send-message"        : self.messages.send_cli,
+                       "send-all-messages"   : self.messages.queue.send_all,
+                       "test-blacklist"      : self.geoip.test_blacklist,
+                       "test-ldap"           : self.accounts.test_ldap,
+                       "tweet"               : self.tweets.tweet,
+                       "update-blog-feeds"   : self.blog.update_feeds,
                }
 
                # Get the task from the list of all tasks
index 61f4ba3f83827274d82d75d91883c69dd24b3155..ceb438a1ce31f49ad268a8b36f3303655502230c 100644 (file)
@@ -2,6 +2,7 @@
 
 import datetime
 import feedparser
+import html2text
 import markdown
 import markdown.extensions
 import markdown.preprocessors
@@ -165,6 +166,14 @@ class Blog(misc.Object):
                for row in res:
                        yield row.year
 
+       async def announce(self):
+               posts = self._get_posts("SELECT * FROM blog \
+                       WHERE (published_at IS NOT NULL AND published_at <= NOW()) \
+                               AND announced_at IS NULL")
+
+               for post in posts:
+                       await post.announce()
+
        async def update_feeds(self):
                """
                        Updates all enabled feeds
@@ -300,6 +309,34 @@ class Post(misc.Object):
                """
                return self.data.html or self.backend.blog._render_text(self.text, lang=self.lang)
 
+       @lazy_property
+       def plaintext(self):
+               h = html2text.HTML2Text()
+               h.ignore_links = True
+
+               return h.handle(self.html)
+
+       # Excerpt
+
+       @property
+       def excerpt(self):
+               paragraphs = self.plaintext.split("\n\n")
+
+               excerpt = []
+
+               for paragraph in paragraphs:
+                       excerpt.append(paragraph)
+
+                       # Add another paragraph if we encountered a headline
+                       if paragraph.startswith("#"):
+                               continue
+
+                       # End if this paragraph was long enough
+                       if len(paragraph) >= 40:
+                               break
+
+               return "\n\n".join(excerpt)
+
        # Tags
 
        @property
@@ -355,6 +392,22 @@ class Post(misc.Object):
                # Update search indices
                self.backend.blog.refresh()
 
+       async def announce(self):
+               # Get people who should receive this message
+               group = self.backend.groups.get_by_gid("promotional-consent")
+               if not group:
+                       return
+
+               with self.db.transaction():
+                       # Generate an email for everybody in this group
+                       for account in group:
+                               self.backend.messages.send_template("blog/messages/announcement",
+                                       account=account, post=self)
+
+                       # Mark this post as announced
+                       self.db.execute("UPDATE blog SET announced_at = CURRENT_TIMESTAMP \
+                               WHERE id = %s", self.id)
+
 
 class PrettyLinksExtension(markdown.extensions.Extension):
        def extendMarkdown(self, md):
index 69dbf802f545d1bba2f3027e5df210594bc93d80..f1481f11a9d0c8c7198024d5316569f936d1893e 100644 (file)
@@ -5,6 +5,7 @@ import email.mime.multipart
 import email.mime.text
 import email.utils
 import logging
+import random
 import subprocess
 import tornado.locale
 import tornado.template
@@ -32,7 +33,7 @@ class Messages(misc.Object):
        def make_recipient(self, recipient):
                # Use the contact instead of the account
                if isinstance(recipient, accounts.Account):
-                       recipient = "%s <%s>" % (recipient, recipient.email)
+                       recipient = account.email_to
 
                # Fall back to pass on strings
                return recipient
@@ -159,6 +160,8 @@ class Messages(misc.Object):
                """
                account = self.backend.accounts.get_by_mail(recipient)
 
+               posts = list(self.backend.blog.get_newest(limit=5))
+
                kwargs = {
                        "account"    : account,
                        "first_name" : account.first_name,
@@ -169,6 +172,9 @@ class Messages(misc.Object):
                        # Random activation/reset codes
                        "activation_code" : util.random_string(36),
                        "reset_code"      : util.random_string(64),
+
+                       # The latest blog post
+                       "post" : random.choice(posts),
                }
 
                return self.send_template(template, recipients=[recipient,], **kwargs)
index df1293c63f6eee867bd91e3bb48541fd1cf92cd3..0bf86500f0d9e1c00e2d2bf5153fa82f547d67a2 100644 (file)
@@ -12,6 +12,9 @@ SHELL=/bin/bash
 # Run campaigns
 */5 * * * *    nobody  ipfire.org run-campaigns
 
+# Announce blog posts
+*/5 * * * *    nobody  ipfire.org announce-blog-posts
+
 # Cleanup once an hour
 30 * * * *     nobody  ipfire.org cleanup
 
diff --git a/src/templates/blog/messages/announcement.html b/src/templates/blog/messages/announcement.html
new file mode 100644 (file)
index 0000000..566069b
--- /dev/null
@@ -0,0 +1,33 @@
+{% extends "../../messages/base-promo.html" %}
+
+{% block content %}
+    <p>
+        {{ _("there is a new post from %s on the IPFire Blog:") % post.author }}
+    </p>
+
+    <p>
+        <strong>{{ post.title }}</strong>
+    </p>
+
+    {% if post.excerpt %}
+        <blockquote>{{ post.excerpt }}</blockquote>
+    {% end %}
+
+       <table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
+               <tbody>
+                       <tr>
+                               <td align="left">
+                                       <table role="presentation" border="0" cellpadding="0" cellspacing="0">
+                                               <tbody>
+                                                       <tr>
+                                                               <td>
+                                    <a href="https://blog.ipfire.org/post/{{ post.slug }}" target="_blank">{{ _("Click Here To Read More") }}</a>
+                                </td>
+                                                       </tr>
+                                               </tbody>
+                                       </table>
+                               </td>
+                       </tr>
+               </tbody>
+       </table>
+{% end block %}
diff --git a/src/templates/blog/messages/announcement.txt b/src/templates/blog/messages/announcement.txt
new file mode 100644 (file)
index 0000000..fb4a06b
--- /dev/null
@@ -0,0 +1,16 @@
+From: IPFire Project <no-reply@ipfire.org>
+To: {{ account.email_to }}
+Subject: {{ post.title }}
+X-Auto-Response-Suppress: OOF
+
+{{ _("Hello %s,") % account.first_name }}
+
+{{ _("there is a new post from %s on the IPFire Blog:") % post.author }}
+
+  {{ post.title }}
+
+{% if post.excerpt %}{{ "\n".join(("  %s" % l for l in post.excerpt.splitlines())) }}{% end %}
+
+{{ _("Click here to read more:") }}
+
+  https://blog.ipfire.org/post/{{ post.slug }}
index 1f469178b001a18c515973ff10fa340b99a10131..2e44dbfc24de92fe2974040af2050f8b94373f76 100644 (file)
@@ -147,6 +147,10 @@ a {
        text-decoration: underline;
 }
 
+blockquote {
+       font-style: italic;
+}
+
 // Buttons
 
 .btn {