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

« back to all changes in this revision

Viewing changes to www/apps/fileservice/action.py

  • Committer: mattgiuca
  • Date: 2008-01-10 05:49:07 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:164
fileservice: Added action "move".

Show diffs side-by-side

added added

removed removed

Lines of Context:
41
41
#       to:     The path of the target filename. Error if the file already
42
42
#               exists.
43
43
#
44
 
# action=putfile: Upload a file to the student workspace, and optionally
45
 
#               accept zip files which will be unpacked.
 
44
# action=putfile: Upload a file to the student workspace.
46
45
#       path:   The path to the file to be written. If it exists, will
47
46
#               overwrite. Error if the target file is a directory.
48
47
#       data:   Bytes to be written to the file verbatim. May either be
49
48
#               a string variable or a file upload.
50
 
#       unpack: Optional. If "true", and the data is a valid ZIP file,
51
 
#               will create a directory instead and unpack the ZIP file
52
 
#               into it.
53
 
#
54
 
# action=putfiles: Upload multiple files to the student workspace, and
55
 
#                 optionally accept zip files which will be unpacked.
56
 
#       path:   The path to the DIRECTORY to place files in. Must not be a
57
 
#               file.
58
 
#       data:   A file upload (may not be a simple string). The filename
59
 
#               will be used to determine the target filename within
60
 
#               the given path.
61
 
#       unpack: Optional. If "true", if any data is a valid ZIP file,
62
 
#               will create a directory instead and unpack the ZIP file
63
 
#               into it.
64
 
#
65
 
# The differences between putfile and putfiles are:
66
 
# * putfile can only accept a single file.
67
 
# * putfile can accept string data, doesn't have to be a file upload.
68
 
# * putfile ignores the upload filename, the entire filename is specified on
69
 
#       path. putfiles calls files after the name on the user's machine.
70
49
#
71
50
# Clipboard-based actions. Cut/copy/paste work in the same way as modern
72
51
# file browsers, by keeping a server-side clipboard of files that have been
113
92
#               message if unspecified.
114
93
115
94
# TODO: Implement the following actions:
116
 
#   putfiles, svnrevert, svnupdate, svncommit
117
 
# TODO: Implement ZIP unpacking in putfile and putfiles.
118
 
# TODO: svnupdate needs a digest to tell the user the files that were updated.
119
 
#   This can be implemented by some message passing between action and
120
 
#   listing, and having the digest included in the listing. (Problem if
121
 
#   the listing is not a directory, but we could make it an error to do an
122
 
#   update if the path is not a directory).
 
95
#   move, copy, cut, paste, svnadd, svnrevert, svnupdate, svncommit
123
96
 
124
97
import os
125
98
import shutil
128
101
 
129
102
from common import (util, studpath)
130
103
 
131
 
# Make a Subversion client object
132
 
svnclient = pysvn.Client()
133
 
 
134
104
DEFAULT_LOGMESSAGE = "No log message supplied."
135
105
 
136
106
# Mime types
150
120
    """
151
121
    pass
152
122
 
153
 
def handle_action(req, action, fields):
 
123
def handle_action(req, svnclient, action, fields):
154
124
    """Perform the "action" part of the response.
155
125
    This function should only be called if the response is a POST.
156
126
    This performs the action's side-effect on the server. If unsuccessful,
201
171
        raise ActionError("Invalid path")
202
172
    return r
203
173
 
204
 
def movefile(req, frompath, topath, copy=False):
205
 
    """Performs a file move, resolving filenames, checking for any errors,
206
 
    and throwing ActionErrors if necessary. Can also be used to do a copy
207
 
    operation instead.
208
 
 
209
 
    frompath and topath are straight paths from the client. Will be checked.
210
 
    """
211
 
    # TODO: Do an SVN mv if the file is versioned.
212
 
    # TODO: Disallow tampering with student's home directory
213
 
    if frompath is None or topath is None:
214
 
        raise ActionError("Required field missing")
215
 
    frompath = actionpath_to_local(req, frompath)
216
 
    topath = actionpath_to_local(req, topath)
217
 
    if not os.path.exists(frompath):
218
 
        raise ActionError("The source file does not exist")
219
 
    if os.path.exists(topath):
220
 
        if frompath == topath:
221
 
            raise ActionError("Source and destination are the same")
222
 
        raise ActionError("Another file already exists with that name")
223
 
 
224
 
    try:
225
 
        if copy:
226
 
            if os.path.isdir(frompath):
227
 
                shutil.copytree(frompath, topath)
228
 
            else:
229
 
                shutil.copy2(frompath, topath)
230
 
        else:
231
 
            shutil.move(frompath, topath)
232
 
    except OSError:
233
 
        raise ActionError("Could not move the file specified")
234
 
    except shutil.Error:
235
 
        raise ActionError("Could not move the file specified")
236
 
 
237
174
### ACTIONS ###
238
175
 
239
176
def action_remove(req, fields):
272
209
    """
273
210
    frompath = fields.getfirst('from')
274
211
    topath = fields.getfirst('to')
275
 
    movefile(req, frompath, topath)
 
212
    if frompath is None or topath is None:
 
213
        raise ActionError("Required field missing")
 
214
    frompath = actionpath_to_local(req, frompath)
 
215
    topath = actionpath_to_local(req, topath)
 
216
    if not os.path.exists(frompath):
 
217
        raise ActionError("The source file does not exist")
 
218
    if os.path.exists(topath):
 
219
        raise ActionError("Another file already exists with that name")
 
220
 
 
221
    try:
 
222
        shutil.move(frompath, topath)
 
223
    except OSError:
 
224
        raise ActionError("Could not move the file specified")
 
225
    except shutil.Error:
 
226
        raise ActionError("Could not move the file specified")
276
227
 
277
228
def action_putfile(req, fields):
278
229
    """Writes data to a file, overwriting it if it exists and creating it if
294
245
    except OSError:
295
246
        raise ActionError("Could not write to target file")
296
247
 
297
 
def action_copy_or_cut(req, fields, mode):
298
 
    """Marks specified files on the clipboard, stored in the
299
 
    browser session. Sets clipboard for either a cut or copy operation
300
 
    as specified.
301
 
 
302
 
    Reads fields: 'path'
303
 
    """
304
 
    # The clipboard object created conforms to the JSON clipboard
305
 
    # specification given at the top of listing.py.
306
 
    # Note that we do not check for the existence of files here. That is done
307
 
    # in the paste operation.
308
 
    files = fields.getlist('path')
309
 
    files = map(lambda field: field.value, files)
310
 
    clipboard = { "mode" : mode, "base" : req.path, "files" : files }
311
 
    session = req.get_session()
312
 
    session['clipboard'] = clipboard
313
 
    session.save()
314
 
 
315
 
def action_copy(req, fields):
316
 
    """Marks specified files on the clipboard, stored in the
317
 
    browser session. Sets clipboard for a "copy" action.
318
 
 
319
 
    Reads fields: 'path'
320
 
    """
321
 
    action_copy_or_cut(req, fields, "copy")
322
 
 
323
 
def action_cut(req, fields):
324
 
    """Marks specified files on the clipboard, stored in the
325
 
    browser session. Sets clipboard for a "cut" action.
326
 
 
327
 
    Reads fields: 'path'
328
 
    """
329
 
    action_copy_or_cut(req, fields, "cut")
330
 
 
331
 
def action_paste(req, fields):
332
 
    """Performs the copy or move action with the files stored on
333
 
    the clipboard in the browser session. Copies/moves the files
334
 
    to the specified directory. Clears the clipboard.
335
 
 
336
 
    Reads fields: 'path'
337
 
    """
338
 
    errormsg = None
339
 
 
340
 
    todir = fields.getfirst('path')
341
 
    if todir is None:
342
 
        raise ActionError("Required field missing")
343
 
    todir_local = actionpath_to_local(req, todir)
344
 
    if not os.path.isdir(todir_local):
345
 
        raise ActionError("Target is not a directory")
346
 
 
347
 
    session = req.get_session()
348
 
    try:
349
 
        clipboard = session['clipboard']
350
 
        files = clipboard['files']
351
 
        base = clipboard['base']
352
 
        if clipboard['mode'] == "copy":
353
 
            copy = True
354
 
        else:
355
 
            copy = False
356
 
    except KeyError:
357
 
        raise ActionError("Clipboard was empty")
358
 
 
359
 
    errorfiles = []
360
 
    for file in files:
361
 
        # The source must not be interpreted as relative to req.path
362
 
        # Add a slash (relative to top-level)
363
 
        frompath = os.sep + os.path.join(base, file)
364
 
        # The destination is found by taking just the basename of the file
365
 
        topath = os.path.join(todir, os.path.basename(file))
366
 
        try:
367
 
            movefile(req, frompath, topath, copy)
368
 
        except ActionError, message:
369
 
            # Store the error for later; we want to copy as many as possible
370
 
            if errormsg is None:
371
 
                errormsg = message
372
 
            else:
373
 
                # Multiple errors; generic message
374
 
                errormsg = "One or more files could not be pasted"
375
 
            # Add this file to errorfiles; it will be put back on the
376
 
            # clipboard for possible future pasting.
377
 
            errorfiles.append(file)
378
 
    # If errors occured, augment the clipboard and raise ActionError
379
 
    if len(errorfiles) > 0:
380
 
        clipboard['files'] = errorfiles
381
 
        session['clipboard'] = clipboard
382
 
        session.save()
383
 
        raise ActionError(errormsg)
384
 
 
385
 
    # Success: Clear the clipboard
386
 
    del session['clipboard']
387
 
    session.save()
388
 
 
389
 
def action_svnadd(req, fields):
390
 
    """Performs a "svn add" to each file specified.
391
 
 
392
 
    Reads fields: 'path' (multiple)
393
 
    """
394
 
    paths = fields.getlist('path')
395
 
    paths = map(lambda path: actionpath_to_local(req, path), paths)
396
 
 
397
 
    try:
398
 
        svnclient.add(paths, recurse=True, force=True)
399
 
    except pysvn.ClientError:
400
 
        raise ActionError("One or more files could not be added")
401
 
 
402
 
def action_svnupdate(req, fields):
403
 
    """Performs a "svn update" to each file specified.
404
 
 
405
 
    Reads fields: 'path'
406
 
    """
407
 
    path = fields.getfirst('path')
408
 
    if path is None:
409
 
        raise ActionError("Required field missing")
410
 
    path = actionpath_to_local(req, path)
411
 
 
412
 
    try:
413
 
        svnclient.update(path, recurse=True)
414
 
    except pysvn.ClientError:
415
 
        raise ActionError("One or more files could not be updated")
416
 
 
417
 
def action_svnrevert(req, fields):
418
 
    """Performs a "svn revert" to each file specified.
419
 
 
420
 
    Reads fields: 'path' (multiple)
421
 
    """
422
 
    paths = fields.getlist('path')
423
 
    paths = map(lambda path: actionpath_to_local(req, path), paths)
424
 
 
425
 
    try:
426
 
        svnclient.revert(paths, recurse=True)
427
 
    except pysvn.ClientError:
428
 
        raise ActionError("One or more files could not be reverted")
429
 
 
430
 
def action_svncommit(req, fields):
431
 
    """Performs a "svn commit" to each file specified.
432
 
 
433
 
    Reads fields: 'path' (multiple), 'logmsg' (optional)
434
 
    """
435
 
    paths = fields.getlist('path')
436
 
    paths = map(lambda path: actionpath_to_local(req, str(path)), paths)
437
 
    logmsg = str(fields.getfirst('logmsg', DEFAULT_LOGMESSAGE))
438
 
    if logmsg == '': logmsg = DEFAULT_LOGMESSAGE
439
 
 
440
 
    try:
441
 
        svnclient.checkin(paths, logmsg, recurse=True)
442
 
    except pysvn.ClientError:
443
 
        raise ActionError("One or more files could not be committed")
444
 
 
445
248
# Table of all action functions #
446
249
# Each function has the interface f(req, fields).
447
250
 
449
252
    "remove" : action_remove,
450
253
    "move" : action_move,
451
254
    "putfile" : action_putfile,
452
 
 
453
 
    "copy" : action_copy,
454
 
    "cut" : action_cut,
455
 
    "paste" : action_paste,
456
 
 
457
 
    "svnadd" : action_svnadd,
458
 
    "svnupdate" : action_svnupdate,
459
 
    "svnrevert" : action_svnrevert,
460
 
    "svncommit" : action_svncommit,
461
255
}