91
91
# action=svnupdate: Bring a file up to date with the head revision.
92
92
# path: The path to the file to be updated. Only one file may be
94
# revision: The revision number to update to. If not provided this
97
95
# action=svnpublish: Set the "published" flag on a file to True.
98
96
# path: The path to the file to be published. Can be specified
121
119
# path: The path to the directory to be checked (under the IVLE
122
120
# repository base).
124
# action=svncleanup: Recursively clean up the working copy, removing locks,
125
# resuming unfinished operations, etc.
126
# path: The path to the directory to be cleaned
128
122
# TODO: Implement the following actions:
129
123
# svnupdate (done?)
130
124
# TODO: Implement ZIP unpacking in putfiles (done?).
144
137
from ivle import (util, studpath, zip)
145
138
from ivle.fileservice_lib.exceptions import WillNotOverwrite
149
# Make a Subversion client object (which will log in with this user's
150
# credentials, upon request)
151
svnclient = ivle.svn.create_auth_svn_client(username=ivle.conf.login,
152
password=ivle.conf.svn_pass)
142
def get_login(_realm, existing_login, _may_save):
143
"""Callback function used by pysvn for authentication.
144
realm, existing_login, _may_save: The 3 arguments passed by pysvn to
146
The following has been determined empirically, not from docs:
147
existing_login will be the name of the user who owns the process on
148
the first attempt, "" on subsequent attempts. We use this fact.
150
# Only provide credentials on the _first_ attempt.
151
# If we're being asked again, then it means the credentials failed for
152
# some reason and we should just fail. (This is not desirable, but it's
153
# better than being asked an infinite number of times).
154
return (existing_login != "", str(ivle.conf.login),
155
str(ivle.conf.svn_pass), True)
157
# Make a Subversion client object
158
svnclient = pysvn.Client()
159
svnclient.callback_get_login = get_login
153
160
svnclient.exception_style = 0 # Simple (string) exceptions
155
162
DEFAULT_LOGMESSAGE = "No log message supplied."
191
198
# Default, just send an error but then continue
192
199
raise ActionError("Unknown action")
193
return action(req, fields)
195
202
def actionpath_to_urlpath(req, path):
196
203
"""Determines the URL path (relative to the student home) upon which the
222
229
Does not mutate req.
224
r = studpath.to_home_path(actionpath_to_urlpath(req, path))
231
(_, _, r) = studpath.url_to_jailpaths(actionpath_to_urlpath(req, path))
226
233
raise ActionError("Invalid path")
413
420
for datum in data:
414
421
# Each of the uploaded files
415
422
filepath = os.path.join(path, datum.filename)
416
filepath_local = studpath.to_home_path(filepath)
423
(_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
417
424
if os.path.isdir(filepath_local):
418
425
raise ActionError("A directory already exists "
419
426
+ "with that name")
431
438
# First get the entire path (within jail)
432
abspath = studpath.to_home_path(path)
439
_, _, abspath = studpath.url_to_jailpaths(path)
433
440
abspath = os.path.join(os.sep, abspath)
434
441
zip.unzip(abspath, filedata)
435
442
except (OSError, IOError):
438
445
raise ActionError("File '" + e.filename + "' already exists.")
441
filepath_local = studpath.to_home_path(filepath)
448
(_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
442
449
if filepath_local is None:
443
450
raise ActionError("Invalid path")
525
532
Reads fields: 'path'
527
534
paths = fields.getlist('path')
528
user = util.split_path(req.path)[0]
535
user = studpath.url_to_local(req.path)[0]
529
536
homedir = "/home/%s" % user
531
538
paths = map(lambda path: actionpath_to_local(req, path), paths)
533
paths = [studpath.to_home_path(req.path)]
540
paths = [studpath.url_to_jailpaths(req.path)[2]]
535
542
# Set all the dirs in home dir world browsable (o+r,o+x)
536
543
#FIXME: Should really only do those in the direct path not all of the
603
610
def action_svnupdate(req, fields):
604
611
"""Performs a "svn update" to each file specified.
606
Reads fields: 'path' and 'revision'
608
615
path = fields.getfirst('path')
609
revision = fields.getfirst('revision')
611
617
raise ActionError("Required field missing")
613
revision = pysvn.Revision( pysvn.opt_revision_kind.head )
616
revision = pysvn.Revision(pysvn.opt_revision_kind.number,
618
except ValueError, e:
619
raise ActionError("Bad revision number: '%s'"%revision,)
620
618
path = actionpath_to_local(req, path)
623
svnclient.update(path, recurse=True, revision=revision)
621
svnclient.update(path, recurse=True)
624
622
except pysvn.ClientError, e:
625
623
raise ActionError(str(e))
715
713
paths = fields.getlist('path')
716
714
if len(paths) != 2:
717
715
raise ActionError("usage: svncheckout url local-path")
718
url = ivle.conf.svn_addr + "/" + urllib.quote(paths[0])
716
url = ivle.conf.svn_addr + "/" + paths[0]
719
717
local_path = actionpath_to_local(req, str(paths[1]))
719
svnclient.callback_get_login = get_login
721
720
svnclient.checkout(url, local_path, recurse=True)
722
721
except pysvn.ClientError, e:
723
722
raise ActionError(str(e))
730
729
path = fields.getfirst('path')
731
730
logmsg = fields.getfirst('logmsg')
732
url = ivle.conf.svn_addr + "/" + urllib.quote(path)
731
url = ivle.conf.svn_addr + "/" + path
733
svnclient.callback_get_login = get_login
734
734
svnclient.mkdir(url, log_message=logmsg)
735
735
except pysvn.ClientError, e:
736
736
raise ActionError(str(e))
738
738
def action_svnrepostat(req, fields):
739
739
"""Discovers whether a path exists in a repo under the IVLE SVN root.
741
If it does exist, returns a dict containing its metadata.
743
741
Reads fields: 'path'
745
743
path = fields.getfirst('path')
746
url = ivle.conf.svn_addr + "/" + urllib.quote(path)
747
svnclient.exception_style = 1
744
url = ivle.conf.svn_addr + "/" + path
745
svnclient.exception_style = 1
750
info = svnclient.info2(url,
751
revision=pysvn.Revision(pysvn.opt_revision_kind.head))[0][1]
752
return {'svnrevision': info['rev'].number
754
info['rev'].kind == pysvn.opt_revision_kind.number
748
svnclient.callback_get_login = get_login
756
750
except pysvn.ClientError, e:
757
751
# Error code 170000 means ENOENT in this revision.
758
752
if e[1][0][1] == 170000:
759
753
raise util.IVLEError(404, 'The specified repository path does not exist')
761
755
raise ActionError(str(e[0]))
764
def action_svncleanup(req, fields):
765
"""Recursively clean up the working copy, removing locks, resuming
766
unfinished operations, etc.
767
path: The path to be cleaned"""
769
path = fields.getfirst('path')
771
raise ActionError("Required field missing")
772
path = actionpath_to_local(req, path)
775
svnclient.cleanup(path)
776
except pysvn.ClientError, e:
777
raise ActionError(str(e))
780
758
# Table of all action functions #
781
759
# Each function has the interface f(req, fields).
801
779
"svncheckout" : action_svncheckout,
802
780
"svnrepomkdir" : action_svnrepomkdir,
803
781
"svnrepostat" : action_svnrepostat,
804
"svncleanup" : action_svncleanup,