]>
Commit | Line | Data |
---|---|---|
940227cb MT |
1 | #!/usr/bin/python |
2 | ||
67ab72b8 | 3 | import datetime |
27066195 | 4 | import re |
940227cb | 5 | import textile |
27066195 | 6 | import unicodedata |
940227cb | 7 | |
a6dc0bad | 8 | from misc import Object |
940227cb | 9 | |
a6dc0bad MT |
10 | class 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 | 144 | class 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] |