<?php
/**
 * This file implements the UI controller for file upload.
 *
 * This file is part of the evoCore framework - {@link http://evocore.net/}
 * See also {@link http://sourceforge.net/projects/evocms/}.
 *
 * @copyright (c)2003-2010 by Francois PLANQUE - {@link http://fplanque.net/}
 * (dh please re-add)
 *
 * {@internal License choice
 * - If you have received this file as part of a package, please find the license.txt file in
 *   the same folder or the closest folder above for complete license terms.
 * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
 *   then you must choose one of the following licenses before using the file:
 *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
 *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
 * }}
 *
 * {@internal Open Source relicensing agreement:
 * (dh please re-add)
 * }}
 *
 * @package admin
 *
 * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
 * @author fplanque: Francois PLANQUE.
 * (dh please re-add)
 *
 * @version $Id: upload.ctrl.php,v 1.37.2.2 2010/10/27 14:56:18 efy-asimo Exp $
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );

/**
 * Filelist
 * fp>> TODO: When the user is viewing details for a file he should (by default) not be presented with the filelist in addition to the file properties
 * In cases like that, we should try to avoid instanciating a Filelist.
 */
load_class( 'files/model/_filelist.class.php', 'FileList' );

global $current_User, $Plugins;

global $dispatcher;

global $blog;

global $item_ID, $iframe_name;


// Check permission:
$current_User->check_perm( 'files', 'add', true, $blog ? $blog : NULL );


$AdminUI->set_path( 'files', 'upload' );

// Params that may need to be passed through:
param( 'fm_mode', 'string', NULL, true );
param( 'item_ID', 'integer', NULL, true );
param( 'user_ID', 'integer', NULL, true );
param( 'iframe_name', 'string', '', true );

$action = param_action();

// Standard vs Advanced mode
param( 'uploadwithproperties', 'integer', NULL, false );
if( !is_null($uploadwithproperties) )
{
	$UserSettings->set( 'fm_uploadwithproperties', $uploadwithproperties );
	$UserSettings->dbupdate();
}
else
{
	$uploadwithproperties = $UserSettings->get( 'fm_uploadwithproperties' );
}

// INIT params:
if( param( 'root_and_path', 'string', '', false ) /* not memorized (default) */ && strpos( $root_and_path, '::' ) )
{ // root and path together: decode and override (used by "radio-click-dirtree")
	list( $root, $path ) = explode( '::', $root_and_path, 2 );
	// Memorize new root:
	memorize_param( 'root', 'string', NULL );
	memorize_param( 'path', 'string', NULL );
}
else
{
	param( 'root', 'string', NULL, true ); // the root directory from the dropdown box (user_X or blog_X; X is ID - 'user' for current user (default))
	param( 'path', 'string', '', true );  // the path relative to the root dir
	if( param( 'new_root', 'string', '' )
		&& $new_root != $root )
	{ // We have changed root in the select list
		$root = $new_root;
		$path = '';
	}
}

// Get root:
$ads_list_path = false; // false by default, gets set if we have a valid root
/**
 * @var FileRoot
 */
$fm_FileRoot = NULL;

$FileRootCache = & get_FileRootCache();

$available_Roots = $FileRootCache->get_available_FileRoots();

if( ! empty($root) )
{ // We have requested a root folder by string:
	$fm_FileRoot = & $FileRootCache->get_by_ID($root, true);

	if( ! $fm_FileRoot || ! isset( $available_Roots[$fm_FileRoot->ID] ) )
	{ // Root not found or not in list of available ones
		$Messages->add( T_('You don\'t have access to the requested root directory.'), 'error' );
		$fm_FileRoot = false;
	}
}

if( ! $fm_FileRoot )
{ // No root requested (or the requested is invalid), get the first one available:
	if( $available_Roots
	    && ( $tmp_keys = array_keys( $available_Roots ) )
	    && $first_Root = & $available_Roots[ $tmp_keys[0] ] )
	{ // get the first one
		$fm_FileRoot = & $first_Root;
	}
	else
	{
		$Messages->add( T_('You don\'t have access to any root directory.'), 'error' );
	}
}

if( $fm_FileRoot )
{ // We have access to a file root:
	if( empty($fm_FileRoot->ads_path) )
	{	// Not sure it's possible to get this far, but just in case...
		$Messages->add( sprintf( T_('The root directory &laquo;%s&raquo; does not exist.'), $fm_FileRoot->ads_path ), 'error' );
	}
	else
	{ // Root exists
		// Let's get into requested list dir...
		$non_canonical_list_path = $fm_FileRoot->ads_path.$path;

		// Dereference any /../ just to make sure, and CHECK if directory exists:
		$ads_list_path = get_canonical_path( $non_canonical_list_path );

		if( !is_dir( $ads_list_path ) )
		{ // This should never happen, but just in case the diretory does not exist:
			$Messages->add( sprintf( T_('The directory &laquo;%s&raquo; does not exist.'), $path ), 'error' );
			$path = '';		// fp> added
			$ads_list_path = NULL;
		}
		elseif( ! preg_match( '#^'.preg_quote($fm_FileRoot->ads_path, '#').'#', $ads_list_path ) )
		{ // cwd is OUTSIDE OF root!
			$Messages->add( T_( 'You are not allowed to go outside your root directory!' ), 'error' );
			$path = '';		// fp> added
			$ads_list_path = $fm_FileRoot->ads_path;
		}
		elseif( $ads_list_path != $non_canonical_list_path )
		{	// We have reduced the absolute path, we should also reduce the relative $path (used in urls params)
			$path = get_canonical_path( $path );
		}
	}
}


file_controller_build_tabs();


// If there were errors, display them and exit (especially in case there's no valid FileRoot ($fm_FileRoot)):
// TODO: dh> this prevents users from uploading if _any_ blog media directory is not writable.
//           See http://forums.b2evolution.net/viewtopic.php?p=49001#49001
if( $Messages->count('error') )
{
	// Display <html><head>...</head> section! (Note: should be done early if actions do not redirect)
	$AdminUI->disp_html_head();

	// Display title, menu, messages, etc. (Note: messages MUST be displayed AFTER the actions)
	$AdminUI->disp_body_top();
	$AdminUI->disp_payload_begin();
	$AdminUI->disp_payload_end();

	$AdminUI->disp_global_footer();
	exit(0);
}


$Debuglog->add( 'FM root: '.var_export( $fm_FileRoot, true ), 'files' );
$Debuglog->add( 'FM _ads_list_path: '.var_export( $ads_list_path, true ), 'files' );


if( empty($ads_list_path) )
{ // We have no Root / list path, there was an error. Unset any action.
	$action = '';
}




// Check permissions:
// Tblue> Note: Perm 'files' (level 'add') gets checked above with $assert = true.
if( ! $Settings->get('upload_enabled') )
{ // Upload is globally disabled
	$Messages->add( T_('Upload is disabled.'), 'error' );
}


// If there were errors, display them and exit (especially in case there's no valid FileRoot ($fm_FileRoot)):
if( $Messages->count('error') )
{
	$AdminUI->disp_html_head();
	// Display title, menu, messages, etc. (Note: messages MUST be displayed AFTER the actions)
	$AdminUI->disp_body_top();
	// Begin payload block:
	$AdminUI->disp_payload_begin();
	// nothing!
	// End payload block:
	$AdminUI->disp_payload_end();
	$AdminUI->disp_global_footer();
	exit(0);
}


// Quick mode means "just upload and leave mode when successful"
param( 'upload_quickmode', 'integer', 0 );

/**
 * Remember failed files (and the error messages)
 * @var array
 */
$failedFiles = array();

/**
 * Remember renamed files (and the messages)
 * @var array
 */
param( 'renamedFiles', 'array', array(), true );
$renamedMessages = array();

// Process files we want to get from an URL:
param( 'uploadfile_url', 'array', array() );
param( 'uploadfile_source', 'array', array() );
if( $uploadfile_url )
{
	// Check that this action request is not a CSRF hacked request:
	$Session->assert_received_crumb( 'file' );

	foreach($uploadfile_url as $k => $url)
	{
		if( ! isset($uploadfile_source[$k]) || $uploadfile_source[$k] != 'upload' )
		{ // upload by URL has not been selected
			continue;
		}
		if( strlen($url) )
		{
			// Validate URL and parse it for the file name
			if( ! is_absolute_url($url)
				|| ! ($parsed_url = parse_url($url))
				|| empty($parsed_url['scheme']) || empty($parsed_url['host'])
				|| empty($parsed_url['path']) || $parsed_url['path'] == '/' )
			{	// Includes forbidding getting the root of a server
				$failedFiles[$k] = T_('The URL must start with <code>http://</code> or <code>https://</code> and point to a valid file!');
				continue;
			}

			$file_contents = fetch_remote_page($url, $info, NULL, $Settings->get('upload_maxkb'));

			if( $file_contents !== false )
			{
				// Create temporary file and insert contents into it.
				$tmpfile_name = tempnam(sys_get_temp_dir(), 'fmupload');
				if( ! $tmpfile_name )
				{
					$failedFiles[$k] = 'Failed to find temporary directory.'; // no trans: very unlikely
					continue;
				}
				$tmpfile = fopen($tmpfile_name, 'w');
				if( ! fwrite($tmpfile, $file_contents) )
				{
					unlink($tmpfile);
					$failedFiles[$k] = sprintf( 'Could not write to temporary file (%s).', $tmpfile );
					continue;
				}
				fclose($tmpfile);

				// Fake/inject info into PHP's array of uploaded files.
	// fp> TODO! This is a nasty dirty hack. That kind of stuff always breaks somewhere down the line. Needs cleanup.
				// This allows us to treat it (nearly) the same way as regular uploads, apart from
				// is_uploaded_file(), which we skip and move_uploaded_file() (where we use rename()).
				$_FILES['uploadfile']['name'][$k] = rawurldecode(basename($parsed_url['path']));
				$_FILES['uploadfile']['size'][$k] = evo_bytes($file_contents);
				$_FILES['uploadfile']['error'][$k] = 0;
				$_FILES['uploadfile']['tmp_name'][$k] = $tmpfile_name;
				$_FILES['uploadfile']['_evo_fetched_url'][$k] = $url; // skip is_uploaded_file and keep info
				unset($file_contents);
			}
			else
			{
				$failedFiles[$k] = sprintf(
					T_('Could not retrieve file. Error: %s (status %s). Used method: %s.'),
					$info['error'],
					isset($info['status']) ? $info['status'] : '-',
					isset($info['used_method']) ? $info['used_method'] : '-');
			}
		}
	}
}

// Process renaming/replacing of old versions:
if( ! empty($renamedFiles) )
{
	foreach( $renamedFiles as $rKey => $rData )
	{
		$replace_old = param( 'Renamed_'.$rKey, 'string', null );
		if( $replace_old == "Yes" )
		{ // replace the old file with the new one
			$FileCache = & get_FileCache();
			$newFile = & $FileCache->get_by_root_and_path( $fm_FileRoot->type, $fm_FileRoot->in_type_ID, trailing_slash($path).$renamedFiles[$rKey]['newName'], true );
			$oldFile = & $FileCache->get_by_root_and_path( $fm_FileRoot->type, $fm_FileRoot->in_type_ID, trailing_slash($path).$renamedFiles[$rKey]['oldName'], true );
			$new_filename = $newFile->get_name();
			$old_filename = $oldFile->get_name();
			$dir = $newFile->get_dir();
			$oldFile->rm_cache();
			$newFile->rm_cache();
			$error_message = '';

			// rename new uploaded file to temp file name
			$index = 0;
			$temp_filename = 'temp'.$index.'-'.$new_filename;
			while( file_exists( $dir.$temp_filename ) )
			{ // find an unused filename
				$index++;
				$temp_filename = 'temp'.$index.'-'.$new_filename;
			}

			// @rename will overwrite a file with the same name if exists. In this case it shouldn't be a problem.
			if( ! @rename( $newFile->get_full_path(), $dir.$temp_filename ) )
			{ // rename new file to temp file name failed
				$error_message = $Messages->add( sprintf( T_('The new file could not be renamed to %s'), $temp_filename ), 'error' );
			}

			if( empty( $error_message ) && ( ! @rename( $oldFile->get_full_path(), $dir.$new_filename ) ) )
			{ // rename original file to the new file name failed
				$error_message = sprintf( T_( "The original file could not be renamed to %s. The new file is now named %s." ), $new_filename, $temp_filename );
			}

			if( empty( $error_message ) && ( ! @rename( $dir.$temp_filename, $dir.$old_filename ) ) )
			{ // rename new file to the original file name failed
				$error_message = sprintf( T_( "The new file could not be renamed to %s. It is now named %s." ), $old_filename, $temp_filename );
			}

			if( empty( $error_message ) )
			{
				$Messages->add( sprintf( T_('%s has been replaced with the new version!'), $old_filename ), 'success' );
			}
			else
			{
				$Messages->add( $error_message, 'error' );
			}
		}
	}
	forget_param( 'renamedFiles' );
	unset( $renamedFiles );

	if( $upload_quickmode )
	{
		header_redirect( regenerate_url( 'ctrl', 'ctrl=files', '', '&' ) );
	}
}

// Process uploaded files:
if( isset($_FILES) && count( $_FILES ) )
{
	// Check that this action request is not a CSRF hacked request:
	$Session->assert_received_crumb( 'file' );

	// Some files have been uploaded:
	param( 'uploadfile_title', 'array', array() );
	param( 'uploadfile_alt', 'array', array() );
	param( 'uploadfile_desc', 'array', array() );
	param( 'uploadfile_name', 'array', array() );

	foreach( $_FILES['uploadfile']['name'] as $lKey => $lName )
	{
		if( empty( $lName ) )
		{ // No file name
			if( $upload_quickmode
				 || !empty( $uploadfile_title[$lKey] )
				 || !empty( $uploadfile_alt[$lKey] )
				 || !empty( $uploadfile_desc[$lKey] )
				 || !empty( $uploadfile_name[$lKey] ) )
			{ // User specified params but NO file!!!
				// Remember the file as failed when additional info provided.
				$failedFiles[$lKey] = T_( 'Please select a local file to upload.' );
			}
			// Abort upload for this file:
			continue;
		}

		if( $Settings->get( 'upload_maxkb' )
				&& $_FILES['uploadfile']['size'][$lKey] > $Settings->get( 'upload_maxkb' )*1024 )
		{ // bigger than defined by blog
			$failedFiles[$lKey] = sprintf(
					T_('The file is too large: %s but the maximum allowed is %s.'),
					bytesreadable( $_FILES['uploadfile']['size'][$lKey] ),
					bytesreadable($Settings->get( 'upload_maxkb' )*1024) );
			// Abort upload for this file:
			continue;
		}

		if( $_FILES['uploadfile']['error'][$lKey] )
		{ // PHP has detected an error!:
			switch( $_FILES['uploadfile']['error'][$lKey] )
			{
				case UPLOAD_ERR_FORM_SIZE:
					// The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form.

					// This can easily be changed, so we do not use it.. file size gets checked for real just above.
					break;

				case UPLOAD_ERR_INI_SIZE: // bigger than allowed in php.ini
					$failedFiles[$lKey] = T_('The file exceeds the upload_max_filesize directive in php.ini.');
					// Abort upload for this file:
					continue;

				case UPLOAD_ERR_PARTIAL:
					$failedFiles[$lKey] = T_('The file was only partially uploaded.');
					// Abort upload for this file:
					continue;

				case UPLOAD_ERR_NO_FILE:
					// Is probably the same as empty($lName) before.
					$failedFiles[$lKey] = T_('No file was uploaded.');
					// Abort upload for this file:
					continue;

				case 6: // numerical value of UPLOAD_ERR_NO_TMP_DIR
				# (min_php: 4.3.10, 5.0.3) case UPLOAD_ERR_NO_TMP_DIR:
					// Missing a temporary folder.
					$failedFiles[$lKey] = T_('Missing a temporary folder (upload_tmp_dir in php.ini).');
					// Abort upload for this file:
					continue;

				default:
					$failedFiles[$lKey] = T_('Unknown error.').' #'.$_FILES['uploadfile']['error'][$lKey];
					// Abort upload for this file:
					continue;
			}
		}

		if( ! isset($_FILES['uploadfile']['_evo_fetched_url'][$lKey]) // skip check for fetched URLs
			&& ! is_uploaded_file( $_FILES['uploadfile']['tmp_name'][$lKey] ) )
		{ // Ensure that a malicious user hasn't tried to trick the script into working on files upon which it should not be working.
			$failedFiles[$lKey] = T_('The file does not seem to be a valid upload! It may exceed the upload_max_filesize directive in php.ini.');
			// Abort upload for this file:
			continue;
		}

		// Use new name on server if specified:
		$newName = !empty( $uploadfile_name[ $lKey ] ) ? $uploadfile_name[ $lKey ] : $lName;

		if( $error_filename = validate_filename( $newName ) )
		{ // Not a file name or not an allowed extension
			$failedFiles[$lKey] = $error_filename;
			// Abort upload for this file:
			continue;
		}

		$uploadfile_path = $_FILES['uploadfile']['tmp_name'][$lKey];
		$image_info = getimagesize($uploadfile_path);
		if( $image_info )
		{ // This is an image, validate mimetype vs. extension
			$FiletypeCache = get_Cache('FiletypeCache');
			$correct_Filetype = $FiletypeCache->get_by_mimetype($image_info['mime']);
			$correct_extension = array_shift($correct_Filetype->get_extensions());

			$path_info = pathinfo($newName);
			$current_extension = $path_info['extension'];

			if( strtolower($current_extension) != strtolower($correct_extension) )
			{
				$old_name = $newName;
				$newName = $path_info['filename'].'.'.$correct_extension;
				$Messages->add( sprintf(T_('The extension of the file &laquo;%s&raquo; has been corrected. The new filename is &laquo;%s&raquo;.'), $old_name, $newName), 'warning' );
			}
		}

		// Get File object for requested target location:
		$FileCache = & get_FileCache();
		$newFile = & $FileCache->get_by_root_and_path( $fm_FileRoot->type, $fm_FileRoot->in_type_ID, trailing_slash($path).$newName, true );

		$num_ext = 0;
		$oldName = $newName;

		while( $newFile->exists() )
		{ // The file already exists in the target location!
			$num_ext++;
			$ext_pos = strrpos( $newName, '.');
			if( $num_ext == 1 )
			{
				$newName = substr_replace( $newName, '-'.$num_ext.'.', $ext_pos, 1 );
				if( $image_info )
				{
					$oldFile_thumb = $newFile->get_preview_thumb( 'fulltype' );
				}
				else
				{
					$oldFile_thumb = $newFile->get_size_formatted();
				}
			}
			else
			{
				$replace_length = strlen( '-'.($num_ext-1) );
				$newName = substr_replace( $newName, '-'.$num_ext, $ext_pos-$replace_length, $replace_length );
			}
			$newFile = & $FileCache->get_by_root_and_path( $fm_FileRoot->type, $fm_FileRoot->in_type_ID, trailing_slash($path).$newName, true );
		}

		// Trigger plugin event
		if( $Plugins->trigger_event_first_false( 'AfterFileUpload', array(
				  'File' => & $newFile,
				  'name' => & $_FILES['uploadfile']['name'][$lKey],
				  'type' => & $_FILES['uploadfile']['type'][$lKey],
				  'tmp_name' => & $_FILES['uploadfile']['tmp_name'][$lKey],
				  'size' => & $_FILES['uploadfile']['size'][$lKey],
			  ) ) )
		{
			// Plugin returned 'false'. Abort file upload
			continue;
		}

		// Attempt to move the uploaded file to the requested target location:
		if( isset($_FILES['uploadfile']['_evo_fetched_url'][$lKey]) )
		{ // fetched remotely
			if( ! rename( $_FILES['uploadfile']['tmp_name'][$lKey], $newFile->get_full_path() ) )
			{
				$failedFiles[$lKey] = T_('An unknown error occurred when moving the uploaded file on the server.');
				// Abort upload for this file:
				continue;
			}
		}
		elseif( ! move_uploaded_file( $_FILES['uploadfile']['tmp_name'][$lKey], $newFile->get_full_path() ) )
		{
			$failedFiles[$lKey] = T_('An unknown error occurred when moving the uploaded file on the server.');
			// Abort upload for this file:
			continue;
		}

		// change to default chmod settings
		if( $newFile->chmod( NULL ) === false )
		{ // add a note, this is no error!
			$Messages->add( sprintf( T_('Could not change permissions of &laquo;%s&raquo; to default chmod setting.'), $newFile->dget('name') ), 'note' );
		}

		// Refreshes file properties (type, size, perms...)
		$newFile->load_properties();

		if( $num_ext )
		{ // The file name was changed!
			if( $image_info )
			{
				$newFile_thumb = $newFile->get_preview_thumb( 'fulltype' );
			}
			else
			{
				$newFile_thumb = $newFile->get_size_formatted();
			}
			//$newFile_size = bytesreadable ($_FILES['uploadfile']['size'][$lKey]);
			$renamedMessages[$lKey]['message'] = sprintf( T_('"%s was renamed to %s. Would you like to replace %s with the new version instead?'),
													   '&laquo;'.$oldName.'&raquo;', '&laquo;'.$newName.'&raquo;', '&laquo;'.$oldName.'&raquo;' );
			$renamedMessages[$lKey]['oldThumb'] = $oldFile_thumb;
			$renamedMessages[$lKey]['newThumb'] = $newFile_thumb;
			$renamedFiles[$lKey]['oldName'] = $oldName;
			$renamedFiles[$lKey]['newName'] = $newName;
		}

		// Store extra info about the file into File Object:
		if( isset( $uploadfile_title[$lKey] ) )
		{ // If a title text has been passed... (does not happen in quick upload mode)
			$newFile->set( 'title', trim( strip_tags($uploadfile_title[$lKey])) );
		}
		if( isset( $uploadfile_alt[$lKey] ) )
		{ // If an alt text has been passed... (does not happen in quick upload mode)
			$newFile->set( 'alt', trim( strip_tags($uploadfile_alt[$lKey])) );
		}
		if( isset( $uploadfile_desc[$lKey] ) )
		{ // If a desc text has been passed... (does not happen in quick upload mode)
			$newFile->set( 'desc', trim( strip_tags($uploadfile_desc[$lKey])) );
		}
		// TODO: dh> store _evo_fetched_url (source URL) somewhere (e.g. at the end of desc)?
		// fp> no. why?

		$success_msg = sprintf( T_('The file &laquo;%s&raquo; has been successfully uploaded to the server.'), $newFile->dget('name') );

		// Allow to insert/link new upload into currently edited post:
		if( $mode =='upload' && !empty($item_ID) )
		{	// The filemanager has been opened from an Item, offer to insert an img tag into original post.
			// TODO: Add plugin hook to allow generating JS insert code(s)
			$img_tag = format_to_output( $newFile->get_tag(), 'formvalue' );
			if( $newFile->is_image() )
			{
				$link_msg = T_('Link this image to your post');
				$link_note = T_('recommended - allows automatic resizing');
			}
			else
			{
				$link_msg = T_('Link this file to your post');
				$link_note = T_('The file will be appended for download at the end of the post');
			}
			$success_msg .= '<ul>'
					.'<li>'.action_icon( T_('Link this file!'), 'link',
								regenerate_url( 'fm_selected,ctrl', 'ctrl=files&amp;action=link_inpost&amp;fm_selected[]='.rawurlencode($newFile->get_rdfp_rel_path()).'&amp;'.url_crumb('file') ),
								' '.$link_msg, 5, 5, array( 'target' => $iframe_name ) )
					.' ('.$link_note.')</li>'

					.'<li>'.T_('or').' <a href="#" onclick="if( window.focus && window.opener ){'
					.'window.opener.focus(); textarea_wrap_selection( window.opener.document.getElementById(\'itemform_post_content\'), \''
					.format_to_output( $newFile->get_tag(), 'formvalue' ).'\', \'\', 1, window.opener.document ); } return false;">'
					.T_('Insert the following code snippet into your post').'</a> : <input type="text" value="'.$img_tag.'" size="60" /></li>'
					// fp> TODO: it would be supacool to have an ajaxy "tumbnail size selector" here that generates a thumnail of requested size on server and then changes the code in the input above
				.'</ul>';
		}

		$Messages->add( $success_msg, 'success' );

		// Store File object into DB:
		$newFile->dbsave();
	}

	if( $upload_quickmode && !empty($failedFiles) )
	{	// Transmit file error to next page!
		$Messages->add( $failedFiles[0], 'error' );
		unset($failedFiles);
	}

	if( empty($failedFiles) && empty($renamedFiles) )
	{ // quick mode or no failed files, Go back to Browsing
		// header_redirect( $dispatcher.'?ctrl=files&root='.$fm_FileRoot->ID.'&path='.rawurlencode($path) );
		header_redirect( regenerate_url( 'ctrl', 'ctrl=files', '', '&' ) );
	}
}


file_controller_build_tabs();

// fp> TODO: this here is a bit sketchy since we have Blog & fileroot not necessarilly in sync. Needs investigation / propositions.
// Note: having both allows to post from any media dir into any blog.
$AdminUI->breadcrumbpath_init();
$AdminUI->breadcrumbpath_add( T_('Files'), '?ctrl=files&amp;blog=$blog$' );
if( !isset($Blog) || $fm_FileRoot->type != 'collection' || $fm_FileRoot->in_type_ID != $Blog->ID )
{	// Display only if we're not browsing our home blog
	$AdminUI->breadcrumbpath_add( $fm_FileRoot->name, '?ctrl=files&amp;blog=$blog$&amp;root='.$fm_FileRoot->ID,
			(isset($Blog) && $fm_FileRoot->type == 'collection') ? sprintf( T_('You are ready to post files from %s into %s...'),
			$fm_FileRoot->name, $Blog->get('shortname') ) : '' );
}
$AdminUI->breadcrumbpath_add( T_('Upload'), '?ctrl=upload&amp;blog=$blog$&amp;root='.$fm_FileRoot->ID );

// Display <html><head>...</head> section! (Note: should be done early if actions do not redirect)
$AdminUI->disp_html_head();

// Display title, menu, messages, etc. (Note: messages MUST be displayed AFTER the actions)
$AdminUI->disp_body_top();


/*
 * Display payload:
 */
$AdminUI->disp_view( 'files/views/_file_upload.view.php' );


// Display body bottom, debug info and close </html>:
$AdminUI->disp_global_footer();


/*
 * $Log: upload.ctrl.php,v $
 */
?>
