72
72
# * putfile ignores the upload filename, the entire filename is specified on
73
73
# path. putfiles calls files after the name on the user's machine.
75
# Clipboard-based actions. Cut/copy/paste work in the same way as modern
76
# file browsers, by keeping a server-side clipboard of files that have been
77
# cut and copied. The clipboard is stored in the session data, so it persists
78
# across navigation, tabs and browser windows, but not across browser
81
# action=copy: Write file(s) to the session-based clipboard. Overrides any
82
# existing clipboard data. Does not actually copy the file.
83
# The files are physically copied when the clipboard is pasted.
84
# path: The path to the file or directory to copy. Can be specified
87
# action=cut: Write file(s) to the session-based clipboard. Overrides any
88
# existing clipboard data. Does not actually move the file.
89
# The files are physically moved when the clipboard is pasted.
90
# path: The path to the file or directory to cut. Can be specified
93
# action=paste: Copy or move the files stored in the clipboard. Clears the
94
# clipboard. The files are copied or moved to a specified dir.
95
# 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.
98
83
# Subversion actions.
99
84
# action=svnadd: Add an existing file(s) to version control.
402
387
raise ActionError(
403
388
"Could not write to one or more of the target files")
405
def action_copy_or_cut(req, fields, mode):
406
"""Marks specified files on the clipboard, stored in the
407
browser session. Sets clipboard for either a cut or copy operation
412
# The clipboard object created conforms to the JSON clipboard
413
# specification given at the top of listing.py.
414
# Note that we do not check for the existence of files here. That is done
415
# in the paste operation.
416
files = fields.getlist('path')
417
clipboard = { "mode" : mode, "base" : req.path, "files" : files }
418
session = req.get_session()
419
session['clipboard'] = clipboard
422
def action_copy(req, fields):
423
"""Marks specified files on the clipboard, stored in the
424
browser session. Sets clipboard for a "copy" action.
428
action_copy_or_cut(req, fields, "copy")
430
def action_cut(req, fields):
431
"""Marks specified files on the clipboard, stored in the
432
browser session. Sets clipboard for a "cut" action.
436
action_copy_or_cut(req, fields, "cut")
438
390
def action_paste(req, fields):
439
"""Performs the copy or move action with the files stored on
440
the clipboard in the browser session. Copies/moves the files
441
to the specified directory. Clears the clipboard.
391
"""Performs the copy or move action with the files specified.
392
Copies/moves the files to the specified directory.
394
Reads fields: 'src', 'dst', 'mode', 'file' (multiple).
395
src: Base path that all the files are relative to (source).
396
dst: Destination path to paste into.
397
mode: 'copy' or 'move'.
398
file: (Multiple) Files relative to base, which will be copied
399
or moved to new locations relative to path.
447
todir = fields.getfirst('path')
403
dst = fields.getfirst('dst')
404
src = fields.getfirst('src')
405
mode = fields.getfirst('mode')
406
files = fields.getlist('file')
407
if dst is None or src is None or mode is None:
449
408
raise ActionError("Required field missing")
450
todir_local = actionpath_to_local(req, todir)
451
if not os.path.isdir(todir_local):
452
raise ActionError("Target is not a directory")
454
session = req.get_session()
456
clipboard = session['clipboard']
457
files = clipboard['files']
458
base = clipboard['base']
459
if clipboard['mode'] == "copy":
464
raise ActionError("Clipboard was empty")
414
raise ActionError("Invalid mode (must be 'copy' or 'move')")
415
dst_local = actionpath_to_local(req, dst)
416
if not os.path.isdir(dst_local):
417
raise ActionError("dst is not a directory")
467
420
for file in files:
468
421
# The source must not be interpreted as relative to req.path
469
422
# Add a slash (relative to top-level)
470
frompath = os.sep + os.path.join(base, file)
425
frompath = os.path.join(src, file)
471
426
# The destination is found by taking just the basename of the file
472
topath = os.path.join(todir, os.path.basename(file))
427
topath = os.path.join(dst, os.path.basename(file))
474
429
movefile(req, frompath, topath, copy)
475
430
except ActionError, message:
482
437
# Add this file to errorfiles; it will be put back on the
483
438
# clipboard for possible future pasting.
484
439
errorfiles.append(file)
485
# If errors occured, augment the clipboard and raise ActionError
486
if len(errorfiles) > 0:
487
clipboard['files'] = errorfiles
488
session['clipboard'] = clipboard
440
if errormsg is not None:
490
441
raise ActionError(errormsg)
492
# Success: Clear the clipboard
493
del session['clipboard']
443
# XXX errorfiles contains a list of files that couldn't be pasted.
444
# we currently do nothing with this.
496
446
def action_svnadd(req, fields):
497
447
"""Performs a "svn add" to each file specified.