diff --git a/trac/versioncontrol/api.py b/trac/versioncontrol/api.py
|
a
|
b
|
|
| 230 | 230 | return [] |
| 231 | 231 | |
| 232 | 232 | def get_changeset(self, rev): |
| 233 | | """Retrieve a Changeset corresponding to the given revision `rev`.""" |
| | 233 | """Retrieve a Changeset corresponding to the given revision `rev`.""" |
| 234 | 234 | raise NotImplementedError |
| 235 | 235 | |
| 236 | 236 | def get_changesets(self, start, stop): |
diff --git a/trac/versioncontrol/cache.py b/trac/versioncontrol/cache.py
|
a
|
b
|
|
| 19 | 19 | from datetime import datetime |
| 20 | 20 | |
| 21 | 21 | from trac.core import TracError |
| | 22 | from trac.util.compat import any |
| 22 | 23 | from trac.util.datefmt import utc, to_timestamp |
| 23 | 24 | from trac.util.translation import _ |
| 24 | 25 | from trac.versioncontrol import Changeset, Node, Repository, Authorizer, \ |
| … |
… |
|
| 56 | 57 | yield category, name, path, rev |
| 57 | 58 | |
| 58 | 59 | def get_changeset(self, rev): |
| 59 | | return CachedChangeset(self.repos, self.repos.normalize_rev(rev), |
| | 60 | cset = CachedChangeset(self.repos, self.repos.normalize_rev(rev), |
| 60 | 61 | self.getdb, self.authz) |
| | 62 | if rev == 0 or any(cset.get_changes()): |
| | 63 | return cset |
| | 64 | raise NoSuchChangeset(rev) |
| 61 | 65 | |
| 62 | 66 | def get_changesets(self, start, stop): |
| 63 | 67 | db = self.getdb() |
| … |
… |
|
| 68 | 72 | (to_timestamp(start), to_timestamp(stop))) |
| 69 | 73 | for rev, in cursor: |
| 70 | 74 | try: |
| 71 | | if self.authz.has_permission_for_changeset(rev): |
| 72 | | yield self.get_changeset(rev) |
| | 75 | yield self.get_changeset(rev) |
| 73 | 76 | except NoSuchChangeset: |
| 74 | | pass # skip changesets currently being resync'ed |
| | 77 | # Skip unauthorized changesets and changesets currently being |
| | 78 | # resync'ed |
| | 79 | pass |
| 75 | 80 | |
| 76 | 81 | def sync_changeset(self, rev): |
| 77 | 82 | cset = self.repos.get_changeset(rev) |
| … |
… |
|
| 270 | 275 | args.append(path) |
| 271 | 276 | sql += " OR " |
| 272 | 277 | # changes on path children |
| 273 | | sql += "path "+db.like() |
| | 278 | sql += "path " + db.like() |
| 274 | 279 | args.append(db.like_escape(path+'/') + '%') |
| 275 | 280 | sql += " OR " |
| 276 | 281 | # deletion of path ancestors |
| … |
… |
|
| 282 | 287 | sql += ")" |
| 283 | 288 | |
| 284 | 289 | sql += " ORDER BY " + db.cast('rev', 'int') + \ |
| 285 | | (direction == '<' and " DESC" or "") + " LIMIT 1" |
| | 290 | (direction == '<' and " DESC" or "") |
| 286 | 291 | |
| 287 | 292 | cursor = db.cursor() |
| 288 | 293 | cursor.execute(sql, args) |
| 289 | 294 | for rev, in cursor: |
| 290 | | return rev |
| | 295 | if self.authz.has_permission_for_changeset(rev): |
| | 296 | return rev |
| 291 | 297 | |
| 292 | 298 | def rev_older_than(self, rev1, rev2): |
| 293 | 299 | return self.repos.rev_older_than(rev1, rev2) |
diff --git a/trac/versioncontrol/svn_authz.py b/trac/versioncontrol/svn_authz.py
|
a
|
b
|
|
| 20 | 20 | |
| 21 | 21 | from trac.config import Option |
| 22 | 22 | from trac.core import * |
| 23 | | from trac.versioncontrol import Authorizer |
| | 23 | from trac.versioncontrol import Authorizer, NoSuchChangeset |
| 24 | 24 | |
| 25 | 25 | |
| 26 | 26 | class SvnAuthzOptions(Component): |
| … |
… |
|
| 105 | 105 | return 0 |
| 106 | 106 | |
| 107 | 107 | def has_permission_for_changeset(self, rev): |
| 108 | | changeset = self.repos.get_changeset(rev) |
| 109 | | for change in changeset.get_changes(): |
| 110 | | # the repository checks permissions for each change, so just check |
| 111 | | # if any changes can be accessed |
| | 108 | try: |
| | 109 | changeset = self.repos.get_changeset(rev) |
| 112 | 110 | return 1 |
| 113 | | return 0 |
| | 111 | except NoSuchChangeset: |
| | 112 | return 0 |
| 114 | 113 | |
| 115 | 114 | # Internal API |
| 116 | 115 | |
diff --git a/trac/versioncontrol/svn_fs.py b/trac/versioncontrol/svn_fs.py
|
a
|
b
|
|
| 61 | 61 | from trac.versioncontrol.svn_authz import SubversionAuthorizer |
| 62 | 62 | from trac.versioncontrol.web_ui.browser import IPropertyRenderer |
| 63 | 63 | from trac.util import sorted, embedded_numbers, reversed |
| | 64 | from trac.util.compat import any |
| 64 | 65 | from trac.util.text import exception_to_unicode, to_unicode |
| 65 | 66 | from trac.util.translation import _ |
| 66 | 67 | from trac.util.datefmt import utc |
| … |
… |
|
| 506 | 507 | |
| 507 | 508 | def get_changeset(self, rev): |
| 508 | 509 | rev = self.normalize_rev(rev) |
| 509 | | return SubversionChangeset(rev, self.authz, self.scope, |
| | 510 | cset = SubversionChangeset(rev, self.authz, self.scope, |
| 510 | 511 | self.fs_ptr, self.pool) |
| | 512 | if rev == 0 or any(cset.get_changes()): |
| | 513 | return cset |
| | 514 | raise NoSuchChangeset(rev) |
| | 515 | |
| | 516 | def get_changesets(self, start, stop): |
| | 517 | # Overrides Repository.get_changesets() to avoid calling |
| | 518 | # get_changeset() twice for every changeset |
| | 519 | rev = self.youngest_rev |
| | 520 | while rev: |
| | 521 | try: |
| | 522 | chgset = self.get_changeset(rev) |
| | 523 | if chgset.date < start: |
| | 524 | return |
| | 525 | if chgset.date < stop: |
| | 526 | yield chgset |
| | 527 | except NoSuchChangeset: |
| | 528 | pass |
| | 529 | rev = self.previous_rev(rev) |
| 511 | 530 | |
| 512 | 531 | def get_node(self, path, rev=None): |
| 513 | 532 | path = path or '' |
| … |
… |
|
| 544 | 563 | tmp1, tmp2 = tmp2, tmp1 |
| 545 | 564 | if history_ptr: |
| 546 | 565 | path_utf8, rev = fs.history_location(history_ptr, tmp2()) |
| | 566 | if not self.authz.has_permission_for_changeset(rev): |
| | 567 | continue |
| 547 | 568 | tmp2.clear() |
| 548 | 569 | if rev < end: |
| 549 | 570 | break |
| … |
… |
|
| 560 | 581 | return prev |
| 561 | 582 | return None |
| 562 | 583 | |
| 563 | | |
| 564 | 584 | def get_oldest_rev(self): |
| 565 | 585 | if self.oldest is None: |
| 566 | 586 | self.oldest = 1 |