]>
Commit | Line | Data |
---|---|---|
940227cb MT |
1 | #!/usr/bin/python |
2 | ||
27066195 | 3 | import re |
940227cb | 4 | import textile |
27066195 MT |
5 | import tornado.database |
6 | import unicodedata | |
940227cb MT |
7 | |
8 | from accounts import Accounts | |
9 | from databases import Databases | |
10 | ||
a6dc0bad | 11 | from misc import Object |
940227cb | 12 | |
a6dc0bad MT |
13 | class 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 | 107 | class 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] |