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
95
# action=svnpublish: Set the "published" flag on a file to True.
96
# path: The path to the file to be published. Can be specified
99
# action=svnunpublish: Set the "published" flag on a file to False.
100
# path: The path to the file to be unpublished. Can be specified
97
103
# action=svncommit: Commit a file(s) or directory(s) to the repository.
98
104
# path: The path to the file or directory to be committed. Can be
113
119
# path: The path to the directory to be checked (under the IVLE
114
120
# repository base).
116
# action=svncleanup: Recursively clean up the working copy, removing locks,
117
# resuming unfinished operations, etc.
118
# path: The path to the directory to be cleaned
120
122
# TODO: Implement the following actions:
121
123
# svnupdate (done?)
122
124
# TODO: Implement ZIP unpacking in putfiles (done?).
136
138
from ivle import (util, studpath, zip)
137
139
from ivle.fileservice_lib.exceptions import WillNotOverwrite
141
# Make a Subversion client object (which will log in with this user's
142
# credentials, upon request)
143
svnclient = ivle.svn.create_auth_svn_client(username=ivle.conf.login,
144
password=ivle.conf.svn_pass)
143
def get_login(_realm, existing_login, _may_save):
144
"""Callback function used by pysvn for authentication.
145
realm, existing_login, _may_save: The 3 arguments passed by pysvn to
147
The following has been determined empirically, not from docs:
148
existing_login will be the name of the user who owns the process on
149
the first attempt, "" on subsequent attempts. We use this fact.
151
# Only provide credentials on the _first_ attempt.
152
# If we're being asked again, then it means the credentials failed for
153
# some reason and we should just fail. (This is not desirable, but it's
154
# better than being asked an infinite number of times).
155
return (existing_login != "", str(ivle.conf.login),
156
str(ivle.conf.svn_pass), True)
158
# Make a Subversion client object
159
svnclient = pysvn.Client()
160
svnclient.callback_get_login = get_login
145
161
svnclient.exception_style = 0 # Simple (string) exceptions
147
163
DEFAULT_LOGMESSAGE = "No log message supplied."
318
334
frompath = fields.getfirst('from')
319
335
topath = fields.getfirst('to')
320
svn = fields.getfirst('svn')
322
svn_movefile(req, frompath, topath)
324
movefile(req, frompath, topath)
336
movefile(req, frompath, topath)
326
338
def action_mkdir(req, fields):
327
339
"""Creates a directory with the given path.
576
588
Reads fields: 'path' (multiple)
578
590
paths = fields.getlist('path')
579
paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
591
paths = map(lambda path: actionpath_to_local(req, path), paths)
583
594
svnclient.add(paths, recurse=True, force=True)
590
601
Reads fields: 'path' (multiple)
592
603
paths = fields.getlist('path')
593
paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
604
paths = map(lambda path: actionpath_to_local(req, path), paths)
597
607
svnclient.remove(paths, force=True)
601
611
def action_svnupdate(req, fields):
602
612
"""Performs a "svn update" to each file specified.
604
Reads fields: 'path' and 'revision'
606
616
path = fields.getfirst('path')
607
revision = fields.getfirst('revision')
609
618
raise ActionError("Required field missing")
611
revision = pysvn.Revision( pysvn.opt_revision_kind.head )
614
revision = pysvn.Revision(pysvn.opt_revision_kind.number,
616
except ValueError, e:
617
raise ActionError("Bad revision number: '%s'"%revision,)
618
path = actionpath_to_local(req, path).decode('utf-8')
619
path = actionpath_to_local(req, path)
621
svnclient.update(path, recurse=True, revision=revision)
622
svnclient.update(path, recurse=True)
622
623
except pysvn.ClientError, e:
623
624
raise ActionError(str(e))
643
644
Reads fields: 'path' (multiple)
645
646
paths = fields.getlist('path')
646
paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
647
paths = map(lambda path: actionpath_to_local(req, path), paths)
650
650
svnclient.revert(paths, recurse=True)
651
651
except pysvn.ClientError, e:
652
652
raise ActionError(str(e))
654
def action_svnpublish(req, fields):
655
"""Sets svn property "ivle:published" on each file specified.
656
Should only be called on directories (only effective on directories
661
XXX Currently unused by the client (calls action_publish instead, which
662
has a completely different publishing model).
664
paths = fields.getlist('path')
666
paths = map(lambda path: actionpath_to_local(req, path), paths)
668
paths = [studpath.to_home_path(req.path)]
672
# Note: Property value doesn't matter
673
svnclient.propset("ivle:published", "", path, recurse=False)
674
except pysvn.ClientError, e:
675
raise ActionError("Directory could not be published")
677
def action_svnunpublish(req, fields):
678
"""Deletes svn property "ivle:published" on each file specified.
682
XXX Currently unused by the client (calls action_unpublish instead, which
683
has a completely different publishing model).
685
paths = fields.getlist('path')
686
paths = map(lambda path: actionpath_to_local(req, path), paths)
690
svnclient.propdel("ivle:published", path, recurse=False)
691
except pysvn.ClientError, e:
692
raise ActionError("Directory could not be unpublished")
654
694
def action_svncommit(req, fields):
655
695
"""Performs a "svn commit" to each file specified.
657
697
Reads fields: 'path' (multiple), 'logmsg' (optional)
659
699
paths = fields.getlist('path')
661
paths = map(lambda path:actionpath_to_local(req,path).decode('utf-8'),
664
paths = [studpath.to_home_path(req.path).decode('utf-8')]
665
logmsg = str(fields.getfirst('logmsg',
666
DEFAULT_LOGMESSAGE)).decode('utf-8')
700
paths = map(lambda path: actionpath_to_local(req, str(path)), paths)
701
logmsg = str(fields.getfirst('logmsg', DEFAULT_LOGMESSAGE))
667
702
if logmsg == '': logmsg = DEFAULT_LOGMESSAGE
681
716
raise ActionError("usage: svncheckout url local-path")
682
717
url = ivle.conf.svn_addr + "/" + urllib.quote(paths[0])
683
718
local_path = actionpath_to_local(req, str(paths[1]))
684
url = url.decode('utf-8')
685
local_path = local_path.decode('utf-8')
720
svnclient.callback_get_login = get_login
687
721
svnclient.checkout(url, local_path, recurse=True)
688
722
except pysvn.ClientError, e:
689
723
raise ActionError(str(e))
696
730
path = fields.getfirst('path')
697
731
logmsg = fields.getfirst('logmsg')
698
url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
732
url = ivle.conf.svn_addr + "/" + path
734
svnclient.callback_get_login = get_login
700
735
svnclient.mkdir(url, log_message=logmsg)
701
736
except pysvn.ClientError, e:
702
737
raise ActionError(str(e))
709
744
Reads fields: 'path'
711
746
path = fields.getfirst('path')
712
url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
747
url = ivle.conf.svn_addr + "/" + path
713
748
svnclient.exception_style = 1
751
svnclient.callback_get_login = get_login
716
752
info = svnclient.info2(url,
717
753
revision=pysvn.Revision(pysvn.opt_revision_kind.head))[0][1]
718
754
return {'svnrevision': info['rev'].number
722
758
except pysvn.ClientError, e:
723
759
# Error code 170000 means ENOENT in this revision.
724
760
if e[1][0][1] == 170000:
726
raise ActionError('The specified repository path does not exist')
761
raise util.IVLEError(404, 'The specified repository path does not exist')
728
763
raise ActionError(str(e[0]))
731
def action_svncleanup(req, fields):
732
"""Recursively clean up the working copy, removing locks, resuming
733
unfinished operations, etc.
734
path: The path to be cleaned"""
736
path = fields.getfirst('path')
738
raise ActionError("Required field missing")
739
path = actionpath_to_local(req, path).decode('utf-8')
742
svnclient.cleanup(path)
743
except pysvn.ClientError, e:
744
raise ActionError(str(e))
747
766
# Table of all action functions #
748
767
# Each function has the interface f(req, fields).
762
781
"svnupdate" : action_svnupdate,
763
782
"svnresolved" : action_svnresolved,
764
783
"svnrevert" : action_svnrevert,
784
"svnpublish" : action_svnpublish,
785
"svnunpublish" : action_svnunpublish,
765
786
"svncommit" : action_svncommit,
766
787
"svncheckout" : action_svncheckout,
767
788
"svnrepomkdir" : action_svnrepomkdir,
768
789
"svnrepostat" : action_svnrepostat,
769
"svncleanup" : action_svncleanup,