+++ /dev/null
-#!/usr/bin/python
-
-import datetime
-import re
-import textile
-import unicodedata
-
-from misc import Object
-
-class PlanetEntry(Object):
- def __init__(self, backend, data):
- Object.__init__(self, backend)
-
- self.data = data
-
- def __cmp__(self, other):
- return cmp(self.published, other.published)
-
- @property
- def id(self):
- return self.data.id
-
- @property
- def slug(self):
- return self.data.slug
-
- def set_title(self, title):
- if self.title == title:
- return
-
- self.db.execute("UPDATE planet SET title = %s WHERE id = %s", title, self.id)
- self.data["title"] = title
-
- title = property(lambda s: s.data.title, set_title)
-
- @property
- def url(self):
- return "https://planet.ipfire.org/post/%s" % self.slug
-
- def set_published(self, published):
- if self.published == published:
- return
-
- self.db.execute("UPDATE planet SET published = %s WHERE id = %s",
- published, self.id)
- self.data["published"] = published
-
- published = property(lambda s: s.data.published, set_published)
-
- @property
- def year(self):
- return self.published.year
-
- @property
- def month(self):
- return self.published.month
-
- @property
- def updated(self):
- return self.data.updated
-
- def get_markdown(self):
- return self.data.markdown
-
- def set_markdown(self, markdown):
- if self.markdown == markdown:
- return
-
- markup = self.render(markdown)
- self.db.execute("UPDATE planet SET markdown = %s, markup = %s WHERE id = %s",
- markdown, markup, self.id)
-
- self.data.update({
- "markdown" : markdown,
- "markup" : markup,
- })
-
- markdown = property(get_markdown, set_markdown)
-
- @property
- def markup(self):
- if self.data.markup:
- return self.data.markup
-
- return self.render(self.markdown)
-
- @property
- def abstract(self):
- return self.render(self.markdown, 400)
-
- def render(self, text, limit=0):
- return self.planet.render(text, limit)
-
- @property
- def text(self):
- # Compat for markup
- return self.markup
-
- @property
- def author(self):
- if not hasattr(self, "_author"):
- self._author = self.accounts.get_by_uid(self.data.author_id)
-
- return self._author
-
- def set_status(self, status):
- if self.status == status:
- return
-
- self.db.execute("UPDATE planet SET status = %s WHERE id = %s", status, self.id)
- self.data["status"] = status
-
- status = property(lambda s: s.data.status, set_status)
-
- def is_draft(self):
- return self.status == "draft"
-
- def is_published(self):
- return self.status == "published"
-
- def count_view(self, referer=None, location=None):
- self.db.execute("INSERT INTO planet_views(post_id, referer, location) \
- VALUES(%s, %s, %s)", self.id, referer, location)
-
- if hasattr(self, "_views"):
- self._views += 1
-
- @property
- def views(self):
- if not hasattr(self, "_views"):
- res = self.db.get("SELECT COUNT(*) AS views FROM planet_views \
- WHERE post_id = %s", self.id)
-
- self._views = res.views
-
- return self._views
-
-
-class Planet(Object):
- def get_authors(self):
- query = self.db.query("SELECT DISTINCT author_id FROM planet WHERE status = %s \
- AND published IS NOT NULL AND published <= NOW()", "published")
-
- authors = []
- for author in query:
- author = self.accounts.search(author.author_id)
- if author:
- authors.append(author)
-
- return sorted(authors)
-
- def get_years(self):
- res = self.db.query("SELECT DISTINCT EXTRACT(YEAR FROM published)::integer AS year \
- FROM planet WHERE status = %s ORDER BY year DESC", "published")
-
- return [row.year for row in res]
-
- def get_entry_by_slug(self, slug):
- entry = self.db.get("SELECT * FROM planet WHERE slug = %s", slug)
-
- if entry:
- return PlanetEntry(self.backend, entry)
-
- def get_entry_by_id(self, id):
- entry = self.db.get("SELECT * FROM planet WHERE id = %s", id)
-
- if entry:
- return PlanetEntry(self.backend, entry)
-
- def get_entries(self, limit=3, offset=None, status="published", author_id=None):
- query = "SELECT * FROM planet"
- args, clauses = [], []
-
- if status:
- clauses.append("status = %s")
- args.append(status)
-
- if status == "published":
- clauses.append("published <= NOW()")
-
- if author_id:
- clauses.append("author_id = %s")
- args.append(author_id)
-
- if clauses:
- query += " WHERE %s" % " AND ".join(clauses)
-
- query += " ORDER BY published DESC"
-
- # Respect limit and offset
- if limit:
- query += " LIMIT %s"
- args.append(limit)
-
- if offset:
- query += " OFFSET %s"
- args.append(offset)
-
- entries = []
- for entry in self.db.query(query, *args):
- entry = PlanetEntry(self.backend, entry)
- entries.append(entry)
-
- return entries
-
- def get_entries_by_author(self, author_id, limit=None, offset=None):
- return self.get_entries(limit=limit, offset=offset, author_id=author_id)
-
- def get_entries_by_year(self, year):
- entries = self.db.query("SELECT * FROM planet \
- WHERE status = %s AND EXTRACT(YEAR FROM published) = %s \
- ORDER BY published DESC", "published", year)
-
- return [PlanetEntry(self.backend, e) for e in entries]
-
- def get_hot_entries(self, days=30, limit=8):
- entries = self.db.query("WITH hottest AS (SELECT post_id, COUNT(post_id) AS count \
- FROM planet_views WHERE \"when\" >= NOW() - INTERVAL '%s days' \
- GROUP BY post_id ORDER BY count DESC) SELECT * FROM planet \
- LEFT JOIN hottest ON planet.id = hottest.post_id \
- WHERE hottest.count IS NOT NULL \
- ORDER BY hottest.count DESC LIMIT %s",
- days, limit)
-
- return [PlanetEntry(self.backend, e) for e in entries]
-
- def render(self, text, limit=0):
- if limit and len(text) >= limit:
- text = text[:limit] + "..."
-
- return textile.textile(text)
-
- def _generate_slug(self, title):
- slug = unicodedata.normalize("NFKD", title).encode("ascii", "ignore")
- slug = re.sub(r"[^\w]+", " ", slug)
- slug = "-".join(slug.lower().strip().split())
-
- if not slug:
- slug = "entry"
-
- while True:
- e = self.db.get("SELECT * FROM planet WHERE slug = %s", slug)
- if not e:
- break
- slug += "-"
-
- return slug
-
- def create(self, title, markdown, author, status="published", published=None):
- slug = self._generate_slug(title)
- markup = self.render(markdown)
-
- if published is None:
- published = datetime.datetime.utcnow()
-
- id = self.db.execute("INSERT INTO planet(author_id, slug, title, status, \
- markdown, markup, published) VALUES(%s, %s, %s, %s, %s, %s, %s) RETURNING id",
- author.uid, slug, title, status, markdown, markup, published)
-
- if id:
- return self.get_entry_by_id(id)
-
- def update_entry(self, entry):
- self.db.execute("UPDATE planet SET title = %s, markdown = %s WHERE id = %s",
- entry.title, entry.markdown, entry.id)
-
- def save_entry(self, entry):
- slug = self._generate_slug(entry.title)
-
- id = self.db.execute("INSERT INTO planet(author_id, title, slug, markdown, published) \
- VALUES(%s, %s, %s, %s, NOW())", entry.author.uid, entry.title, slug, entry.markdown)
-
- return id
-
- def search(self, what):
- res = self.db.query("WITH \
- q AS (SELECT plainto_tsquery(%s, %s) AS query), \
- ranked AS (SELECT id, query, ts_rank_cd(to_tsvector(%s, markdown), query) AS rank \
- FROM planet, q WHERE markdown @@ query ORDER BY rank DESC) \
- SELECT *, ts_headline(markup, ranked.query, 'MinWords=100, MaxWords=110') AS markup FROM planet \
- JOIN ranked ON planet.id = ranked.id \
- WHERE status = %s AND published IS NOT NULL AND published <= NOW() \
- ORDER BY ranked DESC LIMIT 25",
- "english", what, "english", "published")
-
- return [PlanetEntry(self.backend, e) for e in res]