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

« back to all changes in this revision

Viewing changes to ivle/fileservice_lib/action.py

  • Committer: me at id
  • Date: 2009-01-15 05:32:23 UTC
  • mto: This revision was merged to the branch mainline in revision 1090.
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:branches%2Fstorm:1158
ivle.database.User: Add an 'active_enrolments' property, which returns a list
     of nicely ordered active enrolments.

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 != "", str(ivle.conf.login),
 
153
                                  str(ivle.conf.svn_pass), True)
 
154
 
 
155
# Make a Subversion client object
 
156
svnclient = pysvn.Client()
 
157
svnclient.callback_get_login = get_login
153
158
svnclient.exception_style = 0               # Simple (string) exceptions
154
159
 
155
160
DEFAULT_LOGMESSAGE = "No log message supplied."
190
195
    except KeyError:
191
196
        # Default, just send an error but then continue
192
197
        raise ActionError("Unknown action")
193
 
    return action(req, fields)
 
198
    action(req, fields)
194
199
 
195
200
def actionpath_to_urlpath(req, path):
196
201
    """Determines the URL path (relative to the student home) upon which the
221
226
 
222
227
    Does not mutate req.
223
228
    """
224
 
    r = studpath.to_home_path(actionpath_to_urlpath(req, path))
 
229
    (_, _, r) = studpath.url_to_jailpaths(actionpath_to_urlpath(req, path))
225
230
    if r is None:
226
231
        raise ActionError("Invalid path")
227
232
    return r
259
264
    except shutil.Error:
260
265
        raise ActionError("Could not move the file specified")
261
266
 
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
267
### ACTIONS ###
292
268
 
293
269
def action_delete(req, fields):
395
371
 
396
372
    Reads fields: 'path', 'data' (file upload, multiple), 'unpack'
397
373
    """
 
374
 
398
375
    # Important: Data is "None" if the file submitted is empty.
399
376
    path = fields.getfirst('path')
400
377
    data = fields['data']
413
390
    for datum in data:
414
391
        # Each of the uploaded files
415
392
        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
393
        filedata = datum.file
424
394
 
425
395
        if unpack and datum.filename.lower().endswith(".zip"):
429
399
            # filename)
430
400
            try:
431
401
                # First get the entire path (within jail)
432
 
                abspath = studpath.to_home_path(path)
 
402
                _, _, abspath = studpath.url_to_jailpaths(path)
433
403
                abspath = os.path.join(os.sep, abspath)
434
404
                zip.unzip(abspath, filedata)
435
405
            except (OSError, IOError):
436
406
                goterror = True
437
 
            except WillNotOverwrite, e:
438
 
                raise ActionError("File '" + e.filename + "' already exists.")
439
407
        else:
440
408
            # Not a zip file
441
 
            filepath_local = studpath.to_home_path(filepath)
 
409
            (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
442
410
            if filepath_local is None:
443
411
                raise ActionError("Invalid path")
444
412
 
477
445
    files = fields.getlist('file')
478
446
    if dst is None or src is None or mode is None:
479
447
        raise ActionError("Required field missing")
480
 
 
 
448
    if mode == "copy":
 
449
        copy = True
 
450
    elif mode == "move":
 
451
        copy = False
 
452
    else:
 
453
        raise ActionError("Invalid mode (must be 'copy' or 'move')")
481
454
    dst_local = actionpath_to_local(req, dst)
482
455
    if not os.path.isdir(dst_local):
483
456
        raise ActionError("dst is not a directory")
492
465
        # The destination is found by taking just the basename of the file
493
466
        topath = os.path.join(dst, os.path.basename(file))
494
467
        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')")
 
468
            movefile(req, frompath, topath, copy)
505
469
        except ActionError, message:
506
470
            # Store the error for later; we want to copy as many as possible
507
471
            if errormsg is None:
525
489
    Reads fields: 'path'
526
490
    """
527
491
    paths = fields.getlist('path')
528
 
    user = util.split_path(req.path)[0]
 
492
    user = studpath.url_to_local(req.path)[0]
529
493
    homedir = "/home/%s" % user
530
494
    if len(paths):
531
495
        paths = map(lambda path: actionpath_to_local(req, path), paths)
532
496
    else:
533
 
        paths = [studpath.to_home_path(req.path)]
 
497
        paths = [studpath.url_to_jailpaths(req.path)[2]]
534
498
 
535
499
    # Set all the dirs in home dir world browsable (o+r,o+x)
536
500
    #FIXME: Should really only do those in the direct path not all of the 
560
524
    if len(paths):
561
525
        paths = map(lambda path: actionpath_to_local(req, path), paths)
562
526
    else:
563
 
        paths = [studpath.to_home_path(req.path)]
 
527
        paths = [studpath.url_to_jailpaths(req.path)[2]]
564
528
 
565
529
    try:
566
530
        for path in paths:
603
567
def action_svnupdate(req, fields):
604
568
    """Performs a "svn update" to each file specified.
605
569
 
606
 
    Reads fields: 'path' and 'revision'
 
570
    Reads fields: 'path'
607
571
    """
608
572
    path = fields.getfirst('path')
609
 
    revision = fields.getfirst('revision')
610
573
    if path is None:
611
574
        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
575
    path = actionpath_to_local(req, path)
621
576
 
622
577
    try:
623
 
        svnclient.update(path, recurse=True, revision=revision)
 
578
        svnclient.update(path, recurse=True)
624
579
    except pysvn.ClientError, e:
625
580
        raise ActionError(str(e))
626
581
 
666
621
    if len(paths):
667
622
        paths = map(lambda path: actionpath_to_local(req, path), paths)
668
623
    else:
669
 
        paths = [studpath.to_home_path(req.path)]
 
624
        paths = [studpath.url_to_jailpaths(req.path)[2]]
670
625
 
671
626
    try:
672
627
        for path in paths:
715
670
    paths = fields.getlist('path')
716
671
    if len(paths) != 2:
717
672
        raise ActionError("usage: svncheckout url local-path")
718
 
    url = ivle.conf.svn_addr + "/" + urllib.quote(paths[0])
 
673
    url = ivle.conf.svn_addr + "/" + paths[0]
719
674
    local_path = actionpath_to_local(req, str(paths[1]))
720
675
    try:
 
676
        svnclient.callback_get_login = get_login
721
677
        svnclient.checkout(url, local_path, recurse=True)
722
678
    except pysvn.ClientError, e:
723
679
        raise ActionError(str(e))
729
685
    """
730
686
    path = fields.getfirst('path')
731
687
    logmsg = fields.getfirst('logmsg')
732
 
    url = ivle.conf.svn_addr + "/" + urllib.quote(path)
 
688
    url = ivle.conf.svn_addr + "/" + path
733
689
    try:
 
690
        svnclient.callback_get_login = get_login
734
691
        svnclient.mkdir(url, log_message=logmsg)
735
692
    except pysvn.ClientError, e:
736
693
        raise ActionError(str(e))
738
695
def action_svnrepostat(req, fields):
739
696
    """Discovers whether a path exists in a repo under the IVLE SVN root.
740
697
 
741
 
    If it does exist, returns a dict containing its metadata.
742
 
 
743
698
    Reads fields: 'path'
744
699
    """
745
700
    path = fields.getfirst('path')
746
 
    url = ivle.conf.svn_addr + "/" + urllib.quote(path)
747
 
    svnclient.exception_style = 1
 
701
    url = ivle.conf.svn_addr + "/" + path
 
702
    svnclient.exception_style = 1 
748
703
 
749
704
    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}
 
705
        svnclient.callback_get_login = get_login
 
706
        svnclient.info2(url)
756
707
    except pysvn.ClientError, e:
757
708
        # Error code 170000 means ENOENT in this revision.
758
709
        if e[1][0][1] == 170000:
760
711
        else:
761
712
            raise ActionError(str(e[0]))
762
713
 
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
714
# Table of all action functions #
781
715
# Each function has the interface f(req, fields).
782
716
 
801
735
    "svncheckout" : action_svncheckout,
802
736
    "svnrepomkdir" : action_svnrepomkdir,
803
737
    "svnrepostat" : action_svnrepostat,
804
 
    "svncleanup" : action_svncleanup,
805
738
}