~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/fileservice_lib/action.py

  • Committer: William Grant
  • Date: 2009-01-13 01:36:15 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:1123
Merge setup-refactor branch. This completely breaks existing installations;
every path (both filesystem and Python) has changed. Do not upgrade without
knowing what you are doing.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
93
93
#               specified.
94
 
#       revision: The revision number to update to. If not provided this
95
 
#               defaults to HEAD.
96
94
#
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).
123
121
#
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
127
 
#
128
122
# TODO: Implement the following actions:
129
123
#   svnupdate (done?)
130
124
# TODO: Implement ZIP unpacking in putfiles (done?).
137
131
import os
138
132
import cStringIO
139
133
import shutil
140
 
import urllib
141
134
 
142
135
import pysvn
143
136
 
144
137
from ivle import (util, studpath, zip)
145
 
from ivle.fileservice_lib.exceptions import WillNotOverwrite
146
138
import ivle.conf
147
 
import ivle.svn
148
 
 
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)
 
139
 
 
140
def get_login(_realm, existing_login, _may_save):
 
141
    """Callback function used by pysvn for authentication.
 
142
    realm, existing_login, _may_save: The 3 arguments passed by pysvn to
 
143
        callback_get_login.
 
144
        The following has been determined empirically, not from docs:
 
145
        existing_login will be the name of the user who owns the process on
 
146
        the first attempt, "" on subsequent attempts. We use this fact.
 
147
    """
 
148
    # Only provide credentials on the _first_ attempt.
 
149
    # If we're being asked again, then it means the credentials failed for
 
150
    # some reason and we should just fail. (This is not desirable, but it's
 
151
    # better than being asked an infinite number of times).
 
152
    return (existing_login != "", ivle.conf.login, ivle.conf.svn_pass, True)
 
153
 
 
154
# Make a Subversion client object
 
155
svnclient = pysvn.Client()
 
156
svnclient.callback_get_login = get_login
153
157
svnclient.exception_style = 0               # Simple (string) exceptions
154
158
 
155
159
DEFAULT_LOGMESSAGE = "No log message supplied."
190
194
    except KeyError:
191
195
        # Default, just send an error but then continue
192
196
        raise ActionError("Unknown action")
193
 
    return action(req, fields)
 
197
    action(req, fields)
194
198
 
195
199
def actionpath_to_urlpath(req, path):
196
200
    """Determines the URL path (relative to the student home) upon which the
221
225
 
222
226
    Does not mutate req.
223
227
    """
224
 
    r = studpath.to_home_path(actionpath_to_urlpath(req, path))
 
228
    (_, _, r) = studpath.url_to_jailpaths(actionpath_to_urlpath(req, path))
225
229
    if r is None:
226
230
        raise ActionError("Invalid path")
227
231
    return r
259
263
    except shutil.Error:
260
264
        raise ActionError("Could not move the file specified")
261
265
 
262
 
def svn_movefile(req, frompath, topath, copy=False):
263
 
    """Performs an svn move, resolving filenames, checking for any errors,
264
 
    and throwing ActionErrors if necessary. Can also be used to do a copy
265
 
    operation instead.
266
 
 
267
 
    frompath and topath are straight paths from the client. Will be checked.
268
 
    """
269
 
    if frompath is None or topath is None:
270
 
        raise ActionError("Required field missing")
271
 
    frompath = actionpath_to_local(req, frompath)
272
 
    topath = actionpath_to_local(req, topath)
273
 
    if not os.path.exists(frompath):
274
 
        raise ActionError("The source file does not exist")
275
 
    if os.path.exists(topath):
276
 
        if frompath == topath:
277
 
            raise ActionError("Source and destination are the same")
278
 
        raise ActionError("A file already exists with that name")
279
 
 
280
 
    try:
281
 
        if copy:
282
 
            svnclient.copy(frompath, topath)
283
 
        else:
284
 
            svnclient.move(frompath, topath)
285
 
    except OSError:
286
 
        raise ActionError("Could not move the file specified")
287
 
    except pysvn.ClientError:
288
 
        raise ActionError("Could not move the file specified")  
289
 
 
290
 
 
291
266
### ACTIONS ###
292
267
 
293
268
def action_delete(req, fields):
395
370
 
396
371
    Reads fields: 'path', 'data' (file upload, multiple), 'unpack'
397
372
    """
 
373
 
398
374
    # Important: Data is "None" if the file submitted is empty.
399
375
    path = fields.getfirst('path')
400
376
    data = fields['data']
413
389
    for datum in data:
414
390
        # Each of the uploaded files
415
391
        filepath = os.path.join(path, datum.filename)
416
 
        filepath_local = studpath.to_home_path(filepath)
417
 
        if os.path.isdir(filepath_local):
418
 
            raise ActionError("A directory already exists "
419
 
                    + "with that name")
420
 
        else:
421
 
            if os.path.exists(filepath_local):
422
 
                raise ActionError("A file already exists with that name")
423
392
        filedata = datum.file
424
393
 
425
394
        if unpack and datum.filename.lower().endswith(".zip"):
429
398
            # filename)
430
399
            try:
431
400
                # First get the entire path (within jail)
432
 
                abspath = studpath.to_home_path(path)
 
401
                _, _, abspath = studpath.url_to_jailpaths(path)
433
402
                abspath = os.path.join(os.sep, abspath)
434
403
                zip.unzip(abspath, filedata)
435
404
            except (OSError, IOError):
436
405
                goterror = True
437
 
            except WillNotOverwrite, e:
438
 
                raise ActionError("File '" + e.filename + "' already exists.")
439
406
        else:
440
407
            # Not a zip file
441
 
            filepath_local = studpath.to_home_path(filepath)
 
408
            (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
442
409
            if filepath_local is None:
443
410
                raise ActionError("Invalid path")
444
411
 
477
444
    files = fields.getlist('file')
478
445
    if dst is None or src is None or mode is None:
479
446
        raise ActionError("Required field missing")
480
 
 
 
447
    if mode == "copy":
 
448
        copy = True
 
449
    elif mode == "move":
 
450
        copy = False
 
451
    else:
 
452
        raise ActionError("Invalid mode (must be 'copy' or 'move')")
481
453
    dst_local = actionpath_to_local(req, dst)
482
454
    if not os.path.isdir(dst_local):
483
455
        raise ActionError("dst is not a directory")
492
464
        # The destination is found by taking just the basename of the file
493
465
        topath = os.path.join(dst, os.path.basename(file))
494
466
        try:
495
 
            if mode == "copy":
496
 
                movefile(req, frompath, topath, True)
497
 
            elif mode == "move":
498
 
                movefile(req, frompath, topath, False)
499
 
            elif mode == "svncopy":
500
 
                svn_movefile(req, frompath, topath, True)
501
 
            elif mode == "svnmove":
502
 
                svn_movefile(req, frompath, topath, False)
503
 
            else:
504
 
                raise ActionError("Invalid mode (must be '(svn)copy' or '(svn)move')")
 
467
            movefile(req, frompath, topath, copy)
505
468
        except ActionError, message:
506
469
            # Store the error for later; we want to copy as many as possible
507
470
            if errormsg is None:
525
488
    Reads fields: 'path'
526
489
    """
527
490
    paths = fields.getlist('path')
528
 
    user = util.split_path(req.path)[0]
 
491
    user = studpath.url_to_local(req.path)[0]
529
492
    homedir = "/home/%s" % user
530
493
    if len(paths):
531
494
        paths = map(lambda path: actionpath_to_local(req, path), paths)
532
495
    else:
533
 
        paths = [studpath.to_home_path(req.path)]
 
496
        paths = [studpath.url_to_jailpaths(req.path)[2]]
534
497
 
535
498
    # Set all the dirs in home dir world browsable (o+r,o+x)
536
499
    #FIXME: Should really only do those in the direct path not all of the 
560
523
    if len(paths):
561
524
        paths = map(lambda path: actionpath_to_local(req, path), paths)
562
525
    else:
563
 
        paths = [studpath.to_home_path(req.path)]
 
526
        paths = [studpath.url_to_jailpaths(req.path)[2]]
564
527
 
565
528
    try:
566
529
        for path in paths:
603
566
def action_svnupdate(req, fields):
604
567
    """Performs a "svn update" to each file specified.
605
568
 
606
 
    Reads fields: 'path' and 'revision'
 
569
    Reads fields: 'path'
607
570
    """
608
571
    path = fields.getfirst('path')
609
 
    revision = fields.getfirst('revision')
610
572
    if path is None:
611
573
        raise ActionError("Required field missing")
612
 
    if revision is None:
613
 
        revision = pysvn.Revision( pysvn.opt_revision_kind.head )
614
 
    else:
615
 
        try:
616
 
            revision = pysvn.Revision(pysvn.opt_revision_kind.number,
617
 
                    int(revision))
618
 
        except ValueError, e:
619
 
            raise ActionError("Bad revision number: '%s'"%revision,)
620
574
    path = actionpath_to_local(req, path)
621
575
 
622
576
    try:
623
 
        svnclient.update(path, recurse=True, revision=revision)
 
577
        svnclient.update(path, recurse=True)
624
578
    except pysvn.ClientError, e:
625
579
        raise ActionError(str(e))
626
580
 
666
620
    if len(paths):
667
621
        paths = map(lambda path: actionpath_to_local(req, path), paths)
668
622
    else:
669
 
        paths = [studpath.to_home_path(req.path)]
 
623
        paths = [studpath.url_to_jailpaths(req.path)[2]]
670
624
 
671
625
    try:
672
626
        for path in paths:
715
669
    paths = fields.getlist('path')
716
670
    if len(paths) != 2:
717
671
        raise ActionError("usage: svncheckout url local-path")
718
 
    url = ivle.conf.svn_addr + "/" + urllib.quote(paths[0])
 
672
    url = ivle.conf.svn_addr + "/" + paths[0]
719
673
    local_path = actionpath_to_local(req, str(paths[1]))
720
674
    try:
 
675
        svnclient.callback_get_login = get_login
721
676
        svnclient.checkout(url, local_path, recurse=True)
722
677
    except pysvn.ClientError, e:
723
678
        raise ActionError(str(e))
729
684
    """
730
685
    path = fields.getfirst('path')
731
686
    logmsg = fields.getfirst('logmsg')
732
 
    url = ivle.conf.svn_addr + "/" + urllib.quote(path)
 
687
    url = ivle.conf.svn_addr + "/" + path
733
688
    try:
 
689
        svnclient.callback_get_login = get_login
734
690
        svnclient.mkdir(url, log_message=logmsg)
735
691
    except pysvn.ClientError, e:
736
692
        raise ActionError(str(e))
738
694
def action_svnrepostat(req, fields):
739
695
    """Discovers whether a path exists in a repo under the IVLE SVN root.
740
696
 
741
 
    If it does exist, returns a dict containing its metadata.
742
 
 
743
697
    Reads fields: 'path'
744
698
    """
745
699
    path = fields.getfirst('path')
746
 
    url = ivle.conf.svn_addr + "/" + urllib.quote(path)
747
 
    svnclient.exception_style = 1
 
700
    url = ivle.conf.svn_addr + "/" + path
 
701
    svnclient.exception_style = 1 
748
702
 
749
703
    try:
750
 
        info = svnclient.info2(url,
751
 
            revision=pysvn.Revision(pysvn.opt_revision_kind.head))[0][1]
752
 
        return {'svnrevision': info['rev'].number
753
 
                  if info['rev'] and
754
 
                     info['rev'].kind == pysvn.opt_revision_kind.number
755
 
                  else None}
 
704
        svnclient.callback_get_login = get_login
 
705
        svnclient.info2(url)
756
706
    except pysvn.ClientError, e:
757
707
        # Error code 170000 means ENOENT in this revision.
758
708
        if e[1][0][1] == 170000:
760
710
        else:
761
711
            raise ActionError(str(e[0]))
762
712
 
763
 
 
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"""
768
 
 
769
 
    path = fields.getfirst('path')
770
 
    if path is None:
771
 
        raise ActionError("Required field missing")
772
 
    path = actionpath_to_local(req, path)
773
 
 
774
 
    try:
775
 
        svnclient.cleanup(path)
776
 
    except pysvn.ClientError, e:
777
 
        raise ActionError(str(e))
778
 
 
779
 
 
780
713
# Table of all action functions #
781
714
# Each function has the interface f(req, fields).
782
715
 
801
734
    "svncheckout" : action_svncheckout,
802
735
    "svnrepomkdir" : action_svnrepomkdir,
803
736
    "svnrepostat" : action_svnrepostat,
804
 
    "svncleanup" : action_svncleanup,
805
737
}