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

« back to all changes in this revision

Viewing changes to ivle/fileservice_lib/action.py

Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
login page if a page raises an Unauthorized. Alter UserSettingsView to raise
one in the right cases, for testing.

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 != "", str(ivle.conf.login),
 
155
                                  str(ivle.conf.svn_pass), True)
 
156
 
 
157
# Make a Subversion client object
 
158
svnclient = pysvn.Client()
 
159
svnclient.callback_get_login = get_login
145
160
svnclient.exception_style = 0               # Simple (string) exceptions
146
161
 
147
162
DEFAULT_LOGMESSAGE = "No log message supplied."
182
197
    except KeyError:
183
198
        # Default, just send an error but then continue
184
199
        raise ActionError("Unknown action")
185
 
    return action(req, fields)
 
200
    action(req, fields)
186
201
 
187
202
def actionpath_to_urlpath(req, path):
188
203
    """Determines the URL path (relative to the student home) upon which the
213
228
 
214
229
    Does not mutate req.
215
230
    """
216
 
    r = studpath.to_home_path(actionpath_to_urlpath(req, path))
 
231
    (_, _, r) = studpath.url_to_jailpaths(actionpath_to_urlpath(req, path))
217
232
    if r is None:
218
233
        raise ActionError("Invalid path")
219
234
    return r
317
332
    """
318
333
    frompath = fields.getfirst('from')
319
334
    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)
 
335
    movefile(req, frompath, topath)
325
336
 
326
337
def action_mkdir(req, fields):
327
338
    """Creates a directory with the given path.
409
420
    for datum in data:
410
421
        # Each of the uploaded files
411
422
        filepath = os.path.join(path, datum.filename)
412
 
        filepath_local = studpath.to_home_path(filepath)
 
423
        (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
413
424
        if os.path.isdir(filepath_local):
414
425
            raise ActionError("A directory already exists "
415
426
                    + "with that name")
425
436
            # filename)
426
437
            try:
427
438
                # First get the entire path (within jail)
428
 
                abspath = studpath.to_home_path(path)
 
439
                _, _, abspath = studpath.url_to_jailpaths(path)
429
440
                abspath = os.path.join(os.sep, abspath)
430
441
                zip.unzip(abspath, filedata)
431
442
            except (OSError, IOError):
434
445
                raise ActionError("File '" + e.filename + "' already exists.")
435
446
        else:
436
447
            # Not a zip file
437
 
            filepath_local = studpath.to_home_path(filepath)
 
448
            (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
438
449
            if filepath_local is None:
439
450
                raise ActionError("Invalid path")
440
451
 
521
532
    Reads fields: 'path'
522
533
    """
523
534
    paths = fields.getlist('path')
524
 
    user = util.split_path(req.path)[0]
 
535
    user = studpath.url_to_local(req.path)[0]
525
536
    homedir = "/home/%s" % user
526
537
    if len(paths):
527
538
        paths = map(lambda path: actionpath_to_local(req, path), paths)
528
539
    else:
529
 
        paths = [studpath.to_home_path(req.path)]
 
540
        paths = [studpath.url_to_jailpaths(req.path)[2]]
530
541
 
531
542
    # Set all the dirs in home dir world browsable (o+r,o+x)
532
543
    #FIXME: Should really only do those in the direct path not all of the 
556
567
    if len(paths):
557
568
        paths = map(lambda path: actionpath_to_local(req, path), paths)
558
569
    else:
559
 
        paths = [studpath.to_home_path(req.path)]
 
570
        paths = [studpath.url_to_jailpaths(req.path)[2]]
560
571
 
561
572
    try:
562
573
        for path in paths:
576
587
    Reads fields: 'path' (multiple)
577
588
    """
578
589
    paths = fields.getlist('path')
579
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
580
 
                paths)
 
590
    paths = map(lambda path: actionpath_to_local(req, path), paths)
581
591
 
582
592
    try:
583
593
        svnclient.add(paths, recurse=True, force=True)
590
600
    Reads fields: 'path' (multiple)
591
601
    """
592
602
    paths = fields.getlist('path')
593
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
594
 
                paths)
 
603
    paths = map(lambda path: actionpath_to_local(req, path), paths)
595
604
 
596
605
    try:
597
606
        svnclient.remove(paths, force=True)
601
610
def action_svnupdate(req, fields):
602
611
    """Performs a "svn update" to each file specified.
603
612
 
604
 
    Reads fields: 'path' and 'revision'
 
613
    Reads fields: 'path'
605
614
    """
606
615
    path = fields.getfirst('path')
607
 
    revision = fields.getfirst('revision')
608
616
    if path is None:
609
617
        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')
 
618
    path = actionpath_to_local(req, path)
619
619
 
620
620
    try:
621
 
        svnclient.update(path, recurse=True, revision=revision)
 
621
        svnclient.update(path, recurse=True)
622
622
    except pysvn.ClientError, e:
623
623
        raise ActionError(str(e))
624
624
 
630
630
    path = fields.getfirst('path')
631
631
    if path is None:
632
632
        raise ActionError("Required field missing")
633
 
    path = actionpath_to_local(req, path).decode('utf-8')
 
633
    path = actionpath_to_local(req, path)
634
634
 
635
635
    try:
636
636
        svnclient.resolved(path, recurse=True)
643
643
    Reads fields: 'path' (multiple)
644
644
    """
645
645
    paths = fields.getlist('path')
646
 
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
647
 
                paths)
 
646
    paths = map(lambda path: actionpath_to_local(req, path), paths)
648
647
 
649
648
    try:
650
649
        svnclient.revert(paths, recurse=True)
651
650
    except pysvn.ClientError, e:
652
651
        raise ActionError(str(e))
653
652
 
 
653
def action_svnpublish(req, fields):
 
654
    """Sets svn property "ivle:published" on each file specified.
 
655
    Should only be called on directories (only effective on directories
 
656
    anyway).
 
657
 
 
658
    Reads fields: 'path'
 
659
 
 
660
    XXX Currently unused by the client (calls action_publish instead, which
 
661
    has a completely different publishing model).
 
662
    """
 
663
    paths = fields.getlist('path')
 
664
    if len(paths):
 
665
        paths = map(lambda path: actionpath_to_local(req, path), paths)
 
666
    else:
 
667
        paths = [studpath.url_to_jailpaths(req.path)[2]]
 
668
 
 
669
    try:
 
670
        for path in paths:
 
671
            # Note: Property value doesn't matter
 
672
            svnclient.propset("ivle:published", "", path, recurse=False)
 
673
    except pysvn.ClientError, e:
 
674
        raise ActionError("Directory could not be published")
 
675
 
 
676
def action_svnunpublish(req, fields):
 
677
    """Deletes svn property "ivle:published" on each file specified.
 
678
 
 
679
    Reads fields: 'path'
 
680
 
 
681
    XXX Currently unused by the client (calls action_unpublish instead, which
 
682
    has a completely different publishing model).
 
683
    """
 
684
    paths = fields.getlist('path')
 
685
    paths = map(lambda path: actionpath_to_local(req, path), paths)
 
686
 
 
687
    try:
 
688
        for path in paths:
 
689
            svnclient.propdel("ivle:published", path, recurse=False)
 
690
    except pysvn.ClientError, e:
 
691
        raise ActionError("Directory could not be unpublished")
 
692
 
654
693
def action_svncommit(req, fields):
655
694
    """Performs a "svn commit" to each file specified.
656
695
 
657
696
    Reads fields: 'path' (multiple), 'logmsg' (optional)
658
697
    """
659
698
    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')
 
699
    paths = map(lambda path: actionpath_to_local(req, str(path)), paths)
 
700
    logmsg = str(fields.getfirst('logmsg', DEFAULT_LOGMESSAGE))
667
701
    if logmsg == '': logmsg = DEFAULT_LOGMESSAGE
668
702
 
669
703
    try:
679
713
    paths = fields.getlist('path')
680
714
    if len(paths) != 2:
681
715
        raise ActionError("usage: svncheckout url local-path")
682
 
    url = ivle.conf.svn_addr + "/" + urllib.quote(paths[0])
 
716
    url = ivle.conf.svn_addr + "/" + paths[0]
683
717
    local_path = actionpath_to_local(req, str(paths[1]))
684
 
    url = url.decode('utf-8')
685
 
    local_path = local_path.decode('utf-8')
686
718
    try:
 
719
        svnclient.callback_get_login = get_login
687
720
        svnclient.checkout(url, local_path, recurse=True)
688
721
    except pysvn.ClientError, e:
689
722
        raise ActionError(str(e))
695
728
    """
696
729
    path = fields.getfirst('path')
697
730
    logmsg = fields.getfirst('logmsg')
698
 
    url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
 
731
    url = ivle.conf.svn_addr + "/" + path
699
732
    try:
 
733
        svnclient.callback_get_login = get_login
700
734
        svnclient.mkdir(url, log_message=logmsg)
701
735
    except pysvn.ClientError, e:
702
736
        raise ActionError(str(e))
704
738
def action_svnrepostat(req, fields):
705
739
    """Discovers whether a path exists in a repo under the IVLE SVN root.
706
740
 
707
 
    If it does exist, returns a dict containing its metadata.
708
 
 
709
741
    Reads fields: 'path'
710
742
    """
711
743
    path = fields.getfirst('path')
712
 
    url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
713
 
    svnclient.exception_style = 1
 
744
    url = ivle.conf.svn_addr + "/" + path
 
745
    svnclient.exception_style = 1 
714
746
 
715
747
    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}
 
748
        svnclient.callback_get_login = get_login
 
749
        svnclient.info2(url)
722
750
    except pysvn.ClientError, e:
723
751
        # Error code 170000 means ENOENT in this revision.
724
752
        if e[1][0][1] == 170000:
725
 
            req.status = 404
726
 
            raise ActionError('The specified repository path does not exist')
 
753
            raise util.IVLEError(404, 'The specified repository path does not exist')
727
754
        else:
728
755
            raise ActionError(str(e[0]))
729
 
 
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
 
 
 
756
            
746
757
 
747
758
# Table of all action functions #
748
759
# Each function has the interface f(req, fields).
762
773
    "svnupdate" : action_svnupdate,
763
774
    "svnresolved" : action_svnresolved,
764
775
    "svnrevert" : action_svnrevert,
 
776
    "svnpublish" : action_svnpublish,
 
777
    "svnunpublish" : action_svnunpublish,
765
778
    "svncommit" : action_svncommit,
766
779
    "svncheckout" : action_svncheckout,
767
780
    "svnrepomkdir" : action_svnrepomkdir,
768
781
    "svnrepostat" : action_svnrepostat,
769
 
    "svncleanup" : action_svncleanup,
770
782
}