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

« back to all changes in this revision

Viewing changes to ivle/fileservice_lib/action.py

  • Committer: William Grant
  • Date: 2012-06-28 01:52:02 UTC
  • Revision ID: me@williamgrant.id.au-20120628015202-f6ru7o367gt6nvgz
Hah

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
 
#
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.
 
94
#       revision: The revision number to update to. If not provided this
 
95
#               defaults to HEAD.
102
96
#
103
97
# action=svncommit: Commit a file(s) or directory(s) to the repository.
104
98
#       path:   The path to the file or directory to be committed. Can be
119
113
#       path:   The path to the directory to be checked (under the IVLE
120
114
#               repository base).
121
115
#
 
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
#
122
120
# TODO: Implement the following actions:
123
121
#   svnupdate (done?)
124
122
# TODO: Implement ZIP unpacking in putfiles (done?).
131
129
import os
132
130
import cStringIO
133
131
import shutil
 
132
import urllib
134
133
 
135
134
import pysvn
136
135
 
137
136
from ivle import (util, studpath, zip)
138
137
from ivle.fileservice_lib.exceptions import WillNotOverwrite
139
138
import ivle.conf
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
 
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)
160
145
svnclient.exception_style = 0               # Simple (string) exceptions
161
146
 
162
147
DEFAULT_LOGMESSAGE = "No log message supplied."
197
182
    except KeyError:
198
183
        # Default, just send an error but then continue
199
184
        raise ActionError("Unknown action")
200
 
    action(req, fields)
 
185
    return action(req, fields)
201
186
 
202
187
def actionpath_to_urlpath(req, path):
203
188
    """Determines the URL path (relative to the student home) upon which the
228
213
 
229
214
    Does not mutate req.
230
215
    """
231
 
    (_, _, r) = studpath.url_to_jailpaths(actionpath_to_urlpath(req, path))
 
216
    r = studpath.to_home_path(actionpath_to_urlpath(req, path))
232
217
    if r is None:
233
218
        raise ActionError("Invalid path")
234
219
    return r
332
317
    """
333
318
    frompath = fields.getfirst('from')
334
319
    topath = fields.getfirst('to')
335
 
    movefile(req, frompath, topath)
 
320
    svn = fields.getfirst('svn')
 
321
    if svn:
 
322
        svn_movefile(req, frompath, topath)
 
323
    else:
 
324
        movefile(req, frompath, topath)
336
325
 
337
326
def action_mkdir(req, fields):
338
327
    """Creates a directory with the given path.
420
409
    for datum in data:
421
410
        # Each of the uploaded files
422
411
        filepath = os.path.join(path, datum.filename)
423
 
        (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
 
412
        filepath_local = studpath.to_home_path(filepath)
424
413
        if os.path.isdir(filepath_local):
425
414
            raise ActionError("A directory already exists "
426
415
                    + "with that name")
436
425
            # filename)
437
426
            try:
438
427
                # First get the entire path (within jail)
439
 
                _, _, abspath = studpath.url_to_jailpaths(path)
 
428
                abspath = studpath.to_home_path(path)
440
429
                abspath = os.path.join(os.sep, abspath)
441
430
                zip.unzip(abspath, filedata)
442
431
            except (OSError, IOError):
445
434
                raise ActionError("File '" + e.filename + "' already exists.")
446
435
        else:
447
436
            # Not a zip file
448
 
            (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
 
437
            filepath_local = studpath.to_home_path(filepath)
449
438
            if filepath_local is None:
450
439
                raise ActionError("Invalid path")
451
440
 
532
521
    Reads fields: 'path'
533
522
    """
534
523
    paths = fields.getlist('path')
535
 
    user = studpath.url_to_local(req.path)[0]
 
524
    user = util.split_path(req.path)[0]
536
525
    homedir = "/home/%s" % user
537
526
    if len(paths):
538
527
        paths = map(lambda path: actionpath_to_local(req, path), paths)
539
528
    else:
540
 
        paths = [studpath.url_to_jailpaths(req.path)[2]]
 
529
        paths = [studpath.to_home_path(req.path)]
541
530
 
542
531
    # Set all the dirs in home dir world browsable (o+r,o+x)
543
532
    #FIXME: Should really only do those in the direct path not all of the 
567
556
    if len(paths):
568
557
        paths = map(lambda path: actionpath_to_local(req, path), paths)
569
558
    else:
570
 
        paths = [studpath.url_to_jailpaths(req.path)[2]]
 
559
        paths = [studpath.to_home_path(req.path)]
571
560
 
572
561
    try:
573
562
        for path in paths:
587
576
    Reads fields: 'path' (multiple)
588
577
    """
589
578
    paths = fields.getlist('path')
590
 
    paths = map(lambda path: actionpath_to_local(req, path), paths)
 
579
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
 
580
                paths)
591
581
 
592
582
    try:
593
583
        svnclient.add(paths, recurse=True, force=True)
600
590
    Reads fields: 'path' (multiple)
601
591
    """
602
592
    paths = fields.getlist('path')
603
 
    paths = map(lambda path: actionpath_to_local(req, path), paths)
 
593
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
 
594
                paths)
604
595
 
605
596
    try:
606
597
        svnclient.remove(paths, force=True)
610
601
def action_svnupdate(req, fields):
611
602
    """Performs a "svn update" to each file specified.
612
603
 
613
 
    Reads fields: 'path'
 
604
    Reads fields: 'path' and 'revision'
614
605
    """
615
606
    path = fields.getfirst('path')
 
607
    revision = fields.getfirst('revision')
616
608
    if path is None:
617
609
        raise ActionError("Required field missing")
618
 
    path = actionpath_to_local(req, path)
 
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')
619
619
 
620
620
    try:
621
 
        svnclient.update(path, recurse=True)
 
621
        svnclient.update(path, recurse=True, revision=revision)
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)
 
633
    path = actionpath_to_local(req, path).decode('utf-8')
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), paths)
 
646
    paths = map(lambda path: actionpath_to_local(req, path).decode('utf-8'),
 
647
                paths)
647
648
 
648
649
    try:
649
650
        svnclient.revert(paths, recurse=True)
650
651
    except pysvn.ClientError, e:
651
652
        raise ActionError(str(e))
652
653
 
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
 
 
693
654
def action_svncommit(req, fields):
694
655
    """Performs a "svn commit" to each file specified.
695
656
 
696
657
    Reads fields: 'path' (multiple), 'logmsg' (optional)
697
658
    """
698
659
    paths = fields.getlist('path')
699
 
    paths = map(lambda path: actionpath_to_local(req, str(path)), paths)
700
 
    logmsg = str(fields.getfirst('logmsg', DEFAULT_LOGMESSAGE))
 
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')
701
667
    if logmsg == '': logmsg = DEFAULT_LOGMESSAGE
702
668
 
703
669
    try:
713
679
    paths = fields.getlist('path')
714
680
    if len(paths) != 2:
715
681
        raise ActionError("usage: svncheckout url local-path")
716
 
    url = ivle.conf.svn_addr + "/" + paths[0]
 
682
    url = ivle.conf.svn_addr + "/" + urllib.quote(paths[0])
717
683
    local_path = actionpath_to_local(req, str(paths[1]))
 
684
    url = url.decode('utf-8')
 
685
    local_path = local_path.decode('utf-8')
718
686
    try:
719
 
        svnclient.callback_get_login = get_login
720
687
        svnclient.checkout(url, local_path, recurse=True)
721
688
    except pysvn.ClientError, e:
722
689
        raise ActionError(str(e))
728
695
    """
729
696
    path = fields.getfirst('path')
730
697
    logmsg = fields.getfirst('logmsg')
731
 
    url = ivle.conf.svn_addr + "/" + path
 
698
    url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
732
699
    try:
733
 
        svnclient.callback_get_login = get_login
734
700
        svnclient.mkdir(url, log_message=logmsg)
735
701
    except pysvn.ClientError, e:
736
702
        raise ActionError(str(e))
738
704
def action_svnrepostat(req, fields):
739
705
    """Discovers whether a path exists in a repo under the IVLE SVN root.
740
706
 
 
707
    If it does exist, returns a dict containing its metadata.
 
708
 
741
709
    Reads fields: 'path'
742
710
    """
743
711
    path = fields.getfirst('path')
744
 
    url = ivle.conf.svn_addr + "/" + path
745
 
    svnclient.exception_style = 1 
 
712
    url = (ivle.conf.svn_addr + "/" + urllib.quote(path)).decode('utf-8')
 
713
    svnclient.exception_style = 1
746
714
 
747
715
    try:
748
 
        svnclient.callback_get_login = get_login
749
 
        svnclient.info2(url)
 
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}
750
722
    except pysvn.ClientError, e:
751
723
        # Error code 170000 means ENOENT in this revision.
752
724
        if e[1][0][1] == 170000:
753
 
            raise util.IVLEError(404, 'The specified repository path does not exist')
 
725
            req.status = 404
 
726
            raise ActionError('The specified repository path does not exist')
754
727
        else:
755
728
            raise ActionError(str(e[0]))
756
 
            
 
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
 
757
746
 
758
747
# Table of all action functions #
759
748
# Each function has the interface f(req, fields).
773
762
    "svnupdate" : action_svnupdate,
774
763
    "svnresolved" : action_svnresolved,
775
764
    "svnrevert" : action_svnrevert,
776
 
    "svnpublish" : action_svnpublish,
777
 
    "svnunpublish" : action_svnunpublish,
778
765
    "svncommit" : action_svncommit,
779
766
    "svncheckout" : action_svncheckout,
780
767
    "svnrepomkdir" : action_svnrepomkdir,
781
768
    "svnrepostat" : action_svnrepostat,
 
769
    "svncleanup" : action_svncleanup,
782
770
}