]> git.ipfire.org Git - ipfire.org.git/blame - webapp/backend/planet.py
Deliver static content from same domain if accessing admin.ipfire.org.
[ipfire.org.git] / webapp / backend / planet.py
CommitLineData
940227cb
MT
1#!/usr/bin/python
2
27066195 3import re
940227cb 4import textile
27066195
MT
5import tornado.database
6import unicodedata
940227cb
MT
7
8from accounts import Accounts
9from databases import Databases
10
a6dc0bad 11from misc import Object
940227cb 12
a6dc0bad
MT
13class PlanetEntry(Object):
14 def __init__(self, backend, data):
15 Object.__init__(self, backend)
8876f3df 16
a6dc0bad 17 self.data = data
940227cb
MT
18
19 @property
20 def id(self):
a6dc0bad 21 return self.data.id
940227cb
MT
22
23 @property
24 def slug(self):
a6dc0bad 25 return self.data.slug
940227cb
MT
26
27 @property
28 def title(self):
a6dc0bad
MT
29 return self.data.title
30
31 @property
32 def url(self):
33 return "http://planet.ipfire.org/post/%s" % self.slug
940227cb
MT
34
35 @property
36 def published(self):
a6dc0bad 37 return self.data.published
940227cb 38
cc3b928d
MT
39 @property
40 def year(self):
41 return self.published.year
42
43 @property
44 def month(self):
45 return self.published.month
46
940227cb
MT
47 @property
48 def updated(self):
a6dc0bad 49 return self.data.updated
940227cb
MT
50
51 @property
52 def markdown(self):
a6dc0bad
MT
53 return self.data.markdown
54
55 @property
56 def markup(self):
57 if self.data.markup:
58 return self.data.markup
59
60 return self.render(self.markdown)
940227cb
MT
61
62 @property
63 def abstract(self):
64 return self.render(self.markdown, 400)
65
66 def render(self, text, limit=0):
27066195 67 return self.planet.render(text, limit)
940227cb
MT
68
69 @property
70 def text(self):
a6dc0bad
MT
71 # Compat for markup
72 return self.markup
940227cb
MT
73
74 @property
75 def author(self):
a6dc0bad
MT
76 if not hasattr(self, "__author"):
77 self.__author = self.accounts.search(self.data.author_id)
78
79 return self.__author
940227cb 80
8876f3df
MT
81 # Tags
82
83 def get_tags(self):
84 if not hasattr(self, "__tags"):
85 res = self.db.query("SELECT tag FROM planet_tags \
86 WHERE post_id = %s ORDER BY tag", self.id)
87 self.__tags = []
88 for row in res:
89 self.__tags.append(row.tag)
90
91 return self.__tags
92
93 def set_tags(self, tags):
94 # Delete all existing tags.
95 self.db.execute("DELETE FROM planet_tags WHERE post_id = %s", self.id)
96
97 self.db.executemany("INSERT INTO planet_tags(post_id, tag) VALUES(%s, %s)",
98 ((self.id, tag) for tag in tags))
99
100 # Update cache.
101 self.__tags = tags
102 self.__tags.sort()
103
104 tags = property(get_tags, set_tags)
105
940227cb 106
a6dc0bad 107class Planet(Object):
940227cb
MT
108 def get_authors(self):
109 authors = []
a6dc0bad
MT
110 for author in self.db.query("SELECT DISTINCT author_id FROM planet WHERE status = %s", "published"):
111 author = self.accounts.search(author.author_id)
940227cb
MT
112 if author:
113 authors.append(author)
114
27066195 115 return sorted(authors)
940227cb 116
cc3b928d
MT
117 def get_years(self):
118 res = self.db.query("SELECT DISTINCT YEAR(published) AS year \
a6dc0bad 119 FROM planet WHERE status = %s ORDER BY year DESC", "published")
cc3b928d
MT
120
121 return [row.year for row in res]
122
940227cb
MT
123 def get_entry_by_slug(self, slug):
124 entry = self.db.get("SELECT * FROM planet WHERE slug = %s", slug)
125 if entry:
a6dc0bad 126 return PlanetEntry(self.backend, entry)
940227cb 127
27066195
MT
128 def get_entry_by_id(self, id):
129 entry = self.db.get("SELECT * FROM planet WHERE id = %s", id)
130 if entry:
a6dc0bad 131 return PlanetEntry(self.backend, entry)
27066195 132
940227cb
MT
133 def _limit_and_offset_query(self, limit=None, offset=None):
134 query = " "
135
136 if limit:
137 if offset:
138 query += "LIMIT %d,%d" % (offset, limit)
139 else:
140 query += "LIMIT %d" % limit
141
142 return query
143
a6dc0bad
MT
144 def get_entries(self, limit=3, offset=None, status="published", author_id=None):
145 query = "SELECT * FROM planet"
146 args, clauses = [], []
940227cb 147
a6dc0bad
MT
148 if status:
149 clauses.append("status = %s")
150 args.append(status)
940227cb 151
a6dc0bad
MT
152 if author_id:
153 clauses.append("author_id = %s")
154 args.append(author_id)
940227cb 155
a6dc0bad
MT
156 if clauses:
157 query += " WHERE %s" % " AND ".join(clauses)
940227cb 158
a6dc0bad
MT
159 query += " ORDER BY published DESC"
160
161 # Respect limit and offset
162 if limit:
163 if offset:
164 query += " LIMIT %s,%s"
165 args += [offset, limit,]
166 else:
167 query += " LIMIT %s"
168 args.append(limit)
940227cb 169
a6dc0bad
MT
170 entries = []
171 for entry in self.db.query(query, *args):
172 entry = PlanetEntry(self.backend, entry)
173 entries.append(entry)
940227cb 174
a6dc0bad 175 return entries
940227cb 176
a6dc0bad
MT
177 def get_entries_by_author(self, author_id, limit=None, offset=None):
178 return self.get_entries(limit=limit, offset=offset, author_id=author_id)
27066195 179
cc3b928d
MT
180 def get_entries_by_year(self, year):
181 entries = self.db.query("SELECT * FROM planet \
a6dc0bad
MT
182 WHERE status = %s AND YEAR(published) = %s ORDER BY published DESC",
183 "published", year)
cc3b928d 184
a6dc0bad 185 return [PlanetEntry(self.backend, e) for e in entries]
cc3b928d 186
27066195
MT
187 def render(self, text, limit=0):
188 if limit and len(text) >= limit:
189 text = text[:limit] + "..."
a6dc0bad 190
27066195
MT
191 return textile.textile(text)
192
193 def _generate_slug(self, title):
194 slug = unicodedata.normalize("NFKD", title).encode("ascii", "ignore")
195 slug = re.sub(r"[^\w]+", " ", slug)
196 slug = "-".join(slug.lower().strip().split())
197
198 if not slug:
199 slug = "entry"
200
201 while True:
202 e = self.db.get("SELECT * FROM planet WHERE slug = %s", slug)
203 if not e:
204 break
205 slug += "-"
206
207 return slug
208
209 def update_entry(self, entry):
210 self.db.execute("UPDATE planet SET title = %s, markdown = %s WHERE id = %s",
211 entry.title, entry.markdown, entry.id)
212
213 def save_entry(self, entry):
214 slug = self._generate_slug(entry.title)
215
00af5cf1 216 id = self.db.execute("INSERT INTO planet(author_id, title, slug, markdown, published) "
27066195
MT
217 "VALUES(%s, %s, %s, %s, UTC_TIMESTAMP())", entry.author.uid, entry.title,
218 slug, entry.markdown)
219
00af5cf1
MT
220 return id
221
2bdd073f 222 def search(self, what):
a3e3c96d
MT
223 # Split tags.
224 tags = what.split()
225
8876f3df
MT
226 query = "SELECT planet.* FROM planet INNER JOIN ( \
227 SELECT post_id FROM planet_tags \
228 INNER JOIN planet ON planet_tags.post_id = planet.id \
229 WHERE %s GROUP BY post_id HAVING COUNT(post_id) = %%s \
230 ) pt ON planet.id = pt.post_id ORDER BY published DESC"
a3e3c96d 231
8876f3df 232 args = (tags, len(tags))
a3e3c96d 233
8876f3df 234 clauses, args = [], tags
a3e3c96d 235 for tag in tags:
8876f3df
MT
236 clauses.append("planet_tags.tag = %s")
237 args.append(len(tags))
2bdd073f 238
8876f3df 239 entries = self.db.query(query % " OR ".join(clauses), *args)
a6dc0bad 240 return [PlanetEntry(self.backend, e) for e in entries]
a3e3c96d
MT
241
242 def search_autocomplete(self, what):
243 tags = what.split()
244 last_tag = tags.pop()
245
246 res = self.db.query("SELECT tag, COUNT(tag) AS count FROM planet_tags \
247 WHERE tag LIKE %s GROUP BY tag ORDER BY count DESC", "%s%%" % last_tag)
248
249 if tags:
250 return ["%s %s" % (" ".join(tags), row.tag) for row in res]
251
252 return [row.tag for row in res]