171
171
raise ActionError("Invalid path")
174
def movefile(req, frompath, topath, copy=False):
175
"""Performs a file move, resolving filenames, checking for any errors,
176
and throwing ActionErrors if necessary. Can also be used to do a copy
179
frompath and topath are straight paths from the client. Will be checked.
181
# TODO: Do an SVN mv if the file is versioned.
182
# TODO: Disallow tampering with student's home directory
183
if frompath is None or topath is None:
184
raise ActionError("Required field missing")
185
frompath = actionpath_to_local(req, frompath)
186
topath = actionpath_to_local(req, topath)
187
if not os.path.exists(frompath):
188
raise ActionError("The source file does not exist")
189
if os.path.exists(topath):
190
if frompath == topath:
191
raise ActionError("Source and destination are the same")
192
raise ActionError("Another file already exists with that name")
196
if os.path.isdir(frompath):
197
shutil.copytree(frompath, topath)
199
shutil.copy2(frompath, topath)
201
shutil.move(frompath, topath)
203
raise ActionError("Could not move the file specified")
205
raise ActionError("Could not move the file specified")
176
209
def action_remove(req, fields):
210
243
frompath = fields.getfirst('from')
211
244
topath = fields.getfirst('to')
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")
222
shutil.move(frompath, topath)
224
raise ActionError("Could not move the file specified")
226
raise ActionError("Could not move the file specified")
245
movefile(req, frompath, topath)
228
247
def action_putfile(req, fields):
229
248
"""Writes data to a file, overwriting it if it exists and creating it if
246
265
raise ActionError("Could not write to target file")
267
def action_copy_or_cut(req, fields, mode):
268
"""Marks specified files on the clipboard, stored in the
269
browser session. Sets clipboard for either a cut or copy operation
274
# The clipboard object created conforms to the JSON clipboard
275
# specification given at the top of listing.py.
276
# Note that we do not check for the existence of files here. That is done
277
# in the paste operation.
278
files = fields.getlist('path')
279
files = map(lambda field: field.value, files)
280
clipboard = { "mode" : mode, "base" : req.path, "files" : files }
281
session = req.get_session()
282
session['clipboard'] = clipboard
285
def action_copy(req, fields):
286
"""Marks specified files on the clipboard, stored in the
287
browser session. Sets clipboard for a "copy" action.
291
action_copy_or_cut(req, fields, "copy")
293
def action_cut(req, fields):
294
"""Marks specified files on the clipboard, stored in the
295
browser session. Sets clipboard for a "cut" action.
299
action_copy_or_cut(req, fields, "cut")
301
def action_paste(req, fields):
302
"""Performs the copy or move action with the files stored on
303
the clipboard in the browser session. Copies/moves the files
304
to the specified directory. Clears the clipboard.
310
todir = fields.getfirst('path')
312
raise ActionError("Required field missing")
313
todir_local = actionpath_to_local(req, todir)
314
if not os.path.isdir(todir_local):
315
raise ActionError("Target is not a directory")
317
session = req.get_session()
319
clipboard = session['clipboard']
320
files = clipboard['files']
321
base = clipboard['base']
322
if clipboard['mode'] == "copy":
327
raise ActionError("Clipboard was empty")
331
# The source must not be interpreted as relative to req.path
332
# Add a slash (relative to top-level)
333
frompath = os.sep + os.path.join(base, file)
334
# The destination is found by taking just the basename of the file
335
topath = os.path.join(todir, os.path.basename(file))
337
movefile(req, frompath, topath, copy)
338
except ActionError, message:
339
# Store the error for later; we want to copy as many as possible
343
# Multiple errors; generic message
344
errormsg = "One or more files could not be pasted"
345
# Add this file to errorfiles; it will be put back on the
346
# clipboard for possible future pasting.
347
errorfiles.append(file)
348
# If errors occured, augment the clipboard and raise ActionError
349
if len(errorfiles) > 0:
350
clipboard['files'] = errorfiles
351
session['clipboard'] = clipboard
353
raise ActionError(errormsg)
355
# Success: Clear the clipboard
356
del session['clipboard']
248
359
# Table of all action functions #
249
360
# Each function has the interface f(req, fields).