]> git.ipfire.org Git - ipfire.org.git/blame - src/backend/wiki.py
wiki: Borrow markdown rendering from blog
[ipfire.org.git] / src / backend / wiki.py
CommitLineData
181d08f3
MT
1#!/usr/bin/python3
2
3import logging
6ac7e934 4import os.path
181d08f3
MT
5import re
6
7from . import misc
8from .decorators import *
9
181d08f3
MT
10class Wiki(misc.Object):
11 def _get_pages(self, query, *args):
12 res = self.db.query(query, *args)
13
14 for row in res:
15 yield Page(self.backend, row.id, data=row)
16
d398ca08
MT
17 def _get_page(self, query, *args):
18 res = self.db.get(query, *args)
19
20 if res:
21 return Page(self.backend, res.id, data=res)
22
6ac7e934
MT
23 def get_page_title(self, page, default=None):
24 doc = self.get_page(page)
25 if doc:
26 return doc.title
27
28 return default
29
181d08f3
MT
30 def get_page(self, page, revision=None):
31 page = Page.sanitise_page_name(page)
32 assert page
33
34 if revision:
d398ca08 35 return self._get_page("SELECT * FROM wiki WHERE page = %s \
181d08f3
MT
36 AND timestamp = %s", page, revision)
37 else:
d398ca08 38 return self._get_page("SELECT * FROM wiki WHERE page = %s \
181d08f3
MT
39 ORDER BY timestamp DESC LIMIT 1", page)
40
f9db574a 41 def get_recent_changes(self, limit=None):
181d08f3 42 return self._get_pages("SELECT * FROM wiki \
f9db574a
MT
43 WHERE timestamp >= NOW() - INTERVAL '4 weeks' \
44 ORDER BY timestamp DESC LIMIT %s", limit)
181d08f3 45
495e9dc4 46 def create_page(self, page, author, content, changes=None, address=None):
181d08f3
MT
47 page = Page.sanitise_page_name(page)
48
495e9dc4 49 return self._get_page("INSERT INTO wiki(page, author_uid, markdown, changes, address) \
df01767e 50 VALUES(%s, %s, %s, %s, %s) RETURNING *", page, author.uid, content or None, changes, address)
181d08f3 51
495e9dc4 52 def delete_page(self, page, author, **kwargs):
181d08f3
MT
53 # Do nothing if the page does not exist
54 if not self.get_page(page):
55 return
56
57 # Just creates a blank last version of the page
495e9dc4 58 self.create_page(page, author=author, content=None, **kwargs)
181d08f3
MT
59
60 @staticmethod
61 def _split_url(url):
62 parts = list(e for e in url.split("/") if e)
63
64 num_parts = len(parts)
65 for i in range(num_parts):
66 yield "/".join(parts[:i])
67
68 def make_breadcrumbs(self, url):
69 for part in self._split_url(url):
70 title = self.get_page_title(part, os.path.basename(part))
71
72 yield ("/%s" % part, title)
73
74
75class Page(misc.Object):
76 def init(self, id, data=None):
77 self.id = id
78 self.data = data
79
80 def __lt__(self, other):
81 if isinstance(other, self.__class__):
82 if self.page == other.page:
83 return self.timestamp < other.timestamp
84
85 return self.page < other.page
86
87 @staticmethod
88 def sanitise_page_name(page):
89 if not page:
90 return "/"
91
92 # Make sure that the page name does NOT end with a /
93 if page.endswith("/"):
94 page = page[:-1]
95
96 # Make sure the page name starts with a /
97 if not page.startswith("/"):
98 page = "/%s" % page
99
100 # Remove any double slashes
101 page = page.replace("//", "/")
102
103 return page
104
105 @property
106 def url(self):
db8448d9 107 return self.page
181d08f3
MT
108
109 @property
110 def page(self):
111 return self.data.page
112
113 @property
114 def title(self):
115 return self._title or self.page[1:]
116
117 @property
118 def _title(self):
119 if not self.markdown:
120 return
121
122 # Find first H1 headline in markdown
123 markdown = self.markdown.splitlines()
124
125 m = re.match(r"^# (.*)( #)?$", markdown[0])
126 if m:
127 return m.group(1)
128
3b05ef6e
MT
129 @lazy_property
130 def author(self):
131 if self.data.author_uid:
132 return self.backend.accounts.get_by_uid(self.data.author_uid)
133
181d08f3
MT
134 def _render(self, text):
135 logging.debug("Rendering %s" % self)
136
045ea3db
MT
137 # Borrow this from the blog
138 return self.backend.blog._render_text(text, lang="markdown")
181d08f3
MT
139
140 @property
141 def markdown(self):
142 return self.data.markdown
143
144 @property
145 def html(self):
146 return self.data.html or self._render(self.markdown)
147
148 @property
149 def timestamp(self):
150 return self.data.timestamp
151
152 def was_deleted(self):
153 return self.markdown is None
154
155 @lazy_property
156 def breadcrumbs(self):
157 return self.backend.wiki.make_breadcrumbs(self.page)
158
159 def get_latest_revision(self):
7d699684
MT
160 revisions = self.get_revisions()
161
162 # Return first object
163 for rev in revisions:
164 return rev
165
166 def get_revisions(self):
167 return self.backend.wiki._get_pages("SELECT * FROM wiki \
168 WHERE page = %s ORDER BY timestamp DESC", self.page)
091ac36b 169
d398ca08
MT
170 @property
171 def changes(self):
172 return self.data.changes
173
091ac36b
MT
174 # Sidebar
175
176 @lazy_property
177 def sidebar(self):
178 parts = self.page.split("/")
179
180 while parts:
181 sidebar = self.backend.wiki.get_page(os.path.join(*parts, "sidebar"))
182 if sidebar:
183 return sidebar
184
185 parts.pop()