1
0
mirror of https://github.com/vitalif/viewvc-4intranet synced 2019-04-16 04:14:59 +03:00

Compare commits

...

41 Commits

Author SHA1 Message Date
cmpilato
6956afae73 Retroactively "peg" the svn:externals property on these tags which
pulls in the 'templates-contrib' area.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.0@2451 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-08 16:51:21 +00:00
cmpilato
80a5efb953 Tag the 1.1.0 final release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.0@2161 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 13:50:12 +00:00
cmpilato
8843044571 Tag the 1.1.0 final release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.0@2158 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 13:36:38 +00:00
cmpilato
8b426f6fb3 Merge from trunk r2152, recording 1.0.8's CHANGES here.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2153 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 17:26:56 +00:00
cmpilato
19ee8b32df Record a merge that effectively happened in r2147.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2148 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 17:09:57 +00:00
cmpilato
a5389019fa Bump copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2147 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 17:09:01 +00:00
cmpilato
7f272fbe04 Merge from trunk r2123 and r2124, the log message summary of which is:
Try to make ViewVC provide a consistent, predictable data dictionary
   in all of its templated views.  Do this with a custom, dictionary-like
   class that prevents the creation of new keys after an initial
   instantiation of keys and (possibly dummy) values.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2144 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 17:01:10 +00:00
cmpilato
89edf0ac04 Merge from trunk r2142, whose log message read thusly:
Finish issue #402: Split the 'use_pagesize' configuration directive
   into two: 'log_pagesize' and 'dir_pagesize', individually controlling
   the size of pages used (if at all) for the revision log and directory
   views, respectively.
   
   * conf/viewvc.conf.dist
     (use_pagesize): Removed, in favor of...
     (dir_pagesize, log_pagesize): ...these.
   
   * lib/viewvc.py,
   * lib/config.py,
   * bin/standalone.py
     Replace the use and tooling around 'use_pagesize' with
     'dir_pagesize' and 'log_pagesize' as appropriate.
   
   * templates/include/paging.ezt,
   * templates/include/dir_header.ezt
     Stop using the option value to determine whether or not to show
     pagination UI.  Use the presence of some pages instead.
     
   * docs/upgrading-howto.html
     Note the changes in options.
   
   Patch by: Lei Zhang <thestig{_AT_}google.com>
             (Tweaked by me.)

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2143 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 15:49:50 +00:00
cmpilato
71fbec8871 Merge from trunk r2140, whose log message read thusly:
Fix the GUI mode of standalone.py -- it wasn't even starting
   correctly, and we don't need a toggle for 'enscript' any more.
   
   * bin/standalone.py
     (GUI.__init__): Pass the configuration file to handle_config().
       Lose 'enscript'-related stuff.
     (GUI.toggle_use_enscript): Remove.
     (cli): Fix call to gui(), dropping bogus parameter.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2141 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 15:35:06 +00:00
cmpilato
504b1e1a8a Merge from trunk r2137, whose log message read thusly:
Fix issue #409, an exception thrown when sorting by revision in a
   remote Subversion directory view.
   
   * lib/vclib/svn/svn_ra.py
     (RemoteSubversionRepository.dirlogs): Store the entry's revision as
       a string, for consistency with other vclib modules.
   
   Reported by: Wojciech Wróblewski <wojtek{_AT_}elmi.pl>

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2139 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 14:59:32 +00:00
cmpilato
cf9381e0ee Backport to the 1.1.x branch trunk's r2134, whose log message read thusly:
Fix an exception in log views of Subversion repositories with 0
   revisions.
   
   * lib/vclib/svn/svn_repos.py
     (_get_history): Add easy-out for repositories with 0 revisions.
   
   Reported by: Wojciech Wróblewski <wojtek{_AT_}elmi.pl>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2135 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-04 14:58:46 +00:00
cmpilato
3907172131 Merge from trunk r2128-2129.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2130 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-04-20 17:41:32 +00:00
cmpilato
c34752ce92 Merge from trunk r2121, whose log message read thusly:
* templates/include/file_header.ezt
     Lose template reference of 'log_href_rev', which was removed in
     r2116 as unused.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2122 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-24 16:34:22 +00:00
cmpilato
40cc23f12a Merge from trunk r2119, whose log message read thusly:
Clutterkiller!  Put the default configuration files into a conf/
   subdirectory.
   
   * conf/
     New subdirectory.
   
   * cvsgraph.conf.dist,
   * mimetypes.conf.dist,
   * viewvc.conf.dist
     Move these from here ...
   
   * conf/cvsgraph.conf.dist,
   * conf/mimetypes.conf.dist,
   * conf/viewvc.conf.dist
     ... to here.
   
   * viewvc-install
     (FILE_INFO_LIST): Track new locations of configuration files.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2120 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-19 20:44:47 +00:00
cmpilato
9b7fea99d4 Merge from trunk a few relavent and recent changes (r2110:2117).
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2118 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-19 18:09:29 +00:00
cmpilato
85949a6464 Merge from trunk r2107, which updates copyright info only.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2108 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-18 16:45:10 +00:00
cmpilato
d5291a6b98 Merge from trunk r2103, whose log message read thusly:
Fix namespace problem with raised Exception.
   
   * lib/viewvc.py
     (view_revision): raise *debug.*ViewVCException.
   
   Patch by: Kamesh Jayachandran <kamesh@collab.net>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2104 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-18 12:52:24 +00:00
cmpilato
9e41d171c9 Merge from trunk r2101, whose log message read thusly:
Finish issue #401: Support MIME type overrides in ViewVC configuration.
   Trade the 'mime_types_file' option for 'mime_types_files' -- an
   ordered list of MIME mapping files to consult.  And provide our own
   (empty) mapping file that folks can use to override the mappings
   provided by other such files.
   
   * mimetypes.conf.dist
     New file.
   
   * viewvc.conf.dist
     (mime_types_files): Was mime_types_file, and now accepts multiple values.
   
   * lib/config.py
     (Config._force_multi_value): Add "mime_types_files" to the list of
       multi-value configuration options.
     (Config.set_defaults): Track rename of mime_types_file parameter,
       now setting the default to a list containing only "mimetypes.conf".
   
   * lib/viewvc.py
     (load_config): Track new name and format of mime_types_files option.
   
   * viewvc-install
     (FILE_INFO_LIST): Also install mimetypes.conf.dist as itself and as
       mimetypes.conf.
   
   * INSTALL
     (INSTALLING VIEWVC): Update reference to renamed configuration option.
   
   * docs/upgrading-howto.html
     Update this document.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2102 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-06 16:44:37 +00:00
cmpilato
da54e91dea Merge from trunk r2097, whose log message read thusly:
Finish issue #396 - Malformed accept-language header crashes viewvc
   
   * lib/accept.py
     (AcceptLanguageParseError): Was AcceptParseError.
     (_parse): Track renamed Exception.
   
   * lib/viewvc.py
     (Request.__init__): Catch and handle raised AcceptLanguageParseError.
   
   Patch by: Rune Halvorsen <runefh{_AT_}gmail.com>,
             me

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2099 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-26 16:15:55 +00:00
cmpilato
41b2ae2c7a Tweak a CHANGES entry for line length reduction.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2098 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-26 16:14:15 +00:00
cmpilato
1815220abe Note r2092 merge CHANGE.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2094 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-20 15:32:57 +00:00
cmpilato
a080d8eef4 Merge from trunk r2090, whose log message read thusly:
ViewVC doesn't "do" directory entry sorting by revision for CVS, so
   don't imply that it does in the UI.
   
   * lib/viewvc.py
     (view_directory): Don't provide sortby_rev_href to the template.
   
   * templates/directory.ezt
     Only offer sort links when those links are provided by ViewVC.
   
   * templates/dir_new.ezt
     Only offer sort links when those links are provided by ViewVC.  Use
     date-based sorting if rev-based sorting isn't available.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2092 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-20 15:24:39 +00:00
cmpilato
8c4af42d56 Merge from trunk r2088, whose log message read:
Remove dead code.
   
   * lib/popen.py
     (pipe_cmds, _copy): Remove as unused.
   
   * bin/standalone.py
     (StandaloneServer.run_viewvc): Tweak comment that referred to pipe_cmds().

   Found by: Rune Halvorsen <runeh {AT} sanedefaults.net>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2089 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-03 03:54:24 +00:00
cmpilato
7f1d0952fc Backport from trunk r2086, whose log message read thusly:
Fix issue #398 (File Log Viewer Shows Wrong Log Message) by asking the
   vclib layer for revision-sorted output rather than "whatever you want
   to give me".
   
   * lib/viewvc.py
     (markup_or_annotate): Pass vclib.SORTBY_REV instead of vclib.SORTBY_DEFAULT
       to the itemlog() interface.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2087 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-02 18:13:11 +00:00
cmpilato
222aa4ec1f Merge from trunk r2083.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2084 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 19:11:15 +00:00
cmpilato
c03bfd4b89 Merge from trunk r2081.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2082 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 18:52:33 +00:00
cmpilato
6fa8cdf117 On the 1.1.x branch: Merge from trunk r2078 and r2079.
* lib/cvsdb.py,
* bin/make-database


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2080 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 18:22:12 +00:00
cmpilato
c4c777ffe6 Backport from trunk r2070.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2071 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-19 19:49:13 +00:00
cmpilato
3680903ed4 Backport from trunk r2067 and r2068.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2069 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-19 19:46:55 +00:00
cmpilato
6fe61a11bb Merge from trunk r2063 and r2064.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2065 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-10 17:28:43 +00:00
cmpilato
437975652f Merge from trunk r2058, whose log message read thusly:
Allow admins to enable/disable diff/patch views via the allowed_views
   configuration bit.
   
   * lib/viewvc.py
     (view_diff, view_patch): Only show diffs if 'diff' is one of the
       allowed views.
   
   * lib/config.py
     (Config.set_defaults): Add 'diff' to the set of allowed_views (and
       sort the values alphabetically where here).
   
   * viewvc.conf.dist
     (allowed_views): Add 'diff' to the set of views you can specify, and
       include it in the default values.  While here, sort the values
       alphabetically.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2062 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-05 17:25:10 +00:00
cmpilato
7ff75bec7b Record recent merges to this branch.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2060 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-05 17:08:12 +00:00
cmpilato
ea03f20f46 Remove unnecessary empty mergeinfo.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2059 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-05 17:05:29 +00:00
cmpilato
a5df515d79 Merge from trunk r2047, whose log message read thusly:
Avoid returning huge lists of variables from get_file_view_info(), and
   use an ad-hoc class to hold all that stuff instead.
   
   * lib/viewvc.py
     (get_file_view_info): Now return an object with interesting
       member variables instead of a nasty long list of values in an
       easy-to-goof-up order.
     (common_template_data, view_directory, view_log, view_diff): Update
       uses of get_file_view_info to track new calling semantics.

Why?  Just trying to avoid unnecessary branch divergence.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2057 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-20 16:39:43 +00:00
cmpilato
fabe8e8a66 Merge from trunk r2055, whose log message read thusly:
Add a configuration option for telling ViewVC to *not* honor the
   svn:mime-type property.
   
   * lib/config.py
     (Config.set_defaults): Set options.svn_ignore_mimetype default value.
   
   * viewvc.conf.dist
     (svn_ignore_mimetype): New.
   
   * lib/viewvc.py
     (calculate_mime_type): Consult cfg.options.svn_ignore_mimetype
       before looking up the 'svn:mime-type' property.
   
   * docs/upgrading-howto.html
     Add a note about the new configuration option.
   
   Patch (mostly) by: JJ <eggsgloriouseggs {_AT_} gmail.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2056 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-13 21:45:08 +00:00
cmpilato
f01fcc4ec4 Merge from trunk r2053, whose log message read thusly:
It's very confusing to comment out the authorizer configuration line
   and have that mean "use an authorizer".  So let's just prevent that
   confusion right now, okay?
   
   * lib/config.py
     (Config.set_defaults): Set 'authorizer' by default to None.
   
   * viewvc.conf.dist
     (authorizer): Update comments for correctness, and don't imply that
       "forbidden" is the default authorizer.
   
   * docs/upgrading-howto.html
     Stop saying that "forbidden" is the default authorizer.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2054 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-13 21:22:05 +00:00
cmpilato
05e99f4c7b Merge from trunk r2051, whose log message read thusly:
Finish issue #384 - repos._revinfo_raw() is doing far too much work
   when no authz is in use.  Vastly improves performance of
   repos.itemlog() for local Subversion repositories *not* protected by
   an authorizer.
   
   * lib/vclib/svn/svn_repos.py
     (LocalSubversionRepository._revinfo): Was ._revinfo_raw().  Now
       encapsulates the caching logic that used to live in .revinfo(), plus
       the old logic of _revinfo_raw() modified to avoid changed-path
       processing when that processing isn't strictly required by callers.
     (LocalSubversionRepository.revinfo): Make this just a thin wrapper
       around ._revinfo() now.  Internal callers of this function updated
       to use ._revinfo() instead.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2052 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-13 21:16:25 +00:00
cmpilato
d8aff4e58f Merge r2048 from trunk.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2049 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-13 20:07:15 +00:00
cmpilato
e8d3d6ad89 docs/upgrading-howto.html: Don't claim as removed options not in 1.0.x.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2042 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-05 21:13:19 +00:00
cmpilato
1317088e3c * CHANGES: Combine some changes, and remove a now-bogus one.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2040 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-05 16:30:49 +00:00
cmpilato
fe81ee5969 Branch for 1.1 stabilization.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2033 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-10-30 17:31:36 +00:00
40 changed files with 1104 additions and 719 deletions

20
CHANGES
View File

@@ -1,4 +1,4 @@
Version 1.1.0 (released ??-???-????) Version 1.1.0 (released 13-May-2009)
* add support for full content diffs (issue #153) * add support for full content diffs (issue #153)
* make many more data dictionary items available to all views * make many more data dictionary items available to all views
@@ -12,9 +12,7 @@ Version 1.1.0 (released ??-???-????)
* add support for query by log message (issues #22, #121) * add support for query by log message (issues #22, #121)
* fix bug parsing 'svn blame' output with too-long author names (issue #221) * fix bug parsing 'svn blame' output with too-long author names (issue #221)
* fix default standalone.py port to be within private IANA range (issue #234) * fix default standalone.py port to be within private IANA range (issue #234)
* add support for integration with GNU source-highlight (issue #285) * add unified configury of allowed views; checkout view disabled by default
* add unified configury of allowed views
* add support for disabling the checkout view (now the default state)
* add support for ranges of revisions to svndbadmin (issue #224) * add support for ranges of revisions to svndbadmin (issue #224)
* make the query handling more forgiving of malformatted subdirs (issue #244) * make the query handling more forgiving of malformatted subdirs (issue #244)
* add support for per-root configuration overrides (issue #371) * add support for per-root configuration overrides (issue #371)
@@ -36,7 +34,19 @@ Version 1.1.0 (released ??-???-????)
* show RSS/query links only for roots found in commits database (issue #357) * show RSS/query links only for roots found in commits database (issue #357)
* recognize Subversion svn:mime-type property values (issue #364) * recognize Subversion svn:mime-type property values (issue #364)
* hide CVS files when viewing tags/branches on which they don't exist * hide CVS files when viewing tags/branches on which they don't exist
* add support for hiding errorful entries from the directory view (issue #105) * allow hiding of errorful entries from the directory view (issue #105)
* fix directory view sorting UI
* tolerate malformed Accept-Language headers (issue #396)
* allow MIME type mapping overrides in ViewVC configuration (issue #401)
* fix exception in rev-sorted remote Subversion directory views (issue #409)
* allow setting of page sizes for log and dir views individually (issue #402)
Version 1.0.8 (released 05-May-2009)
* fix directory view sorting UI
* tolerate malformed Accept-Language headers (issue #396)
* fix directory log views in revision-less Subversion repositories
* fix exception in rev-sorted remote Subversion directory views (issue #409)
Version 1.0.7 (released 14-Oct-2008) Version 1.0.7 (released 14-Oct-2008)

View File

@@ -139,7 +139,7 @@ installation instructions.
default_root default_root
root_as_url_component root_as_url_component
rcs_dir rcs_dir
mime_types_file mime_types_files
There are some other options that are usually nice to change. See There are some other options that are usually nice to change. See
viewvc.conf for more information. ViewVC provides a working, viewvc.conf for more information. ViewVC provides a working,

View File

@@ -15,7 +15,7 @@
<blockquote> <blockquote>
<p><strong>Copyright &copy; 1999-2008 The ViewCVS Group. All rights <p><strong>Copyright &copy; 1999-2009 The ViewCVS Group. All rights
reserved.</strong></p> reserved.</strong></p>
<p>By using ViewVC, you agree to the terms and conditions set forth <p>By using ViewVC, you agree to the terms and conditions set forth
@@ -59,6 +59,7 @@
<li>March 17, 2006 &mdash; software renamed from "ViewCVS"</li> <li>March 17, 2006 &mdash; software renamed from "ViewCVS"</li>
<li>April 10, 2007 &mdash; copyright years updated</li> <li>April 10, 2007 &mdash; copyright years updated</li>
<li>February 22, 2008 &mdash; copyright years updated</li> <li>February 22, 2008 &mdash; copyright years updated</li>
<li>March 18, 2009 &mdash; copyright years updated</li>
</ul> </ul>
</body> </body>

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -18,22 +18,22 @@
import os, sys, string import os, sys, string
import popen2 import popen2
import getopt
INTRO_TEXT = """\ ## ------------------------------------------------------------------------
This script creates the database and tables in MySQL used by the ## Stuff common to all schemas
ViewVC checkin database. You will be prompted for: database server ##
hostname, database user, database user password, and database name. DATABASE_SCRIPT_COMMON="""\
This script will use the 'mysql' program to create the database for
you. You will then need to set the appropriate parameters in the
[cvsdb] section of your viewvc.conf file.
"""
DATABASE_SCRIPT="""\
DROP DATABASE IF EXISTS <dbname>; DROP DATABASE IF EXISTS <dbname>;
CREATE DATABASE <dbname>; CREATE DATABASE <dbname>;
USE <dbname>; USE <dbname>;
"""
## ------------------------------------------------------------------------
## Version 0: The original, Bonsai-compatible schema.
##
DATABASE_SCRIPT_VERSION_0="""\
DROP TABLE IF EXISTS branches; DROP TABLE IF EXISTS branches;
CREATE TABLE branches ( CREATE TABLE branches (
id mediumint(9) NOT NULL auto_increment, id mediumint(9) NOT NULL auto_increment,
@@ -121,28 +121,213 @@ CREATE TABLE tags (
) TYPE=MyISAM; ) TYPE=MyISAM;
""" """
## ------------------------------------------------------------------------
## Version 1: Adds the 'metadata' table. Adds 'descid' index to
## 'checkins' table, and renames that table to 'commits'.
##
DATABASE_SCRIPT_VERSION_1="""\
DROP TABLE IF EXISTS branches;
CREATE TABLE branches (
id mediumint(9) NOT NULL auto_increment,
branch varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE branch (branch)
) TYPE=MyISAM;
DROP TABLE IF EXISTS commits;
CREATE TABLE commits (
type enum('Change','Add','Remove'),
ci_when datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
whoid mediumint(9) DEFAULT '0' NOT NULL,
repositoryid mediumint(9) DEFAULT '0' NOT NULL,
dirid mediumint(9) DEFAULT '0' NOT NULL,
fileid mediumint(9) DEFAULT '0' NOT NULL,
revision varchar(32) binary DEFAULT '' NOT NULL,
stickytag varchar(255) binary DEFAULT '' NOT NULL,
branchid mediumint(9) DEFAULT '0' NOT NULL,
addedlines int(11) DEFAULT '0' NOT NULL,
removedlines int(11) DEFAULT '0' NOT NULL,
descid mediumint(9),
UNIQUE repositoryid (repositoryid,dirid,fileid,revision),
KEY ci_when (ci_when),
KEY whoid (whoid),
KEY repositoryid_2 (repositoryid),
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid),
KEY descid (descid)
) TYPE=MyISAM;
DROP TABLE IF EXISTS descs;
CREATE TABLE descs (
id mediumint(9) NOT NULL auto_increment,
description text,
hash bigint(20) DEFAULT '0' NOT NULL,
PRIMARY KEY (id),
KEY hash (hash)
) TYPE=MyISAM;
DROP TABLE IF EXISTS dirs;
CREATE TABLE dirs (
id mediumint(9) NOT NULL auto_increment,
dir varchar(255) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE dir (dir)
) TYPE=MyISAM;
DROP TABLE IF EXISTS files;
CREATE TABLE files (
id mediumint(9) NOT NULL auto_increment,
file varchar(255) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE file (file)
) TYPE=MyISAM;
DROP TABLE IF EXISTS people;
CREATE TABLE people (
id mediumint(9) NOT NULL auto_increment,
who varchar(128) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE who (who)
) TYPE=MyISAM;
DROP TABLE IF EXISTS repositories;
CREATE TABLE repositories (
id mediumint(9) NOT NULL auto_increment,
repository varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE repository (repository)
) TYPE=MyISAM;
DROP TABLE IF EXISTS tags;
CREATE TABLE tags (
repositoryid mediumint(9) DEFAULT '0' NOT NULL,
branchid mediumint(9) DEFAULT '0' NOT NULL,
dirid mediumint(9) DEFAULT '0' NOT NULL,
fileid mediumint(9) DEFAULT '0' NOT NULL,
revision varchar(32) binary DEFAULT '' NOT NULL,
UNIQUE repositoryid (repositoryid,dirid,fileid,branchid,revision),
KEY repositoryid_2 (repositoryid),
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
) TYPE=MyISAM;
DROP TABLE IF EXISTS metadata;
CREATE TABLE metadata (
name varchar(255) binary DEFAULT '' NOT NULL,
value text,
PRIMARY KEY (name),
UNIQUE name (name)
) TYPE=MyISAM;
INSERT INTO metadata (name, value) VALUES ('version', '1');
"""
BONSAI_COMPAT="""
WARNING: Creating Bonsai-compatible legacy database version. Some ViewVC
features may not be available, or may not perform especially well.
"""
## ------------------------------------------------------------------------
def usage_and_exit(errmsg=None):
stream = errmsg is None and sys.stdout or sys.stderr
stream.write("""\
Usage: %s [OPTIONS]
This script creates the database and tables in MySQL used by the
ViewVC checkin database. In order to operate correctly, it needs to
know the following: your database server hostname, database user,
database user password, and database name. (You will be prompted for
any of this information that you do not provide via command-line
options.) This script will use the 'mysql' program to create the
database for you. You will then need to set the appropriate
parameters in the [cvsdb] section of your viewvc.conf file.
Options:
--dbname=ARG Use ARG as the ViewVC database name to create.
[Default: ViewVC]
--help Show this usage message.
--hostname=ARG Use ARG as the hostname for the MySQL connection.
[Default: localhost]
--password=ARG Use ARG as the password for the MySQL connection.
--username=ARG Use ARG as the username for the MySQL connection.
--version=ARG Create the database using the schema employed by
version ARG of ViewVC. Valid values are:
[ "1.0" ]
""" % (os.path.basename(sys.argv[0])))
if errmsg is not None:
stream.write("[ERROR] %s.\n" % (errmsg))
sys.exit(1)
sys.exit(0)
if __name__ == "__main__": if __name__ == "__main__":
try: try:
print INTRO_TEXT # Parse the command-line options, if any.
dbname = version = hostname = username = password = None
opts, args = getopt.getopt(sys.argv[1:], '', [ 'dbname=',
'help',
'hostname=',
'password=',
'username=',
'version=',
])
if len(args) > 0:
usage_and_exit("Unexpected command-line parameters")
for name, value in opts:
if name == '--help':
usage_and_exit(0)
elif name == '--dbname':
dbname = value
elif name == '--hostname':
hostname = value
elif name == '--username':
username = value
elif name == '--password':
password = value
elif name == '--version':
if value in ["1.0"]:
version = value
else:
usage_and_exit("Invalid version specified")
# Prompt for necessary information # Prompt for information not provided via command-line options.
host = raw_input("MySQL Hostname [default: localhost]: ") or "" if hostname is None:
user = raw_input("MySQL User: ") hostname = raw_input("MySQL Hostname [default: localhost]: ") or ""
passwd = raw_input("MySQL Password: ") if username is None:
dbase = raw_input("ViewVC Database Name [default: ViewVC]: ") or "ViewVC" username = raw_input("MySQL User: ")
if password is None:
password = raw_input("MySQL Password: ")
if dbname is None:
dbname = raw_input("ViewVC Database Name [default: ViewVC]: ") or "ViewVC"
# Create the database # Create the database
dscript = string.replace(DATABASE_SCRIPT, "<dbname>", dbase) dscript = string.replace(DATABASE_SCRIPT_COMMON, "<dbname>", dbname)
host_option = host and "--host=%s" % (host) or "" if version == "1.0":
print BONSAI_COMPAT
dscript = dscript + DATABASE_SCRIPT_VERSION_0
else:
dscript = dscript + DATABASE_SCRIPT_VERSION_1
host_option = hostname and "--host=%s" % (hostname) or ""
if sys.platform == "win32": if sys.platform == "win32":
cmd = "mysql --user=%s --password=%s %s "\ cmd = "mysql --user=%s --password=%s %s "\
% (user, passwd, host_option) % (username, password, host_option)
mysql = os.popen(cmd, "w") # popen2.Popen3 is not provided on windows mysql = os.popen(cmd, "w") # popen2.Popen3 is not provided on windows
mysql.write(dscript) mysql.write(dscript)
status = mysql.close() status = mysql.close()
else: else:
cmd = "{ mysql --user=%s --password=%s %s ; } 2>&1" \ cmd = "{ mysql --user=%s --password=%s %s ; } 2>&1" \
% (user, passwd, host_option) % (username, password, host_option)
pipes = popen2.Popen3(cmd) pipes = popen2.Popen3(cmd)
pipes.tochild.write(dscript) pipes.tochild.write(dscript)
pipes.tochild.close() pipes.tochild.close()
@@ -150,10 +335,11 @@ if __name__ == "__main__":
status = pipes.wait() status = pipes.wait()
if status: if status:
print "[ERROR] the database did not create sucessfully." print "[ERROR] The database did not create sucessfully."
sys.exit(1) sys.exit(1)
print "Database created successfully." print "Database created successfully. Don't forget to configure the "
print "[cvsdb] section of your viewvc.conf file."
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
sys.exit(0) sys.exit(0)

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -226,9 +226,16 @@ If this doesn't work, please click on the link above.
save_stdout = sys.stdout save_stdout = sys.stdout
save_stderr = sys.stderr save_stderr = sys.stderr
# For external tools like enscript we also need to redirect # For external tools like enscript we also need to redirect
# the real stdout file descriptor. (On windows, reassigning the # the real stdout file descriptor.
# sys.stdout variable is sufficient because pipe_cmds makes it #
# the standard output for child processes.) # FIXME: This code used to carry the following comment:
#
# (On windows, reassigning the sys.stdout variable is sufficient
# because pipe_cmds makes it the standard output for child
# processes.)
#
# But we no longer use pipe_cmds. So at the very least, the
# comment is stale. Is the code okay, though?
if sys.platform != "win32": save_realstdout = os.dup(1) if sys.platform != "win32": save_realstdout = os.dup(1)
try: try:
try: try:
@@ -375,7 +382,7 @@ def gui(host, port):
# Early loading of configuration here. Used to # Early loading of configuration here. Used to
# allow tinkering with configuration settings through the gui: # allow tinkering with configuration settings through the gui:
handle_config() handle_config(options.config_file)
if not LIBRARY_DIR: if not LIBRARY_DIR:
cfg.options.cvsgraph_conf = "../cgi/cvsgraph.conf.dist" cfg.options.cvsgraph_conf = "../cgi/cvsgraph.conf.dist"
@@ -389,14 +396,6 @@ def gui(host, port):
command=self.toggle_use_cvsgraph) command=self.toggle_use_cvsgraph)
self.cvsgraph_toggle.pack(side='top', anchor='w') self.cvsgraph_toggle.pack(side='top', anchor='w')
# enscript toggle:
self.enscript_ivar = Tkinter.IntVar()
self.enscript_ivar.set(cfg.options.use_enscript)
self.enscript_toggle = Tkinter.Checkbutton(self.options_frm,
text="enable enscript (needs binary)", var=self.enscript_ivar,
command=self.toggle_use_enscript)
self.enscript_toggle.pack(side='top', anchor='w')
# show_subdir_lastmod toggle: # show_subdir_lastmod toggle:
self.subdirmod_ivar = Tkinter.IntVar() self.subdirmod_ivar = Tkinter.IntVar()
self.subdirmod_ivar.set(cfg.options.show_subdir_lastmod) self.subdirmod_ivar.set(cfg.options.show_subdir_lastmod)
@@ -422,16 +421,27 @@ def gui(host, port):
command=self.toggle_use_localtime) command=self.toggle_use_localtime)
self.use_localtime_toggle.pack(side='top', anchor='w') self.use_localtime_toggle.pack(side='top', anchor='w')
# use_pagesize integer var: # log_pagesize integer var:
self.usepagesize_lbl = Tkinter.Label(self.options_frm, self.log_pagesize_lbl = Tkinter.Label(self.options_frm,
text='Paging (number of items per page, 0 disables):') text='Paging (number of items per log page, 0 disables):')
self.usepagesize_lbl.pack(side='top', anchor='w') self.log_pagesize_lbl.pack(side='top', anchor='w')
self.use_pagesize_ivar = Tkinter.IntVar() self.log_pagesize_ivar = Tkinter.IntVar()
self.use_pagesize_ivar.set(cfg.options.use_pagesize) self.log_pagesize_ivar.set(cfg.options.log_pagesize)
self.use_pagesize_entry = Tkinter.Entry(self.options_frm, self.log_pagesize_entry = Tkinter.Entry(self.options_frm,
width=10, textvariable=self.use_pagesize_ivar) width=10, textvariable=self.log_pagesize_ivar)
self.use_pagesize_entry.bind('<Return>', self.set_use_pagesize) self.log_pagesize_entry.bind('<Return>', self.set_log_pagesize)
self.use_pagesize_entry.pack(side='top', anchor='w') self.log_pagesize_entry.pack(side='top', anchor='w')
# dir_pagesize integer var:
self.dir_pagesize_lbl = Tkinter.Label(self.options_frm,
text='Paging (number of items per dir page, 0 disables):')
self.dir_pagesize_lbl.pack(side='top', anchor='w')
self.dir_pagesize_ivar = Tkinter.IntVar()
self.dir_pagesize_ivar.set(cfg.options.dir_pagesize)
self.dir_pagesize_entry = Tkinter.Entry(self.options_frm,
width=10, textvariable=self.dir_pagesize_ivar)
self.dir_pagesize_entry.bind('<Return>', self.set_dir_pagesize)
self.dir_pagesize_entry.pack(side='top', anchor='w')
# directory view template: # directory view template:
self.dirtemplate_lbl = Tkinter.Label(self.options_frm, self.dirtemplate_lbl = Tkinter.Label(self.options_frm,
@@ -507,9 +517,6 @@ def gui(host, port):
def toggle_use_cvsgraph(self, event=None): def toggle_use_cvsgraph(self, event=None):
cfg.options.use_cvsgraph = self.cvsgraph_ivar.get() cfg.options.use_cvsgraph = self.cvsgraph_ivar.get()
def toggle_use_enscript(self, event=None):
cfg.options.use_enscript = self.enscript_ivar.get()
def toggle_use_localtime(self, event=None): def toggle_use_localtime(self, event=None):
cfg.options.use_localtime = self.use_localtime_ivar.get() cfg.options.use_localtime = self.use_localtime_ivar.get()
@@ -519,8 +526,11 @@ def gui(host, port):
def toggle_useresearch(self, event=None): def toggle_useresearch(self, event=None):
cfg.options.use_re_search = self.useresearch_ivar.get() cfg.options.use_re_search = self.useresearch_ivar.get()
def set_use_pagesize(self, event=None): def set_log_pagesize(self, event=None):
cfg.options.use_pagesize = self.use_pagesize_ivar.get() cfg.options.log_pagesize = self.log_pagesize_ivar.get()
def set_dir_pagesize(self, event=None):
cfg.options.dir_pagesize = self.dir_pagesize_ivar.get()
def set_templates_log(self, event=None): def set_templates_log(self, event=None):
cfg.templates.log = self.logtemplate_svar.get() cfg.templates.log = self.logtemplate_svar.get()
@@ -614,7 +624,7 @@ def cli(argv):
if pid != 0: if pid != 0:
sys.exit() sys.exit()
if options.start_gui: if options.start_gui:
gui(options.host, options.port, options.config_file) gui(options.host, options.port)
return return
elif options.port: elif options.port:
def ready(server): def ready(server):

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 2004-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2004-2007 James Henstridge # Copyright (C) 2004-2007 James Henstridge
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in

32
conf/mimetypes.conf.dist Normal file
View File

@@ -0,0 +1,32 @@
#---------------------------------------------------------------------------
#
# MIME type mapping file for ViewVC
#
# Information on ViewVC is located at the following web site:
# http://viewvc.org/
#
#---------------------------------------------------------------------------
# THE FORMAT OF THIS FILE
#
# This file contains records -- one per line -- of the following format:
#
# MIME_TYPE [EXTENSION [EXTENSION ...]]
#
# where whitespace separates the MIME_TYPE from the EXTENSION(s),
# and the EXTENSIONs from each other.
#
# For example:
#
# text/x-csh csh
# text/x-csrc c
# text/x-diff diff patch
# image/png png
# image/jpeg jpeg jpg jpe
#
# By default, this file is left empty, allowing ViewVC to continue
# consulting it first without overriding the MIME type mappings
# found in more standard mapping files (such as those provided as
# part of the operating system or web server software).
#
#

View File

@@ -60,7 +60,7 @@
# default_root # default_root
# root_as_url_component # root_as_url_component
# rcs_dir # rcs_dir
# mime_types_file # mime_types_files
# the many options in the [utilities] section # the many options in the [utilities] section
# #
# It is usually desirable to change the following variables: # It is usually desirable to change the following variables:
@@ -127,13 +127,26 @@ cvs_roots = cvs: /home/cvsroot
default_root = cvs default_root = cvs
# #
# This is a pathname to a MIME types file to help viewvc to guess the # This is a list of pathnames to a set of MIME type mapping files to
# correct MIME type on checkout. If you are having problems with the # help ViewVC guess the correct MIME type of a versioned file. The
# default guess on the MIME type, then uncomment this option and point # pathnames listed here are specified in order of authoritativeness
# it at a MIME type file. # either as absolute paths or relative to this configuration file.
# #
# For example, you can use the mime.types provided by Apache here: # As a convenience, ViewVC provides a MIME type mapping file
#mime_types_file = /usr/local/apache2/conf/mime.types # (mimetypes.conf) which is, by default, the preferred provider of
# MIME type mapping answers, but which is also empty. If you find
# that ViewVC is unable to accurately guess MIME types based on the
# extensions of some of your versioned files, you can add records of
# your preferred mappings to the provided mimetypes.conf file (or to
# your system's mapping files, if you wish).
#
# You might, for example, wish to have ViewVC also consult the mapping
# files provided by your operating system and Apache.
#
# mime_types_files = mimetypes.conf,
# /etc/mime.types,
# /usr/local/apache2/conf/mime.types
mime_types_files = mimetypes.conf
# The address of the local repository maintainer. (This option is # The address of the local repository maintainer. (This option is
# provided only as a convenience for ViewVC installations which are # provided only as a convenience for ViewVC installations which are
@@ -266,8 +279,8 @@ checkout_magic = 0
# allowed_views: List the ViewVC views which are enabled. Views not # allowed_views: List the ViewVC views which are enabled. Views not
# in this comma-delited list will not be served (or, will return an # in this comma-delited list will not be served (or, will return an
# error on attempted access). # error on attempted access).
# Possible values: "tar", "annotate", "co", "markup", "roots" # Possible values: "annotate", "co", "diff", "markup", "roots", "tar"
allowed_views = markup, annotate, roots allowed_views = annotate, diff, markup, roots
# authorizer: The name of the ViewVC authorizer plugin to use when # authorizer: The name of the ViewVC authorizer plugin to use when
# authorizing access to repository contents. This value must be the # authorizing access to repository contents. This value must be the
@@ -286,9 +299,9 @@ allowed_views = markup, annotate, roots
# NOTE: Only one authorizer may be in use for a given ViewVC request. # NOTE: Only one authorizer may be in use for a given ViewVC request.
# It doesn't matter if you configure the parameters of multiple # It doesn't matter if you configure the parameters of multiple
# authorizer plugins -- only the authorizer whose name is configured # authorizer plugins -- only the authorizer whose name is configured
# here (or effectively configured here via vhost configuration) will # here (or effectively configured here via per-vhost or per-root
# be activated. # configuration) will be activated.
authorizer = forbidden authorizer =
# hide_cvsroot: Don't show the CVSROOT directory # hide_cvsroot: Don't show the CVSROOT directory
# 1 Hide CVSROOT directory # 1 Hide CVSROOT directory
@@ -332,6 +345,13 @@ http_expiration_time = 600
# 0 Don't generate Etags # 0 Don't generate Etags
generate_etags = 1 generate_etags = 1
# Don't use the svn:mime-type property to determine how to display a
# file in the markup view. This is especially helpful when versioned
# images carry the default Subversion-calculated MIME type of
# "application/octet-stream" (which isn't recognized as viewable type
# by browsers).
svn_ignore_mimetype = 0
# svn_config_dir: Path of the Subversion runtime configuration # svn_config_dir: Path of the Subversion runtime configuration
# directory ViewVC should consult for various things, including cached # directory ViewVC should consult for various things, including cached
# remote authentication credentials. If unset, Subversion will use # remote authentication credentials. If unset, Subversion will use
@@ -506,13 +526,22 @@ use_re_search = 0
# use_re_search = 1 # use_re_search = 1
# #
# Split directories and logs into pages. # Split directory listings across pages as needed. Allows ViewVC to
# Allows ViewVC to present discrete pages to the users instead of the # present discrete pages to the users instead of the entire directory.
# entire log or directory. # Set dir_pagesize to the number of entries you want displayed on a page,
# Set use_pagesize to the number of entries you want displayed on a page. # or 0 to disable pagination.
# #
use_pagesize = 0 dir_pagesize = 0
# use_pagesize = 20 # dir_pagesize = 20
#
# Split revision log listings across multiple pages as needed. Allows
# ViewVC to present discrete pages to the users instead of the entire log.
# Set log_pagesize to the number of entries you want displayed on a page,
# or 0 to disable pagination.
#
log_pagesize = 0
# log_pagesize = 20
# Limit number of changed paths shown per commit in the Subversion revision # Limit number of changed paths shown per commit in the Subversion revision
# view and in query results. This is not a hard limit (the UI provides # view and in query results. This is not a hard limit (the UI provides

View File

@@ -161,12 +161,6 @@ td {
resource. Valid only when <var>pathtype</var> is <tt>file</tt> resource. Valid only when <var>pathtype</var> is <tt>file</tt>
or (for Subversion roots) <tt>dir</tt>.</td> or (for Subversion roots) <tt>dir</tt>.</td>
</tr> </tr>
<tr class="varlevel1">
<td class="varname">log_rev_href</td>
<td>String</td>
<td>Revision number of the file-revision currently being viewed, or
None.</td>
</tr>
<tr class="varlevel1"> <tr class="varlevel1">
<td class="varname">nav_path</td> <td class="varname">nav_path</td>
<td>List</td> <td>List</td>
@@ -450,10 +444,10 @@ td {
<tr class="varlevel1"> <tr class="varlevel1">
<td class="varname">annotation</td> <td class="varname">annotation</td>
<td>String</td> <td>String</td>
<td>If set, indicates that annotations were requested. Valid values <td>Valid values are "none" (no annotations were attempted),
are "annotated" (annotation was successful), "binary" (file contents "annotated" (annotation was successful), "binary" (file contents
are not line-based and human-readable), and "error" (something went are not line-based and human-readable), and "error" (something
wrong during annotation).</td> went wrong during annotation).</td>
</tr> </tr>
<tr class="varlevel1"> <tr class="varlevel1">
<td class="varname">author</td> <td class="varname">author</td>
@@ -1087,18 +1081,11 @@ td {
<td>String</td> <td>String</td>
<td>Current search expression, if any.</td> <td>Current search expression, if any.</td>
</tr> </tr>
<tr class="varlevel1">
<td class="varname">search_re_form</td>
<td>Boolean</td>
<td>Indicates whether or not to display the regular expression search
form. Value depends on the whether searching is enabled in the
configuration and whether or not the current directory is
empty.</td>
</tr>
<tr class="varlevel1"> <tr class="varlevel1">
<td class="varname">search_re_action</td> <td class="varname">search_re_action</td>
<td>String</td> <td>String</td>
<td>Form action URL for the regular expression search form.</td> <td>Form action URL for the regular expression search form,
if searching is available.</td>
</tr> </tr>
<tr class="varlevel1"> <tr class="varlevel1">
<td class="varname">search_re_hidden_values</td> <td class="varname">search_re_hidden_values</td>
@@ -2073,6 +2060,11 @@ td {
<td>String</td> <td>String</td>
<td>URL for the current view but with <tt>limit_changes</tt> disabled.</td> <td>URL for the current view but with <tt>limit_changes</tt> disabled.</td>
</tr> </tr>
<tr class="varlevel1">
<td class="varname">num_changes</td>
<td>String</td>
<td>Number of paths changed in this revision.</td>
</tr>
<tr class="varlevel1"> <tr class="varlevel1">
<td class="varname">next_href</td> <td class="varname">next_href</td>
<td>String</td> <td>String</td>

View File

@@ -137,9 +137,8 @@ td {
configuration's "general" section.</li> configuration's "general" section.</li>
<li>Finally, ensure that that the new <code>authorizer</code> <li>Finally, ensure that that the new <code>authorizer</code>
option is set to either "forbidden" (which is the default) or option is set to either "forbidden" or "forbiddenre", depending
"forbiddenre", depending on which of those you were using in on which of those you were using in ViewVC 1.0.x.</li>
ViewVC 1.0.x.</li>
</ol> </ol>
@@ -215,10 +214,9 @@ td {
<li>options/py2html_path</li> <li>options/py2html_path</li>
<li>options/use_enscript</li> <li>options/use_enscript</li>
<li>options/use_highlight</li> <li>options/use_highlight</li>
<li>options/use_pagesize</li>
<li>options/use_php</li> <li>options/use_php</li>
<li>options/use_py2html</li> <li>options/use_py2html</li>
<li>options/use_pygments</li>
<li>options/use_source_highlight</li>
</ul> </ul>
</div> </div>
@@ -226,16 +224,23 @@ td {
<div class="h3"> <div class="h3">
<h3>Checkin Database</h3> <h3>Checkin Database</h3>
<p>In ViewVC 1.1, the <code>svndbadmin</code> program's "rebuild" <p>ViewVC 1.1 introduces to the <code>cvsdbadmin</code>
subcommand has had its purpose become more defined. It no longer and <code>svndbadmin</code> tools a new "purge" operation, which
accepts a revision argument, and therefore can now only be used to allows you to remove all the information related to a given root
completely rebuild the entirety of the checkin database information from your checkins database (without disturbing the information
for a Subversion repository (instead of being able to only update associated with other roots). Likewise, the "rebuild" command in
the information related to single Subversion revision). For those tools now implies a "purge" followed by an update.</p>
per-revision updating, use <code>svndbadmin update</code> and
<p>As a related change, the <code>svndbadmin</code> program's
"rebuild" subcommand has had its purpose become more defined. It
no longer accepts a revision argument, and therefore can now only
be used to completely rebuild the entirety of the checkin database
information for a Subversion repository (instead of being able to
only update the information related to single Subversion revision).
For per-revision updating, use <code>svndbadmin update</code> and
provide a revision (or revision range). And to get the previous provide a revision (or revision range). And to get the previous
rebuild-a-revision effect, pass the new <code>--force</code> rebuild-a-revision effect, pass the new <code>--force</code> option
option to <code>svndbadmin update</code>.</p> to <code>svndbadmin update</code>.</p>
<p>In other words, where you once did this:</p> <p>In other words, where you once did this:</p>
@@ -249,6 +254,19 @@ td {
</pre> </pre>
</blockquote> </blockquote>
<p>To enhance the performance of the new "purge" operation, ViewVC 1.1
introduces some slight changes to the checkin database schema. If
you use the <code>make-database</code> tool to (re)create your
checkins database, it will by default employ the new database
schema. This should cause the database to be virtually unusable by
previous versions of ViewVC, and that's by design. If, however,
you need to (re)create your checkins database and you require
compatibility with previous versions of ViewVC or ViewCVS, simply
pass the "--version=1.0" option to the <code>make-database</code>
script. Note that your "purge" and "rebuild" operations could be
abysmally slow, though, as that version of the database schema is
not optimized for those operations.</p>
</div> </div>
<div class="h3"> <div class="h3">
@@ -296,6 +314,22 @@ td {
<li>options/allow_tar</li> <li>options/allow_tar</li>
</ul> </ul>
<p>ViewVC now honors the "svn:mime-type" property stored on
Subversion-versioned files as the primary source of MIME type
determination (before falling back to name-based MIME mappings and
such). However, this can negatively affect the viewability of
certain files &mdash; especially images &mdash; whose
"svn:mime-type" properties are set incorrectly, such as will happen
if Subversion itself merely determines that the file isn't
human-readable and uses the "application/octet-stream" MIME type to
record this determination. To make ViewVC <em>not</em> honor the
"svn:mime-type" property value, set the <code>svn_ignore_mimetype</code>
configuration option.</p>
<p>Speaking of MIME types, the option <code>mime_types_file</code> is
now <code>mime_types_files</code>, as it now carries multiple paths
to MIME mappings files, ordered by preference.</p>
<p>The <code>use_rcsparse</code> option was moved from the "general" <p>The <code>use_rcsparse</code> option was moved from the "general"
section to the "options" section.</p> section to the "options" section.</p>
@@ -631,6 +665,11 @@ allow_tar = 1
<td>revision.ezt</td> <td>revision.ezt</td>
<td>now is an iterable list of objects with .name and .value attributes</td> <td>now is an iterable list of objects with .name and .value attributes</td>
</tr> </tr>
<tr class="added">
<td class="varname">num_changes</td>
<td>revision.ezt</td>
<td>added</td>
</tr>
</tbody> </tbody>
</table> </table>
@@ -659,7 +698,7 @@ allow_tar = 1
<h3>Checkin Database</h3> <h3>Checkin Database</h3>
<p>ViewVC 1.0 reads and writes commit times in the MySQL database in <p>ViewVC 1.0 reads and writes commit times in the MySQL database in
UTC time rather than local time. This can cause times displayed on UTC time rather than local time. This can cause times displayed on
the query page to be a few hours off if an old database is being the query page to be a few hours off if an old database is being
used with a new version of ViewVC. The best way to fix this is to used with a new version of ViewVC. The best way to fix this is to
rebuild the database with the new version of cvsdbadmin, but it rebuild the database with the new version of cvsdbadmin, but it
@@ -702,7 +741,8 @@ allow_tar = 1
<li>options/root_as_url_component</li> <li>options/root_as_url_component</li>
<li>options/default_file_view</li> <li>options/default_file_view</li>
<li>options/sort_group_dirs</li> <li>options/sort_group_dirs</li>
<li>options/use_pagesize</li> <li>options/dir_pagesize</li>
<li>options/log_pagesize</li>
<li>options/limit_changes</li> <li>options/limit_changes</li>
<li>options/use_localtime</li> <li>options/use_localtime</li>
<li>options/cross_copies</li> <li>options/cross_copies</li>

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -39,7 +39,7 @@ def _parse(hdr, result):
while pos < len(hdr): while pos < len(hdr):
name = _re_token.match(hdr, pos) name = _re_token.match(hdr, pos)
if not name: if not name:
raise AcceptParseError() raise AcceptLanguageParseError()
a = result.item_class(string.lower(name.group(1))) a = result.item_class(string.lower(name.group(1)))
pos = name.end() pos = name.end()
while 1: while 1:
@@ -210,7 +210,7 @@ class _LanguageSelector:
def append(self, item): def append(self, item):
self.requested.append(item) self.requested.append(item)
class AcceptParseError(Exception): class AcceptLanguageParseError(Exception):
pass pass
def _test(): def _test():

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org> # Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -40,7 +40,7 @@ import fnmatch
class Config: class Config:
_sections = ('general', 'utilities', 'options', 'cvsdb', 'templates') _sections = ('general', 'utilities', 'options', 'cvsdb', 'templates')
_force_multi_value = ('cvs_roots', 'svn_roots', 'languages', 'kv_files', _force_multi_value = ('cvs_roots', 'svn_roots', 'languages', 'kv_files',
'root_parents', 'allowed_views') 'root_parents', 'allowed_views', 'mime_types_files')
def __init__(self): def __init__(self):
for section in self._sections: for section in self._sections:
@@ -197,7 +197,7 @@ class Config:
self.general.svn_roots = { } self.general.svn_roots = { }
self.general.root_parents = [] self.general.root_parents = []
self.general.default_root = '' self.general.default_root = ''
self.general.mime_types_file = '' self.general.mime_types_files = ["mimetypes.conf"]
self.general.address = '' self.general.address = ''
self.general.kv_files = [ ] self.general.kv_files = [ ]
self.general.languages = ['en-us'] self.general.languages = ['en-us']
@@ -213,12 +213,13 @@ class Config:
self.options.root_as_url_component = 1 self.options.root_as_url_component = 1
self.options.checkout_magic = 0 self.options.checkout_magic = 0
self.options.allowed_views = ['markup', 'annotate', 'roots'] self.options.allowed_views = ['annotate', 'diff', 'markup', 'roots']
self.options.authorizer = 'forbidden' self.options.authorizer = None
self.options.mangle_email_addresses = 0 self.options.mangle_email_addresses = 0
self.options.default_file_view = "log" self.options.default_file_view = "log"
self.options.http_expiration_time = 600 self.options.http_expiration_time = 600
self.options.generate_etags = 1 self.options.generate_etags = 1
self.options.svn_ignore_mimetype = 0
self.options.svn_config_dir = None self.options.svn_config_dir = None
self.options.use_rcsparse = 0 self.options.use_rcsparse = 0
self.options.sort_by = 'file' self.options.sort_by = 'file'
@@ -246,7 +247,8 @@ class Config:
self.options.use_cvsgraph = 0 self.options.use_cvsgraph = 0
self.options.cvsgraph_conf = "cvsgraph.conf" self.options.cvsgraph_conf = "cvsgraph.conf"
self.options.use_re_search = 0 self.options.use_re_search = 0
self.options.use_pagesize = 0 self.options.dir_pagesize = 0
self.options.log_pagesize = 0
self.options.limit_changes = 100 self.options.limit_changes = 100
self.templates.diff = None self.templates.diff = None

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -20,6 +20,14 @@ import re
import vclib import vclib
import dbi import dbi
## Current commits database schema version number.
##
## Version 0 was the original Bonsai-compatible version.
##
## Version 1 added the 'metadata' table (which holds the 'version' key)
## and renamed all the 'repository'-related stuff to be 'root'-
##
CURRENT_SCHEMA_VERSION = 1
## error ## error
error = "cvsdb error" error = "cvsdb error"
@@ -37,6 +45,7 @@ class CheckinDatabase:
self._passwd = passwd self._passwd = passwd
self._database = database self._database = database
self._row_limit = row_limit self._row_limit = row_limit
self._version = None
## database lookup caches ## database lookup caches
self._get_cache = {} self._get_cache = {}
@@ -48,6 +57,19 @@ class CheckinDatabase:
self._host, self._port, self._user, self._passwd, self._database) self._host, self._port, self._user, self._passwd, self._database)
cursor = self.db.cursor() cursor = self.db.cursor()
cursor.execute("SET AUTOCOMMIT=1") cursor.execute("SET AUTOCOMMIT=1")
table_list = self.GetTableList()
if 'metadata' in table_list:
version = self.GetMetadataValue("version")
if version is None:
self._version = 0
else:
self._version = int(version)
else:
self._version = 0
if self._version > CURRENT_SCHEMA_VERSION:
raise Exception("Database version %d is newer than the last "
"version supported by this software."
% (self._version))
def sql_get_id(self, table, column, value, auto_set): def sql_get_id(self, table, column, value, auto_set):
sql = "SELECT id FROM %s WHERE %s=%%s" % (table, column) sql = "SELECT id FROM %s WHERE %s=%%s" % (table, column)
@@ -147,6 +169,42 @@ class CheckinDatabase:
return list return list
def GetTableList(self):
sql = "SHOW TABLES"
cursor = self.db.cursor()
cursor.execute(sql)
list = []
while 1:
row = cursor.fetchone()
if row == None:
break
list.append(row[0])
return list
def GetMetadataValue(self, name):
sql = "SELECT value FROM metadata WHERE name=%s"
sql_args = (name)
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
try:
(value,) = cursor.fetchone()
except TypeError:
return None
return value
def SetMetadataValue(self, name, value):
assert(self._version > 0)
sql = "REPLACE INTO metadata (name, value) VALUES (%s, %s)"
sql_args = (name, value)
cursor = self.db.cursor()
try:
cursor.execute(sql, sql_args)
except Exception, e:
raise Exception("Error setting metadata: '%s'\n"
"\tname = %s\n"
"\tvalue = %s\n"
% (str(e), name, value))
def GetBranchID(self, branch, auto_set = 1): def GetBranchID(self, branch, auto_set = 1):
return self.get_id("branches", "branch", branch, auto_set) return self.get_id("branches", "branch", branch, auto_set)
@@ -251,7 +309,9 @@ class CheckinDatabase:
minus_count = commit.GetMinusCount() or '0' minus_count = commit.GetMinusCount() or '0'
description_id = self.GetDescriptionID(commit.GetDescription()) description_id = self.GetDescriptionID(commit.GetDescription())
sql = "REPLACE INTO checkins"\ commits_table = self._version >= 1 and 'commits' or 'checkins'
sql = "REPLACE INTO %s" % (commits_table)
sql = sql + \
" (type,ci_when,whoid,repositoryid,dirid,fileid,revision,"\ " (type,ci_when,whoid,repositoryid,dirid,fileid,revision,"\
" stickytag,branchid,addedlines,removedlines,descid)"\ " stickytag,branchid,addedlines,removedlines,descid)"\
"VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)" "VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
@@ -304,58 +364,69 @@ class CheckinDatabase:
return "(%s)" % (string.join(sqlList, " OR ")) return "(%s)" % (string.join(sqlList, " OR "))
def CreateSQLQueryString(self, query): def CreateSQLQueryString(self, query):
tableList = [("checkins", None)] commits_table = self._version >= 1 and 'commits' or 'checkins'
tableList = [(commits_table, None)]
condList = [] condList = []
if len(query.repository_list): if len(query.repository_list):
tableList.append(("repositories", tableList.append(("repositories",
"(checkins.repositoryid=repositories.id)")) "(%s.repositoryid=repositories.id)"
% (commits_table)))
temp = self.SQLQueryListString("repositories.repository", temp = self.SQLQueryListString("repositories.repository",
query.repository_list) query.repository_list)
condList.append(temp) condList.append(temp)
if len(query.branch_list): if len(query.branch_list):
tableList.append(("branches", "(checkins.branchid=branches.id)")) tableList.append(("branches",
"(%s.branchid=branches.id)" % (commits_table)))
temp = self.SQLQueryListString("branches.branch", temp = self.SQLQueryListString("branches.branch",
query.branch_list) query.branch_list)
condList.append(temp) condList.append(temp)
if len(query.directory_list): if len(query.directory_list):
tableList.append(("dirs", "(checkins.dirid=dirs.id)")) tableList.append(("dirs",
"(%s.dirid=dirs.id)" % (commits_table)))
temp = self.SQLQueryListString("dirs.dir", query.directory_list) temp = self.SQLQueryListString("dirs.dir", query.directory_list)
condList.append(temp) condList.append(temp)
if len(query.file_list): if len(query.file_list):
tableList.append(("files", "(checkins.fileid=files.id)")) tableList.append(("files",
"(%s.fileid=files.id)" % (commits_table)))
temp = self.SQLQueryListString("files.file", query.file_list) temp = self.SQLQueryListString("files.file", query.file_list)
condList.append(temp) condList.append(temp)
if len(query.author_list): if len(query.author_list):
tableList.append(("people", "(checkins.whoid=people.id)")) tableList.append(("people",
"(%s.whoid=people.id)" % (commits_table)))
temp = self.SQLQueryListString("people.who", query.author_list) temp = self.SQLQueryListString("people.who", query.author_list)
condList.append(temp) condList.append(temp)
if len(query.comment_list): if len(query.comment_list):
tableList.append(("descs", "(checkins.descid=descs.id)")) tableList.append(("descs",
"(%s.descid=descs.id)" % (commits_table)))
temp = self.SQLQueryListString("descs.description", temp = self.SQLQueryListString("descs.description",
query.comment_list) query.comment_list)
condList.append(temp) condList.append(temp)
if query.from_date: if query.from_date:
temp = "(checkins.ci_when>=\"%s\")" % (str(query.from_date)) temp = "(%s.ci_when>=\"%s\")" \
% (commits_table, str(query.from_date))
condList.append(temp) condList.append(temp)
if query.to_date: if query.to_date:
temp = "(checkins.ci_when<=\"%s\")" % (str(query.to_date)) temp = "(%s.ci_when<=\"%s\")" \
% (commits_table, str(query.to_date))
condList.append(temp) condList.append(temp)
if query.sort == "date": if query.sort == "date":
order_by = "ORDER BY checkins.ci_when DESC,descid" order_by = "ORDER BY %s.ci_when DESC,descid" % (commits_table)
elif query.sort == "author": elif query.sort == "author":
tableList.append(("people", "(checkins.whoid=people.id)")) tableList.append(("people",
"(%s.whoid=people.id)" % (commits_table)))
order_by = "ORDER BY people.who,descid" order_by = "ORDER BY people.who,descid"
elif query.sort == "file": elif query.sort == "file":
tableList.append(("files", "(checkins.fileid=files.id)")) tableList.append(("files",
"(%s.fileid=files.id)" % (commits_table)))
order_by = "ORDER BY files.file,descid" order_by = "ORDER BY files.file,descid"
## exclude duplicates from the table list, and split out join ## exclude duplicates from the table list, and split out join
@@ -381,8 +452,8 @@ class CheckinDatabase:
elif self._row_limit: elif self._row_limit:
limit = "LIMIT %s" % (str(self._row_limit)) limit = "LIMIT %s" % (str(self._row_limit))
sql = "SELECT checkins.* FROM %s %s %s %s" % ( sql = "SELECT %s.* FROM %s %s %s %s" \
tables, conditions, order_by, limit) % (commits_table, tables, conditions, order_by, limit)
return sql return sql
@@ -433,8 +504,13 @@ class CheckinDatabase:
if file_id == None: if file_id == None:
return None return None
sql = "SELECT * FROM checkins WHERE "\ commits_table = self._version >= 1 and 'commits' or 'checkins'
" repositoryid=%s AND dirid=%s AND fileid=%s AND revision=%s" sql = "SELECT * FROM %s WHERE "\
" repositoryid=%%s "\
" AND dirid=%%s"\
" AND fileid=%%s"\
" AND revision=%%s"\
% (commits_table)
sql_args = (repository_id, dir_id, file_id, commit.GetRevision()) sql_args = (repository_id, dir_id, file_id, commit.GetRevision())
cursor = self.db.cursor() cursor = self.db.cursor()
@@ -451,40 +527,58 @@ class CheckinDatabase:
def sql_delete(self, table, key, value, keep_fkey = None): def sql_delete(self, table, key, value, keep_fkey = None):
sql = "DELETE FROM %s WHERE %s=%%s" % (table, key) sql = "DELETE FROM %s WHERE %s=%%s" % (table, key)
sql_args = (value, ) sql_args = (value, )
commits_table = self._version >= 1 and 'commits' or 'checkins'
if keep_fkey: if keep_fkey:
sql += " AND %s NOT IN (SELECT %s FROM checkins WHERE %s = %%s)" \ sql += " AND %s NOT IN (SELECT %s FROM %s WHERE %s = %%s)" \
% (key, keep_fkey, keep_fkey) % (key, keep_fkey, commits_table, keep_fkey)
sql_args = (value, value) sql_args = (value, value)
cursor = self.db.cursor() cursor = self.db.cursor()
cursor.execute(sql, sql_args) cursor.execute(sql, sql_args)
def sql_purge(self, table, key, fkey, ftable):
sql = "DELETE FROM %s WHERE %s NOT IN (SELECT %s FROM %s)" \
% (table, key, fkey, ftable)
cursor = self.db.cursor()
cursor.execute(sql)
def PurgeRepository(self, repository): def PurgeRepository(self, repository):
rep_id = self.GetRepositoryID(repository) rep_id = self.GetRepositoryID(repository)
if not rep_id: if not rep_id:
raise Exception, "Unknown repository '%s'" % (repository) raise Exception, "Unknown repository '%s'" % (repository)
sql = "SELECT * FROM checkins WHERE repositoryid=%s"
sql_args = (rep_id, )
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
checkins = []
while 1:
try:
(ci_type, ci_when, who_id, repository_id,
dir_id, file_id, revision, sticky_tag, branch_id,
plus_count, minus_count, description_id) = cursor.fetchone()
except TypeError:
break
checkins.append([file_id, dir_id, branch_id, description_id, who_id])
#self.sql_delete('repositories', 'id', rep_id) if (self._version >= 1):
self.sql_delete('checkins', 'repositoryid', rep_id) self.sql_delete('repositories', 'id', rep_id)
for checkin in checkins: self.sql_purge('commits', 'repositoryid', 'id', 'repositories')
self.sql_delete('files', 'id', checkin[0], 'fileid') self.sql_purge('files', 'id', 'fileid', 'commits')
self.sql_delete('dirs', 'id', checkin[1], 'dirid') self.sql_purge('dirs', 'id', 'dirid', 'commits')
self.sql_delete('branches', 'id', checkin[2], 'branchid') self.sql_purge('branches', 'id', 'branchid', 'commits')
self.sql_delete('descs', 'id', checkin[3], 'descid') self.sql_purge('descs', 'id', 'descid', 'commits')
self.sql_delete('people', 'id', checkin[4], 'whoid') self.sql_purge('people', 'id', 'whoid', 'commits')
else:
sql = "SELECT * FROM checkins WHERE repositoryid=%s"
sql_args = (rep_id, )
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
checkins = []
while 1:
try:
(ci_type, ci_when, who_id, repository_id,
dir_id, file_id, revision, sticky_tag, branch_id,
plus_count, minus_count, description_id) = \
cursor.fetchone()
except TypeError:
break
checkins.append([file_id, dir_id, branch_id,
description_id, who_id])
#self.sql_delete('repositories', 'id', rep_id)
self.sql_delete('checkins', 'repositoryid', rep_id)
for checkin in checkins:
self.sql_delete('files', 'id', checkin[0], 'fileid')
self.sql_delete('dirs', 'id', checkin[1], 'dirid')
self.sql_delete('branches', 'id', checkin[2], 'branchid')
self.sql_delete('descs', 'id', checkin[3], 'descid')
self.sql_delete('people', 'id', checkin[4], 'whoid')
## the Commit class holds data on one commit, the representation is as ## the Commit class holds data on one commit, the representation is as
## close as possible to how it should be committed and retrieved to the ## close as possible to how it should be committed and retrieved to the

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -48,13 +48,17 @@ if SHOW_TIMES:
else: else:
_times[which] = t _times[which] = t
def dump(): def t_dump(out):
for name, value in _times.items(): out.write('<div>')
print '%s: %.6f<br />' % (name, value) names = _times.keys()
names.sort()
for name in names:
out.write('%s: %.6fs<br/>\n' % (name, _times[name]))
out.write('</div>')
else: else:
t_start = t_end = dump = lambda *args: None t_start = t_end = t_dump = lambda *args: None
class ViewVCException: class ViewVCException:

View File

@@ -460,7 +460,10 @@ class Template:
def _cmd_print(self, valrefs, ctx): def _cmd_print(self, valrefs, ctx):
value = _get_value(valrefs[0], ctx) value = _get_value(valrefs[0], ctx)
args = map(lambda valref, ctx=ctx: _get_value(valref, ctx), valrefs[1:]) args = map(lambda valref, ctx=ctx: _get_value(valref, ctx), valrefs[1:])
_write_value(value, args, ctx) try:
_write_value(value, args, ctx)
except TypeError:
raise Exception("Unprintable value type for '%s'" % (str(valrefs[0][0])))
def _cmd_format(self, printer, ctx): def _cmd_format(self, printer, ctx):
if type(printer) is TupleType: if type(printer) is TupleType:
@@ -522,7 +525,8 @@ class Template:
((valref,), unused, section) = args ((valref,), unused, section) = args
list = _get_value(valref, ctx) list = _get_value(valref, ctx)
if isinstance(list, StringType): if isinstance(list, StringType):
raise NeedSequenceError() raise NeedSequenceError("The value of '%s' is not a sequence"
% (valref[0]))
refname = valref[0] refname = valref[0]
ctx.for_iterators[refname] = iterator = _iter(list) ctx.for_iterators[refname] = iterator = _iter(list)
for unused in iterator: for unused in iterator:
@@ -672,6 +676,41 @@ def _write_value(value, args, ctx):
ctx.printers.append(printer) ctx.printers.append(printer)
class TemplateData:
"""A custom dictionary-like object that allows one-time definition
of keys, and only value fetches and changes, and key deletions,
thereafter.
EZT doesn't require the use of this special class -- a normal
dict-type data dictionary works fine. But use of this class will
assist those who want the data sent to their templates to have a
consistent set of keys."""
def __init__(self, initial_data={}):
self._items = initial_data
def __getitem__(self, key):
return self._items.__getitem__(key)
def __setitem__(self, key, item):
assert self._items.has_key(key)
return self._items.__setitem__(key, item)
def __delitem__(self, key):
return self._items.__delitem__(key)
def keys(self):
return self._items.keys()
def merge(self, template_data):
"""Merge the data in TemplataData instance TEMPLATA_DATA into this
instance. Avoid the temptation to use this conditionally in your
code -- it rather defeats the purpose of this class."""
assert isinstance(template_data, TemplateData)
self._items.update(template_data._items)
class Context: class Context:
"""A container for the execution context""" """A container for the execution context"""
def __init__(self, fp): def __init__(self, fp):

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -131,194 +131,6 @@ def popen(cmd, args, mode, capture_err=1):
# crap. shouldn't be here. # crap. shouldn't be here.
sys.exit(127) sys.exit(127)
def pipe_cmds(cmds, out=None):
"""Executes a sequence of commands. The output of each command is directed to
the input of the next command. A _pipe object is returned for writing to the
first command's input. The output of the last command is directed to the
"out" file object or the standard output if "out" is None. If "out" is not an
OS file descriptor, a separate thread will be spawned to send data to its
write() method."""
if out is None:
out = sys.stdout
if sys.platform == "win32":
### FIXME: windows implementation ignores "out" argument, always
### writing last command's output to standard out
if debug.SHOW_CHILD_PROCESSES:
dbgIn = StringIO.StringIO()
hStdIn, handle = win32popen.MakeSpyPipe(1, 0, (dbgIn,))
i = 0
for cmd in cmds:
i = i + 1
dbgOut, dbgErr = StringIO.StringIO(), StringIO.StringIO()
if i < len(cmds):
nextStdIn, hStdOut = win32popen.MakeSpyPipe(1, 1, (dbgOut,))
x, hStdErr = win32popen.MakeSpyPipe(None, 1, (dbgErr,))
else:
ehandle = win32event.CreateEvent(None, 1, 0, None)
nextStdIn, hStdOut = win32popen.MakeSpyPipe(None, 1, (dbgOut, sapi.server.file()), ehandle)
x, hStdErr = win32popen.MakeSpyPipe(None, 1, (dbgErr,))
command = win32popen.CommandLine(cmd[0], cmd[1:])
phandle, pid, thandle, tid = win32popen.CreateProcess(command, hStdIn, hStdOut, hStdErr)
if debug.SHOW_CHILD_PROCESSES:
debug.Process(command, dbgIn, dbgOut, dbgErr)
dbgIn = dbgOut
hStdIn = nextStdIn
else:
hStdIn, handle = win32popen.CreatePipe(1, 0)
spool = None
i = 0
for cmd in cmds:
i = i + 1
if i < len(cmds):
nextStdIn, hStdOut = win32popen.CreatePipe(1, 1)
else:
# very last process
nextStdIn = None
if sapi.server.inheritableOut:
# send child output to standard out
hStdOut = win32popen.MakeInheritedHandle(win32popen.FileObject2File(sys.stdout),0)
ehandle = None
else:
ehandle = win32event.CreateEvent(None, 1, 0, None)
x, hStdOut = win32popen.MakeSpyPipe(None, 1, (sapi.server.file(),), ehandle)
command = win32popen.CommandLine(cmd[0], cmd[1:])
phandle, pid, thandle, tid = win32popen.CreateProcess(command, hStdIn, hStdOut, None)
hStdIn = nextStdIn
return _pipe(win32popen.File2FileObject(handle, 'wb'), phandle, ehandle)
# flush the stdio buffers since we are about to change the FD under them
sys.stdout.flush()
sys.stderr.flush()
prev_r, parent_w = os.pipe()
null = os.open('/dev/null', os.O_RDWR)
child_pids = []
for cmd in cmds[:-1]:
r, w = os.pipe()
pid = os.fork()
if not pid:
# in the child
# hook up stdin to the "read" channel
os.dup2(prev_r, 0)
# hook up stdout to the output channel
os.dup2(w, 1)
# toss errors
os.dup2(null, 2)
# close these extra descriptors
os.close(prev_r)
os.close(parent_w)
os.close(null)
os.close(r)
os.close(w)
# time to run the command
try:
os.execvp(cmd[0], cmd)
except:
pass
sys.exit(127)
# in the parent
child_pids.append(pid)
# we don't need these any more
os.close(prev_r)
os.close(w)
# the read channel of this pipe will feed into to the next command
prev_r = r
# no longer needed
os.close(null)
# done with most of the commands. set up the last command to write to "out"
if not hasattr(out, 'fileno'):
r, w = os.pipe()
pid = os.fork()
if not pid:
# in the child (the last command)
# hook up stdin to the "read" channel
os.dup2(prev_r, 0)
# hook up stdout to "out"
if hasattr(out, 'fileno'):
if out.fileno() != 1:
os.dup2(out.fileno(), 1)
out.close()
else:
# "out" can't be hooked up directly, so use a pipe and a thread
os.dup2(w, 1)
os.close(r)
os.close(w)
# close these extra descriptors
os.close(prev_r)
os.close(parent_w)
# run the last command
try:
os.execvp(cmds[-1][0], cmds[-1])
except:
pass
sys.exit(127)
child_pids.append(pid)
# not needed any more
os.close(prev_r)
if not hasattr(out, 'fileno'):
os.close(w)
thread = _copy(r, out)
thread.start()
else:
thread = None
# write into the first pipe, wait on the final process
return _pipe(os.fdopen(parent_w, 'w'), child_pids, thread=thread)
class _copy(threading.Thread):
def __init__(self, srcfd, destfile):
self.srcfd = srcfd
self.destfile = destfile
threading.Thread.__init__(self)
def run(self):
try:
while 1:
s = os.read(self.srcfd, 1024)
if not s:
break
self.destfile.write(s)
finally:
os.close(self.srcfd)
class _pipe: class _pipe:
"Wrapper for a file which can wait() on a child process at close time." "Wrapper for a file which can wait() on a child process at close time."

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -412,13 +412,10 @@ def main(server, cfg, viewvc_link):
commits = [ ] commits = [ ]
query = 'skipped' query = 'skipped'
script_name = server.getenv('SCRIPT_NAME', '') data = ezt.TemplateData({
data = {
'cfg' : cfg, 'cfg' : cfg,
'address' : cfg.general.address, 'address' : cfg.general.address,
'vsn' : viewvc.__version__, 'vsn' : viewvc.__version__,
'repository' : server.escape(form_data.repository, 1), 'repository' : server.escape(form_data.repository, 1),
'branch' : server.escape(form_data.branch, 1), 'branch' : server.escape(form_data.branch, 1),
'directory' : server.escape(form_data.directory, 1), 'directory' : server.escape(form_data.directory, 1),
@@ -435,16 +432,11 @@ def main(server, cfg, viewvc_link):
'commits' : commits, 'commits' : commits,
'num_commits' : len(commits), 'num_commits' : len(commits),
'rss_href' : None, 'rss_href' : None,
} 'hours' : form_data.hours and form_data.hours or 2,
})
if form_data.hours:
data['hours'] = form_data.hours
else:
data['hours'] = 2
server.header()
# generate the page # generate the page
server.header()
template = viewvc.get_view_template(cfg, "query") template = viewvc.get_view_template(cfg, "query")
template.generate(server.file(), data) template.generate(server.file(), data)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org> # Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -21,7 +21,7 @@ import tempfile
import popen2 import popen2
import time import time
import urllib import urllib
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, _compare_paths, _path_parts, _cleanup_path, _rev2optrev from svn_repos import Revision, SVNChangedPath, _datestr_to_date, _compare_paths, _path_parts, _cleanup_path, _rev2optrev, _fix_subversion_exception
from svn import core, delta, client, wc, ra from svn import core, delta, client, wc, ra
@@ -254,7 +254,7 @@ class RemoteSubversionRepository(vclib.Repository):
dirent = dirents[entry.name] dirent = dirents[entry.name]
entry.date, entry.author, entry.log, changes = \ entry.date, entry.author, entry.log, changes = \
self.revinfo(dirent.created_rev) self.revinfo(dirent.created_rev)
entry.rev = dirent.created_rev entry.rev = str(dirent.created_rev)
entry.size = dirent.size entry.size = dirent.size
entry.lockinfo = None entry.lockinfo = None
if locks.has_key(entry.name): if locks.has_key(entry.name):
@@ -362,6 +362,7 @@ class RemoteSubversionRepository(vclib.Repository):
info2 = p2, _date_from_rev(r2), r2 info2 = p2, _date_from_rev(r2), r2
return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args) return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
except core.SubversionException, e: except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == vclib.svn.core.SVN_ERR_FS_NOT_FOUND: if e.apr_err == vclib.svn.core.SVN_ERR_FS_NOT_FOUND:
raise vclib.InvalidRevision raise vclib.InvalidRevision
raise raise
@@ -486,6 +487,7 @@ class RemoteSubversionRepository(vclib.Repository):
try: try:
results = ra.get_locations(self.ra_session, path, rev, [old_rev]) results = ra.get_locations(self.ra_session, path, rev, [old_rev])
except core.SubversionException, e: except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND: if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.ItemNotFound(path) raise vclib.ItemNotFound(path)
raise raise

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -15,11 +15,9 @@
import vclib import vclib
import os import os
import os.path import os.path
import stat
import string import string
import cStringIO import cStringIO
import signal import signal
import shutil
import time import time
import tempfile import tempfile
import popen import popen
@@ -31,6 +29,19 @@ from svn import fs, repos, core, client, delta
if (core.SVN_VER_MAJOR, core.SVN_VER_MINOR, core.SVN_VER_PATCH) < (1, 3, 1): if (core.SVN_VER_MAJOR, core.SVN_VER_MINOR, core.SVN_VER_PATCH) < (1, 3, 1):
raise Exception, "Version requirement not met (needs 1.3.1 or better)" raise Exception, "Version requirement not met (needs 1.3.1 or better)"
### Pre-1.5 Subversion doesn't have SVN_ERR_CEASE_INVOCATION
try:
_SVN_ERR_CEASE_INVOCATION = core.SVN_ERR_CEASE_INVOCATION
except:
_SVN_ERR_CEASE_INVOCATION = core.SVN_ERR_CANCELLED
### Pre-1.5 SubversionException's might not have the .msg and .apr_err members
def _fix_subversion_exception(e):
if not hasattr(e, 'apr_err'):
e.apr_err = e[1]
if not hasattr(e, 'message'):
e.message = e[0]
def _allow_all(root, path, pool): def _allow_all(root, path, pool):
"""Generic authz_read_func that permits access to all paths""" """Generic authz_read_func that permits access to all paths"""
@@ -167,13 +178,16 @@ class NodeHistory:
return return
self.histories.append([revision, _cleanup_path(path)]) self.histories.append([revision, _cleanup_path(path)])
if self.limit and len(self.histories) == self.limit: if self.limit and len(self.histories) == self.limit:
raise core.SubversionException("", core.SVN_ERR_CEASE_INVOCATION) raise core.SubversionException("", _SVN_ERR_CEASE_INVOCATION)
def __getitem__(self, idx): def __getitem__(self, idx):
return self.histories[idx] return self.histories[idx]
def _get_history(svnrepos, path, rev, path_type, limit=0, options={}): def _get_history(svnrepos, path, rev, path_type, limit=0, options={}):
if svnrepos.youngest == 0:
return []
rev_paths = [] rev_paths = []
fsroot = svnrepos._getroot(rev) fsroot = svnrepos._getroot(rev)
show_all_logs = options.get('svn_show_all_dir_logs', 0) show_all_logs = options.get('svn_show_all_dir_logs', 0)
@@ -190,7 +204,8 @@ def _get_history(svnrepos, path, rev, path_type, limit=0, options={}):
repos.svn_repos_history(svnrepos.fs_ptr, path, history.add_history, repos.svn_repos_history(svnrepos.fs_ptr, path, history.add_history,
1, rev, options.get('svn_cross_copies', 0)) 1, rev, options.get('svn_cross_copies', 0))
except core.SubversionException, e: except core.SubversionException, e:
if e.apr_err != core.SVN_ERR_CEASE_INVOCATION: _fix_subversion_exception(e)
if e.apr_err != _SVN_ERR_CEASE_INVOCATION:
raise raise
# Now, iterate over those history items, checking for changes of # Now, iterate over those history items, checking for changes of
@@ -210,7 +225,7 @@ def _log_helper(svnrepos, path, rev, lockinfo):
copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, path) copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, path)
# Assemble our LogEntry # Assemble our LogEntry
date, author, msg, changes = svnrepos.revinfo(rev) date, author, msg, changes = svnrepos._revinfo(rev)
if fs.is_file(rev_root, path): if fs.is_file(rev_root, path):
size = fs.file_length(rev_root, path) size = fs.file_length(rev_root, path)
else: else:
@@ -314,6 +329,7 @@ class BlameSource:
client.blame2(local_url, _rev2optrev(rev), _rev2optrev(first_rev), client.blame2(local_url, _rev2optrev(rev), _rev2optrev(first_rev),
_rev2optrev(rev), self._blame_cb, ctx) _rev2optrev(rev), self._blame_cb, ctx)
except core.SubversionException, e: except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_CLIENT_IS_BINARY_FILE: if e.apr_err == core.SVN_ERR_CLIENT_IS_BINARY_FILE:
raise vclib.NonTextualFileContents raise vclib.NonTextualFileContents
raise raise
@@ -456,7 +472,7 @@ class LocalSubversionRepository(vclib.Repository):
continue continue
path = self._getpath(entry_path_parts) path = self._getpath(entry_path_parts)
entry_rev = _get_last_history_rev(fsroot, path) entry_rev = _get_last_history_rev(fsroot, path)
date, author, msg, changes = self.revinfo(entry_rev) date, author, msg, changes = self._revinfo(entry_rev)
entry.rev = str(entry_rev) entry.rev = str(entry_rev)
entry.date = date entry.date = date
entry.author = author entry.author = author
@@ -554,100 +570,125 @@ class LocalSubversionRepository(vclib.Repository):
youngest_rev, oldest_rev) youngest_rev, oldest_rev)
return source, youngest_rev return source, youngest_rev
def _revinfo_raw(self, rev): def _revinfo(self, rev, include_changed_paths=0):
fsroot = self._getroot(rev) """Internal-use, cache-friendly revision information harvester."""
# Get the changes for the revision
editor = repos.ChangeCollector(self.fs_ptr, fsroot)
e_ptr, e_baton = delta.make_editor(editor)
repos.svn_repos_replay(fsroot, e_ptr, e_baton)
changes = editor.get_changes()
changedpaths = {}
# Now get the revision property info. Would use def _revinfo_helper(rev, include_changed_paths):
# editor.get_root_props(), but something is broken there... # Get the revision property info. (Would use
revprops = fs.revision_proplist(self.fs_ptr, rev) # editor.get_root_props(), but something is broken there...)
msg = revprops.get(core.SVN_PROP_REVISION_LOG) revprops = fs.revision_proplist(self.fs_ptr, rev)
author = revprops.get(core.SVN_PROP_REVISION_AUTHOR) msg = revprops.get(core.SVN_PROP_REVISION_LOG)
datestr = revprops.get(core.SVN_PROP_REVISION_DATE) author = revprops.get(core.SVN_PROP_REVISION_AUTHOR)
datestr = revprops.get(core.SVN_PROP_REVISION_DATE)
# Copy the Subversion changes into a new hash, converting them into date = _datestr_to_date(datestr)
# ChangedPath objects.
found_readable = found_unreadable = 0 # Optimization: If our caller doesn't care about the changed
for path in changes.keys(): # paths, and we don't need them to do authz determinations, let's
change = changes[path] # get outta here.
if change.path: if self.auth is None and not include_changed_paths:
change.path = _cleanup_path(change.path) return date, author, msg, None
if change.base_path:
change.base_path = _cleanup_path(change.base_path) # If we get here, then we either need the changed paths because we
is_copy = 0 # were asked for them, or we need them to do authorization checks.
if not hasattr(change, 'action'): # new to subversion 1.4.0 # Either way, we need 'em, so let's get 'em.
action = vclib.MODIFIED fsroot = self._getroot(rev)
if not change.path: editor = repos.ChangeCollector(self.fs_ptr, fsroot)
action = vclib.DELETED e_ptr, e_baton = delta.make_editor(editor)
elif change.added: repos.svn_repos_replay(fsroot, e_ptr, e_baton)
action = vclib.ADDED changedpaths = {}
replace_check_path = path changes = editor.get_changes()
if change.base_path and change.base_rev:
replace_check_path = change.base_path # Copy the Subversion changes into a new hash, checking
if changedpaths.has_key(replace_check_path) \ # authorization and converting them into ChangedPath objects.
and changedpaths[replace_check_path].action == vclib.DELETED: found_readable = found_unreadable = 0
action = vclib.REPLACED for path in changes.keys():
else: change = changes[path]
if change.action == repos.CHANGE_ACTION_ADD: if change.path:
action = vclib.ADDED change.path = _cleanup_path(change.path)
elif change.action == repos.CHANGE_ACTION_DELETE: if change.base_path:
action = vclib.DELETED change.base_path = _cleanup_path(change.base_path)
elif change.action == repos.CHANGE_ACTION_REPLACE: is_copy = 0
action = vclib.REPLACED if not hasattr(change, 'action'): # new to subversion 1.4.0
else:
action = vclib.MODIFIED action = vclib.MODIFIED
if (action == vclib.ADDED or action == vclib.REPLACED) \ if not change.path:
and change.base_path \ action = vclib.DELETED
and change.base_rev: elif change.added:
is_copy = 1 action = vclib.ADDED
if change.item_kind == core.svn_node_dir: replace_check_path = path
pathtype = vclib.DIR if change.base_path and change.base_rev:
elif change.item_kind == core.svn_node_file: replace_check_path = change.base_path
pathtype = vclib.FILE if changedpaths.has_key(replace_check_path) \
and changedpaths[replace_check_path].action == vclib.DELETED:
action = vclib.REPLACED
else:
if change.action == repos.CHANGE_ACTION_ADD:
action = vclib.ADDED
elif change.action == repos.CHANGE_ACTION_DELETE:
action = vclib.DELETED
elif change.action == repos.CHANGE_ACTION_REPLACE:
action = vclib.REPLACED
else:
action = vclib.MODIFIED
if (action == vclib.ADDED or action == vclib.REPLACED) \
and change.base_path \
and change.base_rev:
is_copy = 1
if change.item_kind == core.svn_node_dir:
pathtype = vclib.DIR
elif change.item_kind == core.svn_node_file:
pathtype = vclib.FILE
else:
pathtype = None
parts = _path_parts(path)
if vclib.check_path_access(self, parts, pathtype, rev):
if is_copy and change.base_path and (change.base_path != path):
parts = _path_parts(change.base_path)
if not vclib.check_path_access(self, parts, pathtype, change.base_rev):
is_copy = 0
change.base_path = None
change.base_rev = None
changedpaths[path] = SVNChangedPath(path, rev, pathtype,
change.base_path,
change.base_rev, action,
is_copy, change.text_changed,
change.prop_changes)
found_readable = 1
else:
found_unreadable = 1
# If our caller doesn't care about changed paths, we must be
# here for authz reasons only. That means the minute we've
# found both a readable and an unreadable path, we can bail out.
if (not include_changed_paths) and found_readable and found_unreadable:
return date, author, None, None
# Okay, we've process all our paths. Let's filter our metadata,
# and return the requested data.
if found_unreadable:
msg = None
if not found_readable:
author = None
date = None
if include_changed_paths:
return date, author, msg, changedpaths.values()
else: else:
pathtype = None return date, author, msg, None
parts = _path_parts(path) # Consult the revinfo cache first. If we don't have cached info,
if vclib.check_path_access(self, parts, pathtype, rev): # or our caller wants changed paths and we don't have those for
if is_copy and change.base_path and (change.base_path != path): # this revision, go do the real work.
parts = _path_parts(change.base_path)
if not vclib.check_path_access(self, parts, pathtype, change.base_rev):
is_copy = 0
change.base_path = None
change.base_rev = None
changedpaths[path] = SVNChangedPath(path, rev, pathtype,
change.base_path,
change.base_rev, action,
is_copy, change.text_changed,
change.prop_changes)
found_readable = 1
else:
found_unreadable = 1
# Return our tuple, auth-filtered: date, author, msg, changes
if found_unreadable:
msg = None
if not found_readable:
author = None
datestr = None
date = _datestr_to_date(datestr)
return date, author, msg, changedpaths.values()
def revinfo(self, rev):
rev = self._getrev(rev) rev = self._getrev(rev)
cached_info = self._revinfo_cache.get(rev) cached_info = self._revinfo_cache.get(rev)
if not cached_info: if not cached_info \
cached_info = self._revinfo_raw(rev) or (include_changed_paths and cached_info[3] is None):
cached_info = _revinfo_helper(rev, include_changed_paths)
self._revinfo_cache[rev] = cached_info self._revinfo_cache[rev] = cached_info
return cached_info[0], cached_info[1], cached_info[2], cached_info[3] return cached_info[0], cached_info[1], cached_info[2], cached_info[3]
def revinfo(self, rev):
return self._revinfo(rev, 1)
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}): def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
p1 = self._getpath(path_parts1) p1 = self._getpath(path_parts1)
p2 = self._getpath(path_parts2) p2 = self._getpath(path_parts2)
@@ -661,7 +702,7 @@ class LocalSubversionRepository(vclib.Repository):
args = vclib._diff_args(type, options) args = vclib._diff_args(type, options)
def _date_from_rev(rev): def _date_from_rev(rev):
date, author, msg, changes = self.revinfo(rev) date, author, msg, changes = self._revinfo(rev)
return date return date
try: try:
@@ -671,6 +712,7 @@ class LocalSubversionRepository(vclib.Repository):
info2 = p2, _date_from_rev(r2), r2 info2 = p2, _date_from_rev(r2), r2
return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args) return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
except core.SubversionException, e: except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND: if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.InvalidRevision raise vclib.InvalidRevision
raise raise
@@ -710,6 +752,7 @@ class LocalSubversionRepository(vclib.Repository):
results = repos.svn_repos_trace_node_locations(self.fs_ptr, path, results = repos.svn_repos_trace_node_locations(self.fs_ptr, path,
rev, [old_rev], _allow_all) rev, [old_rev], _allow_all)
except core.SubversionException, e: except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND: if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.ItemNotFound(path) raise vclib.ItemNotFound(path)
raise raise
@@ -759,6 +802,7 @@ class LocalSubversionRepository(vclib.Repository):
try: try:
mid_id = fs.node_id(self._getroot(mid), path) mid_id = fs.node_id(self._getroot(mid), path)
except core.SubversionException, e: except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND: if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
cmp = -1 cmp = -1
else: else:

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -14,7 +14,7 @@
# #
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
__version__ = '1.1-dev' __version__ = '1.1.0'
# this comes from our library; measure the startup time # this comes from our library; measure the startup time
import debug import debug
@@ -81,7 +81,7 @@ _sticky_vars = [
] ]
# number of extra pages of information on either side of the current # number of extra pages of information on either side of the current
# page to fetch (see use_pagesize configuration option) # page to fetch (see dir_pagesize/log_pagesize configuration option)
EXTRA_PAGES = 3 EXTRA_PAGES = 3
# for reading/writing between a couple descriptors # for reading/writing between a couple descriptors
@@ -106,7 +106,10 @@ class Request:
# process the Accept-Language: header, and load the key/value # process the Accept-Language: header, and load the key/value
# files, given the selected language # files, given the selected language
hal = server.getenv('HTTP_ACCEPT_LANGUAGE','') hal = server.getenv('HTTP_ACCEPT_LANGUAGE','')
self.lang_selector = accept.language(hal) try:
self.lang_selector = accept.language(hal)
except accept.AcceptLanguageParseError:
self.lang_selector = accept.language('en')
self.language = self.lang_selector.select_from(cfg.general.languages) self.language = self.lang_selector.select_from(cfg.general.languages)
self.kv = cfg.load_kv_files(self.language) self.kv = cfg.load_kv_files(self.language)
@@ -993,8 +996,20 @@ def default_view(mime_type, cfg):
return view_checkout return view_checkout
def get_file_view_info(request, where, rev=None, mime_type=None, pathrev=-1): def get_file_view_info(request, where, rev=None, mime_type=None, pathrev=-1):
"""Return common hrefs and a viewability flag used for various views """Return an object holding common hrefs and a viewability flag used
of FILENAME at revision REV whose MIME type is MIME_TYPE.""" for various views of FILENAME at revision REV whose MIME type is
MIME_TYPE.
The object's members include:
view_href
download_href
download_text_href
annotate_href
revision_href
prefer_markup
"""
rev = rev and str(rev) or None rev = rev and str(rev) or None
mime_type = mime_type or guess_mime(where) mime_type = mime_type or guess_mime(where)
if pathrev == -1: # cheesy default value, since we need to preserve None if pathrev == -1: # cheesy default value, since we need to preserve None
@@ -1042,8 +1057,12 @@ def get_file_view_info(request, where, rev=None, mime_type=None, pathrev=-1):
prefer_markup = default_view(mime_type, request.cfg) == view_markup prefer_markup = default_view(mime_type, request.cfg) == view_markup
return view_href, download_href, download_text_href, \ return _item(view_href=view_href,
annotate_href, revision_href, ezt.boolean(prefer_markup) download_href=download_href,
download_text_href=download_text_href,
annotate_href=annotate_href,
revision_href=revision_href,
prefer_markup=ezt.boolean(prefer_markup))
# Regular expressions for location text that looks like URLs and email # Regular expressions for location text that looks like URLs and email
@@ -1154,40 +1173,44 @@ def html_time(request, secs, extended=0):
return s return s
def common_template_data(request, revision=None, mime_type=None): def common_template_data(request, revision=None, mime_type=None):
"""Return a ezt.TemplateData instance with data dictionary items
common to most ViewVC views."""
cfg = request.cfg cfg = request.cfg
data = {
# Initialize data dictionary members (sorted alphanumerically)
data = ezt.TemplateData({
'annotate_href' : None,
'cfg' : cfg, 'cfg' : cfg,
'vsn' : __version__, 'docroot' : cfg.options.docroot is None \
'kv' : request.kv,
'docroot' : cfg.options.docroot is None \
and request.script_name + '/' + docroot_magic_path \ and request.script_name + '/' + docroot_magic_path \
or cfg.options.docroot, or cfg.options.docroot,
'username' : request.username, 'download_href' : None,
'where' : request.server.escape(request.where), 'download_text_href' : None,
'roottype' : request.roottype, 'graph_href': None,
'kv' : request.kv,
'lockinfo' : None,
'log_href' : None,
'nav_path' : nav_path(request),
'pathtype' : None,
'prefer_markup' : ezt.boolean(0),
'queryform_href' : None,
'rev' : None,
'revision_href' : None,
'rootname' : request.rootname \ 'rootname' : request.rootname \
and request.server.escape(request.rootname) or None, and request.server.escape(request.rootname) or None,
'rootpath' : request.rootpath, 'rootpath' : request.rootpath,
'pathtype' : None, 'roots_href' : request.get_url(view_func=view_roots, escape=1, params={}),
'nav_path' : nav_path(request), 'roottype' : request.roottype,
'view' : _view_codes[request.view_func], 'rss_href' : None,
'rev' : None,
'lockinfo' : None,
'view_href' : None,
'annotate_href' : None,
'download_href' : None,
'download_text_href' : None,
'revision_href' : None,
'queryform_href' : None,
'tarball_href' : None, 'tarball_href' : None,
'up_href' : None, 'up_href' : None,
'log_href' : None, 'username' : request.username,
'log_href_rev': None, 'view' : _view_codes[request.view_func],
'graph_href': None, 'view_href' : None,
'rss_href' : None, 'vsn' : __version__,
'roots_href' : request.get_url(view_func=view_roots, escape=1, params={}), 'where' : request.server.escape(request.where),
'prefer_markup' : ezt.boolean(0), })
}
rev = revision rev = revision
if not rev: if not rev:
@@ -1214,11 +1237,14 @@ def common_template_data(request, revision=None, mime_type=None):
params={}, escape=1) params={}, escape=1)
if request.pathtype == vclib.FILE: if request.pathtype == vclib.FILE:
data['view_href'], data['download_href'], data['download_text_href'], \ fvi = get_file_view_info(request, request.where, data['rev'], mime_type)
data['annotate_href'], data['revision_href'], data['prefer_markup'] \ data['view_href'] = fvi.view_href
= get_file_view_info(request, request.where, data['rev'], mime_type) data['download_href'] = fvi.download_href
data['log_href'] = request.get_url(view_func=view_log, data['download_text_href'] = fvi.download_text_href
params={}, escape=1) data['annotate_href'] = fvi.annotate_href
data['revision_href'] = fvi.revision_href
data['prefer_markup'] = fvi.prefer_markup
data['log_href'] = request.get_url(view_func=view_log, params={}, escape=1)
if request.roottype == 'cvs' and cfg.options.use_cvsgraph: if request.roottype == 'cvs' and cfg.options.use_cvsgraph:
data['graph_href'] = request.get_url(view_func=view_cvsgraph, data['graph_href'] = request.get_url(view_func=view_cvsgraph,
params={}, escape=1) params={}, escape=1)
@@ -1417,6 +1443,9 @@ def make_rss_time_string(date, cfg):
return None return None
return time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime(date)) + ' UTC' return time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime(date)) + ' UTC'
def make_comma_sep_list_string(items):
return string.join(map(lambda x: x.name, items), ', ')
def get_itemprops(request, path_parts, rev): def get_itemprops(request, path_parts, rev):
itemprops = request.repos.itemprops(path_parts, rev) itemprops = request.repos.itemprops(path_parts, rev)
propnames = itemprops.keys() propnames = itemprops.keys()
@@ -1444,7 +1473,8 @@ def calculate_mime_type(request, path_parts, rev):
mime_type = None mime_type = None
if not path_parts: if not path_parts:
return None return None
if request.roottype == 'svn': if request.roottype == 'svn' \
and (not request.cfg.options.svn_ignore_mimetype):
try: try:
itemprops = request.repos.itemprops(path_parts, rev) itemprops = request.repos.itemprops(path_parts, rev)
mime_type = itemprops.get('svn:mime-type') mime_type = itemprops.get('svn:mime-type')
@@ -1458,7 +1488,7 @@ def markup_or_annotate(request, is_annotate):
cfg = request.cfg cfg = request.cfg
path, rev = _orig_path(request, is_annotate and 'annotate' or 'revision') path, rev = _orig_path(request, is_annotate and 'annotate' or 'revision')
lines = fp = image_src_href = None lines = fp = image_src_href = None
annotation = None annotation = 'none'
revision = None revision = None
mime_type = calculate_mime_type(request, path, rev) mime_type = calculate_mime_type(request, path, rev)
@@ -1497,7 +1527,7 @@ def markup_or_annotate(request, is_annotate):
fp.close() fp.close()
data = common_template_data(request, revision) data = common_template_data(request, revision)
data.update({ data.merge(ezt.TemplateData({
'mime_type' : mime_type, 'mime_type' : mime_type,
'log' : None, 'log' : None,
'date' : None, 'date' : None,
@@ -1517,20 +1547,18 @@ def markup_or_annotate(request, is_annotate):
'lines' : lines, 'lines' : lines,
'properties' : get_itemprops(request, path, rev), 'properties' : get_itemprops(request, path, rev),
'annotation' : annotation, 'annotation' : annotation,
}) }))
if cfg.options.show_log_in_markup: if cfg.options.show_log_in_markup:
options = {'svn_latest_log': 1} ### FIXME: No longer needed? options = {'svn_latest_log': 1} ### FIXME: No longer needed?
revs = request.repos.itemlog(path, revision, vclib.SORTBY_DEFAULT, revs = request.repos.itemlog(path, revision, vclib.SORTBY_REV,
0, 1, options) 0, 1, options)
entry = revs[-1] entry = revs[-1]
data.update({ data['date'] = make_time_string(entry.date, cfg)
'date' : make_time_string(entry.date, cfg), data['author'] = entry.author
'author' : entry.author, data['changed'] = entry.changed
'changed' : entry.changed, data['log'] = htmlify(entry.log, cfg.options.mangle_email_addresses)
'log' : htmlify(entry.log, cfg.options.mangle_email_addresses), data['size'] = entry.size
'size' : entry.size,
})
if entry.date is not None: if entry.date is not None:
data['ago'] = html_time(request, entry.date, 1) data['ago'] = html_time(request, entry.date, 1)
@@ -1538,15 +1566,14 @@ def markup_or_annotate(request, is_annotate):
if request.roottype == 'cvs': if request.roottype == 'cvs':
branch = entry.branch_number branch = entry.branch_number
prev = entry.prev or entry.parent prev = entry.prev or entry.parent
data.update({ data['state'] = entry.dead and 'dead'
'state' : entry.dead and 'dead', data['prev'] = prev and prev.string
'prev' : prev and prev.string, data['vendor_branch'] = ezt.boolean(branch and branch[2] % 2 == 1)
'vendor_branch' : ezt.boolean(branch and branch[2] % 2 == 1),
'branches' : string.join(map(lambda x: x.name, entry.branches), ', '), ### TODO: Should this be using prep_tags() instead?
'tags' : string.join(map(lambda x: x.name, entry.tags), ', '), data['branches'] = make_comma_sep_list_string(entry.branches)
'branch_points': string.join(map(lambda x: x.name, data['tags'] = make_comma_sep_list_string(entry.tags)
entry.branch_points), ', ') data['branch_points']= make_comma_sep_list_string(entry.branch_points)
})
if path != request.path_parts: if path != request.path_parts:
orig_path = _path_join(path) orig_path = _path_join(path)
@@ -1649,10 +1676,14 @@ def view_roots(request):
href=href)) href=href))
data = common_template_data(request) data = common_template_data(request)
data['roots'] = roots data.merge(ezt.TemplateData({
'roots' : roots,
}))
generate_page(request, "roots", data) generate_page(request, "roots", data)
def view_directory(request): def view_directory(request):
cfg = request.cfg
# For Subversion repositories, the revision acts as a weak validator for # For Subversion repositories, the revision acts as a weak validator for
# the directory listing (to take into account template changes or # the directory listing (to take into account template changes or
# revision property changes). # revision property changes).
@@ -1666,7 +1697,6 @@ def view_directory(request):
return return
# List current directory # List current directory
cfg = request.cfg
options = {} options = {}
if request.roottype == 'cvs': if request.roottype == 'cvs':
hideattic = int(request.query_dict.get('hideattic', hideattic = int(request.query_dict.get('hideattic',
@@ -1686,11 +1716,11 @@ def view_directory(request):
# however, when sorting by other properties or not paging, we've no # however, when sorting by other properties or not paging, we've no
# choice but to fetch dirlogs for everything. # choice but to fetch dirlogs for everything.
debug.t_start("dirlogs") debug.t_start("dirlogs")
if cfg.options.use_pagesize and sortby == 'file': if cfg.options.dir_pagesize and sortby == 'file':
dirlogs_first = int(request.query_dict.get('dir_pagestart', 0)) dirlogs_first = int(request.query_dict.get('dir_pagestart', 0))
if dirlogs_first > len(file_data): if dirlogs_first > len(file_data):
dirlogs_first = 0 dirlogs_first = 0
dirlogs_last = dirlogs_first + cfg.options.use_pagesize dirlogs_last = dirlogs_first + cfg.options.dir_pagesize
for file in file_data: for file in file_data:
file.rev = None file.rev = None
file.date = None file.date = None
@@ -1802,10 +1832,13 @@ def view_directory(request):
row.mime_type = calculate_mime_type(request, _path_parts(file_where), row.mime_type = calculate_mime_type(request, _path_parts(file_where),
file.rev) file.rev)
row.view_href, row.download_href, row.download_text_href, \ fvi = get_file_view_info(request, file_where, file.rev, row.mime_type)
row.annotate_href, row.revision_href, \ row.view_href = fvi.view_href
row.prefer_markup \ row.download_href = fvi.download_href
= get_file_view_info(request, file_where, file.rev, row.mime_type) row.download_text_href = fvi.download_text_href
row.annotate_href = fvi.annotate_href
row.revision_href = fvi.revision_href
row.prefer_markup = fvi.prefer_markup
row.log_href = request.get_url(view_func=view_log, row.log_href = request.get_url(view_func=view_log,
where=file_where, where=file_where,
pathtype=vclib.FILE, pathtype=vclib.FILE,
@@ -1820,9 +1853,10 @@ def view_directory(request):
rows.append(row) rows.append(row)
# prepare the data that will be passed to the template # Prepare the data that will be passed to the template, based on the
# common template data.
data = common_template_data(request) data = common_template_data(request)
data.update({ data.merge(ezt.TemplateData({
'entries' : rows, 'entries' : rows,
'sortby' : sortby, 'sortby' : sortby,
'sortdir' : sortdir, 'sortdir' : sortdir,
@@ -1856,7 +1890,25 @@ def view_directory(request):
'branch_tags': None, 'branch_tags': None,
'plain_tags': None, 'plain_tags': None,
'properties': get_itemprops(request, request.path_parts, request.pathrev), 'properties': get_itemprops(request, request.path_parts, request.pathrev),
}) 'tree_rev' : None,
'tree_rev_href' : None,
'dir_paging_action' : None,
'dir_paging_hidden_values' : [],
'search_re_action' : None,
'search_re_hidden_values' : [],
# Populated by paging()/paging_sws()
'picklist' : [],
'picklist_len' : 0,
# Populated by pathrev_form()
'pathrev_action' : None,
'pathrev_hidden_values' : [],
'pathrev_clear_action' : None,
'pathrev_clear_hidden_values' : [],
'pathrev' : None,
'lastrev' : None,
}))
# clicking on sort column reverses sort order # clicking on sort column reverses sort order
if sortdir == 'down': if sortdir == 'down':
@@ -1867,24 +1919,27 @@ def view_directory(request):
data['sortby_%s_href' % sortby] = request.get_url(params={'sortdir': data['sortby_%s_href' % sortby] = request.get_url(params={'sortdir':
revsortdir}, revsortdir},
escape=1) escape=1)
# CVS doesn't support sorting by rev
if request.roottype == "cvs":
data['sortby_rev_href'] = None
# set cvs-specific fields # set cvs-specific fields
if request.roottype == 'cvs': if request.roottype == 'cvs':
plain_tags = options['cvs_tags'] plain_tags = options['cvs_tags']
plain_tags.sort(icmp) plain_tags.sort(icmp)
plain_tags.reverse() plain_tags.reverse()
data['plain_tags']= plain_tags
branch_tags = options['cvs_branches'] branch_tags = options['cvs_branches']
branch_tags.sort(icmp) branch_tags.sort(icmp)
branch_tags.reverse() branch_tags.reverse()
data['branch_tags']= branch_tags
data.update({
'attic_showing' : ezt.boolean(not hideattic), data['attic_showing'] = ezt.boolean(not hideattic)
'show_attic_href' : request.get_url(params={'hideattic': 0}, escape=1), data['show_attic_href'] = request.get_url(params={'hideattic': 0},
'hide_attic_href' : request.get_url(params={'hideattic': 1}, escape=1), escape=1)
'branch_tags': branch_tags, data['hide_attic_href'] = request.get_url(params={'hideattic': 1},
'plain_tags': plain_tags, escape=1)
})
# set svn-specific fields # set svn-specific fields
elif request.roottype == 'svn': elif request.roottype == 'svn':
@@ -1897,22 +1952,20 @@ def view_directory(request):
params={}, params={},
escape=1) escape=1)
if cfg.options.use_pagesize: if cfg.options.dir_pagesize:
data['dir_paging_action'], data['dir_paging_hidden_values'] = \ data['dir_paging_action'], data['dir_paging_hidden_values'] = \
request.get_form(params={'dir_pagestart': None}) request.get_form(params={'dir_pagestart': None})
pathrev_form(request, data) pathrev_form(request, data)
### one day, if EZT has "or" capability, we can lose this if cfg.options.use_re_search:
data['search_re_form'] = ezt.boolean(cfg.options.use_re_search)
if data['search_re_form']:
data['search_re_action'], data['search_re_hidden_values'] = \ data['search_re_action'], data['search_re_hidden_values'] = \
request.get_form(params={'search': None}) request.get_form(params={'search': None})
if cfg.options.use_pagesize: if cfg.options.dir_pagesize:
data['dir_pagestart'] = int(request.query_dict.get('dir_pagestart',0)) data['dir_pagestart'] = int(request.query_dict.get('dir_pagestart',0))
data['entries'] = paging(data, 'entries', data['dir_pagestart'], 'name', data['entries'] = paging(data, 'entries', data['dir_pagestart'], 'name',
cfg.options.use_pagesize) cfg.options.dir_pagesize)
generate_page(request, "directory", data) generate_page(request, "directory", data)
@@ -2072,11 +2125,11 @@ def view_log(request):
sortby = vclib.SORTBY_DEFAULT sortby = vclib.SORTBY_DEFAULT
first = last = 0 first = last = 0
if cfg.options.use_pagesize: if cfg.options.log_pagesize:
log_pagestart = int(request.query_dict.get('log_pagestart', 0)) log_pagestart = int(request.query_dict.get('log_pagestart', 0))
first = log_pagestart - min(log_pagestart, first = log_pagestart - min(log_pagestart,
(EXTRA_PAGES * cfg.options.use_pagesize)) (EXTRA_PAGES * cfg.options.log_pagesize))
last = log_pagestart + ((EXTRA_PAGES + 1) * cfg.options.use_pagesize) + 1 last = log_pagestart + ((EXTRA_PAGES + 1) * cfg.options.log_pagesize) + 1
show_revs = request.repos.itemlog(request.path_parts, request.pathrev, show_revs = request.repos.itemlog(request.path_parts, request.pathrev,
sortby, first, last - first, options) sortby, first, last - first, options)
@@ -2172,9 +2225,13 @@ def view_log(request):
# view/download links # view/download links
if pathtype is vclib.FILE: if pathtype is vclib.FILE:
entry.view_href, entry.download_href, entry.download_text_href, \ fvi = get_file_view_info(request, request.where, rev.string, mime_type)
entry.annotate_href, entry.revision_href, entry.prefer_markup \ entry.view_href = fvi.view_href
= get_file_view_info(request, request.where, rev.string, mime_type) entry.download_href = fvi.download_href
entry.download_text_href = fvi.download_text_href
entry.annotate_href = fvi.annotate_href
entry.revision_href = fvi.revision_href
entry.prefer_markup = fvi.prefer_markup
else: else:
entry.revision_href = request.get_url(view_func=view_revision, entry.revision_href = request.get_url(view_func=view_revision,
params={'revision': rev.string}, params={'revision': rev.string},
@@ -2232,8 +2289,16 @@ def view_log(request):
entry.copy_path = request.server.escape(entry.copy_path) entry.copy_path = request.server.escape(entry.copy_path)
entries.append(entry) entries.append(entry)
diff_select_action, diff_select_hidden_values = \
request.get_form(view_func=view_diff,
params={'r1': None, 'r2': None, 'tr1': None,
'tr2': None, 'diff_format': None})
logsort_action, logsort_hidden_values = \
request.get_form(params={'logsort': None})
data = common_template_data(request) data = common_template_data(request)
data.update({ data.merge(ezt.TemplateData({
'default_branch' : None, 'default_branch' : None,
'mime_type' : mime_type, 'mime_type' : mime_type,
'rev_selected' : selected_rev, 'rev_selected' : selected_rev,
@@ -2241,6 +2306,8 @@ def view_log(request):
'logsort' : logsort, 'logsort' : logsort,
'human_readable' : ezt.boolean(diff_format in ('h', 'l')), 'human_readable' : ezt.boolean(diff_format in ('h', 'l')),
'log_pagestart' : None, 'log_pagestart' : None,
'log_paging_action' : None,
'log_paging_hidden_values' : [],
'entries': entries, 'entries': entries,
'head_prefer_markup' : ezt.boolean(0), 'head_prefer_markup' : ezt.boolean(0),
'head_view_href' : None, 'head_view_href' : None,
@@ -2252,42 +2319,45 @@ def view_log(request):
'tag_download_href': None, 'tag_download_href': None,
'tag_download_text_href': None, 'tag_download_text_href': None,
'tag_annotate_href': None, 'tag_annotate_href': None,
}) 'diff_select_action' : diff_select_action,
'diff_select_hidden_values' : diff_select_hidden_values,
'logsort_action' : logsort_action,
'logsort_hidden_values' : logsort_hidden_values,
'tags' : [],
'branch_tags' : [],
'plain_tags' : [],
# Populated by paging()/paging_sws()
'picklist' : [],
'picklist_len' : 0,
# Populated by pathrev_form()
'pathrev_action' : None,
'pathrev_hidden_values' : [],
'pathrev_clear_action' : None,
'pathrev_clear_hidden_values' : [],
'pathrev' : None,
'lastrev' : None,
}))
lastrev = pathrev_form(request, data) lastrev = pathrev_form(request, data)
data['diff_select_action'], data['diff_select_hidden_values'] = \
request.get_form(view_func=view_diff,
params={'r1': None, 'r2': None, 'tr1': None,
'tr2': None, 'diff_format': None})
data['logsort_action'], data['logsort_hidden_values'] = \
request.get_form(params={'logsort': None})
if pathtype is vclib.FILE: if pathtype is vclib.FILE:
if not request.pathrev or lastrev is None: if not request.pathrev or lastrev is None:
view_href, download_href, download_text_href, \ fvi = get_file_view_info(request, request.where, None, mime_type, None)
annotate_href, revision_href, prefer_markup \ data['head_view_href']= fvi.view_href
= get_file_view_info(request, request.where, None, mime_type, None) data['head_download_href']= fvi.download_href
data.update({ data['head_download_text_href']= fvi.download_text_href
'head_view_href': view_href, data['head_annotate_href']= fvi.annotate_href
'head_download_href': download_href, data['head_prefer_markup']= fvi.prefer_markup
'head_download_text_href': download_text_href,
'head_annotate_href': annotate_href,
'head_prefer_markup': prefer_markup,
})
if request.pathrev and request.roottype == 'cvs': if request.pathrev and request.roottype == 'cvs':
view_href, download_href, download_text_href, \ fvi = get_file_view_info(request, request.where, None, mime_type)
annotate_href, revision_href, prefer_markup \ data['tag_view_href']= fvi.view_href
= get_file_view_info(request, request.where, None, mime_type) data['tag_download_href']= fvi.download_href
data.update({ data['tag_download_text_href']= fvi.download_text_href
'tag_view_href': view_href, data['tag_annotate_href']= fvi.annotate_href
'tag_download_href': download_href, data['tag_prefer_markup']= fvi.prefer_markup
'tag_download_text_href': download_text_href,
'tag_annotate_href': annotate_href,
'tag_prefer_markup': prefer_markup,
})
else: else:
data['head_view_href'] = request.get_url(view_func=view_directory, data['head_view_href'] = request.get_url(view_func=view_directory,
params={}, escape=1) params={}, escape=1)
@@ -2307,23 +2377,20 @@ def view_log(request):
branches.append(branch) branches.append(branch)
data['default_branch'] = prep_tags(request, branches) data['default_branch'] = prep_tags(request, branches)
data['tags'] = tags = [ ]
data['branch_tags'] = branch_tags = []
data['plain_tags'] = plain_tags = []
for tag, rev in tagitems: for tag, rev in tagitems:
if rev.co_rev: if rev.co_rev:
tags.append(_item(rev=rev.co_rev.string, name=tag)) data['tags'].append(_item(rev=rev.co_rev.string, name=tag))
if rev.is_branch: if rev.is_branch:
branch_tags.append(tag) data['branch_tags'].append(tag)
else: else:
plain_tags.append(tag) data['plain_tags'].append(tag)
if cfg.options.use_pagesize: if cfg.options.log_pagesize:
data['log_paging_action'], data['log_paging_hidden_values'] = \ data['log_paging_action'], data['log_paging_hidden_values'] = \
request.get_form(params={'log_pagestart': None}) request.get_form(params={'log_pagestart': None})
data['log_pagestart'] = int(request.query_dict.get('log_pagestart',0)) data['log_pagestart'] = int(request.query_dict.get('log_pagestart',0))
data['entries'] = paging_sws(data, 'entries', data['log_pagestart'], data['entries'] = paging_sws(data, 'entries', data['log_pagestart'],
'rev', cfg.options.use_pagesize, first) 'rev', cfg.options.log_pagesize, first)
generate_page(request, "log", data) generate_page(request, "log", data)
@@ -2377,8 +2444,6 @@ def view_cvsgraph(request):
if not cfg.options.use_cvsgraph: if not cfg.options.use_cvsgraph:
raise debug.ViewVCException('Graph view is disabled', '403 Forbidden') raise debug.ViewVCException('Graph view is disabled', '403 Forbidden')
data = common_template_data(request)
# If cvsgraph can't find its supporting libraries, uncomment and set # If cvsgraph can't find its supporting libraries, uncomment and set
# accordingly. Do the same in view_cvsgraph_image(). # accordingly. Do the same in view_cvsgraph_image().
#os.environ['LD_LIBRARY_PATH'] = '/usr/lib:/usr/local/lib:/path/to/cvsgraph' #os.environ['LD_LIBRARY_PATH'] = '/usr/lib:/usr/local/lib:/path/to/cvsgraph'
@@ -2410,11 +2475,11 @@ def view_cvsgraph(request):
escape=1, partial=1), escape=1, partial=1),
rcsfile), 'rb', 0) rcsfile), 'rb', 0)
data.update({ data = common_template_data(request)
data.merge(ezt.TemplateData({
'imagemap' : fp, 'imagemap' : fp,
'imagesrc' : imagesrc, 'imagesrc' : imagesrc,
}) }))
generate_page(request, "graph", data) generate_page(request, "graph", data)
def search_file(repos, path_parts, rev, search_re): def search_file(repos, path_parts, rev, search_re):
@@ -2789,6 +2854,10 @@ def setup_diff(request):
def view_patch(request): def view_patch(request):
if 'diff' not in request.cfg.options.allowed_views:
raise debug.ViewVCException('Diff generation is disabled',
'403 Forbidden')
cfg = request.cfg cfg = request.cfg
query_dict = request.query_dict query_dict = request.query_dict
p1, p2, rev1, rev2, sym1, sym2 = setup_diff(request) p1, p2, rev1, rev2, sym1, sym2 = setup_diff(request)
@@ -2822,6 +2891,10 @@ def view_patch(request):
def view_diff(request): def view_diff(request):
if 'diff' not in request.cfg.options.allowed_views:
raise debug.ViewVCException('Diff generation is disabled',
'403 Forbidden')
cfg = request.cfg cfg = request.cfg
query_dict = request.query_dict query_dict = request.query_dict
p1, p2, rev1, rev2, sym1, sym2 = setup_diff(request) p1, p2, rev1, rev2, sym1, sym2 = setup_diff(request)
@@ -2913,20 +2986,35 @@ def view_diff(request):
no_format_params = request.query_dict.copy() no_format_params = request.query_dict.copy()
no_format_params['diff_format'] = None no_format_params['diff_format'] = None
fvi = get_file_view_info(request, path_left, rev1)
left = _item(date=rcsdiff_date_reformat(date1, cfg), left = _item(date=rcsdiff_date_reformat(date1, cfg),
path=path_left, rev=rev1, tag=sym1) path=path_left,
left.view_href, left.download_href, left.download_text_href, \ rev=rev1,
left.annotate_href, left.revision_href, left.prefer_markup \ tag=sym1,
= get_file_view_info(request, path_left, rev1) view_href=fvi.view_href,
download_href=fvi.download_href,
download_text_href=fvi.download_text_href,
annotate_href=fvi.annotate_href,
revision_href=fvi.revision_href,
prefer_markup=fvi.prefer_markup)
fvi = get_file_view_info(request, path_right, rev2)
right = _item(date=rcsdiff_date_reformat(date2, cfg), right = _item(date=rcsdiff_date_reformat(date2, cfg),
path=path_right, rev=rev2, tag=sym2) path=path_right,
right.view_href, right.download_href, right.download_text_href, \ rev=rev2,
right.annotate_href, right.revision_href, right.prefer_markup \ tag=sym2,
= get_file_view_info(request, path_right, rev2) view_href=fvi.view_href,
download_href=fvi.download_href,
download_text_href=fvi.download_text_href,
annotate_href=fvi.annotate_href,
revision_href=fvi.revision_href,
prefer_markup=fvi.prefer_markup)
diff_format_action, diff_format_hidden_values = \
request.get_form(params=no_format_params)
data = common_template_data(request) data = common_template_data(request)
data.update({ data.merge(ezt.TemplateData({
'left' : left, 'left' : left,
'right' : right, 'right' : right,
'raw_diff' : raw_diff_fp, 'raw_diff' : raw_diff_fp,
@@ -2938,11 +3026,9 @@ def view_diff(request):
'patch_href' : request.get_url(view_func=view_patch, 'patch_href' : request.get_url(view_func=view_patch,
params=no_format_params, params=no_format_params,
escape=1), escape=1),
}) 'diff_format_action' : diff_format_action,
'diff_format_hidden_values' : diff_format_hidden_values,
data['diff_format_action'], data['diff_format_hidden_values'] = \ }))
request.get_form(params=no_format_params)
generate_page(request, "diff", data) generate_page(request, "diff", data)
@@ -3136,11 +3222,11 @@ def download_tarball(request):
def view_revision(request): def view_revision(request):
if request.roottype == "cvs": if request.roottype == "cvs":
raise ViewVCException("Revision view not supported for CVS repositories " raise debug.ViewVCException("Revision view not supported for CVS "
"at this time.", "400 Bad Request") "repositories at this time.",
"400 Bad Request")
cfg = request.cfg cfg = request.cfg
data = common_template_data(request)
query_dict = request.query_dict query_dict = request.query_dict
try: try:
rev = request.repos._getrev(query_dict.get('revision')) rev = request.repos._getrev(query_dict.get('revision'))
@@ -3169,6 +3255,7 @@ def view_revision(request):
more_changes_href = None more_changes_href = None
first_changes = None first_changes = None
first_changes_href = None first_changes_href = None
num_changes = len(changes)
if limit_changes and len(changes) > limit_changes: if limit_changes and len(changes) > limit_changes:
more_changes = len(changes) - limit_changes more_changes = len(changes) - limit_changes
params = query_dict.copy() params = query_dict.copy()
@@ -3262,28 +3349,28 @@ def view_revision(request):
pathtype=None, pathtype=None,
params={'revision': str(rev + 1)}, params={'revision': str(rev + 1)},
escape=1) escape=1)
data.update({ jump_rev_action, jump_rev_hidden_values = \
request.get_form(params={'revision': None})
data = common_template_data(request)
data.merge(ezt.TemplateData({
'rev' : str(rev), 'rev' : str(rev),
'author' : author, 'author' : author,
'date' : date_str, 'date' : date_str,
'log' : msg and htmlify(msg, cfg.options.mangle_email_addresses) or None, 'log' : msg and htmlify(msg, cfg.options.mangle_email_addresses) or None,
'ago' : None, 'ago' : date is not None and html_time(request, date, 1) or None,
'changes' : changes, 'changes' : changes,
'prev_href' : prev_rev_href, 'prev_href' : prev_rev_href,
'next_href' : next_rev_href, 'next_href' : next_rev_href,
'num_changes' : num_changes,
'limit_changes': limit_changes, 'limit_changes': limit_changes,
'more_changes': more_changes, 'more_changes': more_changes,
'more_changes_href': more_changes_href, 'more_changes_href': more_changes_href,
'first_changes': first_changes, 'first_changes': first_changes,
'first_changes_href': first_changes_href, 'first_changes_href': first_changes_href,
}) 'jump_rev_action' : jump_rev_action,
'jump_rev_hidden_values' : jump_rev_hidden_values,
if date is not None: }))
data['ago'] = html_time(request, date, 1)
data['jump_rev_action'], data['jump_rev_hidden_values'] = \
request.get_form(params={'revision': None})
if rev == youngest_rev: if rev == youngest_rev:
request.server.addheader("Cache-control", "no-store") request.server.addheader("Cache-control", "no-store")
generate_page(request, "revision", data) generate_page(request, "revision", data)
@@ -3316,32 +3403,32 @@ def view_queryform(request):
% (request.rootname, request.where), % (request.rootname, request.where),
'403 Forbidden') '403 Forbidden')
data = common_template_data(request) query_action, query_hidden_values = \
data['query_action'], data['query_hidden_values'] = \
request.get_form(view_func=view_query, params={'limit_changes': None}) request.get_form(view_func=view_query, params={'limit_changes': None})
limit_changes = \
# default values ... int(request.query_dict.get('limit_changes',
data['branch'] = request.query_dict.get('branch', '') request.cfg.options.limit_changes))
data['branch_match'] = request.query_dict.get('branch_match', 'exact')
data['dir'] = request.query_dict.get('dir', '') data = common_template_data(request)
data['file'] = request.query_dict.get('file', '') data.merge(ezt.TemplateData({
data['file_match'] = request.query_dict.get('file_match', 'exact') 'branch' : request.query_dict.get('branch', ''),
data['who'] = request.query_dict.get('who', '') 'branch_match' : request.query_dict.get('branch_match', 'exact'),
data['who_match'] = request.query_dict.get('who_match', 'exact') 'dir' : request.query_dict.get('dir', ''),
data['comment'] = request.query_dict.get('comment', '') 'file' : request.query_dict.get('file', ''),
data['comment_match'] = request.query_dict.get('comment_match', 'exact') 'file_match' : request.query_dict.get('file_match', 'exact'),
data['querysort'] = request.query_dict.get('querysort', 'date') 'who' : request.query_dict.get('who', ''),
data['date'] = request.query_dict.get('date', 'hours') 'who_match' : request.query_dict.get('who_match', 'exact'),
data['hours'] = request.query_dict.get('hours', '2') 'comment' : request.query_dict.get('comment', ''),
data['mindate'] = request.query_dict.get('mindate', '') 'comment_match' : request.query_dict.get('comment_match', 'exact'),
data['maxdate'] = request.query_dict.get('maxdate', '') 'querysort' : request.query_dict.get('querysort', 'date'),
data['limit_changes'] = int(request.query_dict.get('limit_changes', 'date' : request.query_dict.get('date', 'hours'),
request.cfg.options.limit_changes)) 'hours' : request.query_dict.get('hours', '2'),
'mindate' : request.query_dict.get('mindate', ''),
data['dir_href'] = request.get_url(view_func=view_directory, params={}, 'maxdate' : request.query_dict.get('maxdate', ''),
escape=1) 'limit_changes' : limit_changes,
'dir_href' : request.get_url(view_func=view_directory, params={},
escape=1),
}))
generate_page(request, "query_form", data) generate_page(request, "query_form", data)
def parse_date(datestr): def parse_date(datestr):
@@ -3812,7 +3899,7 @@ def view_query(request):
return return
data = common_template_data(request) data = common_template_data(request)
data.update({ data.merge(ezt.TemplateData({
'sql': sql, 'sql': sql,
'english_query': english_query(request), 'english_query': english_query(request),
'queryform_href': request.get_url(view_func=view_queryform, escape=1), 'queryform_href': request.get_url(view_func=view_queryform, escape=1),
@@ -3828,8 +3915,7 @@ def view_query(request):
params={'date': 'month'}, params={'date': 'month'},
escape=1, escape=1,
prefix=1), prefix=1),
}) }))
if format == 'rss': if format == 'rss':
generate_page(request, "rss", data, "application/rss+xml") generate_page(request, "rss", data, "application/rss+xml")
else: else:
@@ -3965,9 +4051,14 @@ def load_config(pathname=None, server=None):
cfg.set_defaults() cfg.set_defaults()
cfg.load_config(pathname, server and server.getenv("HTTP_HOST")) cfg.load_config(pathname, server and server.getenv("HTTP_HOST"))
# load mime types file # Load mime types file(s), but reverse the order -- our
if cfg.general.mime_types_file: # configuration uses a most-to-least preferred approach, but the
mimetypes.init([cfg.general.mime_types_file]) # 'mimetypes' package wants things the other way around.
if cfg.general.mime_types_files:
files = cfg.general.mime_types_files[:]
files.reverse()
files = map(lambda x: os.path.join(os.path.dirname(pathname), x), files)
mimetypes.init(files)
debug.t_end('load-config') debug.t_end('load-config')
return cfg return cfg
@@ -4012,7 +4103,7 @@ def main(server, cfg):
finally: finally:
debug.t_end('main') debug.t_end('main')
debug.dump() debug.t_dump(server.file())
debug.DumpChildren(server) debug.DumpChildren(server)

View File

@@ -50,7 +50,7 @@ numbers, and not literal):
9. Go into an empty directory and run the 'make-release' script: 9. Go into an empty directory and run the 'make-release' script:
tools/make-release viewvc-X.Y.Z X.Y.Z tools/make-release viewvc-X.Y.Z tags/X.Y.Z
10. Verify the archive files: 10. Verify the archive files:

View File

@@ -49,7 +49,7 @@
[else] [else]
[is changes.type "add"] [is changes.type "add"]
<tr> <tr>
<td id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td> <td class="vc_diff_line_number" id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
<td class="vc_diff_empty">&nbsp;</td> <td class="vc_diff_empty">&nbsp;</td>
<td class="vc_diff_add">&nbsp;[changes.right]</td> <td class="vc_diff_add">&nbsp;[changes.right]</td>
</tr> </tr>
@@ -64,7 +64,7 @@
[is changes.type "change"] [is changes.type "change"]
<tr> <tr>
[if-any changes.have_right] [if-any changes.have_right]
<td id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td> <td class="vc_diff_line_number" id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
[else] [else]
<td></td> <td></td>
[end] [end]
@@ -112,7 +112,7 @@
</tr> </tr>
[else] [else]
<tr> <tr>
<td id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td> <td class="vc_diff_line_number" id="l[changes.line_number]">[if-any right.annotate_href]<a href="[right.annotate_href]#l[changes.line_number]">[changes.line_number]</a>[else][changes.line_number][end]</td>
<td class="vc_diff_nochange">&nbsp;[changes.left]</td> <td class="vc_diff_nochange">&nbsp;[changes.left]</td>
<td class="vc_diff_nochange">&nbsp;[changes.right]</td> <td class="vc_diff_nochange">&nbsp;[changes.right]</td>
</tr> </tr>

View File

@@ -4,23 +4,31 @@
<thead> <thead>
<tr> <tr>
<th style="width: 200px" class="vc_header[is sortby "file"]_sort[end]"> <th style="width: 200px" class="vc_header[is sortby "file"]_sort[end]">
<a href="[sortby_file_href]#dirlist">File [if-any sortby_file_href]<a href="[sortby_file_href]#dirlist">File</a>[else]File[end]
[is sortby "file"] [is sortby "file"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a>
</th> </th>
<th style="width: 96px" class="vc_header"></th> <th style="width: 96px" class="vc_header"></th>
[if-any sortby_rev_href]
<th class="vc_header[is sortby "rev"]_sort[end]"> <th class="vc_header[is sortby "rev"]_sort[end]">
<a href="[sortby_rev_href]#dirlist">Last Change <a href="[sortby_rev_href]#dirlist">Last Change</a>
[is sortby "rev"] [is sortby "rev"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a> [else]
<th class="vc_header[is sortby "date"]_sort[end]">
[if-any sortby_date_href]<a href="[sortby_date_href]#dirlist">Last Change</a>[else]Last Change[end]
[is sortby "date"]
<img class="vc_sortarrow" alt="[is sortdir "down"](date)[end]"
width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end]
[end]
</th> </th>
</tr> </tr>
</thead> </thead>

View File

@@ -4,50 +4,45 @@
<thead> <thead>
<tr> <tr>
<th class="vc_header[is sortby "file"]_sort[end]" colspan="2"> <th class="vc_header[is sortby "file"]_sort[end]" colspan="2">
<a href="[sortby_file_href]#dirlist">File [if-any sortby_file_href]<a href="[sortby_file_href]#dirlist">File</a>[else]File[end]
[is sortby "file"] [is sortby "file"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a>
</th> </th>
<th class="vc_header[is sortby "rev"]_sort[end]"> <th class="vc_header[is sortby "rev"]_sort[end]">
<a href="[sortby_rev_href]#dirlist">Rev. [if-any sortby_rev_href]<a href="[sortby_rev_href]#dirlist">Rev.</a>[else]Rev.[end]
[is sortby "rev"] [is sortby "rev"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a>
</th> </th>
<th class="vc_header[is sortby "date"]_sort[end]"> <th class="vc_header[is sortby "date"]_sort[end]">
<a href="[sortby_date_href]#dirlist">Age [if-any sortby_date_href]<a href="[sortby_date_href]#dirlist">Age</a>[else]Age[end]
[is sortby "date"] [is sortby "date"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a>
</th> </th>
<th class="vc_header[is sortby "author"]_sort[end]"> <th class="vc_header[is sortby "author"]_sort[end]">
<a href="[sortby_author_href]#dirlist">Author [if-any sortby_author_href]<a href="[sortby_author_href]#dirlist">Author</a>[else]Author[end]
[is sortby "author"] [is sortby "author"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a>
</th> </th>
[is cfg.options.show_logs "1"] [is cfg.options.show_logs "1"]
<th class="vc_header[is sortby "log"]_sort[end]"> <th class="vc_header[is sortby "log"]_sort[end]">
<a href="[sortby_log_href]#dirlist">Last log entry [if-any sortby_log_href]<a href="[sortby_log_href]#dirlist">Last log entry</a>[else]Last log entry[end]
[is sortby "log"] [is sortby "log"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]" <img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13" width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" /> src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end] [end]
</a>
</th> </th>
[end] [end]
</tr> </tr>

View File

@@ -229,6 +229,8 @@ form { margin: 0; }
font-family: sans-serif; font-family: sans-serif;
font-size: smaller; font-size: smaller;
} }
.vc_diff_line_number {
}
.vc_raw_diff { .vc_raw_diff {
background-color: #cccccc; background-color: #cccccc;
font-size: smaller; font-size: smaller;

View File

@@ -8,7 +8,7 @@
<hr /> <hr />
<div class="vc_summary"> <div class="vc_summary">
Revision [if-any revision_href]<a href="[revision_href]"><strong>[rev]</strong></a>[else]<strong>[rev]</strong>[end] - Revision [if-any revision_href]<a href="[revision_href]"><strong>[rev]</strong></a>[else]<strong>[rev]</strong>[end] -
([if-any annotation][is annotation "annotated"]<a href="[view_href]"><strong>hide annotations</strong></a>[end][else]<a href="[annotate_href]"><strong>show annotations</strong></a>[end]) ([is annotation "annotated"]<a href="[view_href]"><strong>hide annotations</strong></a>[else]<a href="[annotate_href]"><strong>show annotations</strong></a>[end])
[if-any download_href](<a href="[download_href]"><strong>download</strong></a>)[end] [if-any download_href](<a href="[download_href]"><strong>download</strong></a>)[end]
[if-any download_text_href](<a href="[download_text_href]"><strong>as text</strong></a>)[end] [if-any download_text_href](<a href="[download_text_href]"><strong>as text</strong></a>)[end]
@@ -43,14 +43,12 @@ Revision [if-any revision_href]<a href="[revision_href]"><strong>[rev]</strong><
[if-any lockinfo] [if-any lockinfo]
<br />Lock status: <img src="[docroot]/images/lock.png" alt="Locked" width="16" height="16" /> [lockinfo] <br />Lock status: <img src="[docroot]/images/lock.png" alt="Locked" width="16" height="16" /> [lockinfo]
[end] [end]
[if-any annotation]
[is annotation "binary"] [is annotation "binary"]
<br /><strong>Unable to calculate annotation data on binary file contents.</strong> <br /><strong>Unable to calculate annotation data on binary file contents.</strong>
[end] [end]
[is annotation "error"] [is annotation "error"]
<br /><strong>Error occurred while calculating annotation data.</strong> <br /><strong>Error occurred while calculating annotation data.</strong>
[end] [end]
[end]
[is state "dead"] [is state "dead"]
<br /><strong><em>FILE REMOVED</em></strong> <br /><strong><em>FILE REMOVED</em></strong>
[end] [end]

View File

@@ -32,7 +32,7 @@
<td>[include "pathrev_form.ezt"]</td> <td>[include "pathrev_form.ezt"]</td>
</tr> </tr>
[if-any search_re_form] [if-any search_re_action]
<tr> <tr>
<td>Filter files by content:</td> <td>Filter files by content:</td>
<td><form method="get" action="[search_re_action]" style="display: inline;"> <td><form method="get" action="[search_re_action]" style="display: inline;">
@@ -63,7 +63,7 @@
</table> </table>
[is cfg.options.use_pagesize "0"] [is picklist_len "0"]
[else] [else]
[is picklist_len "1"] [is picklist_len "1"]
[else] [else]

View File

@@ -2,7 +2,7 @@
[is pathtype "file"] [is pathtype "file"]
<a href="[up_href]"><img src="[docroot]/images/back_small.png" class="vc_icon" alt="Parent Directory" /> Parent Directory</a> <a href="[up_href]"><img src="[docroot]/images/back_small.png" class="vc_icon" alt="Parent Directory" /> Parent Directory</a>
[if-any log_href] [if-any log_href]
| <a href="[log_href][if-any log_href_rev]#rev[log_href_rev][end]"><img src="[docroot]/images/log.png" class="vc_icon" alt="Revision Log" /> Revision Log</a> | <a href="[log_href]"><img src="[docroot]/images/log.png" class="vc_icon" alt="Revision Log" /> Revision Log</a>
[end] [end]
[if-any graph_href] [if-any graph_href]
| <a href="[graph_href]"><img src="[docroot]/images/cvsgraph_16x16.png" class="vc_icon" alt="View Revision Graph" /> Revision Graph</a> | <a href="[graph_href]"><img src="[docroot]/images/cvsgraph_16x16.png" class="vc_icon" alt="View Revision Graph" /> Revision Graph</a>

View File

@@ -1,4 +1,4 @@
[is cfg.options.use_pagesize "0"] [is picklist_len "0"]
[else] [else]
[is picklist_len "1"] [is picklist_len "1"]
[else] [else]
@@ -17,6 +17,4 @@
</select> </select>
</form> </form>
[end] [end]
[end] [end]

View File

@@ -28,6 +28,11 @@
<td>[if-any date][date][else]<em>(unknown date)</em>[end] <td>[if-any date][date][else]<em>(unknown date)</em>[end]
[if-any ago]<em>([ago] ago)</em>[end]</td> [if-any ago]<em>([ago] ago)</em>[end]</td>
</tr> </tr>
<tr align="left">
<th>Changed paths:</th>
<td><strong>[num_changes]</strong>
[if-any more_changes](showing only [limit_changes]; <a href="[more_changes_href]">show all</a>)[end][if-any first_changes](<a href="[first_changes_href]">show only first [first_changes]</a>)[end]</td>
</tr>
<tr align="left"> <tr align="left">
<th>Log Message:</th> <th>Log Message:</th>
<td><pre class="vc_log">[log]</pre></td> <td><pre class="vc_log">[log]</pre></td>
@@ -39,17 +44,6 @@
<p><strong>Changed paths:</strong></p> <p><strong>Changed paths:</strong></p>
[if-any more_changes]
<div>
Only [limit_changes] changes shown,
<a href="[more_changes_href]">display [more_changes] more changes...</a>
</div>
[end]
[if-any first_changes]
<div><a href="[first_changes_href]">Show only first [first_changes] changes...</div>
[end]
<table cellspacing="1" cellpadding="2"> <table cellspacing="1" cellpadding="2">
<thead> <thead>
<tr align="left"> <tr align="left">
@@ -75,6 +69,11 @@
<td colspan="5">No changed paths.</td> <td colspan="5">No changed paths.</td>
</tr> </tr>
[end] [end]
[if-any more_changes]
<tr>
<td colspan="5">[[]<a href="[more_changes_href]">...</a>]</td>
</tr>
[end]
</tbody> </tbody>
</table> </table>

View File

@@ -17,17 +17,18 @@
### Validate input ### Validate input
if test $# != 2 && test $# != 1; then if test $# != 2 && test $# != 1; then
echo "Usage: $0 TARGET-DIRECTORY [TAGNAME]" echo "Usage: $0 TARGET-DIRECTORY [BRANCH]"
echo "" echo ""
echo "If TAGNAME is not provided, the release will be rolled from trunk." echo "If BRANCH (i.e. \"tags/1.1.0\" or \"branches/1.0.x\") is not provided,"
echo "the release will be rolled from trunk."
exit 1 exit 1
fi fi
TARGET=${1} TARGET=${1}
if test $# == 1; then if test $# = 1; then
ROOT=trunk ROOT=trunk
else else
ROOT=tags/${2} ROOT=${2}
fi fi
if test -e ${TARGET}; then if test -e ${TARGET}; then
@@ -36,7 +37,8 @@ if test -e ${TARGET}; then
fi fi
### Grab an export from the Subversion repository. ### Grab an export from the Subversion repository.
echo "Exporting into:" ${TARGET} EXPORT_URL="http://viewvc.tigris.org/svn/viewvc/${ROOT}"
echo "Exporting '${EXPORT_URL}' into '${TARGET}'"
for PLATFORM in unix windows; do for PLATFORM in unix windows; do
if test ${PLATFORM} = windows; then if test ${PLATFORM} = windows; then
@@ -48,7 +50,7 @@ for PLATFORM in unix windows; do
echo "Beginning build for ${PLATFORM}:" echo "Beginning build for ${PLATFORM}:"
echo " Exporting source code..." echo " Exporting source code..."
svn export --quiet ${EOL} http://viewvc.tigris.org/svn/viewvc/${ROOT} ${TARGET} svn export --quiet ${EOL} ${EXPORT_URL} ${TARGET}
### Various shifting, cleanup. ### Various shifting, cleanup.

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- Mode: python -*- # -*- Mode: python -*-
# #
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -58,10 +58,12 @@ FILE_INFO_LIST = [
("bin/cvsdbadmin", "bin/cvsdbadmin", 0755, 1, 0, 0), ("bin/cvsdbadmin", "bin/cvsdbadmin", 0755, 1, 0, 0),
("bin/svndbadmin", "bin/svndbadmin", 0755, 1, 0, 0), ("bin/svndbadmin", "bin/svndbadmin", 0755, 1, 0, 0),
("bin/make-database", "bin/make-database", 0755, 1, 0, 0), ("bin/make-database", "bin/make-database", 0755, 1, 0, 0),
("viewvc.conf.dist", "viewvc.conf.dist", 0644, 0, 0, 0), ("conf/viewvc.conf.dist", "viewvc.conf.dist", 0644, 0, 0, 0),
("viewvc.conf.dist", "viewvc.conf", 0644, 0, 1, 0), ("conf/viewvc.conf.dist", "viewvc.conf", 0644, 0, 1, 0),
("cvsgraph.conf.dist", "cvsgraph.conf.dist", 0644, 0, 0, 0), ("conf/cvsgraph.conf.dist", "cvsgraph.conf.dist", 0644, 0, 0, 0),
("cvsgraph.conf.dist", "cvsgraph.conf", 0644, 0, 1, 0), ("conf/cvsgraph.conf.dist", "cvsgraph.conf", 0644, 0, 1, 0),
("conf/mimetypes.conf.dist", "mimetypes.conf.dist", 0644, 0, 0, 0),
("conf/mimetypes.conf.dist", "mimetypes.conf", 0644, 0, 1, 0),
] ]
if sys.platform == "win32": if sys.platform == "win32":
FILE_INFO_LIST.extend([ FILE_INFO_LIST.extend([