]> git.ipfire.org Git - ipfire.org.git/blobdiff - src/backend/blog.py
fireinfo: Store ASN
[ipfire.org.git] / src / backend / blog.py
index 094a565ffbfac2bb65786127195cbd9598feb3f1..aed8311b1c40edfc87108180759e5d8733c998a0 100644 (file)
@@ -2,16 +2,14 @@
 
 import datetime
 import feedparser
+import html2text
 import markdown
-import markdown.extensions
-import markdown.preprocessors
 import re
 import textile
-import tornado.gen
 import unicodedata
 
 from . import misc
-from . import util
+from . import wiki
 from .decorators import *
 
 class Blog(misc.Object):
@@ -31,20 +29,18 @@ class Blog(misc.Object):
                return self._get_post("SELECT * FROM blog \
                        WHERE id = %s", id)
 
-       def get_by_slug(self, slug, published=True):
-               if published:
-                       return self._get_post("SELECT * FROM blog \
-                               WHERE slug = %s AND published_at <= NOW()", slug)
-
+       def get_by_slug(self, slug):
                return self._get_post("SELECT * FROM blog \
                        WHERE slug = %s", slug)
 
        def get_newest(self, limit=None):
-               return self._get_posts("SELECT * FROM blog \
+               posts = self._get_posts("SELECT * FROM blog \
                        WHERE published_at IS NOT NULL \
                                AND published_at <= NOW() \
                        ORDER BY published_at DESC LIMIT %s", limit)
 
+               return list(posts)
+
        def get_by_tag(self, tag, limit=None):
                return self._get_posts("SELECT * FROM blog \
                        WHERE published_at IS NOT NULL \
@@ -52,14 +48,6 @@ class Blog(misc.Object):
                                AND %s = ANY(tags) \
                        ORDER BY published_at DESC LIMIT %s", tag, limit)
 
-       def get_by_author(self, author, limit=None):
-               return self._get_posts("SELECT * FROM blog \
-                       WHERE (author = %s OR author_uid = %s) \
-                               AND published_at IS NOT NULL \
-                               AND published_at <= NOW() \
-                       ORDER BY published_at DESC LIMIT %s",
-                       author.name, author.uid, limit)
-
        def get_by_year(self, year):
                return self._get_posts("SELECT * FROM blog \
                        WHERE EXTRACT(year FROM published_at) = %s \
@@ -67,33 +55,27 @@ class Blog(misc.Object):
                                AND published_at <= NOW() \
                        ORDER BY published_at DESC", year)
 
-       def get_drafts(self, author=None, limit=None):
-               if author:
-                       return self._get_posts("SELECT * FROM blog \
-                               WHERE author_uid = %s \
-                                       AND (published_at IS NULL OR published_at > NOW()) \
-                               ORDER BY COALESCE(updated_at, created_at) DESC LIMIT %s",
-                               author.uid, limit)
-
+       def get_drafts(self, author, limit=None):
                return self._get_posts("SELECT * FROM blog \
-                       WHERE (published_at IS NULL OR published_at > NOW()) \
-                       ORDER BY COALESCE(updated_at, created_at) DESC LIMIT %s", limit)
+                       WHERE author_uid = %s \
+                               AND (published_at IS NULL OR published_at > NOW()) \
+                       ORDER BY COALESCE(updated_at, created_at) DESC LIMIT %s",
+                       author.uid, limit)
 
        def search(self, query, limit=None):
-               query = util.parse_search_query(query)
-
-               return self._get_posts("SELECT blog.* FROM blog \
+               posts = self._get_posts("SELECT blog.* FROM blog \
                        LEFT JOIN blog_search_index search_index ON blog.id = search_index.post_id \
-                       WHERE search_index.document @@ to_tsquery('english', %s) \
-                               ORDER BY ts_rank(search_index.document, to_tsquery('english', %s)) DESC \
+                       WHERE search_index.document @@ websearch_to_tsquery('english', %s) \
+                               ORDER BY ts_rank(search_index.document, websearch_to_tsquery('english', %s)) DESC \
                        LIMIT %s", query, query, limit)
 
-       def has_had_recent_activity(self, t=None):
-               if t is None:
-                       t = datetime.timedelta(hours=24)
+               return list(posts)
+
+       def has_had_recent_activity(self, **kwargs):
+               t = datetime.timedelta(**kwargs)
 
                res = self.db.get("SELECT COUNT(*) AS count FROM blog \
-                       WHERE published_at IS NOT NULL AND published_at >= NOW() - %s", t)
+                       WHERE published_at IS NOT NULL AND published_at BETWEEN NOW() - %s AND NOW()", t)
 
                if res and res.count > 0:
                        return True
@@ -136,7 +118,7 @@ class Blog(misc.Object):
                if lang == "markdown":
                        return markdown.markdown(text,
                                extensions=[
-                                       PrettyLinksExtension(),
+                                       wiki.PrettyLinksExtension(),
                                        "codehilite",
                                        "fenced_code",
                                        "footnotes",
@@ -149,7 +131,8 @@ class Blog(misc.Object):
                elif lang == "textile":
                        return textile.textile(text)
 
-               return text
+               else:
+                       return text
 
        def refresh(self):
                """
@@ -167,8 +150,15 @@ class Blog(misc.Object):
                for row in res:
                        yield row.year
 
-       @tornado.gen.coroutine
-       def update_feeds(self):
+       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
                """
@@ -303,6 +293,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
@@ -320,9 +338,21 @@ class Post(misc.Object):
                return self.backend.releases._get_release("SELECT * FROM releases \
                        WHERE published IS NOT NULL AND published <= NOW() AND blog_id = %s", self.id)
 
-       def is_editable(self, editor):
+       def is_editable(self, user):
+               # Anonymous users cannot do anything
+               if not user:
+                       return False
+
+               # Admins can edit anything
+               if user.is_admin():
+                       return True
+
+               # User must have permission for the blog
+               if not user.is_blog_author():
+                       return False
+
                # Authors can edit their own posts
-               return self.author == editor
+               return self.author == user
 
        def update(self, title, text, tags=[]):
                """
@@ -358,24 +388,18 @@ 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
 
-class PrettyLinksExtension(markdown.extensions.Extension):
-       def extendMarkdown(self, md):
-               md.preprocessors.register(BugzillaLinksPreprocessor(md), "bugzilla", 10)
-               md.preprocessors.register(CVELinksPreprocessor(md), "cve", 10)
-
-
-class BugzillaLinksPreprocessor(markdown.preprocessors.Preprocessor):
-       regex = re.compile(r"(?:#(\d{5,}))", re.I)
-
-       def run(self, lines):
-               for line in lines:
-                       yield self.regex.sub(r"[#\1](https://bugzilla.ipfire.org/show_bug.cgi?id=\1)", line)
-
-
-class CVELinksPreprocessor(markdown.preprocessors.Preprocessor):
-       regex = re.compile(r"(?:CVE)[\s\-](\d{4}\-\d+)")
-
-       def run(self, lines):
-               for line in lines:
-                       yield self.regex.sub(r"[CVE-\1](https://cve.mitre.org/cgi-bin/cvename.cgi?name=\1)", line)
+               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)