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 \
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
import datetime
import feedparser
+import html2text
import markdown
import markdown.extensions
import markdown.preprocessors
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
"""
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
# 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):
import email.mime.text
import email.utils
import logging
+import random
import subprocess
import tornado.locale
import tornado.template
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
"""
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,
# 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)
# 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
--- /dev/null
+{% 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 %}
--- /dev/null
+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 }}
text-decoration: underline;
}
+blockquote {
+ font-style: italic;
+}
+
// Buttons
.btn {