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

« back to all changes in this revision

Viewing changes to ivle/fileservice_lib/action.py

  • Committer: David Coles
  • Date: 2009-12-10 01:18:36 UTC
  • Revision ID: coles.david@gmail.com-20091210011836-6kk2omcmr9hvphj0
Correct documentation's system diagram (console communication goes via Application Slaves)

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.
94
96
#
95
97
# action=svnpublish: Set the "published" flag on a file to True.
96
98
#       path:   The path to the file to be published. Can be specified
119
121
#       path:   The path to the directory to be checked (under the IVLE
120
122
#               repository base).
121
123
#
 
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
#
122
128
# TODO: Implement the following actions:
123
129
#   svnupdate (done?)
124
130
# TODO: Implement ZIP unpacking in putfiles (done?).
131
137
import os
132
138
import cStringIO
133
139
import shutil
 
140
import urllib
134
141
 
135
142
import pysvn
136
143
 
137
144
from ivle import (util, studpath, zip)
 
145
from ivle.fileservice_lib.exceptions import WillNotOverwrite
138
146
import ivle.conf
139
147
 
 
148
 
140
149
def get_login(_realm, existing_login, _may_save):
141
150
    """Callback function used by pysvn for authentication.
142
151
    realm, existing_login, _may_save: The 3 arguments passed by pysvn to
195
204
    except KeyError:
196
205
        # Default, just send an error but then continue
197
206
        raise ActionError("Unknown action")
198
 
    action(req, fields)
 
207
    return action(req, fields)
199
208
 
200
209
def actionpath_to_urlpath(req, path):
201
210
    """Determines the URL path (relative to the student home) upon which the
226
235
 
227
236
    Does not mutate req.
228
237
    """
229
 
    (_, _, r) = studpath.url_to_jailpaths(actionpath_to_urlpath(req, path))
 
238
    r = studpath.to_home_path(actionpath_to_urlpath(req, path))
230
239
    if r is None:
231
240
        raise ActionError("Invalid path")
232
241
    return r
264
273
    except shutil.Error:
265
274
        raise ActionError("Could not move the file specified")
266
275
 
 
276
def svn_movefile(req, frompath, topath, copy=False):
 
277
    """Performs an svn move, resolving filenames, checking for any errors,
 
278
    and throwing ActionErrors if necessary. Can also be used to do a copy
 
279
    operation instead.
 
280
 
 
281
    frompath and topath are straight paths from the client. Will be checked.
 
282
    """
 
283
    if frompath is None or topath is None:
 
284
        raise ActionError("Required field missing")
 
285
    frompath = actionpath_to_local(req, frompath)
 
286
    topath = actionpath_to_local(req, topath)
 
287
    if not os.path.exists(frompath):
 
288
        raise ActionError("The source file does not exist")
 
289
    if os.path.exists(topath):
 
290
        if frompath == topath:
 
291
            raise ActionError("Source and destination are the same")
 
292
        raise ActionError("A file already exists with that name")
 
293
 
 
294
    try:
 
295
        if copy:
 
296
            svnclient.copy(frompath, topath)
 
297
        else:
 
298
            svnclient.move(frompath, topath)
 
299
    except OSError:
 
300
        raise ActionError("Could not move the file specified")
 
301
    except pysvn.ClientError:
 
302
        raise ActionError("Could not move the file specified")  
 
303
 
 
304
 
267
305
### ACTIONS ###
268
306
 
269
307
def action_delete(req, fields):
371
409
 
372
410
    Reads fields: 'path', 'data' (file upload, multiple), 'unpack'
373
411
    """
374
 
 
375
412
    # Important: Data is "None" if the file submitted is empty.
376
413
    path = fields.getfirst('path')
377
414
    data = fields['data']
390
427
    for datum in data:
391
428
        # Each of the uploaded files
392
429
        filepath = os.path.join(path, datum.filename)
 
430
        filepath_local = studpath.to_home_path(filepath)
 
431
        if os.path.isdir(filepath_local):
 
432
            raise ActionError("A directory already exists "
 
433
                    + "with that name")
 
434
        else:
 
435
            if os.path.exists(filepath_local):
 
436
                raise ActionError("A file already exists with that name")
393
437
        filedata = datum.file
394
438
 
395
439
        if unpack and datum.filename.lower().endswith(".zip"):
399
443
            # filename)
400
444
            try:
401
445
                # First get the entire path (within jail)
402
 
                _, _, abspath = studpath.url_to_jailpaths(path)
 
446
                abspath = studpath.to_home_path(path)
403
447
                abspath = os.path.join(os.sep, abspath)
404
448
                zip.unzip(abspath, filedata)
405
449
            except (OSError, IOError):
406
450
                goterror = True
 
451
            except WillNotOverwrite, e:
 
452
                raise ActionError("File '" + e.filename + "' already exists.")
407
453
        else:
408
454
            # Not a zip file
409
 
            (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
 
455
            filepath_local = studpath.to_home_path(filepath)
410
456
            if filepath_local is None:
411
457
                raise ActionError("Invalid path")
412
458
 
445
491
    files = fields.getlist('file')
446
492
    if dst is None or src is None or mode is None:
447
493
        raise ActionError("Required field missing")
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')")
 
494
 
454
495
    dst_local = actionpath_to_local(req, dst)
455
496
    if not os.path.isdir(dst_local):
456
497
        raise ActionError("dst is not a directory")
465
506
        # The destination is found by taking just the basename of the file
466
507
        topath = os.path.join(dst, os.path.basename(file))
467
508
        try:
468
 
            movefile(req, frompath, topath, copy)
 
509
            if mode == "copy":
 
510
                movefile(req, frompath, topath, True)
 
511
            elif mode == "move":
 
512
                movefile(req, frompath, topath, False)
 
513
            elif mode == "svncopy":
 
514
                svn_movefile(req, frompath, topath, True)
 
515
            elif mode == "svnmove":
 
516
                svn_movefile(req, frompath, topath, False)
 
517
            else:
 
518
                raise ActionError("Invalid mode (must be '(svn)copy' or '(svn)move')")
469
519
        except ActionError, message:
470
520
            # Store the error for later; we want to copy as many as possible
471
521
            if errormsg is None:
489
539
    Reads fields: 'path'
490
540
    """
491
541
    paths = fields.getlist('path')
492
 
    user = studpath.url_to_local(req.path)[0]
 
542
    user = util.split_path(req.path)[0]
493
543
    homedir = "/home/%s" % user
494
544
    if len(paths):
495
545
        paths = map(lambda path: actionpath_to_local(req, path), paths)
496
546
    else:
497
 
        paths = [studpath.url_to_jailpaths(req.path)[2]]
 
547
        paths = [studpath.to_home_path(req.path)]
498
548
 
499
549
    # Set all the dirs in home dir world browsable (o+r,o+x)
500
550
    #FIXME: Should really only do those in the direct path not all of the 
524
574
    if len(paths):
525
575
        paths = map(lambda path: actionpath_to_local(req, path), paths)
526
576
    else:
527
 
        paths = [studpath.url_to_jailpaths(req.path)[2]]
 
577
        paths = [studpath.to_home_path(req.path)]
528
578
 
529
579
    try:
530
580
        for path in paths:
567
617
def action_svnupdate(req, fields):
568
618
    """Performs a "svn update" to each file specified.
569
619
 
570
 
    Reads fields: 'path'
 
620
    Reads fields: 'path' and 'revision'
571
621
    """
572
622
    path = fields.getfirst('path')
 
623
    revision = fields.getfirst('revision')
573
624
    if path is None:
574
625
        raise ActionError("Required field missing")
 
626
    if revision is None:
 
627
        revision = pysvn.Revision( pysvn.opt_revision_kind.head )
 
628
    else:
 
629
        try:
 
630
            revision = pysvn.Revision(pysvn.opt_revision_kind.number,
 
631
                    int(revision))
 
632
        except ValueError, e:
 
633
            raise ActionError("Bad revision number: '%s'"%revision,)
575
634
    path = actionpath_to_local(req, path)
576
635
 
577
636
    try:
578
 
        svnclient.update(path, recurse=True)
 
637
        svnclient.update(path, recurse=True, revision=revision)
579
638
    except pysvn.ClientError, e:
580
639
        raise ActionError(str(e))
581
640
 
621
680
    if len(paths):
622
681
        paths = map(lambda path: actionpath_to_local(req, path), paths)
623
682
    else:
624
 
        paths = [studpath.url_to_jailpaths(req.path)[2]]
 
683
        paths = [studpath.to_home_path(req.path)]
625
684
 
626
685
    try:
627
686
        for path in paths:
670
729
    paths = fields.getlist('path')
671
730
    if len(paths) != 2:
672
731
        raise ActionError("usage: svncheckout url local-path")
673
 
    url = ivle.conf.svn_addr + "/" + paths[0]
 
732
    url = ivle.conf.svn_addr + "/" + urllib.quote(paths[0])
674
733
    local_path = actionpath_to_local(req, str(paths[1]))
675
734
    try:
676
735
        svnclient.callback_get_login = get_login
695
754
def action_svnrepostat(req, fields):
696
755
    """Discovers whether a path exists in a repo under the IVLE SVN root.
697
756
 
 
757
    If it does exist, returns a dict containing its metadata.
 
758
 
698
759
    Reads fields: 'path'
699
760
    """
700
761
    path = fields.getfirst('path')
701
762
    url = ivle.conf.svn_addr + "/" + path
702
 
    svnclient.exception_style = 1 
 
763
    svnclient.exception_style = 1
703
764
 
704
765
    try:
705
766
        svnclient.callback_get_login = get_login
706
 
        svnclient.info2(url)
 
767
        info = svnclient.info2(url,
 
768
            revision=pysvn.Revision(pysvn.opt_revision_kind.head))[0][1]
 
769
        return {'svnrevision': info['rev'].number
 
770
                  if info['rev'] and
 
771
                     info['rev'].kind == pysvn.opt_revision_kind.number
 
772
                  else None}
707
773
    except pysvn.ClientError, e:
708
774
        # Error code 170000 means ENOENT in this revision.
709
775
        if e[1][0][1] == 170000:
711
777
        else:
712
778
            raise ActionError(str(e[0]))
713
779
 
 
780
 
 
781
def action_svncleanup(req, fields):
 
782
    """Recursively clean up the working copy, removing locks, resuming 
 
783
    unfinished operations, etc.
 
784
        path:   The path to be cleaned"""
 
785
 
 
786
    path = fields.getfirst('path')
 
787
    if path is None:
 
788
        raise ActionError("Required field missing")
 
789
    path = actionpath_to_local(req, path)
 
790
 
 
791
    try:
 
792
        svnclient.cleanup(path)
 
793
    except pysvn.ClientError, e:
 
794
        raise ActionError(str(e))
 
795
 
 
796
 
714
797
# Table of all action functions #
715
798
# Each function has the interface f(req, fields).
716
799
 
735
818
    "svncheckout" : action_svncheckout,
736
819
    "svnrepomkdir" : action_svnrepomkdir,
737
820
    "svnrepostat" : action_svnrepostat,
 
821
    "svncleanup" : action_svncleanup,
738
822
}