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

« back to all changes in this revision

Viewing changes to ivle/fileservice_lib/action.py

  • Committer: chadnickbok
  • Date: 2009-01-22 02:14:14 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:1173
Fixes Issue #14

When uploading files, students will now be shown an
error message if there upload would replace already existing
files.

This upload will not be performed until it will not clobber
any old files.

Note that there is currently a way to change this behaviour in
the code in action.py, to allow files to be overwritten. However
there is no way this flag can be set through the browser interface
(yet).

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
138
from ivle.fileservice_lib.exceptions import WillNotOverwrite
138
139
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)
 
140
 
 
141
 
 
142
def get_login(_realm, existing_login, _may_save):
 
143
    """Callback function used by pysvn for authentication.
 
144
    realm, existing_login, _may_save: The 3 arguments passed by pysvn to
 
145
        callback_get_login.
 
146
        The following has been determined empirically, not from docs:
 
147
        existing_login will be the name of the user who owns the process on
 
148
        the first attempt, "" on subsequent attempts. We use this fact.
 
149
    """
 
150
    # Only provide credentials on the _first_ attempt.
 
151
    # If we're being asked again, then it means the credentials failed for
 
152
    # some reason and we should just fail. (This is not desirable, but it's
 
153
    # better than being asked an infinite number of times).
 
154
    return (existing_login != "", ivle.conf.login, ivle.conf.svn_pass, True)
 
155
 
 
156
# Make a Subversion client object
 
157
svnclient = pysvn.Client()
 
158
svnclient.callback_get_login = get_login
145
159
svnclient.exception_style = 0               # Simple (string) exceptions
146
160
 
147
161
DEFAULT_LOGMESSAGE = "No log message supplied."
182
196
    except KeyError:
183
197
        # Default, just send an error but then continue
184
198
        raise ActionError("Unknown action")
185
 
    return action(req, fields)
 
199
    action(req, fields)
186
200
 
187
201
def actionpath_to_urlpath(req, path):
188
202
    """Determines the URL path (relative to the student home) upon which the
213
227
 
214
228
    Does not mutate req.
215
229
    """
216
 
    r = studpath.to_home_path(actionpath_to_urlpath(req, path))
 
230
    (_, _, r) = studpath.url_to_jailpaths(actionpath_to_urlpath(req, path))
217
231
    if r is None:
218
232
        raise ActionError("Invalid path")
219
233
    return r
405
419
    for datum in data:
406
420
        # Each of the uploaded files
407
421
        filepath = os.path.join(path, datum.filename)
408
 
        filepath_local = studpath.to_home_path(filepath)
 
422
        (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
409
423
        if os.path.isdir(filepath_local):
410
424
            raise ActionError("A directory already exists "
411
425
                    + "with that name")
421
435
            # filename)
422
436
            try:
423
437
                # First get the entire path (within jail)
424
 
                abspath = studpath.to_home_path(path)
 
438
                _, _, abspath = studpath.url_to_jailpaths(path)
425
439
                abspath = os.path.join(os.sep, abspath)
426
440
                zip.unzip(abspath, filedata)
427
441
            except (OSError, IOError):
430
444
                raise ActionError("File '" + e.filename + "' already exists.")
431
445
        else:
432
446
            # Not a zip file
433
 
            filepath_local = studpath.to_home_path(filepath)
 
447
            (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
434
448
            if filepath_local is None:
435
449
                raise ActionError("Invalid path")
436
450
 
517
531
    Reads fields: 'path'
518
532
    """
519
533
    paths = fields.getlist('path')
520
 
    user = util.split_path(req.path)[0]
 
534
    user = studpath.url_to_local(req.path)[0]
521
535
    homedir = "/home/%s" % user
522
536
    if len(paths):
523
537
        paths = map(lambda path: actionpath_to_local(req, path), paths)
524
538
    else:
525
 
        paths = [studpath.to_home_path(req.path)]
 
539
        paths = [studpath.url_to_jailpaths(req.path)[2]]
526
540
 
527
541
    # Set all the dirs in home dir world browsable (o+r,o+x)
528
542
    #FIXME: Should really only do those in the direct path not all of the 
552
566
    if len(paths):
553
567
        paths = map(lambda path: actionpath_to_local(req, path), paths)
554
568
    else:
555
 
        paths = [studpath.to_home_path(req.path)]
 
569
        paths = [studpath.url_to_jailpaths(req.path)[2]]
556
570
 
557
571
    try:
558
572
        for path in paths:
572
586
    Reads fields: 'path' (multiple)
573
587
    """
574
588
    paths = fields.getlist('path')
575
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
576
 
                paths)
 
589
    paths = map(lambda path: actionpath_to_local(req, path), paths)
577
590
 
578
591
    try:
579
592
        svnclient.add(paths, recurse=True, force=True)
586
599
    Reads fields: 'path' (multiple)
587
600
    """
588
601
    paths = fields.getlist('path')
589
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
590
 
                paths)
 
602
    paths = map(lambda path: actionpath_to_local(req, path), paths)
591
603
 
592
604
    try:
593
605
        svnclient.remove(paths, force=True)
597
609
def action_svnupdate(req, fields):
598
610
    """Performs a "svn update" to each file specified.
599
611
 
600
 
    Reads fields: 'path' and 'revision'
 
612
    Reads fields: 'path'
601
613
    """
602
614
    path = fields.getfirst('path')
603
 
    revision = fields.getfirst('revision')
604
615
    if path is None:
605
616
        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')
 
617
    path = actionpath_to_local(req, path)
615
618
 
616
619
    try:
617
 
        svnclient.update(path, recurse=True, revision=revision)
 
620
        svnclient.update(path, recurse=True)
618
621
    except pysvn.ClientError, e:
619
622
        raise ActionError(str(e))
620
623
 
626
629
    path = fields.getfirst('path')
627
630
    if path is None:
628
631
        raise ActionError("Required field missing")
629
 
    path = actionpath_to_local(req, path).decode('utf-8')
 
632
    path = actionpath_to_local(req, path)
630
633
 
631
634
    try:
632
635
        svnclient.resolved(path, recurse=True)
639
642
    Reads fields: 'path' (multiple)
640
643
    """
641
644
    paths = fields.getlist('path')
642
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
643
 
                paths)
 
645
    paths = map(lambda path: actionpath_to_local(req, path), paths)
644
646
 
645
647
    try:
646
648
        svnclient.revert(paths, recurse=True)
647
649
    except pysvn.ClientError, e:
648
650
        raise ActionError(str(e))
649
651
 
 
652
def action_svnpublish(req, fields):
 
653
    """Sets svn property "ivle:published" on each file specified.
 
654
    Should only be called on directories (only effective on directories
 
655
    anyway).
 
656
 
 
657
    Reads fields: 'path'
 
658
 
 
659
    XXX Currently unused by the client (calls action_publish instead, which
 
660
    has a completely different publishing model).
 
661
    """
 
662
    paths = fields.getlist('path')
 
663
    if len(paths):
 
664
        paths = map(lambda path: actionpath_to_local(req, path), paths)
 
665
    else:
 
666
        paths = [studpath.url_to_jailpaths(req.path)[2]]
 
667
 
 
668
    try:
 
669
        for path in paths:
 
670
            # Note: Property value doesn't matter
 
671
            svnclient.propset("ivle:published", "", path, recurse=False)
 
672
    except pysvn.ClientError, e:
 
673
        raise ActionError("Directory could not be published")
 
674
 
 
675
def action_svnunpublish(req, fields):
 
676
    """Deletes svn property "ivle:published" on each file specified.
 
677
 
 
678
    Reads fields: 'path'
 
679
 
 
680
    XXX Currently unused by the client (calls action_unpublish instead, which
 
681
    has a completely different publishing model).
 
682
    """
 
683
    paths = fields.getlist('path')
 
684
    paths = map(lambda path: actionpath_to_local(req, path), paths)
 
685
 
 
686
    try:
 
687
        for path in paths:
 
688
            svnclient.propdel("ivle:published", path, recurse=False)
 
689
    except pysvn.ClientError, e:
 
690
        raise ActionError("Directory could not be unpublished")
 
691
 
650
692
def action_svncommit(req, fields):
651
693
    """Performs a "svn commit" to each file specified.
652
694
 
653
695
    Reads fields: 'path' (multiple), 'logmsg' (optional)
654
696
    """
655
697
    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')
 
698
    paths = map(lambda path: actionpath_to_local(req, str(path)), paths)
 
699
    logmsg = str(fields.getfirst('logmsg', DEFAULT_LOGMESSAGE))
663
700
    if logmsg == '': logmsg = DEFAULT_LOGMESSAGE
664
701
 
665
702
    try:
675
712
    paths = fields.getlist('path')
676
713
    if len(paths) != 2:
677
714
        raise ActionError("usage: svncheckout url local-path")
678
 
    url = ivle.conf.svn_addr + "/" + urllib.quote(paths[0])
 
715
    url = ivle.conf.svn_addr + "/" + paths[0]
679
716
    local_path = actionpath_to_local(req, str(paths[1]))
680
 
    url = url.decode('utf-8')
681
 
    local_path = local_path.decode('utf-8')
682
717
    try:
 
718
        svnclient.callback_get_login = get_login
683
719
        svnclient.checkout(url, local_path, recurse=True)
684
720
    except pysvn.ClientError, e:
685
721
        raise ActionError(str(e))
691
727
    """
692
728
    path = fields.getfirst('path')
693
729
    logmsg = fields.getfirst('logmsg')
694
 
    url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
 
730
    url = ivle.conf.svn_addr + "/" + path
695
731
    try:
 
732
        svnclient.callback_get_login = get_login
696
733
        svnclient.mkdir(url, log_message=logmsg)
697
734
    except pysvn.ClientError, e:
698
735
        raise ActionError(str(e))
700
737
def action_svnrepostat(req, fields):
701
738
    """Discovers whether a path exists in a repo under the IVLE SVN root.
702
739
 
703
 
    If it does exist, returns a dict containing its metadata.
704
 
 
705
740
    Reads fields: 'path'
706
741
    """
707
742
    path = fields.getfirst('path')
708
 
    url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
709
 
    svnclient.exception_style = 1
 
743
    url = ivle.conf.svn_addr + "/" + path
 
744
    svnclient.exception_style = 1 
710
745
 
711
746
    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}
 
747
        svnclient.callback_get_login = get_login
 
748
        svnclient.info2(url)
718
749
    except pysvn.ClientError, e:
719
750
        # Error code 170000 means ENOENT in this revision.
720
751
        if e[1][0][1] == 170000:
721
752
            raise util.IVLEError(404, 'The specified repository path does not exist')
722
753
        else:
723
754
            raise ActionError(str(e[0]))
724
 
 
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
 
 
 
755
            
741
756
 
742
757
# Table of all action functions #
743
758
# Each function has the interface f(req, fields).
757
772
    "svnupdate" : action_svnupdate,
758
773
    "svnresolved" : action_svnresolved,
759
774
    "svnrevert" : action_svnrevert,
 
775
    "svnpublish" : action_svnpublish,
 
776
    "svnunpublish" : action_svnunpublish,
760
777
    "svncommit" : action_svncommit,
761
778
    "svncheckout" : action_svncheckout,
762
779
    "svnrepomkdir" : action_svnrepomkdir,
763
780
    "svnrepostat" : action_svnrepostat,
764
 
    "svncleanup" : action_svncleanup,
765
781
}