Package trac :: Package wiki :: Module model

Source Code for Module trac.wiki.model

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright (C) 2003-2020 Edgewall Software 
  4  # Copyright (C) 2003-2005 Jonas Borgström <[email protected]> 
  5  # Copyright (C) 2005 Christopher Lenz <[email protected]> 
  6  # All rights reserved. 
  7  # 
  8  # This software is licensed as described in the file COPYING, which 
  9  # you should have received as part of this distribution. The terms 
 10  # are also available at https://trac.edgewall.org/wiki/TracLicense. 
 11  # 
 12  # This software consists of voluntary contributions made by many 
 13  # individuals. For the exact contribution history, see the revision 
 14  # history and logs, available at https://trac.edgewall.org/log/. 
 15  # 
 16  # Author: Jonas Borgström <[email protected]> 
 17  #         Christopher Lenz <[email protected]> 
 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
35 - def resource(self):
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 # The version attribute always returns the version of the page, 64 # however resource.version will be None when version hasn't been 65 # specified when creating the object and the object represents the 66 # most recent version of the page. This behavior is used in web_ui.py 67 # to determine whether to render a versioned page, or just the most 68 # recent version of the page. 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 # Delete a wiki page completely 122 db("DELETE FROM wiki WHERE name=%s", (self.name,)) 123 self.env.log.info("Deleted page %s", self.name) 124 else: 125 # Delete only a specific page version 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 # Invalidate page name cache 136 del WikiSystem(self.env).pages 137 # Delete orphaned attachments 138 from trac.attachment import Attachment 139 Attachment.delete_all(self.env, self.realm, self.name) 140 141 # Let change listeners know about the deletion 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 # Invalidate page name cache 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 # Invalidate page name cache 221 del WikiSystem(self.env).pages 222 # Reparent attachments 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
234 - def edit_comment(self, new_comment):
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