~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-19 17:28:59 UTC
  • mto: This revision was merged to the branch mainline in revision 1090.
  • Revision ID: matt.giuca@gmail.com-20090119172859-htjq3rfpp0fhtpc9
ivle.worksheet: Added calculate_score. This is a nice clean Storm port of
    ivle.db.calculate_worksheet_score.
tutorial: Replaced use of ivle.db.calculate_worksheet_score with
    ivle.worksheet.calculate_score.
    Guess What!! Removed this module's dependency on ivle.db! Hooray!
    (Note: tutorialservice still depends on it).
bin/ivle-marks: Updated this script to use ivle.worksheet_calculate_score.
    Note that this DOES NOT execute properly -- it seems it didn't even
    before my changes (tries to call db.get_users). Not my fault; I'm
    committing and going to bed!
ivle.db: Removed calculate_worksheet_score.
    As this removes the dependency on get_problem_status, removed that too!
    Almost all the worksheet stuff is gone now!

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 != "", 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
145
158
svnclient.exception_style = 0               # Simple (string) exceptions
146
159
 
147
160
DEFAULT_LOGMESSAGE = "No log message supplied."
182
195
    except KeyError:
183
196
        # Default, just send an error but then continue
184
197
        raise ActionError("Unknown action")
185
 
    return action(req, fields)
 
198
    action(req, fields)
186
199
 
187
200
def actionpath_to_urlpath(req, path):
188
201
    """Determines the URL path (relative to the student home) upon which the
213
226
 
214
227
    Does not mutate req.
215
228
    """
216
 
    r = studpath.to_home_path(actionpath_to_urlpath(req, path))
 
229
    (_, _, r) = studpath.url_to_jailpaths(actionpath_to_urlpath(req, path))
217
230
    if r is None:
218
231
        raise ActionError("Invalid path")
219
232
    return r
251
264
    except shutil.Error:
252
265
        raise ActionError("Could not move the file specified")
253
266
 
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
267
### ACTIONS ###
284
268
 
285
269
def action_delete(req, fields):
317
301
    """
318
302
    frompath = fields.getfirst('from')
319
303
    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)
 
304
    movefile(req, frompath, topath)
325
305
 
326
306
def action_mkdir(req, fields):
327
307
    """Creates a directory with the given path.
391
371
 
392
372
    Reads fields: 'path', 'data' (file upload, multiple), 'unpack'
393
373
    """
 
374
 
394
375
    # Important: Data is "None" if the file submitted is empty.
395
376
    path = fields.getfirst('path')
396
377
    data = fields['data']
409
390
    for datum in data:
410
391
        # Each of the uploaded files
411
392
        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
393
        filedata = datum.file
420
394
 
421
395
        if unpack and datum.filename.lower().endswith(".zip"):
425
399
            # filename)
426
400
            try:
427
401
                # First get the entire path (within jail)
428
 
                abspath = studpath.to_home_path(path)
 
402
                _, _, abspath = studpath.url_to_jailpaths(path)
429
403
                abspath = os.path.join(os.sep, abspath)
430
404
                zip.unzip(abspath, filedata)
431
405
            except (OSError, IOError):
432
406
                goterror = True
433
 
            except WillNotOverwrite, e:
434
 
                raise ActionError("File '" + e.filename + "' already exists.")
435
407
        else:
436
408
            # Not a zip file
437
 
            filepath_local = studpath.to_home_path(filepath)
 
409
            (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
438
410
            if filepath_local is None:
439
411
                raise ActionError("Invalid path")
440
412
 
473
445
    files = fields.getlist('file')
474
446
    if dst is None or src is None or mode is None:
475
447
        raise ActionError("Required field missing")
476
 
 
 
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')")
477
454
    dst_local = actionpath_to_local(req, dst)
478
455
    if not os.path.isdir(dst_local):
479
456
        raise ActionError("dst is not a directory")
488
465
        # The destination is found by taking just the basename of the file
489
466
        topath = os.path.join(dst, os.path.basename(file))
490
467
        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')")
 
468
            movefile(req, frompath, topath, copy)
501
469
        except ActionError, message:
502
470
            # Store the error for later; we want to copy as many as possible
503
471
            if errormsg is None:
521
489
    Reads fields: 'path'
522
490
    """
523
491
    paths = fields.getlist('path')
524
 
    user = util.split_path(req.path)[0]
 
492
    user = studpath.url_to_local(req.path)[0]
525
493
    homedir = "/home/%s" % user
526
494
    if len(paths):
527
495
        paths = map(lambda path: actionpath_to_local(req, path), paths)
528
496
    else:
529
 
        paths = [studpath.to_home_path(req.path)]
 
497
        paths = [studpath.url_to_jailpaths(req.path)[2]]
530
498
 
531
499
    # Set all the dirs in home dir world browsable (o+r,o+x)
532
500
    #FIXME: Should really only do those in the direct path not all of the 
556
524
    if len(paths):
557
525
        paths = map(lambda path: actionpath_to_local(req, path), paths)
558
526
    else:
559
 
        paths = [studpath.to_home_path(req.path)]
 
527
        paths = [studpath.url_to_jailpaths(req.path)[2]]
560
528
 
561
529
    try:
562
530
        for path in paths:
576
544
    Reads fields: 'path' (multiple)
577
545
    """
578
546
    paths = fields.getlist('path')
579
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
580
 
                paths)
 
547
    paths = map(lambda path: actionpath_to_local(req, path), paths)
581
548
 
582
549
    try:
583
550
        svnclient.add(paths, recurse=True, force=True)
590
557
    Reads fields: 'path' (multiple)
591
558
    """
592
559
    paths = fields.getlist('path')
593
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
594
 
                paths)
 
560
    paths = map(lambda path: actionpath_to_local(req, path), paths)
595
561
 
596
562
    try:
597
563
        svnclient.remove(paths, force=True)
601
567
def action_svnupdate(req, fields):
602
568
    """Performs a "svn update" to each file specified.
603
569
 
604
 
    Reads fields: 'path' and 'revision'
 
570
    Reads fields: 'path'
605
571
    """
606
572
    path = fields.getfirst('path')
607
 
    revision = fields.getfirst('revision')
608
573
    if path is None:
609
574
        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')
 
575
    path = actionpath_to_local(req, path)
619
576
 
620
577
    try:
621
 
        svnclient.update(path, recurse=True, revision=revision)
 
578
        svnclient.update(path, recurse=True)
622
579
    except pysvn.ClientError, e:
623
580
        raise ActionError(str(e))
624
581
 
630
587
    path = fields.getfirst('path')
631
588
    if path is None:
632
589
        raise ActionError("Required field missing")
633
 
    path = actionpath_to_local(req, path).decode('utf-8')
 
590
    path = actionpath_to_local(req, path)
634
591
 
635
592
    try:
636
593
        svnclient.resolved(path, recurse=True)
643
600
    Reads fields: 'path' (multiple)
644
601
    """
645
602
    paths = fields.getlist('path')
646
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
647
 
                paths)
 
603
    paths = map(lambda path: actionpath_to_local(req, path), paths)
648
604
 
649
605
    try:
650
606
        svnclient.revert(paths, recurse=True)
651
607
    except pysvn.ClientError, e:
652
608
        raise ActionError(str(e))
653
609
 
 
610
def action_svnpublish(req, fields):
 
611
    """Sets svn property "ivle:published" on each file specified.
 
612
    Should only be called on directories (only effective on directories
 
613
    anyway).
 
614
 
 
615
    Reads fields: 'path'
 
616
 
 
617
    XXX Currently unused by the client (calls action_publish instead, which
 
618
    has a completely different publishing model).
 
619
    """
 
620
    paths = fields.getlist('path')
 
621
    if len(paths):
 
622
        paths = map(lambda path: actionpath_to_local(req, path), paths)
 
623
    else:
 
624
        paths = [studpath.url_to_jailpaths(req.path)[2]]
 
625
 
 
626
    try:
 
627
        for path in paths:
 
628
            # Note: Property value doesn't matter
 
629
            svnclient.propset("ivle:published", "", path, recurse=False)
 
630
    except pysvn.ClientError, e:
 
631
        raise ActionError("Directory could not be published")
 
632
 
 
633
def action_svnunpublish(req, fields):
 
634
    """Deletes svn property "ivle:published" on each file specified.
 
635
 
 
636
    Reads fields: 'path'
 
637
 
 
638
    XXX Currently unused by the client (calls action_unpublish instead, which
 
639
    has a completely different publishing model).
 
640
    """
 
641
    paths = fields.getlist('path')
 
642
    paths = map(lambda path: actionpath_to_local(req, path), paths)
 
643
 
 
644
    try:
 
645
        for path in paths:
 
646
            svnclient.propdel("ivle:published", path, recurse=False)
 
647
    except pysvn.ClientError, e:
 
648
        raise ActionError("Directory could not be unpublished")
 
649
 
654
650
def action_svncommit(req, fields):
655
651
    """Performs a "svn commit" to each file specified.
656
652
 
657
653
    Reads fields: 'path' (multiple), 'logmsg' (optional)
658
654
    """
659
655
    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')
 
656
    paths = map(lambda path: actionpath_to_local(req, str(path)), paths)
 
657
    logmsg = str(fields.getfirst('logmsg', DEFAULT_LOGMESSAGE))
667
658
    if logmsg == '': logmsg = DEFAULT_LOGMESSAGE
668
659
 
669
660
    try:
679
670
    paths = fields.getlist('path')
680
671
    if len(paths) != 2:
681
672
        raise ActionError("usage: svncheckout url local-path")
682
 
    url = ivle.conf.svn_addr + "/" + urllib.quote(paths[0])
 
673
    url = ivle.conf.svn_addr + "/" + paths[0]
683
674
    local_path = actionpath_to_local(req, str(paths[1]))
684
 
    url = url.decode('utf-8')
685
 
    local_path = local_path.decode('utf-8')
686
675
    try:
 
676
        svnclient.callback_get_login = get_login
687
677
        svnclient.checkout(url, local_path, recurse=True)
688
678
    except pysvn.ClientError, e:
689
679
        raise ActionError(str(e))
695
685
    """
696
686
    path = fields.getfirst('path')
697
687
    logmsg = fields.getfirst('logmsg')
698
 
    url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
 
688
    url = ivle.conf.svn_addr + "/" + path
699
689
    try:
 
690
        svnclient.callback_get_login = get_login
700
691
        svnclient.mkdir(url, log_message=logmsg)
701
692
    except pysvn.ClientError, e:
702
693
        raise ActionError(str(e))
704
695
def action_svnrepostat(req, fields):
705
696
    """Discovers whether a path exists in a repo under the IVLE SVN root.
706
697
 
707
 
    If it does exist, returns a dict containing its metadata.
708
 
 
709
698
    Reads fields: 'path'
710
699
    """
711
700
    path = fields.getfirst('path')
712
 
    url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
713
 
    svnclient.exception_style = 1
 
701
    url = ivle.conf.svn_addr + "/" + path
 
702
    svnclient.exception_style = 1 
714
703
 
715
704
    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}
 
705
        svnclient.callback_get_login = get_login
 
706
        svnclient.info2(url)
722
707
    except pysvn.ClientError, e:
723
708
        # Error code 170000 means ENOENT in this revision.
724
709
        if e[1][0][1] == 170000:
725
 
            req.status = 404
726
 
            raise ActionError('The specified repository path does not exist')
 
710
            raise util.IVLEError(404, 'The specified repository path does not exist')
727
711
        else:
728
712
            raise ActionError(str(e[0]))
729
713
 
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
714
# Table of all action functions #
748
715
# Each function has the interface f(req, fields).
749
716
 
762
729
    "svnupdate" : action_svnupdate,
763
730
    "svnresolved" : action_svnresolved,
764
731
    "svnrevert" : action_svnrevert,
 
732
    "svnpublish" : action_svnpublish,
 
733
    "svnunpublish" : action_svnunpublish,
765
734
    "svncommit" : action_svncommit,
766
735
    "svncheckout" : action_svncheckout,
767
736
    "svnrepomkdir" : action_svnrepomkdir,
768
737
    "svnrepostat" : action_svnrepostat,
769
 
    "svncleanup" : action_svncleanup,
770
738
}