--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<rss version="2.0">
+ <channel>
+ <title>{% block title %}{% end block %}</title>
+ <link>{% block url %}{% end block %}</link>
+ <description>{% block description %}{% end block %}</description>
+ <language>{{ lang }}</language>
+ <copyright>{% block copyright %}IPFire Team{% end block %}</copyright>
+ <pubDate>Thu, 8 Nov 2007 00:00:00 +0200</pubDate>
+ {% for item in items %}
+ <item>
+ <title>{{ item.title }}</title>
+ <link>{{ item.url }}</link>
+ <author>{{ item.author.email }} ({{ item.author.name }})</author>
+ <guid>{{ item.url }}</guid>
+ <pubDate>{{ item.published.strftime("%a, %d %b %Y %H:%M:%S +0200") }}</pubDate>
+ <description>
+ <![CDATA[{% raw item.markup %}]]>
+ </description>
+ </item>
+ {% end %}
+ </channel>
+</rss>
--- /dev/null
+{% extends "base.xml" %}
+
+{% block title %}IPFire.org - News{% end block%}
+{% block url %}http://www.ipfire.org/{% end block %}
+{% block description %}IPFire News Feed{% end block %}
--- /dev/null
+{% extends "base.xml" %}
+
+{% block title %}planet.ipfire.org{% end block%}
+{% block url %}http://planet.ipfire.org/{% end block %}
+{% block description %}IPFire Planet Feed{% end block %}
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<rss version="2.0">
- <channel>
- <title>{{ title }}</title>
- <link>{{ url }}</link>
- <description>{{ description }}</description>
- <language>{{ lang }}</language>
- <copyright>IPFire Team</copyright>
- <pubDate>Thu, 8 Nov 2007 00:00:00 +0200</pubDate>
- {% for item in items %}
- <item>
- <title>{{ escape(item.title) }}</title>
- <link>{{ item.url }}</link>
- <author>{{ item.author.mail }} ({{ item.author.name }})</author>
- <guid>{{ item.url }}</guid>
- <pubDate>{{ item.date.strftime("%a, %d %b %Y %H:%M:%S +0200") }}</pubDate>
- <description>
- <![CDATA[
- {% raw item.text %}
- ]]>
- </description>
- </item>
- {% end %}
- </channel>
-</rss>
class Application(tornado.web.Application):
def __init__(self):
+ self.__backend = None
+
settings = dict(
cookie_secret = "aXBmaXJlY29va2llc2VjcmV0Cg==",
debug = options.debug,
def __del__(self):
logging.info("Shutting down application")
+ @property
+ def backend(self):
+ if self.__backend is None:
+ self.__backend = backend.Backend()
+
+ return self.__backend
+
@property
def ioloop(self):
return tornado.ioloop.IOLoop.instance()
define("debug", default=False, help="Run in debug mode", type=bool)
parse_command_line()
+from base import Backend
+
from ads import Advertisements
from accounts import Accounts
from banners import Banners
import logging
import urllib
-from misc import Singleton
+from misc import Object
from settings import Settings
-class Accounts(object):
- __metaclass__ = Singleton
-
+class Accounts(Object):
@property
def settings(self):
return Settings()
- def __init__(self):
+ def __init__(self, backend):
+ Object.__init__(self, backend)
self.__db = None
self._init()
for dn, attrs in results:
#if attrs["loginShell"] == ["/bin/bash"]:
- self._accounts[dn] = Account(dn)
+ self._accounts[dn] = Account(self.backend, dn)
def list(self):
return sorted(self._accounts.values())
search = find
-class Account(object):
- def __init__(self, dn):
+class Account(Object):
+ def __init__(self, backend, dn):
+ Object.__init__(self, backend)
self.dn = dn
self.__attributes = {}
@property
def db(self):
- return Accounts().db
+ return self.accounts.db
@property
def attributes(self):
def is_admin(self):
return True # XXX todo
+ @property
+ def name(self):
+ return self.cn
+
@property
def email(self):
name = self.cn.lower()
--- /dev/null
+#!/usr/bin/python
+
+import tornado.database
+
+import accounts
+import planet
+
+MYSQL_SERVER = "mysql-master.ipfire.org"
+MYSQL_DB = "webapp"
+MYSQL_USER = "webapp"
+
+class Backend(object):
+ def __init__(self):
+ self.db = tornado.database.Connection(MYSQL_SERVER, MYSQL_DB, user=MYSQL_USER)
+
+ self.accounts = accounts.Accounts(self)
+ self.planet = planet.Planet(self)
#!/usr/bin/python
+class Object(object):
+ def __init__(self, backend):
+ self.backend = backend
+
+ @property
+ def db(self):
+ return self.backend.db
+
+ @property
+ def accounts(self):
+ return self.backend.accounts
+
+ @property
+ def planet(self):
+ return self.backend.planet
+
+
class Singleton(type):
"""
A class for using the singleton pattern
from accounts import Accounts
from databases import Databases
-from misc import Singleton
+from misc import Object
-class PlanetEntry(object):
- def __init__(self, db, entry=None):
- self.db = db
+class PlanetEntry(Object):
+ def __init__(self, backend, data):
+ Object.__init__(self, backend)
- if entry:
- self.__entry = entry
- else:
- self.__entry = tornado.database.Row({
- "id" : None,
- "title" : "",
- "markdown" : "",
- "tags" : [],
- })
-
- def set(self, key, val):
- self.__entry[key] = val
-
- @property
- def planet(self):
- return Planet()
+ self.data = data
@property
def id(self):
- return self.__entry.id
+ return self.data.id
@property
def slug(self):
- return self.__entry.slug
+ return self.data.slug
@property
def title(self):
- return self.__entry.title
+ return self.data.title
+
+ @property
+ def url(self):
+ return "http://planet.ipfire.org/post/%s" % self.slug
@property
def published(self):
- return self.__entry.published
+ return self.data.published
@property
def year(self):
@property
def updated(self):
- return self.__entry.updated
+ return self.data.updated
@property
def markdown(self):
- return self.__entry.markdown
+ return self.data.markdown
+
+ @property
+ def markup(self):
+ if self.data.markup:
+ return self.data.markup
+
+ return self.render(self.markdown)
@property
def abstract(self):
@property
def text(self):
- return self.render(self.markdown)
+ # Compat for markup
+ return self.markup
@property
def author(self):
- return Accounts().search(self.__entry.author_id)
+ if not hasattr(self, "__author"):
+ self.__author = self.accounts.search(self.data.author_id)
+
+ return self.__author
# Tags
tags = property(get_tags, set_tags)
-class Planet(object):
- __metaclass__ = Singleton
-
- @property
- def db(self):
- return Databases().webapp
-
+class Planet(Object):
def get_authors(self):
authors = []
- for author in self.db.query("SELECT DISTINCT author_id FROM planet"):
- author = Accounts().search(author.author_id)
+ for author in self.db.query("SELECT DISTINCT author_id FROM planet WHERE status = %s", "published"):
+ author = self.accounts.search(author.author_id)
if author:
authors.append(author)
def get_years(self):
res = self.db.query("SELECT DISTINCT YEAR(published) AS year \
- FROM planet ORDER BY year DESC")
+ 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.db, 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.db, entry)
+ return PlanetEntry(self.backend, entry)
def _limit_and_offset_query(self, limit=None, offset=None):
query = " "
return query
- def get_entries(self, limit=3, offset=None):
- query = "SELECT * FROM planet WHERE acknowledged='Y' ORDER BY published DESC"
+ def get_entries(self, limit=3, offset=None, status="published", author_id=None):
+ query = "SELECT * FROM planet"
+ args, clauses = [], []
- # Respect limit and offset
- query += self._limit_and_offset_query(limit=limit, offset=offset)
+ if status:
+ clauses.append("status = %s")
+ args.append(status)
- entries = []
- for entry in self.db.query(query):
- entries.append(PlanetEntry(self.db, entry))
+ if author_id:
+ clauses.append("author_id = %s")
+ args.append(author_id)
- return entries
+ if clauses:
+ query += " WHERE %s" % " AND ".join(clauses)
- def get_entries_by_author(self, author_id, limit=None, offset=None):
- query = "SELECT * FROM planet WHERE author_id = '%s'" % author_id
- query += " AND acknowledged='Y' ORDER BY published DESC"
+ query += " ORDER BY published DESC"
+
+ # Respect limit and offset
+ if limit:
+ if offset:
+ query += " LIMIT %s,%s"
+ args += [offset, limit,]
+ else:
+ query += " LIMIT %s"
+ args.append(limit)
- # Respect limit and offset
- query += self._limit_and_offset_query(limit=limit, offset=offset)
+ entries = []
+ for entry in self.db.query(query, *args):
+ entry = PlanetEntry(self.backend, entry)
+ entries.append(entry)
- entries = self.db.query(query)
+ return entries
- return [PlanetEntry(self.db, e) for e in 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 YEAR(published) = %s ORDER BY published DESC", year)
+ WHERE status = %s AND YEAR(published) = %s ORDER BY published DESC",
+ "published", year)
- return [PlanetEntry(self.db, e) for e in entries]
+ 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):
args.append(len(tags))
entries = self.db.query(query % " OR ".join(clauses), *args)
- return [PlanetEntry(self.db, e) for e in entries]
+ return [PlanetEntry(self.backend, e) for e in entries]
def search_autocomplete(self, what):
tags = what.split()
return ret
+ @property
+ def backend(self):
+ return self.application.backend
+
@property
def advertisements(self):
return backend.Advertisements()
@property
def accounts(self):
- return backend.Accounts()
+ return self.backend.accounts
@property
def banners(self):
@property
def planet(self):
- return backend.Planet()
+ return self.backend.planet
@property
def wishlist(self):
from handlers_base import *
-import backend
-
-from backend.databases import Databases
-
class PlanetBaseHandler(BaseHandler):
- @property
- def db(self):
- return Databases().webapp
-
- @property
- def planet(self):
- return backend.Planet()
+ pass
class PlanetMainHandler(PlanetBaseHandler):
#!/usr/bin/python
+import logging
import textile
import tornado.database
from handlers_base import *
class RSSHandler(BaseHandler):
- _cache_prefix = ""
-
- @property
- def cache_prefix(self):
- return self._cache_prefix
+ _default_limit = 10
+ _default_offset = 0
def prepare(self):
self.set_header("Content-Type", "application/rss+xml")
- def get(self):
- offset = int(self.get_argument("offset", 0))
- limit = int(self.get_argument("limit", 10))
+ @property
+ def limit(self):
+ value = self.get_argument("limit", None)
+
+ try:
+ return int(value)
+ except (TypeError, ValueError):
+ return self._default_limit
+
+ @property
+ def offset(self):
+ value = self.get_argument("offset", None)
+
+ try:
+ return int(value)
+ except (TypeError, ValueError):
+ return self._default_offset
+
+ def get(self, *args, **kwargs):
+ url = "%s%s" % (self.request.host, self.request.path)
rss_id = "rss-%s-locale=%s-limit=%s-offset=%s" % \
- (self.cache_prefix, self.locale.code, limit, offset)
+ (url, self.locale.code, self.limit, self.offset)
- items = self.memcached.get(rss_id)
- if not items:
- items = self.generate()
-
- self.memcached.set(rss_id, items, 15)
+ rss = self.memcached.get(rss_id)
+ if not rss:
+ logging.debug("Generating RSS feed (%s)..." % rss_id)
+ rss = self.generate(*args, **kwargs)
- self.render("rss.xml", items=items, title=self.title,
- url=self.url, description=self.description)
+ self.memcached.set(rss_id, rss, 900)
+
+ self.finish(rss)
def generate(self):
raise NotImplementedError
class RSSNewsHandler(RSSHandler):
- _cache_prefix = "news"
-
- title = "IPFire.org - News"
- url = "http://www.ipfire.org/"
- description = "IPFire News Feed"
-
def generate(self):
- offset = int(self.get_argument("offset", 0))
- limit = int(self.get_argument("limit", 10))
-
- news = self.news.get_latest(
- locale=self.locale,
- limit=limit,
- offset=offset,
- )
+ news = self.news.get_latest(locale=self.locale,
+ limit=self.limit, offset=self.offset)
items = []
for n in news:
# Get author information
- author = self.get_account(n.author_id)
- n.author = tornado.database.Row(
- name = author.cn,
- mail = author.email,
- )
+ n.author = self.get_account(n.author_id)
# Render text
n.text = textile.textile(n.text)
item = tornado.database.Row({
- "title" : n.title,
- "author" : n.author,
- "date" : n.date,
- "url" : "http://www.ipfire.org/news/%s" % n.slug,
- "text" : n.text,
+ "title" : n.title,
+ "author" : n.author,
+ "published" : n.date,
+ "url" : "http://www.ipfire.org/news/%s" % n.slug,
+ "markup" : n.text,
})
items.append(item)
- return items
+ return self.render_string("feeds/news.xml", items=items)
class RSSPlanetAllHandler(RSSHandler):
- _cache_prefix = "planet"
-
- title = "IPFire.org - Planet"
- url = "http://planet.ipfire.org/"
- description = "IPFire Planet Feed"
-
def generate(self):
- offset = int(self.get_argument("offset", 0))
- limit = int(self.get_argument("limit", 10))
+ items = self.planet.get_entries(limit=self.limit, offset=self.offset)
- news = self.planet.get_entries(
- limit=limit,
- offset=offset,
- )
-
- items = []
- for n in news:
- # Get author information
- author = tornado.database.Row(
- name = n.author.cn,
- mail = n.author.email,
- )
-
- item = tornado.database.Row({
- "title" : n.title,
- "author" : author,
- "date" : n.published,
- "url" : "http://planet.ipfire.org/post/%s" % n.slug,
- "text" : textile.textile(n.text),
- })
- items.append(item)
-
- return items
+ return self.render_string("feeds/planet.xml", items=items)
class RSSPlanetUserHandler(RSSPlanetAllHandler):
- @property
- def cache_prefix(self):
- return "%s-user=%s" % (self._cache_prefix, self.user)
-
- def get(self, user):
- self.user = user
-
- return RSSPlanetAllHandler.get(self)
-
- def generate(self):
- offset = int(self.get_argument("offset", 0))
- limit = int(self.get_argument("limit", 10))
-
- news = self.planet.get_entries_by_author(
- self.user,
- limit=limit,
- offset=offset,
- )
-
- items = []
- for n in news:
- # Get author information
- author = tornado.database.Row(
- name = n.author.cn,
- mail = n.author.email,
- )
-
- item = tornado.database.Row({
- "title" : n.title,
- "author" : author,
- "date" : n.published,
- "url" : "http://planet.ipfire.org/post/%s" % n.slug,
- "text" : textile.textile(n.text),
- })
- items.append(item)
+ def generate(self, user):
+ items = self.planet.get_entries_by_author(user,
+ limit=self.limit, offset=self.offset)
- return items
+ return self.render_string("feeds/planet.xml", items=items)