1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 from __future__ import with_statement
20
21 from trac.core import *
22 from trac.resource import Resource
23 from trac.util.datefmt import datetime_now, from_utimestamp, to_utimestamp, utc
24 from trac.util.translation import _
25 from trac.wiki.api import WikiSystem, validate_page_name
26
27
28 -class WikiPage(object):
29 """Represents a wiki page (new or existing).
30
31 :since 1.0.3: the `ipnr` is deprecated and will be removed in 1.3.1
32 """
33
34 realm = 'wiki'
35
36 - def __init__(self, env, name=None, version=None, db=None):
37 self.env = env
38 if isinstance(name, Resource):
39 self.resource = name
40 name = self.resource.id
41 else:
42 if version:
43 version = int(version)
44 self.resource = Resource('wiki', name, version)
45 self.name = name
46 if name:
47 self._fetch(name, version, db)
48 else:
49 self.version = 0
50 self.text = self.comment = self.author = ''
51 self.time = None
52 self.readonly = 0
53 self.old_text = self.text
54 self.old_readonly = self.readonly
55
56 - def _fetch(self, name, version=None, db=None):
57 if version is not None:
58 sql = """SELECT version, time, author, text, comment, readonly
59 FROM wiki WHERE name=%s AND version=%s"""
60 args = (name, int(version))
61 else:
62 sql = """SELECT version, time, author, text, comment, readonly
63 FROM wiki WHERE name=%s ORDER BY version DESC LIMIT 1"""
64 args = (name,)
65 for version, time, author, text, comment, readonly in \
66 self.env.db_query(sql, args):
67 self.version = int(version)
68 self.author = author
69 self.time = from_utimestamp(time)
70 self.text = text
71 self.comment = comment
72 self.readonly = int(readonly) if readonly else 0
73 break
74 else:
75 self.version = 0
76 self.text = self.comment = self.author = ''
77 self.time = None
78 self.readonly = 0
79
80 exists = property(lambda self: self.version > 0)
81
82 - def delete(self, version=None, db=None):
83 """Delete one or all versions of a page.
84
85 :since 1.0: the `db` parameter is no longer needed and will be removed
86 in version 1.1.1
87 """
88 if not self.exists:
89 raise TracError(_("Cannot delete non-existent page"))
90
91 with self.env.db_transaction as db:
92 if version is None:
93
94 db("DELETE FROM wiki WHERE name=%s", (self.name,))
95 self.env.log.info("Deleted page %s", self.name)
96 else:
97
98 db("DELETE FROM wiki WHERE name=%s and version=%s",
99 (self.name, version))
100 self.env.log.info("Deleted version %d of page %s", version,
101 self.name)
102
103 if version is None or version == self.version:
104 self._fetch(self.name, None)
105
106 if not self.exists:
107
108 del WikiSystem(self.env).pages
109
110 from trac.attachment import Attachment
111 Attachment.delete_all(self.env, 'wiki', self.name)
112
113
114 if not self.exists:
115 for listener in WikiSystem(self.env).change_listeners:
116 listener.wiki_page_deleted(self)
117 else:
118 for listener in WikiSystem(self.env).change_listeners:
119 if hasattr(listener, 'wiki_page_version_deleted'):
120 listener.wiki_page_version_deleted(self)
121
122 - def save(self, author, comment, remote_addr=None, t=None, db=None):
123 """Save a new version of a page.
124
125 :since 1.0: the `db` parameter is no longer needed and will be removed
126 in version 1.1.1
127 :since 1.0.3: `remote_addr` is optional and deprecated, and will be
128 removed in 1.3.1
129 """
130 if not validate_page_name(self.name):
131 raise TracError(_("Invalid Wiki page name '%(name)s'",
132 name=self.name))
133
134 new_text = self.text != self.old_text
135 if not new_text and self.readonly == self.old_readonly:
136 raise TracError(_("Page not modified"))
137 t = t or datetime_now(utc)
138
139 with self.env.db_transaction as db:
140 if new_text:
141 db("""INSERT INTO wiki (name, version, time, author, ipnr,
142 text, comment, readonly)
143 VALUES (%s,%s,%s,%s,%s,%s,%s,%s)
144 """, (self.name, self.version + 1, to_utimestamp(t),
145 author, remote_addr, self.text, comment,
146 self.readonly))
147 self.version += 1
148 self.resource = self.resource(version=self.version)
149 else:
150 db("UPDATE wiki SET readonly=%s WHERE name=%s",
151 (self.readonly, self.name))
152 if self.version == 1:
153
154 del WikiSystem(self.env).pages
155
156 self.author = author
157 self.comment = comment
158 self.time = t
159
160 for listener in WikiSystem(self.env).change_listeners:
161 if self.version == 1:
162 listener.wiki_page_added(self)
163 else:
164 from trac.util import arity
165 if arity(listener.wiki_page_changed) == 6:
166 listener.wiki_page_changed(self, self.version, t,
167 comment, author, remote_addr)
168 else:
169 listener.wiki_page_changed(self, self.version, t,
170 comment, author)
171
172 self.old_readonly = self.readonly
173 self.old_text = self.text
174
175 - def rename(self, new_name):
176 """Rename wiki page in-place, keeping the history intact.
177 Renaming a page this way will eventually leave dangling references
178 to the old page - which literally doesn't exist anymore.
179 """
180 if not self.exists:
181 raise TracError(_("Cannot rename non-existent page"))
182
183 if not validate_page_name(new_name):
184 raise TracError(_("Invalid Wiki page name '%(name)s'",
185 name=new_name))
186 old_name = self.name
187
188 with self.env.db_transaction as db:
189 new_page = WikiPage(self.env, new_name)
190 if new_page.exists:
191 raise TracError(_("Can't rename to existing %(name)s page.",
192 name=new_name))
193
194 db("UPDATE wiki SET name=%s WHERE name=%s", (new_name, old_name))
195
196 del WikiSystem(self.env).pages
197
198 from trac.attachment import Attachment
199 Attachment.reparent_all(self.env, 'wiki', old_name, 'wiki',
200 new_name)
201
202 self.name = self.resource.id = new_name
203 self.env.log.info("Renamed page %s to %s", old_name, new_name)
204
205 for listener in WikiSystem(self.env).change_listeners:
206 if hasattr(listener, 'wiki_page_renamed'):
207 listener.wiki_page_renamed(self, old_name)
208
209 - def get_history(self, db=None):
210 """Retrieve the edit history of a wiki page.
211
212 :return: a tuple containing the `version`, `datetime`, `author`,
213 `comment` and `ipnr`.
214 :since 1.0: the `db` parameter is no longer needed and will be removed
215 in version 1.1.1
216 :since 1.0.3: use of `ipnr` is deprecated and will be removed in 1.3.1
217 """
218 for version, ts, author, comment, ipnr in self.env.db_query("""
219 SELECT version, time, author, comment, ipnr FROM wiki
220 WHERE name=%s AND version<=%s ORDER BY version DESC
221 """, (self.name, self.version)):
222 yield version, from_utimestamp(ts), author, comment, ipnr
223