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

« back to all changes in this revision

Viewing changes to ivle/fileservice_lib/action.py

  • Committer: matt.giuca
  • Date: 2009-01-14 07:17:29 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:1128
ivle.caps: Bugfix - now Role accepts unicode strings.

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
#
 
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
 
97
#               multiple times.
 
98
#
 
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
 
101
#               multiple times.
96
102
#
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).
115
121
#
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
119
 
#
120
122
# TODO: Implement the following actions:
121
123
#   svnupdate (done?)
122
124
# TODO: Implement ZIP unpacking in putfiles (done?).
129
131
import os
130
132
import cStringIO
131
133
import shutil
132
 
import urllib
133
134
 
134
135
import pysvn
135
136
 
136
137
from ivle import (util, studpath, zip)
137
 
from ivle.fileservice_lib.exceptions import WillNotOverwrite
138
138
import ivle.conf
139
 
import ivle.svn
140
 
 
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)
 
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
145
157
svnclient.exception_style = 0               # Simple (string) exceptions
146
158
 
147
159
DEFAULT_LOGMESSAGE = "No log message supplied."
182
194
    except KeyError:
183
195
        # Default, just send an error but then continue
184
196
        raise ActionError("Unknown action")
185
 
    return action(req, fields)
 
197
    action(req, fields)
186
198
 
187
199
def actionpath_to_urlpath(req, path):
188
200
    """Determines the URL path (relative to the student home) upon which the
213
225
 
214
226
    Does not mutate req.
215
227
    """
216
 
    r = studpath.to_home_path(actionpath_to_urlpath(req, path))
 
228
    (_, _, r) = studpath.url_to_jailpaths(actionpath_to_urlpath(req, path))
217
229
    if r is None:
218
230
        raise ActionError("Invalid path")
219
231
    return r
251
263
    except shutil.Error:
252
264
        raise ActionError("Could not move the file specified")
253
265
 
254
 
def svn_movefile(req, frompath, topath, copy=False):
255
 
    """Performs an svn move, resolving filenames, checking for any errors,
256
 
    and throwing ActionErrors if necessary. Can also be used to do a copy
257
 
    operation instead.
258
 
 
259
 
    frompath and topath are straight paths from the client. Will be checked.
260
 
    """
261
 
    if frompath is None or topath is None:
262
 
        raise ActionError("Required field missing")
263
 
    frompath = actionpath_to_local(req, frompath)
264
 
    topath = actionpath_to_local(req, topath)
265
 
    if not os.path.exists(frompath):
266
 
        raise ActionError("The source file does not exist")
267
 
    if os.path.exists(topath):
268
 
        if frompath == topath:
269
 
            raise ActionError("Source and destination are the same")
270
 
        raise ActionError("A file already exists with that name")
271
 
 
272
 
    try:
273
 
        if copy:
274
 
            svnclient.copy(frompath, topath)
275
 
        else:
276
 
            svnclient.move(frompath, topath)
277
 
    except OSError:
278
 
        raise ActionError("Could not move the file specified")
279
 
    except pysvn.ClientError:
280
 
        raise ActionError("Could not move the file specified")  
281
 
 
282
 
 
283
266
### ACTIONS ###
284
267
 
285
268
def action_delete(req, fields):
387
370
 
388
371
    Reads fields: 'path', 'data' (file upload, multiple), 'unpack'
389
372
    """
 
373
 
390
374
    # Important: Data is "None" if the file submitted is empty.
391
375
    path = fields.getfirst('path')
392
376
    data = fields['data']
405
389
    for datum in data:
406
390
        # Each of the uploaded files
407
391
        filepath = os.path.join(path, datum.filename)
408
 
        filepath_local = studpath.to_home_path(filepath)
409
 
        if os.path.isdir(filepath_local):
410
 
            raise ActionError("A directory already exists "
411
 
                    + "with that name")
412
 
        else:
413
 
            if os.path.exists(filepath_local):
414
 
                raise ActionError("A file already exists with that name")
415
392
        filedata = datum.file
416
393
 
417
394
        if unpack and datum.filename.lower().endswith(".zip"):
421
398
            # filename)
422
399
            try:
423
400
                # First get the entire path (within jail)
424
 
                abspath = studpath.to_home_path(path)
 
401
                _, _, abspath = studpath.url_to_jailpaths(path)
425
402
                abspath = os.path.join(os.sep, abspath)
426
403
                zip.unzip(abspath, filedata)
427
404
            except (OSError, IOError):
428
405
                goterror = True
429
 
            except WillNotOverwrite, e:
430
 
                raise ActionError("File '" + e.filename + "' already exists.")
431
406
        else:
432
407
            # Not a zip file
433
 
            filepath_local = studpath.to_home_path(filepath)
 
408
            (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
434
409
            if filepath_local is None:
435
410
                raise ActionError("Invalid path")
436
411
 
469
444
    files = fields.getlist('file')
470
445
    if dst is None or src is None or mode is None:
471
446
        raise ActionError("Required field missing")
472
 
 
 
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')")
473
453
    dst_local = actionpath_to_local(req, dst)
474
454
    if not os.path.isdir(dst_local):
475
455
        raise ActionError("dst is not a directory")
484
464
        # The destination is found by taking just the basename of the file
485
465
        topath = os.path.join(dst, os.path.basename(file))
486
466
        try:
487
 
            if mode == "copy":
488
 
                movefile(req, frompath, topath, True)
489
 
            elif mode == "move":
490
 
                movefile(req, frompath, topath, False)
491
 
            elif mode == "svncopy":
492
 
                svn_movefile(req, frompath, topath, True)
493
 
            elif mode == "svnmove":
494
 
                svn_movefile(req, frompath, topath, False)
495
 
            else:
496
 
                raise ActionError("Invalid mode (must be '(svn)copy' or '(svn)move')")
 
467
            movefile(req, frompath, topath, copy)
497
468
        except ActionError, message:
498
469
            # Store the error for later; we want to copy as many as possible
499
470
            if errormsg is None:
517
488
    Reads fields: 'path'
518
489
    """
519
490
    paths = fields.getlist('path')
520
 
    user = util.split_path(req.path)[0]
 
491
    user = studpath.url_to_local(req.path)[0]
521
492
    homedir = "/home/%s" % user
522
493
    if len(paths):
523
494
        paths = map(lambda path: actionpath_to_local(req, path), paths)
524
495
    else:
525
 
        paths = [studpath.to_home_path(req.path)]
 
496
        paths = [studpath.url_to_jailpaths(req.path)[2]]
526
497
 
527
498
    # Set all the dirs in home dir world browsable (o+r,o+x)
528
499
    #FIXME: Should really only do those in the direct path not all of the 
552
523
    if len(paths):
553
524
        paths = map(lambda path: actionpath_to_local(req, path), paths)
554
525
    else:
555
 
        paths = [studpath.to_home_path(req.path)]
 
526
        paths = [studpath.url_to_jailpaths(req.path)[2]]
556
527
 
557
528
    try:
558
529
        for path in paths:
572
543
    Reads fields: 'path' (multiple)
573
544
    """
574
545
    paths = fields.getlist('path')
575
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
576
 
                paths)
 
546
    paths = map(lambda path: actionpath_to_local(req, path), paths)
577
547
 
578
548
    try:
579
549
        svnclient.add(paths, recurse=True, force=True)
586
556
    Reads fields: 'path' (multiple)
587
557
    """
588
558
    paths = fields.getlist('path')
589
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
590
 
                paths)
 
559
    paths = map(lambda path: actionpath_to_local(req, path), paths)
591
560
 
592
561
    try:
593
562
        svnclient.remove(paths, force=True)
597
566
def action_svnupdate(req, fields):
598
567
    """Performs a "svn update" to each file specified.
599
568
 
600
 
    Reads fields: 'path' and 'revision'
 
569
    Reads fields: 'path'
601
570
    """
602
571
    path = fields.getfirst('path')
603
 
    revision = fields.getfirst('revision')
604
572
    if path is None:
605
573
        raise ActionError("Required field missing")
606
 
    if revision is None:
607
 
        revision = pysvn.Revision( pysvn.opt_revision_kind.head )
608
 
    else:
609
 
        try:
610
 
            revision = pysvn.Revision(pysvn.opt_revision_kind.number,
611
 
                    int(revision))
612
 
        except ValueError, e:
613
 
            raise ActionError("Bad revision number: '%s'"%revision,)
614
 
    path = actionpath_to_local(req, path).decode('utf-8')
 
574
    path = actionpath_to_local(req, path)
615
575
 
616
576
    try:
617
 
        svnclient.update(path, recurse=True, revision=revision)
 
577
        svnclient.update(path, recurse=True)
618
578
    except pysvn.ClientError, e:
619
579
        raise ActionError(str(e))
620
580
 
626
586
    path = fields.getfirst('path')
627
587
    if path is None:
628
588
        raise ActionError("Required field missing")
629
 
    path = actionpath_to_local(req, path).decode('utf-8')
 
589
    path = actionpath_to_local(req, path)
630
590
 
631
591
    try:
632
592
        svnclient.resolved(path, recurse=True)
639
599
    Reads fields: 'path' (multiple)
640
600
    """
641
601
    paths = fields.getlist('path')
642
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
643
 
                paths)
 
602
    paths = map(lambda path: actionpath_to_local(req, path), paths)
644
603
 
645
604
    try:
646
605
        svnclient.revert(paths, recurse=True)
647
606
    except pysvn.ClientError, e:
648
607
        raise ActionError(str(e))
649
608
 
 
609
def action_svnpublish(req, fields):
 
610
    """Sets svn property "ivle:published" on each file specified.
 
611
    Should only be called on directories (only effective on directories
 
612
    anyway).
 
613
 
 
614
    Reads fields: 'path'
 
615
 
 
616
    XXX Currently unused by the client (calls action_publish instead, which
 
617
    has a completely different publishing model).
 
618
    """
 
619
    paths = fields.getlist('path')
 
620
    if len(paths):
 
621
        paths = map(lambda path: actionpath_to_local(req, path), paths)
 
622
    else:
 
623
        paths = [studpath.url_to_jailpaths(req.path)[2]]
 
624
 
 
625
    try:
 
626
        for path in paths:
 
627
            # Note: Property value doesn't matter
 
628
            svnclient.propset("ivle:published", "", path, recurse=False)
 
629
    except pysvn.ClientError, e:
 
630
        raise ActionError("Directory could not be published")
 
631
 
 
632
def action_svnunpublish(req, fields):
 
633
    """Deletes svn property "ivle:published" on each file specified.
 
634
 
 
635
    Reads fields: 'path'
 
636
 
 
637
    XXX Currently unused by the client (calls action_unpublish instead, which
 
638
    has a completely different publishing model).
 
639
    """
 
640
    paths = fields.getlist('path')
 
641
    paths = map(lambda path: actionpath_to_local(req, path), paths)
 
642
 
 
643
    try:
 
644
        for path in paths:
 
645
            svnclient.propdel("ivle:published", path, recurse=False)
 
646
    except pysvn.ClientError, e:
 
647
        raise ActionError("Directory could not be unpublished")
 
648
 
650
649
def action_svncommit(req, fields):
651
650
    """Performs a "svn commit" to each file specified.
652
651
 
653
652
    Reads fields: 'path' (multiple), 'logmsg' (optional)
654
653
    """
655
654
    paths = fields.getlist('path')
656
 
    if len(paths):
657
 
        paths = map(lambda path:actionpath_to_local(req,path).decode('utf-8'),
658
 
                    paths)
659
 
    else:
660
 
        paths = [studpath.to_home_path(req.path).decode('utf-8')]
661
 
    logmsg = str(fields.getfirst('logmsg',
662
 
                 DEFAULT_LOGMESSAGE)).decode('utf-8')
 
655
    paths = map(lambda path: actionpath_to_local(req, str(path)), paths)
 
656
    logmsg = str(fields.getfirst('logmsg', DEFAULT_LOGMESSAGE))
663
657
    if logmsg == '': logmsg = DEFAULT_LOGMESSAGE
664
658
 
665
659
    try:
675
669
    paths = fields.getlist('path')
676
670
    if len(paths) != 2:
677
671
        raise ActionError("usage: svncheckout url local-path")
678
 
    url = ivle.conf.svn_addr + "/" + urllib.quote(paths[0])
 
672
    url = ivle.conf.svn_addr + "/" + paths[0]
679
673
    local_path = actionpath_to_local(req, str(paths[1]))
680
 
    url = url.decode('utf-8')
681
 
    local_path = local_path.decode('utf-8')
682
674
    try:
 
675
        svnclient.callback_get_login = get_login
683
676
        svnclient.checkout(url, local_path, recurse=True)
684
677
    except pysvn.ClientError, e:
685
678
        raise ActionError(str(e))
691
684
    """
692
685
    path = fields.getfirst('path')
693
686
    logmsg = fields.getfirst('logmsg')
694
 
    url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
 
687
    url = ivle.conf.svn_addr + "/" + path
695
688
    try:
 
689
        svnclient.callback_get_login = get_login
696
690
        svnclient.mkdir(url, log_message=logmsg)
697
691
    except pysvn.ClientError, e:
698
692
        raise ActionError(str(e))
700
694
def action_svnrepostat(req, fields):
701
695
    """Discovers whether a path exists in a repo under the IVLE SVN root.
702
696
 
703
 
    If it does exist, returns a dict containing its metadata.
704
 
 
705
697
    Reads fields: 'path'
706
698
    """
707
699
    path = fields.getfirst('path')
708
 
    url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
709
 
    svnclient.exception_style = 1
 
700
    url = ivle.conf.svn_addr + "/" + path
 
701
    svnclient.exception_style = 1 
710
702
 
711
703
    try:
712
 
        info = svnclient.info2(url,
713
 
            revision=pysvn.Revision(pysvn.opt_revision_kind.head))[0][1]
714
 
        return {'svnrevision': info['rev'].number
715
 
                  if info['rev'] and
716
 
                     info['rev'].kind == pysvn.opt_revision_kind.number
717
 
                  else None}
 
704
        svnclient.callback_get_login = get_login
 
705
        svnclient.info2(url)
718
706
    except pysvn.ClientError, e:
719
707
        # Error code 170000 means ENOENT in this revision.
720
708
        if e[1][0][1] == 170000:
722
710
        else:
723
711
            raise ActionError(str(e[0]))
724
712
 
725
 
 
726
 
def action_svncleanup(req, fields):
727
 
    """Recursively clean up the working copy, removing locks, resuming 
728
 
    unfinished operations, etc.
729
 
        path:   The path to be cleaned"""
730
 
 
731
 
    path = fields.getfirst('path')
732
 
    if path is None:
733
 
        raise ActionError("Required field missing")
734
 
    path = actionpath_to_local(req, path).decode('utf-8')
735
 
 
736
 
    try:
737
 
        svnclient.cleanup(path)
738
 
    except pysvn.ClientError, e:
739
 
        raise ActionError(str(e))
740
 
 
741
 
 
742
713
# Table of all action functions #
743
714
# Each function has the interface f(req, fields).
744
715
 
757
728
    "svnupdate" : action_svnupdate,
758
729
    "svnresolved" : action_svnresolved,
759
730
    "svnrevert" : action_svnrevert,
 
731
    "svnpublish" : action_svnpublish,
 
732
    "svnunpublish" : action_svnunpublish,
760
733
    "svncommit" : action_svncommit,
761
734
    "svncheckout" : action_svncheckout,
762
735
    "svnrepomkdir" : action_svnrepomkdir,
763
736
    "svnrepostat" : action_svnrepostat,
764
 
    "svncleanup" : action_svncleanup,
765
737
}