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