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