62
62
# will create a directory instead and unpack the ZIP file
65
# action=mkdir: Create a directory. The parent dir must exist.
66
# path: The path to a file which does not exist, but whose parent
67
# does. The dir will be made with this name.
65
69
# The differences between putfile and putfiles are:
66
70
# * putfile can only accept a single file.
67
71
# * putfile can accept string data, doesn't have to be a file upload.
68
72
# * putfile ignores the upload filename, the entire filename is specified on
69
73
# path. putfiles calls files after the name on the user's machine.
71
# Clipboard-based actions. Cut/copy/paste work in the same way as modern
72
# file browsers, by keeping a server-side clipboard of files that have been
73
# cut and copied. The clipboard is stored in the session data, so it persists
74
# across navigation, tabs and browser windows, but not across browser
77
# action=copy: Write file(s) to the session-based clipboard. Overrides any
78
# existing clipboard data. Does not actually copy the file.
79
# The files are physically copied when the clipboard is pasted.
80
# path: The path to the file or directory to copy. Can be specified
83
# action=cut: Write file(s) to the session-based clipboard. Overrides any
84
# existing clipboard data. Does not actually move the file.
85
# The files are physically moved when the clipboard is pasted.
86
# path: The path to the file or directory to cut. Can be specified
89
# action=paste: Copy or move the files stored in the clipboard. Clears the
90
# clipboard. The files are copied or moved to a specified dir.
91
# path: The path to the DIRECTORY to paste the files to. Must not
75
# action=paste: Copy or move the files to a specified dir.
76
# src: The path to the DIRECTORY to get the files from (relative).
77
# dst: The path to the DIRECTORY to paste the files to. Must not
79
# mode: 'copy' or 'move'
80
# file: File to be copied or moved, relative to src, to a destination
81
# relative to dst. Can be specified multiple times.
94
83
# Subversion actions.
95
84
# action=svnadd: Add an existing file(s) to version control.
296
285
topath = fields.getfirst('to')
297
286
movefile(req, frompath, topath)
288
def action_mkdir(req, fields):
289
"""Creates a directory with the given path.
292
path = fields.getfirst('path')
294
raise ActionError("Required field missing")
295
path = actionpath_to_local(req, path)
297
# Create the directory
301
raise ActionError("Could not create directory")
299
303
def action_putfile(req, fields):
300
304
"""Writes data to a file, overwriting it if it exists and creating it if
379
390
raise ActionError(
380
391
"Could not write to one or more of the target files")
382
def action_copy_or_cut(req, fields, mode):
383
"""Marks specified files on the clipboard, stored in the
384
browser session. Sets clipboard for either a cut or copy operation
389
# The clipboard object created conforms to the JSON clipboard
390
# specification given at the top of listing.py.
391
# Note that we do not check for the existence of files here. That is done
392
# in the paste operation.
393
files = fields.getlist('path')
394
clipboard = { "mode" : mode, "base" : req.path, "files" : files }
395
session = req.get_session()
396
session['clipboard'] = clipboard
399
def action_copy(req, fields):
400
"""Marks specified files on the clipboard, stored in the
401
browser session. Sets clipboard for a "copy" action.
405
action_copy_or_cut(req, fields, "copy")
407
def action_cut(req, fields):
408
"""Marks specified files on the clipboard, stored in the
409
browser session. Sets clipboard for a "cut" action.
413
action_copy_or_cut(req, fields, "cut")
415
393
def action_paste(req, fields):
416
"""Performs the copy or move action with the files stored on
417
the clipboard in the browser session. Copies/moves the files
418
to the specified directory. Clears the clipboard.
394
"""Performs the copy or move action with the files specified.
395
Copies/moves the files to the specified directory.
397
Reads fields: 'src', 'dst', 'mode', 'file' (multiple).
398
src: Base path that all the files are relative to (source).
399
dst: Destination path to paste into.
400
mode: 'copy' or 'move'.
401
file: (Multiple) Files relative to base, which will be copied
402
or moved to new locations relative to path.
424
todir = fields.getfirst('path')
406
dst = fields.getfirst('dst')
407
src = fields.getfirst('src')
408
mode = fields.getfirst('mode')
409
files = fields.getlist('file')
410
if dst is None or src is None or mode is None:
426
411
raise ActionError("Required field missing")
427
todir_local = actionpath_to_local(req, todir)
428
if not os.path.isdir(todir_local):
429
raise ActionError("Target is not a directory")
431
session = req.get_session()
433
clipboard = session['clipboard']
434
files = clipboard['files']
435
base = clipboard['base']
436
if clipboard['mode'] == "copy":
441
raise ActionError("Clipboard was empty")
417
raise ActionError("Invalid mode (must be 'copy' or 'move')")
418
dst_local = actionpath_to_local(req, dst)
419
if not os.path.isdir(dst_local):
420
raise ActionError("dst is not a directory")
444
423
for file in files:
445
424
# The source must not be interpreted as relative to req.path
446
425
# Add a slash (relative to top-level)
447
frompath = os.sep + os.path.join(base, file)
428
frompath = os.path.join(src, file)
448
429
# The destination is found by taking just the basename of the file
449
topath = os.path.join(todir, os.path.basename(file))
430
topath = os.path.join(dst, os.path.basename(file))
451
432
movefile(req, frompath, topath, copy)
452
433
except ActionError, message:
459
440
# Add this file to errorfiles; it will be put back on the
460
441
# clipboard for possible future pasting.
461
442
errorfiles.append(file)
462
# If errors occured, augment the clipboard and raise ActionError
463
if len(errorfiles) > 0:
464
clipboard['files'] = errorfiles
465
session['clipboard'] = clipboard
443
if errormsg is not None:
467
444
raise ActionError(errormsg)
469
# Success: Clear the clipboard
470
del session['clipboard']
446
# XXX errorfiles contains a list of files that couldn't be pasted.
447
# we currently do nothing with this.
449
def action_publish(req,fields):
450
"""Marks the folder as published by adding a '.published' file to the
451
directory and ensuring that the parent directory permissions are correct
455
paths = fields.getlist('path')
456
user = studpath.url_to_local(req.path)[0]
457
homedir = "/home/%s" % user
459
paths = map(lambda path: actionpath_to_local(req, path), paths)
461
paths = [studpath.url_to_jailpaths(req.path)[2]]
463
# Set all the dirs in home dir world browsable (o+r,o+x)
464
#FIXME: Should really only do those in the direct path not all of the
465
# folders in a students home directory
466
for root,dirs,files in os.walk(homedir):
467
os.chmod(root, os.stat(root).st_mode|0005)
471
if os.path.isdir(path):
472
pubfile = open(os.path.join(path,'.published'),'w')
473
pubfile.write("This directory is published\n")
476
raise ActionError("Can only publish directories")
478
raise ActionError("Directory could not be published")
480
def action_unpublish(req,fields):
481
"""Marks the folder as unpublished by removing a '.published' file in the
482
directory (if it exits). It does not change the permissions of the parent
487
paths = fields.getlist('path')
489
paths = map(lambda path: actionpath_to_local(req, path), paths)
491
paths = [studpath.url_to_jailpaths(req.path)[2]]
495
if os.path.isdir(path):
496
pubfile = os.path.join(path,'.published')
497
if os.path.isfile(pubfile):
500
raise ActionError("Can only unpublish directories")
502
raise ActionError("Directory could not be unpublished")
473
505
def action_svnadd(req, fields):
474
506
"""Performs a "svn add" to each file specified.