1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
1 |
# IVLE
|
2 |
# Copyright (C) 2007-2008 The University of Melbourne
|
|
3 |
#
|
|
4 |
# This program is free software; you can redistribute it and/or modify
|
|
5 |
# it under the terms of the GNU General Public License as published by
|
|
6 |
# the Free Software Foundation; either version 2 of the License, or
|
|
7 |
# (at your option) any later version.
|
|
8 |
#
|
|
9 |
# This program is distributed in the hope that it will be useful,
|
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12 |
# GNU General Public License for more details.
|
|
13 |
#
|
|
14 |
# You should have received a copy of the GNU General Public License
|
|
15 |
# along with this program; if not, write to the Free Software
|
|
16 |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17 |
||
18 |
# Module: File Service / Listing
|
|
19 |
# Author: Matt Giuca
|
|
20 |
# Date: 10/1/2008
|
|
21 |
||
22 |
# Handles the return part of the 2-stage process of fileservice. This
|
|
23 |
# is both the directory listing, and the raw serving of non-directory files.
|
|
24 |
||
25 |
# File Service Format.
|
|
26 |
# If a non-directory file is requested, then the HTTP response body will be
|
|
27 |
# the verbatim bytes of that file (if the file is valid). The HTTP response
|
|
28 |
# headers will include the guessed content type of the file, and the header
|
|
29 |
# "X-IVLE-Return: File".
|
|
30 |
||
31 |
# Directory Listing Format.
|
|
32 |
# If the path requested is a directory, then the HTTP response body will be
|
|
33 |
# a valid JSON string describing the directory. The HTTP response headers
|
|
34 |
# will include the header "X-IVLE-Return: Dir".
|
|
35 |
#
|
|
36 |
# The JSON structure is as follows:
|
|
37 |
# * The top-level value is an object. It always contains the key "listing",
|
|
38 |
# whose value is the primary listing object. It may also contain a key
|
|
39 |
# "clipboard" which contains the clipboard object.
|
|
40 |
# * The value for "listing" is an object, with one member for each file in the
|
|
41 |
# directory, plus an additional member (key ".") for the directory itself.
|
|
42 |
# * Each member's key is the filename. Its value is an object, which has
|
|
43 |
# various members describing the file.
|
|
44 |
# The members of this object are as follows:
|
|
45 |
# * svnstatus: String. The svn status of the file. Either all files in a
|
|
46 |
# directory or no files have an svnstatus. String may take the values:
|
|
47 |
# - none - does not exist
|
|
48 |
# - unversioned - is not a versioned thing in this wc
|
|
49 |
# - normal - exists, but uninteresting.
|
|
50 |
# - added - is scheduled for addition
|
|
51 |
# - missing - under v.c., but is missing
|
|
52 |
# - deleted - scheduled for deletion
|
|
53 |
# - replaced - was deleted and then re-added
|
|
54 |
# - modified - text or props have been modified
|
|
55 |
# - merged - local mods received repos mods
|
|
56 |
# - conflicted - local mods received conflicting repos mods
|
|
57 |
# - ignored - a resource marked as ignored
|
|
58 |
# - obstructed - an unversioned resource is in the way of the versioned resource
|
|
59 |
# - external - an unversioned path populated by an svn:external property
|
|
60 |
# - incomplete - a directory doesn't contain a complete entries list
|
|
61 |
# (From pysvn)
|
|
62 |
# If svnstatus is "Missing" then the file has no other attributes.
|
|
63 |
# * published: Boolean. True if the file is published. (Marked by a
|
|
64 |
# .published file in the folder)
|
|
65 |
# * isdir: Boolean. True if the file is a directory. Always present unless
|
|
66 |
# svnstatus is "missing".
|
|
67 |
# * size: Number. Size of the file in bytes. Present for non-directory
|
|
68 |
# files.
|
|
69 |
# * type: String. Guessed mime type of the file. Present for non-directory
|
|
70 |
# files.
|
|
71 |
# * mtime: Number. Number of seconds elapsed since the epoch.
|
|
72 |
# The epoch is not defined (this is an arbitrary number used for sorting
|
|
73 |
# dates).
|
|
74 |
# * mtime_nice: String. Modification time of the file or directory. Always
|
|
75 |
# present unless svnstatus is "Missing". Human-friendly.
|
|
76 |
#
|
|
77 |
# Members are not guaranteed to be present - client code should always check
|
|
78 |
# for each member that it is present, and handle gracefully if a member is not
|
|
79 |
# present.
|
|
80 |
#
|
|
81 |
# The listing object is guaranteed to have a "." key. Use this key to
|
|
82 |
# determine whether the directory is under version control or not. If this
|
|
83 |
# member does NOT have a "svnstatus" key, or "svnstatus" is "unversioned",
|
|
84 |
# then the directory is not under revision control (and no other files will
|
|
85 |
# have "svnstatus" either).
|
|
86 |
#
|
|
87 |
# The top-level object MAY contain a "clipboard" key, which specifies the
|
|
88 |
# files copied to the clipboard. This can be used by the client to show the
|
|
89 |
# user what files will be pasted. At the very least, the client should take
|
|
90 |
# the presence or absence of a "clipboard" key as whether to grey out the
|
|
91 |
# "paste" button.
|
|
92 |
#
|
|
93 |
# The "clipboard" object has three members:
|
|
94 |
# * mode: String. Either "copy" or "cut".
|
|
95 |
# * base: String. Path relative to the user's root. The common path between
|
|
96 |
# the files.
|
|
97 |
# * files: Array of Strings. Each element is a filename relative to base.
|
|
98 |
# Base and files exactly correspond to the listing path and argument paths
|
|
99 |
# which were supplied during the last copy or cut request.
|
|
100 |
||
101 |
import os |
|
102 |
import sys |
|
103 |
import stat |
|
104 |
import mimetypes |
|
105 |
import urlparse |
|
106 |
from cgi import parse_qs |
|
107 |
||
108 |
import cjson |
|
109 |
import pysvn |
|
110 |
||
111 |
import ivle.svn |
|
112 |
import ivle.date |
|
1207
by William Grant
Move ivle.conf.mimetypes to ivle.mimetypes, and rename things in it. |
113 |
import ivle.mimetypes |
114 |
from ivle import studpath |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
115 |
|
116 |
# Make a Subversion client object
|
|
117 |
svnclient = pysvn.Client() |
|
118 |
||
119 |
# Whether or not to ignore dot files.
|
|
120 |
# TODO check settings!
|
|
121 |
ignore_dot_files = True |
|
122 |
||
123 |
# Mime types
|
|
124 |
# application/json is the "best" content type but is not good for
|
|
125 |
# debugging because Firefox just tries to download it
|
|
126 |
mime_dirlisting = "text/plain" |
|
127 |
#mime_dirlisting = "application/json"
|
|
128 |
||
1632
by Matt Giuca
listing.py: Fixed regression crash when viewing unversioned directories or files, due to silly code raising pysvn.ClientError (not an instance of it). Now raise a special exception UnversionedDir, which is checked properly. |
129 |
# Indicates that a directory is unversioned
|
130 |
class UnversionedDir(Exception): |
|
131 |
pass
|
|
132 |
||
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
133 |
def handle_return(req, return_contents): |
134 |
"""
|
|
135 |
Perform the "return" part of the response.
|
|
136 |
This function returns the file or directory listing contained in
|
|
137 |
req.path. Sets the HTTP response code in req, writes additional headers,
|
|
138 |
and writes the HTTP response, if any.
|
|
139 |
||
140 |
If return_contents is True, and the path is a non-directory, returns the
|
|
141 |
contents of the file verbatim. If False, returns a directory listing
|
|
142 |
with a single file, ".", and info about the file.
|
|
143 |
||
144 |
If the path is a directory, return_contents is ignored.
|
|
145 |
"""
|
|
146 |
||
1272
by William Grant
.. and one more |
147 |
path = studpath.to_home_path(req.path) |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
148 |
|
149 |
# FIXME: What to do about req.path == ""?
|
|
150 |
# Currently goes to 403 Forbidden.
|
|
1089
by chadnickbok
Fixes Issue #14 |
151 |
json = None |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
152 |
if path is None: |
153 |
req.status = req.HTTP_FORBIDDEN |
|
154 |
req.headers_out['X-IVLE-Return-Error'] = 'Forbidden' |
|
155 |
req.write("Forbidden") |
|
1193
by Matt Giuca
ivle.svn: Added revision_is_dir (like os.path.isdir for revision history). |
156 |
return
|
157 |
||
158 |
# If this is a repository-revision request, it needs to be treated
|
|
159 |
# differently than if it were a regular file request.
|
|
160 |
# Note: If there IS a revision requested but the file doesn't exist in
|
|
161 |
# that revision, this will terminate.
|
|
162 |
revision = _get_revision_or_die(req, svnclient, path) |
|
163 |
||
1194
by Matt Giuca
fileservice: Fixed a bug when browsing previous revisions, that the |
164 |
if revision is None: |
165 |
if not os.access(path, os.R_OK): |
|
166 |
req.status = req.HTTP_NOT_FOUND |
|
167 |
req.headers_out['X-IVLE-Return-Error'] = 'File not found' |
|
168 |
req.write("File not found") |
|
169 |
return
|
|
170 |
is_dir = os.path.isdir(path) |
|
171 |
else: |
|
172 |
is_dir = ivle.svn.revision_is_dir(svnclient, path, revision) |
|
173 |
||
174 |
if is_dir: |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
175 |
# It's a directory. Return the directory listing.
|
176 |
req.content_type = mime_dirlisting |
|
177 |
req.headers_out['X-IVLE-Return'] = 'Dir' |
|
1089
by chadnickbok
Fixes Issue #14 |
178 |
# TODO: Fix this dirty, dirty hack
|
1193
by Matt Giuca
ivle.svn: Added revision_is_dir (like os.path.isdir for revision history). |
179 |
newjson = get_dirlisting(req, svnclient, path, revision) |
1089
by chadnickbok
Fixes Issue #14 |
180 |
if ("X-IVLE-Action-Error" in req.headers_out): |
181 |
newjson["Error"] = req.headers_out["X-IVLE-Action-Error"] |
|
182 |
req.write(cjson.encode(newjson)) |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
183 |
elif return_contents: |
184 |
# It's a file. Return the file contents.
|
|
185 |
# First get the mime type of this file
|
|
186 |
(type, _) = mimetypes.guess_type(path) |
|
187 |
if type is None: |
|
1207
by William Grant
Move ivle.conf.mimetypes to ivle.mimetypes, and rename things in it. |
188 |
type = ivle.mimetypes.DEFAULT_MIMETYPE |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
189 |
req.content_type = type |
190 |
req.headers_out['X-IVLE-Return'] = 'File' |
|
191 |
||
1193
by Matt Giuca
ivle.svn: Added revision_is_dir (like os.path.isdir for revision history). |
192 |
send_file(req, svnclient, path, revision) |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
193 |
else: |
194 |
# It's a file. Return a "fake directory listing" with just this file.
|
|
195 |
req.content_type = mime_dirlisting |
|
196 |
req.headers_out['X-IVLE-Return'] = 'File' |
|
1193
by Matt Giuca
ivle.svn: Added revision_is_dir (like os.path.isdir for revision history). |
197 |
req.write(cjson.encode(get_dirlisting(req, svnclient, path, |
198 |
revision))) |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
199 |
|
200 |
def _get_revision_or_die(req, svnclient, path): |
|
1192
by Matt Giuca
ivle.fileservice_lib.listing: Proper docstring for the rather confusing |
201 |
"""Looks for a revision specification in req's URL.
|
202 |
Errors and terminates the request if the specification was bad, or it
|
|
203 |
doesn't exist for the given path.
|
|
204 |
@param req: Request object.
|
|
205 |
@param svnclient: pysvn Client object.
|
|
206 |
@param path: Path to the file whose revision is to be retrieved.
|
|
207 |
@returns: pysvn Revision object, for the file+revision specified, or None
|
|
208 |
if there was no revision specified.
|
|
209 |
"""
|
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
210 |
# Work out the revisions from query
|
211 |
r_str = req.get_fieldstorage().getfirst("r") |
|
212 |
revision = ivle.svn.revision_from_string(r_str) |
|
213 |
||
214 |
# Was some revision specified AND (it didn't resolve OR it was nonexistent)
|
|
215 |
if r_str and not (revision and |
|
216 |
ivle.svn.revision_exists(svnclient, path, revision)): |
|
217 |
req.status = req.HTTP_NOT_FOUND |
|
1195
by Matt Giuca
Fileservice: Improved the error message "Revision not found" to |
218 |
message = ('Revision not found or file not found in revision %d' % |
219 |
revision.number) |
|
220 |
req.headers_out['X-IVLE-Return-Error'] = message |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
221 |
req.ensure_headers_written() |
1195
by Matt Giuca
Fileservice: Improved the error message "Revision not found" to |
222 |
req.write(message) |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
223 |
req.flush() |
224 |
sys.exit() |
|
225 |
return revision |
|
226 |
||
1193
by Matt Giuca
ivle.svn: Added revision_is_dir (like os.path.isdir for revision history). |
227 |
def send_file(req, svnclient, path, revision): |
228 |
"""Given a local absolute path to a file, sends the contents of the file
|
|
229 |
to the client.
|
|
230 |
||
231 |
@param req: Request object. Will not be mutated; just reads the session.
|
|
232 |
@param svnclient: Svn client object.
|
|
233 |
@param path: String. Absolute path on the local file system. Not checked,
|
|
234 |
must already be guaranteed safe. May be a file or a directory.
|
|
235 |
@param revision: pysvn Revision object for the given path, or None.
|
|
236 |
"""
|
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
237 |
if revision: |
238 |
req.write(svnclient.cat(path, revision=revision)) |
|
239 |
else: |
|
240 |
req.sendfile(path) |
|
241 |
||
1193
by Matt Giuca
ivle.svn: Added revision_is_dir (like os.path.isdir for revision history). |
242 |
def get_dirlisting(req, svnclient, path, revision): |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
243 |
"""Given a local absolute path, creates a directory listing object
|
244 |
ready to be JSONized and sent to the client.
|
|
245 |
||
1193
by Matt Giuca
ivle.svn: Added revision_is_dir (like os.path.isdir for revision history). |
246 |
@param req: Request object. Will not be mutated; just reads the session.
|
247 |
@param svnclient: Svn client object.
|
|
248 |
@param path: String. Absolute path on the local file system. Not checked,
|
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
249 |
must already be guaranteed safe. May be a file or a directory.
|
1193
by Matt Giuca
ivle.svn: Added revision_is_dir (like os.path.isdir for revision history). |
250 |
@param revision: pysvn Revision object for the given path, or None.
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
251 |
"""
|
252 |
||
253 |
# Start by trying to do an SVN status, so we can report file version
|
|
254 |
# status
|
|
255 |
listing = {} |
|
1629
by Matt Giuca
ivle.fileservice_lib.listing: No longer detect not-a-working-copy errors by string matching; instead check error code. |
256 |
svnclient.exception_style = 1 # Get rich exceptions |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
257 |
try: |
258 |
if revision: |
|
259 |
ls_list = svnclient.list(path, revision=revision, recurse=False) |
|
260 |
for ls in ls_list: |
|
261 |
filename, attrs = PysvnList_to_fileinfo(path, ls) |
|
1634
by Matt Giuca
fileservice_lib.listing: Fix some encode/decode errors. Now possible to get a file listing of a SVN directory with Unicode characters in filenames, although a lot of other things still don't work. |
262 |
if isinstance(filename, str): # Expect a unicode from pysvn |
263 |
filename = filename.decode('utf-8') |
|
264 |
listing[filename] = attrs |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
265 |
else: |
266 |
status_list = svnclient.status(path, recurse=False, get_all=True, |
|
267 |
update=False) |
|
268 |
for status in status_list: |
|
269 |
filename, attrs = PysvnStatus_to_fileinfo(path, status) |
|
1634
by Matt Giuca
fileservice_lib.listing: Fix some encode/decode errors. Now possible to get a file listing of a SVN directory with Unicode characters in filenames, although a lot of other things still don't work. |
270 |
if isinstance(filename, str): # Expect a unicode from pysvn |
271 |
filename = filename.decode('utf-8') |
|
272 |
listing[filename] = attrs |
|
1632
by Matt Giuca
listing.py: Fixed regression crash when viewing unversioned directories or files, due to silly code raising pysvn.ClientError (not an instance of it). Now raise a special exception UnversionedDir, which is checked properly. |
273 |
except (pysvn.ClientError, UnversionedDir), e: |
1627
by Matt Giuca
ivle.fileservice_lib.listing: Previously assumed any SVN client error meant the directory was not versioned, and silently dropped SVN metadata. Now checks the error message, and for any unexpected errors, raises the exception rather than assuming unversioned. Fixes Launchpad Bug #523592. |
274 |
# Could indicate a serious SVN error, or just that the directory is
|
275 |
# not under version control (which is perfectly normal).
|
|
1629
by Matt Giuca
ivle.fileservice_lib.listing: No longer detect not-a-working-copy errors by string matching; instead check error code. |
276 |
# Error code 155007 is "<dir> is not a working copy"
|
1632
by Matt Giuca
listing.py: Fixed regression crash when viewing unversioned directories or files, due to silly code raising pysvn.ClientError (not an instance of it). Now raise a special exception UnversionedDir, which is checked properly. |
277 |
if isinstance(e, pysvn.ClientError) and e[1][0][1] != 155007: |
1627
by Matt Giuca
ivle.fileservice_lib.listing: Previously assumed any SVN client error meant the directory was not versioned, and silently dropped SVN metadata. Now checks the error message, and for any unexpected errors, raises the exception rather than assuming unversioned. Fixes Launchpad Bug #523592. |
278 |
# This is a serious error -- let it propagate upwards
|
279 |
raise
|
|
280 |
# The directory is not under version control.
|
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
281 |
# Fallback to just an OS file listing.
|
282 |
try: |
|
283 |
for filename in os.listdir(path): |
|
284 |
listing[filename.decode('utf-8')] = file_to_fileinfo(path, filename)[1] |
|
1086
by chadnickbok
This commit fixes issue #10 and part of issue #9 |
285 |
try: |
286 |
svnclient.status(os.path.join(path, filename), recurse = False) |
|
287 |
listing[filename.decode('utf-8')]['svnstatus'] = 'normal' |
|
288 |
except: |
|
289 |
pass
|
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
290 |
except OSError: |
291 |
# Non-directories will error - that's OK, we just want the "."
|
|
292 |
pass
|
|
293 |
# The subversion one includes "." while the OS one does not.
|
|
294 |
# Add "." to the output, so the caller can see we are
|
|
295 |
# unversioned.
|
|
296 |
listing["."] = file_to_fileinfo(path, "")[1] |
|
297 |
||
298 |
if ignore_dot_files: |
|
299 |
for fn in listing.keys(): |
|
300 |
if fn != "." and fn.startswith("."): |
|
301 |
del listing[fn] |
|
302 |
||
303 |
# Listing is a nested object inside the top-level JSON.
|
|
304 |
listing = {"listing" : listing} |
|
305 |
||
306 |
if revision: |
|
307 |
listing['revision'] = revision.number |
|
308 |
||
309 |
# The other object is the clipboard, if present in the browser session.
|
|
310 |
# This can go straight from the session to JSON.
|
|
311 |
session = req.get_session() |
|
312 |
if session and 'clipboard' in session: |
|
313 |
# In CGI mode, we can't get our hands on the
|
|
314 |
# session (for the moment), so just leave it out.
|
|
315 |
listing['clipboard'] = session['clipboard'] |
|
316 |
||
317 |
return listing |
|
318 |
||
319 |
def _fullpath_stat_fileinfo(fullpath): |
|
1634
by Matt Giuca
fileservice_lib.listing: Fix some encode/decode errors. Now possible to get a file listing of a SVN directory with Unicode characters in filenames, although a lot of other things still don't work. |
320 |
if isinstance(fullpath, unicode): |
321 |
fullpath = fullpath.encode('utf-8') # os.stat can't handle unicode |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
322 |
file_stat = os.stat(fullpath) |
323 |
return _stat_fileinfo(fullpath, file_stat) |
|
324 |
||
325 |
def _stat_fileinfo(fullpath, file_stat): |
|
326 |
d = {} |
|
327 |
if stat.S_ISDIR(file_stat.st_mode): |
|
328 |
d["isdir"] = True |
|
1207
by William Grant
Move ivle.conf.mimetypes to ivle.mimetypes, and rename things in it. |
329 |
d["type_nice"] = ivle.mimetypes.nice_filetype("/") |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
330 |
# Only directories can be published
|
331 |
d["published"] = studpath.published(fullpath) |
|
332 |
else: |
|
333 |
d["isdir"] = False |
|
334 |
d["size"] = file_stat.st_size |
|
335 |
(type, _) = mimetypes.guess_type(fullpath) |
|
336 |
if type is None: |
|
1207
by William Grant
Move ivle.conf.mimetypes to ivle.mimetypes, and rename things in it. |
337 |
type = ivle.mimetypes.DEFAULT_MIMETYPE |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
338 |
d["type"] = type |
1207
by William Grant
Move ivle.conf.mimetypes to ivle.mimetypes, and rename things in it. |
339 |
d["type_nice"] = ivle.mimetypes.nice_filetype(fullpath) |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
340 |
d["mtime"] = file_stat.st_mtime |
341 |
d["mtime_nice"] = ivle.date.make_date_nice(file_stat.st_mtime) |
|
342 |
d["mtime_short"] = ivle.date.make_date_nice_short(file_stat.st_mtime) |
|
343 |
return d |
|
344 |
||
345 |
def file_to_fileinfo(path, filename): |
|
346 |
"""Given a filename (relative to a given path), gets all the info "ls"
|
|
347 |
needs to display about the filename. Returns pair mapping filename to
|
|
348 |
a dict containing a number of other fields."""
|
|
349 |
fullpath = path if filename in ('', '.') else os.path.join(path, filename) |
|
350 |
return filename, _fullpath_stat_fileinfo(fullpath) |
|
351 |
||
352 |
def PysvnStatus_to_fileinfo(path, status): |
|
353 |
"""Given a PysvnStatus object, gets all the info "ls"
|
|
354 |
needs to display about the filename. Returns a pair mapping filename to
|
|
355 |
a dict containing a number of other fields."""
|
|
356 |
path = os.path.normcase(path) |
|
1639
by Matt Giuca
fileservice: Fixed a comparison from unicode and not unicode strings, so the directory listing of a file/dir with non-ASCII characters works. Fixes editor for such files. |
357 |
if isinstance(status.path, str): |
358 |
fullpath = status.path |
|
359 |
else: |
|
360 |
fullpath = status.path.encode("utf-8") |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
361 |
# If this is "." (the directory itself)
|
362 |
if path == os.path.normcase(fullpath): |
|
363 |
# If this directory is unversioned, then we aren't
|
|
364 |
# looking at any interesting files, so throw
|
|
365 |
# an exception and default to normal OS-based listing.
|
|
366 |
if status.text_status == pysvn.wc_status_kind.unversioned: |
|
1632
by Matt Giuca
listing.py: Fixed regression crash when viewing unversioned directories or files, due to silly code raising pysvn.ClientError (not an instance of it). Now raise a special exception UnversionedDir, which is checked properly. |
367 |
raise UnversionedDir() |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
368 |
# We actually want to return "." because we want its
|
369 |
# subversion status.
|
|
370 |
filename = "." |
|
371 |
else: |
|
372 |
filename = os.path.basename(fullpath) |
|
373 |
text_status = status.text_status |
|
374 |
d = {'svnstatus': str(text_status)} |
|
1165.1.31
by William Grant
Expose SVN revision and URL information through fileservice. |
375 |
|
376 |
if status.entry is not None: |
|
377 |
d.update({ |
|
378 |
'svnurl': status.entry.url, |
|
379 |
'svnrevision': status.entry.revision.number |
|
380 |
if status.entry.revision.kind == pysvn.opt_revision_kind.number |
|
381 |
else None, |
|
382 |
})
|
|
383 |
||
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
384 |
try: |
385 |
d.update(_fullpath_stat_fileinfo(fullpath)) |
|
386 |
except OSError: |
|
387 |
# Here if, eg, the file is missing.
|
|
388 |
# Can't get any more information so just return d
|
|
389 |
pass
|
|
390 |
return filename, d |
|
391 |
||
392 |
def PysvnList_to_fileinfo(path, list): |
|
393 |
"""Given a List object from pysvn.Client.list, gets all the info "ls"
|
|
394 |
needs to display about the filename. Returns a pair mapping filename to
|
|
395 |
a dict containing a number of other fields."""
|
|
396 |
path = os.path.normcase(path) |
|
397 |
pysvnlist = list[0] |
|
398 |
fullpath = pysvnlist.path |
|
399 |
# If this is "." (the directory itself)
|
|
400 |
if path == os.path.normcase(fullpath): |
|
401 |
# If this directory is unversioned, then we aren't
|
|
402 |
# looking at any interesting files, so throw
|
|
403 |
# an exception and default to normal OS-based listing.
|
|
404 |
#if status.text_status == pysvn.wc_status_kind.unversioned:
|
|
1632
by Matt Giuca
listing.py: Fixed regression crash when viewing unversioned directories or files, due to silly code raising pysvn.ClientError (not an instance of it). Now raise a special exception UnversionedDir, which is checked properly. |
405 |
# raise UnversionedDir()
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
406 |
# We actually want to return "." because we want its
|
407 |
# subversion status.
|
|
408 |
filename = "." |
|
409 |
else: |
|
410 |
filename = os.path.basename(fullpath) |
|
411 |
d = {'svnstatus': 'revision'} # A special status. |
|
412 |
||
413 |
wrapped = ivle.svn.PysvnListStatWrapper(pysvnlist) |
|
414 |
d.update(_stat_fileinfo(fullpath, wrapped)) |
|
415 |
||
416 |
return filename, d |