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