]> git.ipfire.org Git - ipfire.org.git/blame - src/backend/wiki.py
CSS: Make breadcrumb backround white
[ipfire.org.git] / src / backend / wiki.py
CommitLineData
181d08f3
MT
1#!/usr/bin/python3
2
3import logging
4import markdown2
6ac7e934 5import os.path
181d08f3
MT
6import re
7
8from . import misc
9from .decorators import *
10
11# Used to automatically link some things
12link_patterns = (
13 # Find bug reports
14 (re.compile(r"(?:#(\d+))", re.I), r"https://bugzilla.ipfire.org/show_bug.cgi?id=\1"),
15
16 # Email Addresses
17 (re.compile(r"([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)"), r"mailto:\1"),
18
19 # CVE Numbers
20 (re.compile(r"(?:CVE)[\s\-](\d{4}\-\d+)"), r"https://cve.mitre.org/cgi-bin/cvename.cgi?name=\1"),
21)
22
23class Wiki(misc.Object):
24 def _get_pages(self, query, *args):
25 res = self.db.query(query, *args)
26
27 for row in res:
28 yield Page(self.backend, row.id, data=row)
29
6ac7e934
MT
30 def get_page_title(self, page, default=None):
31 doc = self.get_page(page)
32 if doc:
33 return doc.title
34
35 return default
36
181d08f3
MT
37 def get_page(self, page, revision=None):
38 page = Page.sanitise_page_name(page)
39 assert page
40
41 if revision:
42 res = self.db.get("SELECT * FROM wiki WHERE page = %s \
43 AND timestamp = %s", page, revision)
44 else:
45 res = self.db.get("SELECT * FROM wiki WHERE page = %s \
46 ORDER BY timestamp DESC LIMIT 1", page)
47
48 if res:
49 return Page(self.backend, res.id, data=res)
50
51 def get_recent_changes(self):
52 return self._get_pages("SELECT * FROM wiki \
53 WHERE timestamp >= NOW() - INTERVAL '4 weeks' ORDER BY timestamp DESC")
54
55 def create_page(self, page, author, markdown):
56 page = Page.sanitise_page_name(page)
57
58 res = self.db.get("INSERT INTO wiki(page, author_id, markdown) \
59 VALUES(%s, %s, %s) RETURNING id", page, author.id, markdown)
60
61 if res:
62 return self.get_page_by_id(res.id)
63
64 def delete_page(self, page, author):
65 # Do nothing if the page does not exist
66 if not self.get_page(page):
67 return
68
69 # Just creates a blank last version of the page
70 self.create_page(page, author, None)
71
72 @staticmethod
73 def _split_url(url):
74 parts = list(e for e in url.split("/") if e)
75
76 num_parts = len(parts)
77 for i in range(num_parts):
78 yield "/".join(parts[:i])
79
80 def make_breadcrumbs(self, url):
81 for part in self._split_url(url):
82 title = self.get_page_title(part, os.path.basename(part))
83
84 yield ("/%s" % part, title)
85
86
87class Page(misc.Object):
88 def init(self, id, data=None):
89 self.id = id
90 self.data = data
91
92 def __lt__(self, other):
93 if isinstance(other, self.__class__):
94 if self.page == other.page:
95 return self.timestamp < other.timestamp
96
97 return self.page < other.page
98
99 @staticmethod
100 def sanitise_page_name(page):
101 if not page:
102 return "/"
103
104 # Make sure that the page name does NOT end with a /
105 if page.endswith("/"):
106 page = page[:-1]
107
108 # Make sure the page name starts with a /
109 if not page.startswith("/"):
110 page = "/%s" % page
111
112 # Remove any double slashes
113 page = page.replace("//", "/")
114
115 return page
116
117 @property
118 def url(self):
119 return "/%s" % self.page
120
121 @property
122 def page(self):
123 return self.data.page
124
125 @property
126 def title(self):
127 return self._title or self.page[1:]
128
129 @property
130 def _title(self):
131 if not self.markdown:
132 return
133
134 # Find first H1 headline in markdown
135 markdown = self.markdown.splitlines()
136
137 m = re.match(r"^# (.*)( #)?$", markdown[0])
138 if m:
139 return m.group(1)
140
141 def _render(self, text):
142 logging.debug("Rendering %s" % self)
143
144 return markdown2.markdown(text, link_patterns=link_patterns,
145 extras=["footnotes", "link-patterns", "wiki-tables"])
146
147 @property
148 def markdown(self):
149 return self.data.markdown
150
151 @property
152 def html(self):
153 return self.data.html or self._render(self.markdown)
154
155 @property
156 def timestamp(self):
157 return self.data.timestamp
158
159 def was_deleted(self):
160 return self.markdown is None
161
162 @lazy_property
163 def breadcrumbs(self):
164 return self.backend.wiki.make_breadcrumbs(self.page)
165
166 def get_latest_revision(self):
167 return self.backend.wiki.get_page(self.page)