3
* This file implements the UI controller for file upload.
5
* This file is part of Quam Plures - {@link http://quamplures.net/}
6
* See also {@link https://launchpad.net/quam-plures}.
8
* @copyright (c) 2009 - 2011 by the Quam Plures developers - {@link http://quamplures.net/}
9
* @copyright (c)2003-2009 by Francois PLANQUE - {@link http://fplanque.net/}
12
* {@internal License choice
13
* - If you have received this file as part of a package, please find the license.txt file in
14
* the same folder or the closest folder above for complete license terms.
15
* - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
16
* then you must choose one of the following licenses before using the file:
17
* - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
18
* - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
21
* {@internal Open Source relicensing agreement:
25
* {@internal Below is a list of authors who have contributed to design/coding of this file: }}
26
* @author fplanque: Francois PLANQUE.
3
* $ctrl_mappings: 'upload' || Files -> Upload
5
* @author {@link http://wonderwinds.com/ Ed Bennett}
6
* @author {@link http://daniel.hahler.de/ Daniel HAHLER}
7
* @author {@link http://fplanque.net/ Francois PLANQUE}
8
* @copyright (c) 2009 by {@link http://quamplures.net/ the Quam Plures project}
9
* @license http://www.gnu.org/licenses/gpl.txt GNU General Public License v3
31
if( !defined('QP_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
35
* 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
36
* In cases like that, we should try to avoid instanciating a Filelist.
38
load_class('files/model/_filelist.class.php');
12
if(!defined('QP_MAIN_INIT')) die('fail');
40
14
global $current_User;
41
15
global $dispatcher;
43
// Check global access permissions:
17
// Check global access permissions
44
18
if( ! $Settings->get( 'fm_enabled' ) )
46
20
// File Manager is disabled, so let's be kind and give them something to work with
47
$AdminUI->set_coll_list_params( 'blog_ismember', 'view', array(), T_('Global'), '?blog=0' );
21
$AdminUI->set_blog_list_params( 'blog_ismember', 'view', array(), T_('Global'), '?blog=0' );
48
22
$AdminUI->set_path( 'dashboard' );
49
23
$AdminUI->disp_html_head();
50
24
$AdminUI->disp_body_top();
78
52
$uploadwithproperties = $UserSettings->get( 'fm_uploadwithproperties' );
82
56
if( param( 'root_and_path', 'string', '', false ) /* not memorized (default) */ && strpos( $root_and_path, '::' ) )
83
{ // root and path together: decode and override (used by "radio-click-dirtree")
58
// root and path together: decode and override (used by "radio-click-dirtree")
84
59
list( $root, $path ) = explode( '::', $root_and_path, 2 );
86
61
memorize_param( 'root', 'string', NULL );
87
62
memorize_param( 'path', 'string', NULL );
91
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))
92
param( 'path', 'string', '', true ); // the path relative to the root dir
66
// the root directory from the dropdown box (user_X or blog_X; X is ID - 'user' for current user (default))
67
param( 'root', 'string', NULL, true );
68
// the path relative to the root dir
69
param( 'path', 'string', '', true );
93
70
if( param( 'new_root', 'string', '' )
94
71
&& $new_root != $root )
95
{ // We have changed root in the select list
73
// We have changed root in the select list
101
79
// EdB: I've no idea why I had this in my v247 package. I had a comment "fix
102
80
// the fucked up path ... maybe" but I've no idea what fucked up path needed
103
81
// fixing. If a path is missing a "/" one day this is probably it ;)
109
88
$ads_list_path = false; // false by default, gets set if we have a valid root
113
89
$fm_FileRoot = NULL;
115
91
$FileRootCache = & get_Cache( 'FileRootCache' );
117
93
$available_Roots = $FileRootCache->get_available_FileRoots();
120
{ // We have requested a root folder by string:
121
$fm_FileRoot = & $FileRootCache->get_by_ID($root, true);
95
if( ! empty( $root ) )
97
// We have requested a root folder by string
98
$fm_FileRoot = & $FileRootCache->get_by_ID( $root, true );
123
100
if( ! $fm_FileRoot || ! isset( $available_Roots[$fm_FileRoot->ID] ) )
124
{ // Root not found or not in list of available ones
102
// Root not found or not in list of available ones
125
103
$Messages->add( T_('You don\'t have access to the requested root directory.'), 'error' );
126
104
$fm_FileRoot = false;
130
108
if( ! $fm_FileRoot )
131
{ // No root requested (or the requested is invalid), get the first one available:
110
// No root requested (or the requested is invalid), get the first one available
132
111
if( $available_Roots
133
&& ( $tmp_keys = array_keys( $available_Roots ) )
134
&& $first_Root = & $available_Roots[ $tmp_keys[0] ] )
135
{ // get the first one
112
&& ( $tmp_keys = array_keys( $available_Roots ) )
113
&& $first_Root = & $available_Roots[$tmp_keys[0]] )
136
116
$fm_FileRoot = & $first_Root;
144
124
if( $fm_FileRoot )
145
{ // We have access to a file root:
146
if( empty($fm_FileRoot->ads_path) )
147
{ // Not sure it's possible to get this far, but just in case...
126
// We have access to a file root
127
if( empty( $fm_FileRoot->ads_path ) )
129
// Not sure it's possible to get this far, but just in case...
148
130
$Messages->add( sprintf( T_('The root directory «%s» does not exist.'), $fm_FileRoot->ads_path ), 'error' );
152
135
// Let's get into requested list dir...
153
136
$non_canonical_list_path = $fm_FileRoot->ads_path.$path;
155
// Dereference any /../ just to make sure, and CHECK if directory exists:
138
// Dereference any /../ just to make sure, and CHECK if directory exists
156
139
$ads_list_path = get_canonical_path( $non_canonical_list_path );
158
141
if( !is_dir( $ads_list_path ) )
159
{ // This should never happen, but just in case the diretory does not exist:
143
// This should never happen, but just in case the diretory does not exist
160
144
$Messages->add( sprintf( T_('The directory «%s» does not exist.'), $path ), 'error' );
161
$path = ''; // fp> added
145
$path = ''; // fp> added
162
146
$ads_list_path = NULL;
164
elseif( ! preg_match( '#^'.preg_quote($fm_FileRoot->ads_path, '#').'#', $ads_list_path ) )
165
{ // cwd is OUTSIDE OF root!
148
elseif( ! preg_match( '#^'.preg_quote( $fm_FileRoot->ads_path, '#' ).'#', $ads_list_path ) )
150
// cwd is OUTSIDE OF root!
166
151
$Messages->add( T_( 'You are not allowed to go outside your root directory!' ), 'error' );
167
$path = ''; // fp> added
152
$path = ''; // fp> added
168
153
$ads_list_path = $fm_FileRoot->ads_path;
170
155
elseif( $ads_list_path != $non_canonical_list_path )
171
{ // We have reduced the absolute path, we should also reduce the relative $path (used in urls params)
157
// We have reduced the absolute path, we should also reduce the relative $path (used in urls params)
172
158
$path = get_canonical_path( $path );
177
// If there were errors, display them and exit (especially in case there's no valid FileRoot ($fm_FileRoot)):
178
// TODO: dh> this prevents users from uploading if _any_ blog media directory is not writable.
179
// See http://forums.b2evolution.net/viewtopic.php?p=49001#49001
163
// If there were errors, display them and exit (especially in case there's no valid FileRoot ( $fm_FileRoot ))
180
164
if( $Messages->count('error') )
182
// Display <html><head>...</head> section! (Note: should be done early if actions do not redirect)
166
// Display <html><head>...</head> section (should be done early if actions do not redirect)
183
167
$AdminUI->disp_html_head();
185
// Display title, menu, messages, etc. (Note: messages MUST be displayed AFTER the actions)
168
// Display title, menu, messages, etc... (messages MUST be displayed AFTER the actions)
186
169
$AdminUI->disp_body_top();
170
// Begin payload block
187
171
$AdminUI->disp_payload_begin();
188
172
$AdminUI->disp_payload_end();
194
$Debuglog->add( 'FM root: '.var_export( $fm_FileRoot, true ), 'files' );
195
$Debuglog->add( 'FM _ads_list_path: '.var_export( $ads_list_path, true ), 'files' );
197
if( empty($ads_list_path) )
198
{ // We have no Root / list path, there was an error. Unset any action.
178
if( empty( $ads_list_path ) )
180
// We have no Root / list path, there was an error. Unset any action
202
// Check permissions:
203
if( ! $Settings->get('upload_enabled') )
204
{ // Upload is globally disabled
185
if( ! $Settings->get( 'upload_enabled' ) )
187
// Upload is globally disabled
205
188
$Messages->add( T_('Upload is disabled.'), 'error' );
208
191
if( ! $current_User->check_perm( 'files', 'add' ) )
209
{ // We do not have permission to add files
193
// We do not have permission to add files
210
194
$Messages->add( T_('You have no permission to add/upload files.'), 'error' );
213
// If there were errors, display them and exit (especially in case there's no valid FileRoot ($fm_FileRoot)):
197
// If there were errors, display them and exit (especially in case there's no valid FileRoot ( $fm_FileRoot )):
214
198
if( $Messages->count('error') )
216
200
$AdminUI->disp_html_head();
223
207
// Quick mode means "just upload and leave mode when successful"
224
208
param( 'upload_quickmode', 'integer', 0 );
227
* Remember failed files (and the error messages)
210
// Remember failed files (and the error messages)
230
211
$failedFiles = array();
232
// Process uploaded files:
233
if( isset($_FILES) && count( $_FILES ) )
234
{ // Some files have been uploaded:
213
// Process uploaded files
214
if( isset( $_FILES ) && count( $_FILES ) )
216
// Some files have been uploaded
235
217
param( 'uploadfile_name', 'array', array() );
236
218
param( 'uploadfile_title', 'array', array() );
237
219
param( 'uploadfile_cascade', 'array', array() );
241
223
foreach( $_FILES['uploadfile']['name'] as $lKey => $lName )
243
225
if( empty( $lName ) )
245
228
if( $upload_quickmode
246
|| !empty( $uploadfile_title[$lKey] )
247
|| !empty( $uploadfile_alt[$lKey] )
248
|| !empty( $uploadfile_desc[$lKey] )
249
|| !empty( $uploadfile_name[$lKey] ) )
250
{ // User specified params but NO file!!!
229
|| ! empty( $uploadfile_title[$lKey] )
230
|| ! empty( $uploadfile_alt[$lKey] )
231
|| ! empty( $uploadfile_desc[$lKey] )
232
|| ! empty( $uploadfile_name[$lKey] ) )
234
// User specified params but NO file!!!
251
235
// Remember the file as failed when additional info provided.
252
236
$failedFiles[$lKey] = T_( 'Please select a local file to upload.' );
254
// Abort upload for this file:
238
// Abort upload for this file
258
242
if( $Settings->get( 'upload_maxkb' )
259
243
&& $_FILES['uploadfile']['size'][$lKey] > $Settings->get( 'upload_maxkb' )*1024 )
260
{ // bigger than defined by blog
245
// bigger than defined by blog
261
246
$failedFiles[$lKey] = sprintf(
262
T_('The file is too large: %s but the maximum allowed is %s.'),
263
bytesreadable( $_FILES['uploadfile']['size'][$lKey] ),
264
bytesreadable($Settings->get( 'upload_maxkb' )*1024) );
265
// Abort upload for this file:
247
T_('The file is too large: %s but the maximum allowed is %s.'),
248
bytesreadable( $_FILES['uploadfile']['size'][$lKey] ),
249
bytesreadable( $Settings->get( 'upload_maxkb' )*1024 ) );
250
// Abort upload for this file
269
254
if( $_FILES['uploadfile']['error'][$lKey] )
270
{ // PHP has detected an error!:
256
// PHP has detected an error!
271
257
switch( $_FILES['uploadfile']['error'][$lKey] )
273
259
case UPLOAD_ERR_FORM_SIZE:
274
// The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form.
276
// This can easily be changed, so we do not use it.. file size gets checked for real just above.
260
// The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form
261
// This can easily be changed, so we do not use it.. file size gets checked for real just above
279
264
case UPLOAD_ERR_INI_SIZE: // bigger than allowed in php.ini
280
$failedFiles[$lKey] = T_('The file exceeds the upload_max_filesize directive in php.ini.');
281
// Abort upload for this file:
265
$failedFiles[$lKey] = T_('The file exceeds the upload_max_filesize directive in php.ini.');
266
// Abort upload for this file
284
269
case UPLOAD_ERR_PARTIAL:
285
$failedFiles[$lKey] = T_('The file was only partially uploaded.');
286
// Abort upload for this file:
270
$failedFiles[$lKey] = T_('The file was only partially uploaded.');
271
// Abort upload for this file
289
274
case UPLOAD_ERR_NO_FILE:
290
// Is probably the same as empty($lName) before.
291
$failedFiles[$lKey] = T_('No file was uploaded.');
292
// Abort upload for this file:
275
// Is probably the same as empty( $lName ) before
276
$failedFiles[$lKey] = T_('No file was uploaded.');
277
// Abort upload for this file
295
280
case 6: // numerical value of UPLOAD_ERR_NO_TMP_DIR
296
281
# (min_php: 4.3.10, 5.0.3) case UPLOAD_ERR_NO_TMP_DIR:
297
// Missing a temporary folder.
298
$failedFiles[$lKey] = T_('Missing a temporary folder (upload_tmp_dir in php.ini).');
299
// Abort upload for this file:
282
// Missing a temporary folder.
283
$failedFiles[$lKey] = T_('Missing a temporary folder (upload_tmp_dir in php.ini).');
284
// Abort upload for this file
303
$failedFiles[$lKey] = T_('Unknown error.').' #'.$_FILES['uploadfile']['error'][$lKey];
304
// Abort upload for this file:
288
$failedFiles[$lKey] = T_('Unknown error.').' #'.$_FILES['uploadfile']['error'][$lKey];
289
// Abort upload for this file:
309
294
if( !is_uploaded_file( $_FILES['uploadfile']['tmp_name'][$lKey] ) )
310
{ // Ensure that a malicious user hasn't tried to trick the script into working on files upon which it should not be working.
296
// Ensure that a malicious user hasn't tried to trick the script into working
297
// on files upon which it should not be working
311
298
$failedFiles[$lKey] = T_('The file does not seem to be a valid upload! It may exceed the upload_max_filesize directive in php.ini.');
312
// Abort upload for this file:
299
// Abort upload for this file
316
// If new name on server is specified check the extension and use the new name:
317
if( ! empty( $uploadfile_name[ $lKey ] ) )
303
// If new name on server is specified check the extension and use the new name
304
if( ! empty( $uploadfile_name[$lKey] ) )
319
306
$original_pathinfo = pathinfo( $lName );
320
$newname_pathinfo = pathinfo( $uploadfile_name[ $lKey ] );
307
$newname_pathinfo = pathinfo( $uploadfile_name[$lKey] );
321
308
if( ! isset( $newname_pathinfo['extension'] ) && isset( $original_pathinfo['extension'] ) )
322
{ // Try to append the old extension to the new file name:
323
$newName = $uploadfile_name[ $lKey ].'.'.$original_pathinfo['extension'];
310
// Try to append the old extension to the new file name
311
$newName = $uploadfile_name[$lKey].'.'.$original_pathinfo['extension'];
326
{ // Well, this will most likely cause an error below (when
327
// validate_filename() is called).
328
$newName = $uploadfile_name[ $lKey ];
315
// this will most likely cause an error below (when validate_filename() is called)
316
$newName = $uploadfile_name[$lKey];
336
324
if( $error_filename = validate_filename( $newName ) )
337
{ // Not a file name or not an allowed extension
326
// Not a file name or not an allowed extension
338
327
$failedFiles[$lKey] = $error_filename;
339
// Abort upload for this file:
328
// Abort upload for this file
343
// Get File object for requested target location:
332
// Get File object for requested target location
344
333
$FileCache = & get_Cache( 'FileCache' );
345
$newFile = & $FileCache->get_by_root_and_path( $fm_FileRoot->type, $fm_FileRoot->in_type_ID, trailing_slash($path).$newName, true );
334
$newFile = & $FileCache->get_by_root_and_path( $fm_FileRoot->type, $fm_FileRoot->in_type_ID, trailing_slash( $path ).$newName, true );
347
336
if( $newFile->exists() )
348
{ // The file already exists in the target location!
349
// TODO: Rename/Overwriting (save as filename_<numeric_extension> and provide interface to confirm, rename or overwrite)
350
$failedFiles[$lKey] = sprintf( T_('The file «%s» already exists.'), $newFile->dget('name') );
351
// Abort upload for this file:
338
// The file already exists in the target location!
339
// @todo (0000) Rename/Overwriting (save as filename_<numeric_extension> and
340
// provide interface to confirm, rename or overwrite)
341
$failedFiles[$lKey] = sprintf( T_('The file «%s» already exists.'), $newFile->dget( 'name' ) );
342
// Abort upload for this file
355
346
// Trigger plugin event
356
347
if( $Plugins->trigger_event_first_false( 'AfterFileUpload', array(
357
'File' => & $newFile,
358
'name' => & $_FILES['uploadfile']['name'][$lKey],
359
'type' => & $_FILES['uploadfile']['type'][$lKey],
360
'tmp_name' => & $_FILES['uploadfile']['tmp_name'][$lKey],
361
'size' => & $_FILES['uploadfile']['size'][$lKey],
348
'File' => & $newFile,
349
'name' => & $_FILES['uploadfile']['name'][$lKey],
350
'type' => & $_FILES['uploadfile']['type'][$lKey],
351
'tmp_name' => & $_FILES['uploadfile']['tmp_name'][$lKey],
352
'size' => & $_FILES['uploadfile']['size'][$lKey],
364
355
// Plugin returned 'false'. Abort file upload
368
// Attempt to move the uploaded file to the requested target location:
359
// Attempt to move the uploaded file to the requested target location
369
360
if( ! @move_uploaded_file( $_FILES['uploadfile']['tmp_name'][$lKey], $newFile->get_full_path() ) )
371
362
$failedFiles[$lKey] = T_('An unknown error occurred when moving the uploaded file on the server.');
372
// Abort upload for this file:
363
// Abort upload for this file
376
367
// change to default chmod settings
377
368
if( $newFile->chmod( NULL ) === false )
378
{ // add a note, this is no error!
379
$Messages->add( sprintf( T_('Could not change permissions of «%s» to default chmod setting.'), $newFile->dget('name') ), 'note' );
370
// add a note, this is no error!
371
$Messages->add( sprintf( T_('Could not change permissions of «%s» to default chmod setting.'), $newFile->dget( 'name' ) ), 'note' );
382
374
// Refreshes file properties (type, size, perms...)
383
375
$newFile->load_properties();
386
* cascade info: file name without extension -> title -> alt -> desc
387
* use the preceding value if no value is provided during upload
389
* @todo Tblue> Replace underscores (_) and dashes (-) with spaces?
377
// cascade info: file name without extension -> title -> alt -> desc
378
// use the preceding value if no value is provided during upload
391
379
$file_cascade = isset( $uploadfile_cascade[$lKey] );
392
380
// get the file name without the extension
393
381
$file_name_no_ext = preg_replace( '/\.[^.]*$/', '', $newFile->get_name() );
398
386
$newFile->set( 'title', trim( strip_tags( $uploadfile_title[$lKey] ) ) );
400
else if( $file_cascade )
401
{ // store the file name without extension as title
388
elseif( $file_cascade )
390
// store the file name without extension as title
402
391
$newFile->set( 'title', trim( strip_tags( $file_name_no_ext ) ) );
405
394
// Set the new file's alt
406
395
if( isset( $uploadfile_alt[$lKey] ) && $uploadfile_alt[$lKey] !== '' )
407
{ // store the info provided during upload
397
// store the info provided during upload
408
398
$newFile->set( 'alt', trim( strip_tags( $uploadfile_alt[$lKey] ) ) );
410
else if( $file_cascade )
411
{ // store the new file's title as alt
400
elseif( $file_cascade )
402
// store the new file's title as alt
412
403
$newFile->set( 'alt', $newFile->title );
415
406
// Set the new file's desc
416
407
if( isset( $uploadfile_desc[$lKey] ) && $uploadfile_desc[$lKey] !== '' )
417
{ // store the info provided during upload
409
// store the info provided during upload
418
410
$newFile->set( 'desc', trim( strip_tags( $uploadfile_desc[$lKey] ) ) );
420
else if( $file_cascade )
421
{ // store the new file's alt as desc
412
elseif( $file_cascade )
414
// store the new file's alt as desc
422
415
$newFile->set( 'desc', $newFile->alt );
425
$success_msg = sprintf( T_('The file «%s» has been successfully uploaded to the server.'), $newFile->dget('name') );
418
$success_msg = sprintf( T_('The file «%s» has been successfully uploaded to the server.'), $newFile->dget( 'name' ) );
427
420
// get three links for img/link code or one link for files into any post
428
421
if( $mode == 'upload' )
430
// @todo (legacy): Add plugin hook to allow generating JS insert code(s)
423
// @todo (0000) Add plugin hook to allow generating JS insert code(s)
431
424
if( $newFile->is_image() )
433
426
$success_msg .= '<br />';
470
464
$link_note = T_('The file will be appended for download at the end of the post');
472
466
$success_msg .= '<ul>'
473
.'<li>'.action_icon( T_('Link this file!'), 'link',
474
regenerate_url( 'fm_selected,ctrl', 'ctrl=files&action=link_inpost&fm_selected[]='.rawurlencode($newFile->get_rdfp_rel_path()) ),
475
' '.$link_msg, 5, 5, array( 'target' => $iframe_name ) )
476
.' ('.$link_note.')</li>'
467
.'<li>'.action_icon( T_('Link this file!'), 'link',
468
regenerate_url( 'fm_selected,ctrl', 'ctrl=files&action=link_inpost&fm_selected[]='.rawurlencode( $newFile->get_rdfp_rel_path() ) ),
469
' '.$link_msg, 5, 5, array( 'target' => $iframe_name ) )
470
.' ('.$link_note.')</li>'
478
.'<li>'.T_('or').' <a href="#" onclick="if( window.focus && window.opener ){'
479
.'window.opener.focus(); textarea_wrap_selection( window.opener.document.getElementById(\'itemform_post_content\'), \''
480
.format_to_output( $newFile->get_tag(), 'formvalue' ).'\', \'\', 1, window.opener.document ); } return false;">'
481
.T_('Insert the following code snippet into your post').'</a>: <input type="text" value="'.$img_tag.'" size="60" /></li>'
482
// 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
472
.'<li>'.T_('or').' <a href="#" onclick="if( window.focus && window.opener ){'
473
.'window.opener.focus(); textarea_wrap_selection( window.opener.document.getElementById(\'itemform_post_content\'), \''
474
.format_to_output( $newFile->get_tag(), 'formvalue' ).'\', \'\', 1, window.opener.document ); } return false;">'
475
.T_('Insert the following code snippet into your post').'</a>: <input type="text" value="'.$img_tag.'" size="60" /></li>'
476
// 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
486
480
$Messages->add( $success_msg, 'success' );
488
// Store File object into DB:
482
// Store File object into DB
489
483
$newFile->dbsave();
491
485
$Plugins->trigger_event( 'AfterFileSave', array(
492
'newFile' => & $newFile,
486
'newFile' => & $newFile
497
if( $upload_quickmode && !empty($failedFiles) )
498
{ // Transmit file error to next page!
491
if( $upload_quickmode && ! empty( $failedFiles ) )
493
// Transmit file error to next page!
499
494
$Messages->add( $failedFiles[0], 'error' );
495
unset( $failedFiles );
502
if( empty($failedFiles) )
503
{ // quick mode or no failed files, Go back to Browsing
504
// header_redirect( $dispatcher.'?ctrl=files&root='.$fm_FileRoot->ID.'&path='.rawurlencode($path) );
497
if( empty( $failedFiles ) )
499
// quick mode or no failed files, Go back to Browsing
500
// header_redirect( $dispatcher.'?ctrl=files&root='.$fm_FileRoot->ID.'&path='.rawurlencode( $path ) );
505
501
header_redirect( regenerate_url( 'ctrl', 'ctrl=files', '', '&' ) );
511
507
if( $current_User->check_perm( 'files', 'add' ) )
512
{ // Permission to upload: (no subtabs needed otherwise)
509
// Permission to upload (no subtabs needed otherwise)
513
510
$AdminUI->add_menu_entries(
515
512
'browse' => array(