Ticket #1242: xref_cs1343.diff
| File xref_cs1343.diff, 59.6 KB (added by cboos@…, 4 years ago) |
|---|
-
scripts/trac-admin
30 30 import sqlite 31 31 import StringIO 32 32 33 import trac.Environment # has to be imported before Xref 33 34 from trac import perm 34 35 from trac import util 35 36 from trac import sync 37 from trac import Xref 38 from trac import Href 36 39 import trac.siteconfig 37 import trac.Environment38 40 39 41 def my_sum(list): 40 42 """Python2.1 doesn't have sum()""" … … 272 274 docs = (self._help_about + self._help_help + 273 275 self._help_initenv + self._help_hotcopy + 274 276 self._help_resync + self._help_upgrade + 277 self._help_xref + 275 278 self._help_wiki + 276 279 # self._help_config + self._help_wiki + 277 280 self._help_permission + self._help_component + … … 542 545 print ' Installing wiki pages' 543 546 cursor = cnx.cursor() 544 547 self._do_wiki_load(trac.siteconfig.__default_wiki_dir__,cursor) 548 print ' Cross-referencing' 549 self.__env.href = Href('trac-admin::initenv') 550 self.__env._wiki_pages = {} 551 Xref.rebuild_cross_references(self.__env, cnx) 545 552 546 553 print ' Indexing repository' 547 sync.sync( cnx, rep, fs_ptr, pool)554 sync.sync(self.__env, cnx, rep, fs_ptr, pool) 548 555 except Exception, e: 549 556 print 'Failed to initialize database.', e 550 557 sys.exit(2) … … 592 599 pool = core.svn_pool_create(None) 593 600 594 601 self.db_open() # We need to call this function to open the env, really stupid 602 self.__env.href = Href.Href('trac-admin::resync') # We need this for the WikiFormatter 603 self.__env._wiki_pages = {} 595 604 596 605 # Remove any trailing slash or else subversion might abort 597 606 repository_dir = self.__env.get_config('trac', 'repository_dir') … … 605 614 print 'resyncing...' 606 615 self.db_execsql("DELETE FROM revision") 607 616 self.db_execsql("DELETE FROM node_change") 608 sync.sync( cnx, rep, fs_ptr, pool)617 sync.sync(self.__env, cnx, rep, fs_ptr, pool) 609 618 print 'done.' 610 619 620 _help_xref = [('xref', 'Regenerate all the cross-reference information between trac objects')] 621 622 ## XRef 623 def do_xref(self, line): 624 self.db_open() # We need to call this function to open the env, really stupid 625 cnx = self.__env.get_db_cnx() 626 print 'cross-referencing... (except changesets: use \'resync\' for that)' 627 self.__env.href = Href.Href('trac-admin::xref') 628 self.__env._wiki_pages = {} 629 Xref.rebuild_cross_references(self.__env, cnx, False) 630 print 'done.' 631 611 632 ## Wiki 612 633 _help_wiki = [('wiki list', 'List wiki pages'), 613 634 ('wiki remove <name>', 'Remove wiki page'), -
trac/core.py
40 40 'search' : ('Search', 'Search', 0), 41 41 'report' : ('Report', 'Report', 0), 42 42 'ticket' : ('Ticket', 'TicketModule', 0), 43 'bug' : ('Ticket', 'TicketModule', 0), 44 'issue' : ('Ticket', 'TicketModule', 0), 45 'source' : ('Browser', 'Browser', 1), 46 'repos' : ('Browser', 'Browser', 1), 43 47 'browser' : ('Browser', 'Browser', 1), 44 48 'timeline' : ('Timeline', 'Timeline', 1), 45 49 'changeset' : ('Changeset', 'Changeset', 1), … … 48 52 'attachment' : ('File', 'Attachment', 0), 49 53 'roadmap' : ('Roadmap', 'Roadmap', 0), 50 54 'settings' : ('Settings', 'Settings', 0), 55 'xref' : ('Xref', 'XrefModule', 0), 56 'orphans' : ('Xref', 'XrefModule', 0), 51 57 'milestone' : ('Milestone', 'Milestone', 0) 52 58 } 53 59 … … 74 80 pool, rep, fs_ptr = open_svn_repos(repos_dir) 75 81 module.repos = rep 76 82 module.fs_ptr = fs_ptr 77 sync.sync( db, rep, fs_ptr, pool)83 sync.sync(env, db, rep, fs_ptr, pool) 78 84 module.pool = pool 79 85 80 86 return module -
trac/db_default.py
21 21 22 22 23 23 # Database version identifier. Used for automatic upgrades. 24 db_version = 924 db_version = 10 25 25 26 26 def __mkreports(reports): 27 27 """Utility function used to create report data in same syntax as the … … 159 159 UNIQUE(sid,var_name) 160 160 ); 161 161 162 CREATE TABLE xref ( 163 src_type text, 164 src_id text, 165 relation text, 166 dest_type text, 167 dest_id text, 168 facet text, 169 context text 170 ); 171 172 162 173 CREATE INDEX node_change_idx ON node_change(rev); 163 174 CREATE INDEX ticket_change_idx ON ticket_change(ticket, time); 164 175 CREATE INDEX wiki_idx ON wiki(name,version); 165 176 CREATE INDEX session_idx ON session(sid,var_name); 177 CREATE INDEX xref_src_idx ON xref(src_id,src_type); 178 CREATE INDEX xref_dest_idx ON xref(dest_id,dest_type); 166 179 """ 167 180 168 181 ## -
trac/Milestone.py
24 24 from trac.Ticket import get_custom_fields, Ticket 25 25 from trac.WikiFormatter import wiki_to_html 26 26 from trac.util import * 27 from trac.Xref import TracRef 27 28 28 29 import time 29 30 … … 159 160 cursor.execute("INSERT INTO milestone (name,due,completed,description) " 160 161 "VALUES (%s,%s,%s,%s)", 161 162 (name, due, completed, description)) 163 TracRef('milestone', name).replace_xrefs_from_wiki(self.env, self.db, 'description', description) 162 164 self.db.commit() 163 165 req.redirect(self.env.href.milestone(name)) 164 166 … … 182 184 "WHERE milestone=%s", (id,)) 183 185 self.log.info('Deleting milestone %s' % id) 184 186 cursor.execute("DELETE FROM milestone WHERE name=%s", (id,)) 187 TracRef('milestone', id).delete_xrefs(self.db, 'description') 185 188 self.db.commit() 186 189 req.redirect(self.env.href.roadmap()) 187 190 else: … … 266 269 action = req.args.get('action', 'view') 267 270 id = req.args.get('id') 268 271 272 TracRef('milestone', id).add_backlinks(self.db, req) 273 269 274 if action == 'new': 270 275 self.perm.assert_permission(perm.MILESTONE_CREATE) 271 276 self.render_editor(req) -
trac/tests/href.py
94 94 self.assertEqual('/attachment/ticket/42/foo.txt?format=raw', 95 95 self.href.attachment('ticket', '42', 'foo.txt', 'raw')) 96 96 97 def test_xref(self): 98 """Testing Href.xref""" 99 self.assertEqual('/xref', self.href.xref()) 100 self.assertEqual('/xref/wiki', self.href.xref('wiki')) 101 self.assertEqual('/xref/wiki/WikiStart', self.href.xref('wiki', 'WikiStart')) 102 103 def test_orphans(self): 104 """Testing Href.orphans""" 105 self.assertEqual('/orphans', self.href.orphans()) 106 97 107 def suite(): 98 108 return unittest.makeSuite(HrefTestCase,'test') 99 109 -
trac/Report.py
22 22 from trac import perm, util 23 23 from trac.Module import Module 24 24 from trac.WikiFormatter import wiki_to_html 25 from trac.Xref import TracRef 25 26 26 27 import re 27 28 import time … … 113 114 cursor.execute("INSERT INTO report (title,sql,description) " 114 115 "VALUES (%s,%s,%s)", (title, sql, description)) 115 116 id = self.db.get_last_id() 117 TracRef('report', id).replace_xrefs_from_wiki(self.env, self.db, 'description', description) 116 118 self.db.commit() 117 119 req.redirect(self.env.href.report(id)) 118 120 … … 122 124 if not req.args.has_key('cancel'): 123 125 cursor = self.db.cursor () 124 126 cursor.execute("DELETE FROM report WHERE id=%s", (id,)) 127 TracRef('report', id).delete_xrefs(self.db, 'description') 125 128 self.db.commit() 126 129 req.redirect(self.env.href.report()) 127 130 else: … … 401 404 req.args.get('description', ''), 402 405 req.args.get('sql', '')) 403 406 407 if id != -1: 408 TracRef('report', id).add_backlinks(self.db, req) 409 404 410 if id != -1 or action == 'new': 405 411 self.add_link('up', self.env.href.report(), 'Available Reports') 406 412 -
trac/sync.py
20 20 # Author: Jonas Borgstr�jonas@edgewall.com> 21 21 22 22 from svn import fs, util, delta, repos, core 23 from trac.Xref import TracRef 23 24 24 25 import posixpath 25 26 26 27 27 def sync( db, repos, fs_ptr, pool):28 def sync(env, db, repos, fs_ptr, pool): 28 29 """ 29 30 Update the revision and node_change tables to be in sync with 30 31 the repository. … … 56 57 cursor.execute ('INSERT INTO revision (rev, time, author, message) ' 57 58 'VALUES (%s, %s, %s, %s)', rev + offset, date, 58 59 author, message) 60 TracRef('changeset', rev + offset).replace_xrefs_from_wiki(env, db, 'content', message) 59 61 insert_change (subpool, fs_ptr, rev + offset, cursor) 60 62 core.svn_pool_clear(subpool) 61 63 … … 125 127 old_path = posixpath.join(old_path, posixpath.split(path)[1]) 126 128 action = 'A' 127 129 else: 128 self._save_change(core.svn_node_ file, 'A', path)130 self._save_change(core.svn_node_dir, 'A', path) 129 131 action = None 130 132 131 133 if action: -
trac/File.py
25 25 from trac import perm, util 26 26 from trac.Module import Module 27 27 from trac.WikiFormatter import wiki_to_html 28 from trac.Xref import TracRef 28 29 29 30 import svn.core 30 31 import svn.fs … … 261 262 262 263 self.rev = req.args.get('rev', None) 263 264 self.path = req.args.get('path', '/') 265 266 TracRef('source', self.path).add_backlinks(self.db, req) 267 264 268 if not self.rev: 265 269 rev_specified = 0 266 270 self.rev = svn.fs.youngest_rev(self.fs_ptr, self.pool) -
trac/Log.py
22 22 from trac import perm, util 23 23 from trac.Module import Module 24 24 from trac.WikiFormatter import wiki_to_oneliner 25 from trac.Xref import TracRef 25 26 26 27 import svn.core 27 28 import svn.fs … … 121 122 122 123 self.path = req.args.get('path', '/') 123 124 self.authzperm.assert_permission(self.path) 125 126 TracRef('source', self.path).add_backlinks(self.db, req) 127 124 128 if req.args.has_key('rev'): 125 129 try: 126 130 rev = int(req.args.get('rev')) -
trac/upgrades/db10.py
1 import time 2 3 sql = """ 4 -- Initial creation of the general cross-reference table 5 CREATE TABLE xref ( 6 src_type text, 7 src_id text, 8 relation text, 9 dest_type text, 10 dest_id text, 11 facet text, 12 context text 13 ); 14 15 CREATE INDEX xref_src_idx ON xref(src_id,src_type); 16 CREATE INDEX xref_dest_idx ON xref(dest_id,dest_type); 17 """ 18 19 def do_upgrade(env, ver, cursor): 20 cursor.execute(sql) 21 22 def do_db_upgrade(env, ver, db): 23 """Renumbering of ticket comments (using the spare 'oldvalue' field)""" 24 cursor = db.cursor() 25 update_cursor = db.cursor() 26 cursor.execute("SELECT ticket, time, author FROM ticket_change " 27 "WHERE field = 'comment' " 28 "ORDER BY ticket, time, author ") 29 previous_ticket = None 30 for ticket, time, author in cursor: 31 if ticket != previous_ticket: 32 previous_ticket = ticket 33 n = 1 34 update_cursor.execute("UPDATE ticket_change SET " 35 "oldvalue = %s ", 36 (n)) 37 n += 1 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 -
trac/Href.py
56 56 else: 57 57 return href_join(self.base, 'browser', path) 58 58 59 def source(self, path, rev=None): 60 return self.browser(path, rev) 61 59 62 def login(self): 60 63 return href_join(self.base, 'login') 61 64 … … 176 179 if format: 177 180 href += '?format=%s' % format 178 181 return href 182 183 def xref(self, module=None, id=None): 184 if module and id: 185 href = href_join(self.base, 'xref', str(module), str(id)) 186 else: 187 href = href_join(self.base, 'xref') 188 return href 189 190 def orphans(self, module=None): 191 if module: 192 href = href_join(self.base, 'orphans', str(module)) 193 else: 194 href = href_join(self.base, 'orphans') 195 return href 196 -
trac/web/main.py
153 153 if match: 154 154 set_if_missing(args, 'mode', match.group(1)) 155 155 return args 156 match = re.search('^/(ticket| report)(?:/([0-9]+)/*)?', path_info)156 match = re.search('^/(ticket|bug|issue|report)(?:/([0-9]+)/*)?', path_info) 157 157 if match: 158 158 set_if_missing(args, 'mode', match.group(1)) 159 159 if match.group(2): 160 160 set_if_missing(args, 'id', match.group(2)) 161 161 return args 162 match = re.search('^/(browser| log|file)(?:(/.*))?', path_info)162 match = re.search('^/(browser|source|repos|log|file)(?:(/.*))?', path_info) 163 163 if match: 164 164 set_if_missing(args, 'mode', match.group(1)) 165 165 if match.group(2): … … 183 183 if match.group(1): 184 184 set_if_missing(args, 'id', urllib.unquote(match.group(1))) 185 185 return args 186 match = re.search('^/(xref|orphans)(?:/([^/]+))?(?:/(.*)/?)?', path_info) 187 if match: 188 set_if_missing(args, 'mode', match.group(1)) 189 set_if_missing(args, 'type', match.group(2)) 190 id = match.group(3) 191 if id: 192 set_if_missing(args, 'id', urllib.unquote(id)) 193 return args 186 194 return args 187 195 188 196 def populate_hdf(hdf, env, req=None): … … 208 216 'login': env.href.login(), 209 217 'logout': env.href.logout(), 210 218 'settings': env.href.settings(), 219 'xref': env.href.xref(), 220 'orphans': env.href.orphans(), 211 221 'homepage': 'http://trac.edgewall.com/' 212 222 } 213 223 -
trac/Changeset.py
24 24 from trac.Diff import get_diff_options, hdf_diff, unified_diff 25 25 from trac.Module import Module 26 26 from trac.WikiFormatter import wiki_to_html 27 from trac.Xref import TracRef 27 28 from trac import authzperm, perm 28 29 29 30 import svn.core … … 435 436 else: 436 437 self.rev = youngest_rev 437 438 439 TracRef('changeset', self.rev).add_backlinks(self.db, req) 440 438 441 self.diff_options = get_diff_options(req) 439 442 if req.args.has_key('update'): 440 443 req.redirect(self.env.href.changeset(self.rev)) … … 462 465 req.hdf['changeset.revision'] = self.rev 463 466 req.hdf['changeset.changes'] = change_info 464 467 req.hdf['changeset.href'] = self.env.href.changeset(self.rev) 465 468 466 469 if len(change_info) == 0: 467 470 raise authzperm.AuthzPermissionError() 468 471 -
trac/Wiki.py
24 24 from trac.Module import Module 25 25 from trac.util import escape, TracError, get_reporter_id 26 26 from trac.WikiFormatter import * 27 from trac.Xref import TracRef 27 28 28 29 import os 29 30 import time … … 51 52 Represents a wiki page (new or existing). 52 53 """ 53 54 54 def __init__(self, name, version, perm_, db): 55 self.db = db 55 def __init__(self, name, version, perm_, env, db): 56 self.env = env 57 self.db = db 56 58 self.name = name 57 59 self.perm = perm_ 58 60 cursor = self.db.cursor () … … 108 110 "%s,%s)", (self.name, self.version + 1, 109 111 int(time.time()), author, remote_addr, self.text, 110 112 comment, self.readonly)) 113 TracRef('wiki', self.name).replace_xrefs_from_wiki(self.env, self.db, 'content', self.text) 111 114 self.db.commit() 112 115 self.version += 1 113 116 self.old_readonly = self.readonly … … 126 129 req.hdf['wiki.page_name'] = escape(pagename) 127 130 req.hdf['wiki.current_href'] = escape(self.env.href.wiki(pagename)) 128 131 132 self.ref = TracRef('wiki', pagename) 133 self.ref.add_backlinks(self.db, req) 134 129 135 if action == 'diff': 130 136 version = int(req.args.get('version', 0)) 131 137 self._render_diff(req, pagename, version) … … 136 142 elif action == 'delete': 137 143 version = None 138 144 if req.args.has_key('delete_version'): 139 version = int(req.args ['version'])145 version = int(req.args.get('version')) 140 146 self._delete_page(req, pagename, version) 141 147 elif action == 'save': 142 148 if req.args.has_key('cancel'): … … 164 170 cursor = self.db.cursor() 165 171 cursor.execute("DELETE FROM wiki WHERE name=%s and version=%s", 166 172 (pagename, version)) 173 self.ref.delete_xrefs(self.db, 'comment:%s' % version) 167 174 self.log.info('Deleted version %d of page %s' % (version, pagename)) 168 175 cursor.execute("SELECT COUNT(*) FROM wiki WHERE name=%s", (pagename,)) 169 if not cursor.fetchone(): 176 last_version = cursor.fetchone()[0] 177 if last_version == 0: 170 178 page_deleted = 1 179 elif version > last_version: # resurrect the previous 'content' 180 cursor.execute("SELECT text FROM wiki WHERE name=%s ORDER BY version DESC", 181 (pagename)) 182 text = cursor.fetchone()[0] 183 self.ref.replace_xrefs_from_wiki(self.env, self.db, 'content', text) 171 184 else: # Delete a wiki page completely 172 185 cursor.execute("DELETE FROM wiki WHERE name=%s", (pagename,)) 173 186 page_deleted = 1 … … 175 188 self.db.commit() 176 189 177 190 if page_deleted: 191 self.ref.delete_xrefs(self.db) 178 192 # Delete orphaned attachments 179 193 for attachment in self.env.get_attachments(self.db, 'wiki', pagename): 180 194 self.env.delete_attachment(self.db, 'wiki', pagename, … … 233 247 def _render_editor(self, req, pagename, preview=0): 234 248 self.perm.assert_permission(perm.WIKI_MODIFY) 235 249 236 page = WikiPage(pagename, None, self.perm, self. db)250 page = WikiPage(pagename, None, self.perm, self.env, self.db) 237 251 if req.args.has_key('text'): 238 252 page.set_content(req.args.get('text')) 239 253 if preview: … … 311 325 self.add_link('alternate', '?format=txt', 'Plain Text', 312 326 'text/plain') 313 327 314 page = WikiPage(pagename, version, self.perm, self. db)328 page = WikiPage(pagename, version, self.perm, self.env, self.db) 315 329 316 330 info = { 317 331 'version': page.version, … … 331 345 def _save_page(self, req, pagename): 332 346 self.perm.assert_permission(perm.WIKI_MODIFY) 333 347 334 page = WikiPage(pagename, None, self.perm, self. db)348 page = WikiPage(pagename, None, self.perm, self.env, self.db) 335 349 if req.args.has_key('text'): 336 350 page.set_content(req.args.get('text')) 337 351 -
trac/Environment.py
23 23 # 24 24 25 25 from trac import db, db_default, Logging, Mimeview, util 26 from trac.Xref import TracRef 26 27 28 27 29 import ConfigParser 28 30 import os 29 31 import shutil … … 253 255 cursor.execute('INSERT INTO attachment VALUES(%s,%s,%s,%s,%s,%s,%s,%s)', 254 256 (type, id, filename, length, int(time.time()), 255 257 description, author, ipnr)) 258 TracRef(type, id).replace_xrefs_from_wiki(self, cnx, 'attachment:%s' % filename, description) 256 259 shutil.copyfileobj(attachment.file, fd) 257 260 self.log.info('New attachment: %s/%s/%s by %s', type, id, filename, author) 258 261 cnx.commit() … … 265 268 cursor = cnx.cursor() 266 269 cursor.execute('DELETE FROM attachment WHERE type=%s AND id=%s AND ' 267 270 'filename=%s', (type, id, filename)) 271 TracRef(type, id).delete_xrefs(cnx, 'attachment:%s' % filename) 268 272 os.unlink(path) 269 273 self.log.info('Attachment removed: %s/%s/%s', type, id, filename) 270 274 cnx.commit() … … 324 328 err = 'No upgrade module for version %i (%s.py)' % (i, upg) 325 329 raise EnvironmentError, err 326 330 d.do_upgrade(self, i, cursor) 331 if hasattr(d, 'do_db_upgrade'): # for more complex upgrades, as in 'db9.py' 332 d.do_db_upgrade(self, i, cnx) 327 333 cursor.execute("UPDATE system SET value=%s WHERE " 328 334 "name='database_version'", (db_default.db_version)) 329 335 self.log.info('Upgraded db version from %d to %d', -
trac/Xref.py
1 # -*- coding: iso8859-1 -*- 2 # 3 # Copyright (C) 2005 Edgewall Software 4 # Copyright (C) 2005 Christian Boos <cboos@wanadoo.fr> 5 # 6 # Trac is free software; you can redistribute it and/or 7 # modify it under the terms of the GNU General Public License as 8 # published by the Free Software Foundation; either version 2 of the 9 # License, or (at your option) any later version. 10 # 11 # Trac is distributed in the hope that it will be useful, 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 # General Public License for more details. 15 # 16 # You should have received a copy of the GNU General Public License 17 # along with this program; if not, write to the Free Software 18 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 # 20 # Author: Christian Boos <cboos@wanadoo.fr> 21 22 """ 23 24 This module implements a general cross-reference facility for Trac, 25 as suggested in #1242. 26 27 Currently, cross-references between the following Trac objects are supported: 28 * Wiki page 29 * Ticket 30 * Changeset 31 * Report 32 * Milestone 33 * Source (only as targets for now) 34 35 Basically, two kinds of references are supported: 36 * ''implicit references between objects'' 37 Implicit references are created for every TracLinks that can be found 38 in (any of) the wiki text of a Trac object. 39 Indeed, some objects may have separately editable wiki texts, 40 each of them being a ''facet'' of this object. 41 (TODO: generic fine grained anchoring: <object href>#<facet> should go to the facet) 42 * ''explicit relation between objects'' 43 An explicit relation must be created explicitely as such, 44 by some programmatic mean. 45 Currently, some of the ticket fields are setting up explicit relationships. 46 47 Note that implicit references always have an empty 'relation', 48 whereas explicit references may use the 'facet' for informative purpose. 49 50 """ 51 52 53 from trac.Module import Module 54 from trac.util import escape 55 from trac.WikiFormatter import XRefFormatter 56 57 __all__ = ['TracRef', 'rebuild_cross_references'] 58 59 60 how_much_context = 40 61 62 63 class TracRef: 64 """
