Edgewall Software

TighterSubversionIntegration: svn-wiki-backend-patch

File svn-wiki-backend-patch, 17.0 kB (added by pscholl@…, 3 years ago)

patch to enable svn wiki backend

Line 
1diff -Naur trac_edgewall/trac/versioncontrol/api.py trac_teco/trac/versioncontrol/api.py
2--- trac_edgewall/trac/versioncontrol/api.py    2005-09-30 10:33:35.000000000 +0200
3+++ trac_teco/trac/versioncontrol/api.py        2005-10-01 13:24:03.000000000 +0200
4@@ -146,6 +146,17 @@
5         """
6         raise NotImplementedError
7 
8+    def set_content(self, content, commitmsg=None):
9+        """
10+        Set the content of the node. Add the commitmsg to the logs. If Node is
11+        of type DIRECTORY this method raise an Error.
12+       
13+        If the Node doesn't exist it's created.
14+
15+        Returns the revision Number that tags the commit.
16+        """
17+        raise NotImplementedError
18+
19     def get_entries(self):
20         """
21         Generator that yields the immediate child entries of a directory, in no
22@@ -233,7 +244,7 @@
23     access to view certain parts of a repository.
24     """
25 
26-    def assert_permission(self, path):
27+    def assert_permission(self, path, perm='r'):
28         if not self.has_permission(path):
29             raise PermissionDenied, \
30                   'Insufficient permissions to access %s' % path
31@@ -243,7 +254,7 @@
32             raise PermissionDenied, \
33                   'Insufficient permissions to access changeset %s' % rev
34 
35-    def has_permission(self, path):
36+    def has_permission(self, path, perm='r'):
37         return 1
38     
39     def has_permission_for_changeset(self, rev):
40diff -Naur trac_edgewall/trac/versioncontrol/svn_authz.py trac_teco/trac/versioncontrol/svn_authz.py
41--- trac_edgewall/trac/versioncontrol/svn_authz.py      2005-09-21 15:01:32.000000000 +0200
42+++ trac_teco/trac/versioncontrol/svn_authz.py  2005-10-01 13:24:03.000000000 +0200
43@@ -75,13 +75,13 @@
44                 return 1
45         return 0
46 
47-    def has_permission(self, path):
48+    def has_permission(self, path, perm='r'):
49         if path is None:
50             return 1
51 
52         for p in parent_iter(path):
53             if self.module_name:
54-                for perm in self.get_section(self.module_name + ':' + p):
55+                for perm in self.get_section(self.module_name + ':' + p, perm):
56                     if perm is not None:
57                         return perm
58             for perm in self.get_section(p):
59@@ -90,15 +90,15 @@
60 
61         return 0
62 
63-    def get_section(self, section):
64+    def get_section(self, section, perm='r'):
65         if not self.conf_authz.has_section(section):
66             return
67 
68-        yield self.get_permission(section, self.auth_name)
69+        yield self.get_permission(section, self.auth_name, perm)
70 
71         group_perm = None
72         for g in self.groups:
73-            p = self.get_permission(section, '@' + g)
74+            p = self.get_permission(section, '@' + g, perm)
75             if p is not None:
76                 group_perm = p
77 
78@@ -107,11 +107,11 @@
79 
80         yield group_perm
81 
82-        yield self.get_permission(section, '*')
83+        yield self.get_permission(section, '*', perm)
84 
85-    def get_permission(self, section, subject):
86+    def get_permission(self, section, subject, perm='r'):
87         if self.conf_authz.has_option(section, subject):
88-            return 'r' in self.conf_authz.get(section, subject)
89+            return perm in self.conf_authz.get(section, subject)
90         return None
91 
92     def has_permission_for_changeset(self, rev):
93diff -Naur trac_edgewall/trac/versioncontrol/svn_fs.py trac_teco/trac/versioncontrol/svn_fs.py
94--- trac_edgewall/trac/versioncontrol/svn_fs.py 2005-09-29 14:01:05.000000000 +0200
95+++ trac_teco/trac/versioncontrol/svn_fs.py     2005-10-01 13:57:31.000000000 +0200
96@@ -261,7 +261,10 @@
97         rev = self.normalize_rev(rev)
98 
99         return SubversionNode(path, rev, self.authz, self.scope, self.fs_ptr,
100-                              self.pool)
101+                              self.repos, self.pool)
102+
103+    def delete_node(self, path, commitmsg):
104+        return NotImplementedError
105 
106     def get_oldest_rev(self):
107         rev = 0
108@@ -352,10 +355,11 @@
109 
110 class SubversionNode(Node):
111 
112-    def __init__(self, path, rev, authz, scope, fs_ptr, pool=None):
113+    def __init__(self, path, rev, authz, scope, fs_ptr, repos, pool=None):
114+        self.repos = repos
115         self.authz = authz
116         self.scope = scope
117-        if scope != '/':
118+        if scope != '/' and path.find(scope) == -1:
119             self.scoped_path = scope + path
120         else:
121             self.scoped_path = path
122@@ -365,11 +369,17 @@
123 
124         self.root = fs.revision_root(fs_ptr, rev, self.pool())
125         node_type = fs.check_path(self.root, self.scoped_path, self.pool())
126-        if not node_type in _kindmap:
127+        if node_type == core.svn_node_none:
128+            self.created_rev = 0
129+            self.created_path = self.scoped_path
130+            self.rev = self.created_rev
131+            node_type = core.svn_node_file
132+        elif not node_type in _kindmap:
133             raise TracError, "No node at %s in revision %s" % (path, rev)
134-        self.created_rev = fs.node_created_rev(self.root, self.scoped_path,
135+        else:
136+            self.created_rev = fs.node_created_rev(self.root, self.scoped_path,
137                                                self.pool())
138-        self.created_path = fs.node_created_path(self.root, self.scoped_path,
139+            self.created_path = fs.node_created_path(self.root, self.scoped_path,
140                                                  self.pool())
141         # 'created_path' differs from 'path' if the last operation is a copy,
142         # and furthermore, 'path' might not exist at 'create_rev'
143@@ -382,6 +392,29 @@
144             return None
145         return core.Stream(fs.file_contents(self.root, self.scoped_path,
146                                             self.pool()))
147+    def set_content(self,content,commitmsg="changed by trac",uname=""):
148+        # this check is untested!
149+        self.authz.assert_permission(self.scoped_path, 'w')
150+
151+        # based on the putfile.py example of the subversion dist.
152+        # open a transaction against HEAD.
153+        rev = fs.youngest_rev(repos.fs(self.repos),self.pool())
154+        txn = repos.fs_begin_txn_for_commit(self.repos, rev, uname,
155+                                            commitmsg, self.pool())
156+
157+        root = fs.txn_root(txn,self.pool())
158+        kind = fs.check_path(root,self.scoped_path,self.pool())
159+
160+        if kind == core.svn_node_none:
161+            fs.make_file(root,self.scoped_path,self.pool())
162+        elif not kind == core.svn_node_file:
163+            raise TracError("Node is not a File.")
164+       
165+        handler, baton = fs.apply_textdelta(root,self.scoped_path,
166+                                            None, None, self.pool())
167+
168+        delta.svn_txdelta_send_string(content,handler,baton,self.pool())
169+        return repos.fs_commit_txn(self.repos,txn,self.pool())
170 
171     def get_entries(self):
172         if self.isfile:
173@@ -393,7 +426,7 @@
174             if not self.authz.has_permission(path):
175                 continue
176             yield SubversionNode(path, self._requested_rev, self.authz,
177-                                 self.scope, self.fs_ptr, self.pool)
178+                                 self.scope, self.fs_ptr, self.repos, self.pool)
179 
180     def get_history(self,limit=None):
181         newer = None # 'newer' is the previously seen history tuple
182diff -Naur trac_edgewall/trac/versioncontrol/tests/svn_fs.py trac_teco/trac/versioncontrol/tests/svn_fs.py
183--- trac_edgewall/trac/versioncontrol/tests/svn_fs.py   2005-09-29 13:41:44.000000000 +0200
184+++ trac_teco/trac/versioncontrol/tests/svn_fs.py       2005-10-01 12:06:11.000000000 +0200
185@@ -166,6 +166,34 @@
186         self.assertEqual('text/plain', node.content_type)
187         self.assertEqual('A test.\n', node.get_content().read())
188 
189+    def test_set_file_content(self):
190+        node = self.repos.get_node('/trunk/README.txt')
191+        node.set_content('This is the test Content\n', 'change.')
192+
193+        # reload the repository for the newly commited change.
194+        self.repos = SubversionRepository(REPOS_PATH, None,
195+                                          logger_factory('test'))
196+
197+        node = self.repos.get_node('/trunk/README.txt')
198+        chgset = self.repos.get_changeset(self.repos.get_youngest_rev())
199+
200+        self.assertEqual('This is the test Content\n',
201+            node.get_content().read())
202+        self.assertEqual('change.', chgset.message )
203+
204+    def test_delete_node(self):
205+        node = self.repos.delete_node('/trunk/README.txt', "delete.")
206+       
207+        self.repos = SubversionRepository(REPOS_PATH, None,
208+                                          logger_factory('test'))
209+
210+        node = self.repos.get_node('/trunk/README.txt')
211+        chgset = self.repos.get_changeset(self.repos.get_youngest_rev())
212+
213+        self.assertNotEqual(Node.DIRECTORY, node.kind)
214+        self.assertNotEqual(Node.FILE, node.kind)
215+        self.assertEqual('delete.', chgset.message )
216+
217     def test_get_dir_properties(self):
218         f = self.repos.get_node('/trunk')
219         props = f.get_properties()
220diff -Naur trac_edgewall/trac/wiki/api.py trac_teco/trac/wiki/api.py
221--- trac_edgewall/trac/wiki/api.py      2005-09-29 14:20:29.000000000 +0200
222+++ trac_teco/trac/wiki/api.py  2005-10-01 12:06:11.000000000 +0200
223@@ -28,6 +28,7 @@
224 from trac.core import *
225 from trac.util import to_utf8, TRUE
226 
227+from trac.wiki.model_svn import WikiPage
228 
229 class IWikiChangeListener(Interface):
230     """Extension point interface for components that should get notified about
231@@ -97,8 +98,9 @@
232                 cursor = db.cursor()
233                 cursor.execute("SELECT DISTINCT name FROM wiki")
234                 self._index = {}
235-                for (name,) in cursor:
236-                    self._index[name] = True
237+                for page in WikiPage.select(self.env):
238+                    self._index[page.name] = True
239+
240                 self._last_index_update = now
241         finally:
242             self._index_lock.release()
243diff -Naur trac_edgewall/trac/wiki/macros.py trac_teco/trac/wiki/macros.py
244--- trac_edgewall/trac/wiki/macros.py   2005-09-29 22:22:21.000000000 +0200
245+++ trac_teco/trac/wiki/macros.py       2005-10-01 12:06:11.000000000 +0200
246@@ -30,7 +30,7 @@
247 from trac.util import escape, format_date
248 from trac.env import IEnvironmentSetupParticipant
249 from trac.wiki.api import IWikiMacroProvider, WikiSystem
250-from trac.wiki.model import WikiPage
251+from trac.wiki.model_svn import WikiPage
252 
253 
254 class TitleIndexMacro(Component):
255@@ -303,7 +303,7 @@
256             else:
257                 raise Exception("%s module can't have attachments" % parts[0])
258         elif len(parts) == 2:
259-            from trac.versioncontrol.web_ui import BrowserModule
260+            from trac.versioncontrol.web_ui.browser import BrowserModule
261             try:
262                 browser_links = [link for link,_ in
263                                  BrowserModule(self.env).get_link_resolvers()]
264diff -Naur trac_edgewall/trac/wiki/model.py trac_teco/trac/wiki/model.py
265--- trac_edgewall/trac/wiki/model.py    2005-09-17 18:50:25.000000000 +0200
266+++ trac_teco/trac/wiki/model.py        2005-10-01 12:06:11.000000000 +0200
267@@ -146,3 +146,16 @@
268                        "ORDER BY version DESC", (self.name, self.version))
269         for version,time,author,comment,ipnr in cursor:
270             yield version,time,author,comment,ipnr
271+
272+    def select(cls, env, db=None):
273+        if not db:
274+            db = env.get_db_cnx()
275+
276+        cursor = db.cursor()
277+        cursor.execute("SELECT DISTINCT name FROM wiki")
278+        for (name,) in cursor:
279+            yield WikiPage(env,name)
280+
281+    select = classmethod(select)
282+
283+
284diff -Naur trac_edgewall/trac/wiki/model_svn.py trac_teco/trac/wiki/model_svn.py
285--- trac_edgewall/trac/wiki/model_svn.py        1970-01-01 01:00:00.000000000 +0100
286+++ trac_teco/trac/wiki/model_svn.py    2005-10-01 13:39:18.000000000 +0200
287@@ -0,0 +1,121 @@
288+# -*- coding: iso8859-1 -*-
289+#
290+# Copyright (C) 2003-2005 Edgewall Software
291+# Copyright (C) 2005 Philipp Scholl <pscholl@bawue.de>
292+# All rights reserved.
293+#
294+# This software is licensed as described in the file COPYING, which
295+# you should have received as part of this distribution. The terms
296+# are also available at http://trac.edgewall.com/license.html.
297+#
298+# This software consists of voluntary contributions made by many
299+# individuals. For the exact contribution history, see the revision
300+# history and logs, available at http://projects.edgewall.com/trac/.
301+#
302+# Author: Philipp Scholl <pscholl@bawue.de>
303+
304+from __future__ import generators
305+import time
306+
307+from trac.core import *
308+#from trac.wiki.api import WikiSystem
309+from trac.versioncontrol.api import Node
310+
311+
312+class WikiPage(object):
313+    """Represents a wiki page (new or existing)."""
314+
315+    def __init__(self, env, name=None, version=None, db=None):
316+        self.env = env
317+        self.root = env.config.get( 'wiki', 'svn_root' )
318+        if not env.get_repository().get_node(self.root).kind in (Node.DIRECTORY, Node.FILE):
319+            raise TracError( "wiki root couldn't be found!" )
320+        if self.root[:-1] != '/': self.root += '/'
321+        self.name = name
322+
323+        if name:
324+            self._fetch(name, version, db)
325+        else:
326+            self.version = 0
327+            self.text = ''
328+            self.readonly = 0
329+        self.old_text = self.text
330+        self.old_readonly = self.readonly
331+
332+    def _fetch(self, name, version=None, db=None):
333+        repos = self.env.get_repository()
334+        try:
335+            if version:
336+                node = repos.get_node(self.root+name,version)
337+                self.text = node.get_content().read()
338+                self.version = version
339+                self.readonly = 0
340+            else:
341+                node = repos.get_node(self.root+name)
342+                self.text = node.get_content().read()
343+                history = node.get_history()
344+                self.version = 0
345+                for path, rev, chg in history:
346+                    if self.version < rev: self.version = rev
347+                self.readonly = 0
348+        except Exception:
349+            self.version = 0
350+            self.text = ''
351+            self.readonly = 0
352+        except:
353+            repos.close()
354+
355+    exists = property(fget=lambda self: self.version > 0)
356+
357+    def delete(self, version=None, db=None):
358+        assert self.exists, 'Cannot delete non-existent page'
359+        if version:
360+            raise NotImplementedError( "deletion of versions not supported in \
361+            wiki_svn" )
362+        repos = self.env.get_repository()
363+        repos.delete_node(self.root+self.name)
364+
365+    def save(self, author, comment, remote_addr, t=None, db=None):
366+        repos = self.env.get_repository()
367+       
368+        # make sure that noone can escape the wiki root.
369+        if self.name.find( "..") != -1:
370+            raise TracError( ".. not allowed in wiki names." )
371+
372+        node = repos.get_node(self.root+self.name)
373+
374+        commitmsg  = self.name+" edited by "+author+" from "+remote_addr+"\n"
375+        commitmsg += comment
376+        self.version = node.set_content(self.text, commitmsg, author)
377+
378+        if t is None:
379+            t = time.time()
380+           
381+#        for listener in WikiSystem(self.env).change_listeners:
382+#            if self.version == repos.get_youngest_rev():
383+#                listener.wiki_page_added(self)
384+#            else:
385+#                listener.wiki_page_changed(self, self.version, t, author,
386+#                                           comment, remote_addr)
387+
388+    def get_history(self, db=None):
389+        repos = self.env.get_repository()
390+
391+        history = repos.get_path_history(self.root+self.name)
392+
393+        for path, rev, kind in history:
394+            chgset = repos.get_changeset(rev)
395+            yield rev,chgset.date,chgset.author,chgset.message,"missing"
396+
397+    def select(cls, env, db=None):
398+        if not 'get_repository' in dir(env):
399+            return
400+        repos = env.get_repository()
401+        dnode = repos.get_node( env.config.get('wiki', 'svn_root') )
402+       
403+        for node in dnode.get_entries():
404+            yield WikiPage(env,node.get_name())
405+
406+    select = classmethod(select)
407+
408+
409diff -Naur trac_edgewall/trac/wiki/web_ui.py trac_teco/trac/wiki/web_ui.py
410--- trac_edgewall/trac/wiki/web_ui.py   2005-09-22 10:28:10.000000000 +0200
411+++ trac_teco/trac/wiki/web_ui.py       2005-10-01 12:06:11.000000000 +0200
412@@ -1,7 +1,7 @@
413 # -*- coding: iso8859-1 -*-
414 #
415 # Copyright (C) 2003-2005 Edgewall Software
416-# Copyright (C) 2003-2005 Jonas Borgstr�jonas@edgewall.com>
417+# Copyright (C) 2003-2005 Jonas Borgstr� <jonas@edgewall.com>
418 # Copyright (C) 2004-2005 Christopher Lenz <cmlenz@gmx.de>
419 # All rights reserved.
420 #
421@@ -13,7 +13,7 @@
422 # individuals. For the exact contribution history, see the revision
423 # history and logs, available at http://projects.edgewall.com/trac/.
424 #
425-# Author: Jonas Borgstr�jonas@edgewall.com>
426+# Author: Jonas Borgstr� <jonas@edgewall.com>
427 #         Christopher Lenz <cmlenz@gmx.de>
428 
429 from __future__ import generators
430@@ -30,7 +30,7 @@
431 from trac.versioncontrol.diff import get_diff_options, hdf_diff
432 from trac.web.chrome import add_link, add_stylesheet, INavigationContributor
433 from trac.web import IRequestHandler
434-from trac.wiki.model import WikiPage
435+from trac.wiki.model_svn import WikiPage
436 from trac.wiki.formatter import wiki_to_html, wiki_to_oneliner
437 
438