~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 01:18:00 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:1145
Give ivle.database.User {password,account}_expired attributes, and get
ivle.dispatch.login to use them.

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):
317
300
    """
318
301
    frompath = fields.getfirst('from')
319
302
    topath = fields.getfirst('to')
320
 
    svn = fields.getfirst('svn')
321
 
    if svn:
322
 
        svn_movefile(req, frompath, topath)
323
 
    else:
324
 
        movefile(req, frompath, topath)
 
303
    movefile(req, frompath, topath)
325
304
 
326
305
def action_mkdir(req, fields):
327
306
    """Creates a directory with the given path.
391
370
 
392
371
    Reads fields: 'path', 'data' (file upload, multiple), 'unpack'
393
372
    """
 
373
 
394
374
    # Important: Data is "None" if the file submitted is empty.
395
375
    path = fields.getfirst('path')
396
376
    data = fields['data']
409
389
    for datum in data:
410
390
        # Each of the uploaded files
411
391
        filepath = os.path.join(path, datum.filename)
412
 
        filepath_local = studpath.to_home_path(filepath)
413
 
        if os.path.isdir(filepath_local):
414
 
            raise ActionError("A directory already exists "
415
 
                    + "with that name")
416
 
        else:
417
 
            if os.path.exists(filepath_local):
418
 
                raise ActionError("A file already exists with that name")
419
392
        filedata = datum.file
420
393
 
421
394
        if unpack and datum.filename.lower().endswith(".zip"):
425
398
            # filename)
426
399
            try:
427
400
                # First get the entire path (within jail)
428
 
                abspath = studpath.to_home_path(path)
 
401
                _, _, abspath = studpath.url_to_jailpaths(path)
429
402
                abspath = os.path.join(os.sep, abspath)
430
403
                zip.unzip(abspath, filedata)
431
404
            except (OSError, IOError):
432
405
                goterror = True
433
 
            except WillNotOverwrite, e:
434
 
                raise ActionError("File '" + e.filename + "' already exists.")
435
406
        else:
436
407
            # Not a zip file
437
 
            filepath_local = studpath.to_home_path(filepath)
 
408
            (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
438
409
            if filepath_local is None:
439
410
                raise ActionError("Invalid path")
440
411
 
473
444
    files = fields.getlist('file')
474
445
    if dst is None or src is None or mode is None:
475
446
        raise ActionError("Required field missing")
476
 
 
 
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')")
477
453
    dst_local = actionpath_to_local(req, dst)
478
454
    if not os.path.isdir(dst_local):
479
455
        raise ActionError("dst is not a directory")
488
464
        # The destination is found by taking just the basename of the file
489
465
        topath = os.path.join(dst, os.path.basename(file))
490
466
        try:
491
 
            if mode == "copy":
492
 
                movefile(req, frompath, topath, True)
493
 
            elif mode == "move":
494
 
                movefile(req, frompath, topath, False)
495
 
            elif mode == "svncopy":
496
 
                svn_movefile(req, frompath, topath, True)
497
 
            elif mode == "svnmove":
498
 
                svn_movefile(req, frompath, topath, False)
499
 
            else:
500
 
                raise ActionError("Invalid mode (must be '(svn)copy' or '(svn)move')")
 
467
            movefile(req, frompath, topath, copy)
501
468
        except ActionError, message:
502
469
            # Store the error for later; we want to copy as many as possible
503
470
            if errormsg is None:
521
488
    Reads fields: 'path'
522
489
    """
523
490
    paths = fields.getlist('path')
524
 
    user = util.split_path(req.path)[0]
 
491
    user = studpath.url_to_local(req.path)[0]
525
492
    homedir = "/home/%s" % user
526
493
    if len(paths):
527
494
        paths = map(lambda path: actionpath_to_local(req, path), paths)
528
495
    else:
529
 
        paths = [studpath.to_home_path(req.path)]
 
496
        paths = [studpath.url_to_jailpaths(req.path)[2]]
530
497
 
531
498
    # Set all the dirs in home dir world browsable (o+r,o+x)
532
499
    #FIXME: Should really only do those in the direct path not all of the 
556
523
    if len(paths):
557
524
        paths = map(lambda path: actionpath_to_local(req, path), paths)
558
525
    else:
559
 
        paths = [studpath.to_home_path(req.path)]
 
526
        paths = [studpath.url_to_jailpaths(req.path)[2]]
560
527
 
561
528
    try:
562
529
        for path in paths:
576
543
    Reads fields: 'path' (multiple)
577
544
    """
578
545
    paths = fields.getlist('path')
579
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
580
 
                paths)
 
546
    paths = map(lambda path: actionpath_to_local(req, path), paths)
581
547
 
582
548
    try:
583
549
        svnclient.add(paths, recurse=True, force=True)
590
556
    Reads fields: 'path' (multiple)
591
557
    """
592
558
    paths = fields.getlist('path')
593
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
594
 
                paths)
 
559
    paths = map(lambda path: actionpath_to_local(req, path), paths)
595
560
 
596
561
    try:
597
562
        svnclient.remove(paths, force=True)
601
566
def action_svnupdate(req, fields):
602
567
    """Performs a "svn update" to each file specified.
603
568
 
604
 
    Reads fields: 'path' and 'revision'
 
569
    Reads fields: 'path'
605
570
    """
606
571
    path = fields.getfirst('path')
607
 
    revision = fields.getfirst('revision')
608
572
    if path is None:
609
573
        raise ActionError("Required field missing")
610
 
    if revision is None:
611
 
        revision = pysvn.Revision( pysvn.opt_revision_kind.head )
612
 
    else:
613
 
        try:
614
 
            revision = pysvn.Revision(pysvn.opt_revision_kind.number,
615
 
                    int(revision))
616
 
        except ValueError, e:
617
 
            raise ActionError("Bad revision number: '%s'"%revision,)
618
 
    path = actionpath_to_local(req, path).decode('utf-8')
 
574
    path = actionpath_to_local(req, path)
619
575
 
620
576
    try:
621
 
        svnclient.update(path, recurse=True, revision=revision)
 
577
        svnclient.update(path, recurse=True)
622
578
    except pysvn.ClientError, e:
623
579
        raise ActionError(str(e))
624
580
 
630
586
    path = fields.getfirst('path')
631
587
    if path is None:
632
588
        raise ActionError("Required field missing")
633
 
    path = actionpath_to_local(req, path).decode('utf-8')
 
589
    path = actionpath_to_local(req, path)
634
590
 
635
591
    try:
636
592
        svnclient.resolved(path, recurse=True)
643
599
    Reads fields: 'path' (multiple)
644
600
    """
645
601
    paths = fields.getlist('path')
646
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
647
 
                paths)
 
602
    paths = map(lambda path: actionpath_to_local(req, path), paths)
648
603
 
649
604
    try:
650
605
        svnclient.revert(paths, recurse=True)
651
606
    except pysvn.ClientError, e:
652
607
        raise ActionError(str(e))
653
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
 
654
649
def action_svncommit(req, fields):
655
650
    """Performs a "svn commit" to each file specified.
656
651
 
657
652
    Reads fields: 'path' (multiple), 'logmsg' (optional)
658
653
    """
659
654
    paths = fields.getlist('path')
660
 
    if len(paths):
661
 
        paths = map(lambda path:actionpath_to_local(req,path).decode('utf-8'),
662
 
                    paths)
663
 
    else:
664
 
        paths = [studpath.to_home_path(req.path).decode('utf-8')]
665
 
    logmsg = str(fields.getfirst('logmsg',
666
 
                 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))
667
657
    if logmsg == '': logmsg = DEFAULT_LOGMESSAGE
668
658
 
669
659
    try:
679
669
    paths = fields.getlist('path')
680
670
    if len(paths) != 2:
681
671
        raise ActionError("usage: svncheckout url local-path")
682
 
    url = ivle.conf.svn_addr + "/" + urllib.quote(paths[0])
 
672
    url = ivle.conf.svn_addr + "/" + paths[0]
683
673
    local_path = actionpath_to_local(req, str(paths[1]))
684
 
    url = url.decode('utf-8')
685
 
    local_path = local_path.decode('utf-8')
686
674
    try:
 
675
        svnclient.callback_get_login = get_login
687
676
        svnclient.checkout(url, local_path, recurse=True)
688
677
    except pysvn.ClientError, e:
689
678
        raise ActionError(str(e))
695
684
    """
696
685
    path = fields.getfirst('path')
697
686
    logmsg = fields.getfirst('logmsg')
698
 
    url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
 
687
    url = ivle.conf.svn_addr + "/" + path
699
688
    try:
 
689
        svnclient.callback_get_login = get_login
700
690
        svnclient.mkdir(url, log_message=logmsg)
701
691
    except pysvn.ClientError, e:
702
692
        raise ActionError(str(e))
704
694
def action_svnrepostat(req, fields):
705
695
    """Discovers whether a path exists in a repo under the IVLE SVN root.
706
696
 
707
 
    If it does exist, returns a dict containing its metadata.
708
 
 
709
697
    Reads fields: 'path'
710
698
    """
711
699
    path = fields.getfirst('path')
712
 
    url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
713
 
    svnclient.exception_style = 1
 
700
    url = ivle.conf.svn_addr + "/" + path
 
701
    svnclient.exception_style = 1 
714
702
 
715
703
    try:
716
 
        info = svnclient.info2(url,
717
 
            revision=pysvn.Revision(pysvn.opt_revision_kind.head))[0][1]
718
 
        return {'svnrevision': info['rev'].number
719
 
                  if info['rev'] and
720
 
                     info['rev'].kind == pysvn.opt_revision_kind.number
721
 
                  else None}
 
704
        svnclient.callback_get_login = get_login
 
705
        svnclient.info2(url)
722
706
    except pysvn.ClientError, e:
723
707
        # Error code 170000 means ENOENT in this revision.
724
708
        if e[1][0][1] == 170000:
725
 
            req.status = 404
726
 
            raise ActionError('The specified repository path does not exist')
 
709
            raise util.IVLEError(404, 'The specified repository path does not exist')
727
710
        else:
728
711
            raise ActionError(str(e[0]))
729
712
 
730
 
 
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"""
735
 
 
736
 
    path = fields.getfirst('path')
737
 
    if path is None:
738
 
        raise ActionError("Required field missing")
739
 
    path = actionpath_to_local(req, path).decode('utf-8')
740
 
 
741
 
    try:
742
 
        svnclient.cleanup(path)
743
 
    except pysvn.ClientError, e:
744
 
        raise ActionError(str(e))
745
 
 
746
 
 
747
713
# Table of all action functions #
748
714
# Each function has the interface f(req, fields).
749
715
 
762
728
    "svnupdate" : action_svnupdate,
763
729
    "svnresolved" : action_svnresolved,
764
730
    "svnrevert" : action_svnrevert,
 
731
    "svnpublish" : action_svnpublish,
 
732
    "svnunpublish" : action_svnunpublish,
765
733
    "svncommit" : action_svncommit,
766
734
    "svncheckout" : action_svncheckout,
767
735
    "svnrepomkdir" : action_svnrepomkdir,
768
736
    "svnrepostat" : action_svnrepostat,
769
 
    "svncleanup" : action_svncleanup,
770
737
}