fileManage.lib.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * This is the file manage library for Chamilo.
  5. * Include/require it in your code to use its functionality.
  6. * @package chamilo.library
  7. */
  8. /**
  9. * Update the file or directory path in the document db document table
  10. *
  11. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  12. * @param - action (string) - action type require : 'delete' or 'update'
  13. * @param - old_path (string) - old path info stored to change
  14. * @param - new_path (string) - new path info to substitute
  15. * @desc Update the file or directory path in the document db document table
  16. *
  17. */
  18. function update_db_info($action, $old_path, $new_path = '')
  19. {
  20. $dbTable = Database::get_course_table(TABLE_DOCUMENT);
  21. $course_id = api_get_course_int_id();
  22. switch ($action) {
  23. case 'delete':
  24. $old_path = Database::escape_string($old_path);
  25. $to_delete = "WHERE c_id = $course_id AND (path LIKE BINARY '".$old_path."' OR path LIKE BINARY '".$old_path."/%')";
  26. $query = "DELETE FROM $dbTable " . $to_delete;
  27. $result = Database::query("SELECT id FROM $dbTable " . $to_delete);
  28. if (Database::num_rows($result)) {
  29. require_once api_get_path(INCLUDE_PATH).'../metadata/md_funcs.php';
  30. $mdStore = new mdstore(TRUE); // create if needed
  31. $md_type = (substr($dbTable, -13) == 'scormdocument') ? 'Scorm' : 'Document';
  32. while ($row = Database::fetch_array($result)) {
  33. $eid = $md_type . '.' . $row['id'];
  34. $mdStore->mds_delete($eid);
  35. $mdStore->mds_delete_offspring($eid);
  36. }
  37. }
  38. Database::query($query);
  39. break;
  40. case 'update':
  41. if ($new_path[0] == '.') $new_path = substr($new_path, 1);
  42. $new_path = str_replace('//', '/', $new_path);
  43. // Attempt to update - tested & working for root dir
  44. $new_path = Database::escape_string($new_path);
  45. $query = "UPDATE $dbTable SET
  46. path = CONCAT('".$new_path."', SUBSTRING(path, LENGTH('".$old_path."')+1) )
  47. WHERE c_id = $course_id AND (path LIKE BINARY '".$old_path."' OR path LIKE BINARY '".$old_path."/%')";
  48. Database::query($query);
  49. break;
  50. }
  51. }
  52. /**
  53. * Cheks a file or a directory actually exist at this location
  54. *
  55. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  56. * @param - file_path (string) - path of the presume existing file or dir
  57. * @return - boolean TRUE if the file or the directory exists
  58. * boolean FALSE otherwise.
  59. */
  60. function check_name_exist($file_path) {
  61. clearstatcache();
  62. $save_dir = getcwd();
  63. if (!is_dir(dirname($file_path))) {
  64. return false;
  65. }
  66. chdir(dirname($file_path));
  67. $file_name = basename($file_path);
  68. if (file_exists($file_name)) {
  69. chdir($save_dir);
  70. return true;
  71. } else {
  72. chdir($save_dir);
  73. return false;
  74. }
  75. }
  76. /**
  77. * Deletes a file or a directory
  78. *
  79. * @author - Hugues Peeters
  80. * @param $file (String) - the path of file or directory to delete
  81. * @return boolean - true if the delete succeed, false otherwise.
  82. * @see - delete() uses check_name_exist() and removeDir() functions
  83. */
  84. function my_delete($file) {
  85. if (check_name_exist($file)) {
  86. if (is_file($file)) { // FILE CASE
  87. unlink($file);
  88. return true;
  89. } elseif (is_dir($file)) { // DIRECTORY CASE
  90. removeDir($file);
  91. return true;
  92. }
  93. } else {
  94. return false; // no file or directory to delete
  95. }
  96. }
  97. /**
  98. * Removes a directory recursively
  99. *
  100. * @returns true if OK, otherwise false
  101. *
  102. * @author Amary <MasterNES@aol.com> (from Nexen.net)
  103. * @author Olivier Brouckaert <oli.brouckaert@skynet.be>
  104. *
  105. * @param string $dir directory to remove
  106. */
  107. function removeDir($dir) {
  108. if (!@$opendir = opendir($dir)) {
  109. return false;
  110. }
  111. while ($readdir = readdir($opendir)) {
  112. if ($readdir != '..' && $readdir != '.') {
  113. if (is_file($dir.'/'.$readdir)) {
  114. if (!@unlink($dir.'/'.$readdir)) {
  115. return false;
  116. }
  117. } elseif (is_dir($dir.'/'.$readdir)) {
  118. if (!removeDir($dir.'/'.$readdir)) {
  119. return false;
  120. }
  121. }
  122. }
  123. }
  124. closedir($opendir);
  125. if (!@rmdir($dir)) {
  126. return false;
  127. }
  128. return true;
  129. }
  130. /**
  131. * Return true if folder is empty
  132. * @author : hubert.borderiou@grenet.fr
  133. * @param string $in_folder : folder path on disk
  134. * @return 1 if folder is empty, 0 otherwise
  135. */
  136. function folder_is_empty($in_folder) {
  137. $folder_is_empty = 0;
  138. if (is_dir($in_folder)) {
  139. $tab_folder_content = scandir($in_folder);
  140. if ((count($tab_folder_content) == 2 && in_array(".", $tab_folder_content) && in_array("..", $tab_folder_content)) || (count($tab_folder_content) < 2)) {
  141. $folder_is_empty = 1;
  142. }
  143. }
  144. return $folder_is_empty;
  145. }
  146. /**
  147. * Renames a file or a directory
  148. *
  149. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  150. * @param - $file_path (string) - complete path of the file or the directory
  151. * @param - $new_file_name (string) - new name for the file or the directory
  152. * @return - boolean - true if succeed
  153. * - boolean - false otherwise
  154. * @see - rename() uses the check_name_exist() and php2phps() functions
  155. */
  156. function my_rename($file_path, $new_file_name) {
  157. $save_dir = getcwd();
  158. $path = dirname($file_path);
  159. $old_file_name = basename($file_path);
  160. $new_file_name = replace_dangerous_char($new_file_name);
  161. // If no extension, take the old one
  162. if ((strpos($new_file_name, '.') === false) && ($dotpos = strrpos($old_file_name, '.'))) {
  163. $new_file_name .= substr($old_file_name, $dotpos);
  164. }
  165. // Note: still possible: 'xx.yy' -rename-> '.yy' -rename-> 'zz'
  166. // This is useful for folder names, where otherwise '.' would be sticky
  167. // Extension PHP is not allowed, change to PHPS
  168. $new_file_name = php2phps($new_file_name);
  169. if ($new_file_name == $old_file_name) {
  170. return $old_file_name;
  171. }
  172. if (strtolower($new_file_name) != strtolower($old_file_name) && check_name_exist($path.'/'.$new_file_name)) {
  173. return false;
  174. }
  175. // On a Windows server, it would be better not to do the above check
  176. // because it succeeds for some new names resembling the old name.
  177. // But on Unix/Linux the check must be done because rename overwrites.
  178. chdir($path);
  179. $res = rename($old_file_name, $new_file_name) ? $new_file_name : false;
  180. chdir($save_dir);
  181. return $res;
  182. }
  183. /**
  184. * Moves a file or a directory to an other area
  185. *
  186. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  187. * @param - $source (String) - the path of file or directory to move
  188. * @param - $target (String) - the path of the new area
  189. * @return - bolean - true if the move succeed
  190. * bolean - false otherwise.
  191. * @see - move() uses check_name_exist() and copyDirTo() functions
  192. */
  193. function move($source, $target) {
  194. if (check_name_exist($source)) {
  195. $file_name = basename($source);
  196. if (check_name_exist($target.'/'.$file_name)) {
  197. return false;
  198. } else {
  199. /* File case */
  200. if (is_file($source)) {
  201. copy($source , $target.'/'.$file_name);
  202. unlink($source);
  203. return true;
  204. }
  205. /* Directory case */
  206. elseif (is_dir($source)) {
  207. // Check to not copy the directory inside itself
  208. if (ereg('^'.$source.'/', $target.'/')) { // TODO: ereg() function is deprecated in PHP 5.3
  209. return false;
  210. } else {
  211. copyDirTo($source, $target);
  212. return true;
  213. }
  214. }
  215. }
  216. } else {
  217. return false;
  218. }
  219. }
  220. /**
  221. * Moves a directory and its content to an other area
  222. *
  223. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  224. * @param - $orig_dir_path (string) - the path of the directory to move
  225. * @param - $destination (string) - the path of the new area
  226. * @return - no return
  227. */
  228. function copyDirTo($orig_dir_path, $destination, $move = true) {
  229. $save_dir = getcwd();
  230. // Extract directory name - create it at destination - update destination trail
  231. $dir_name = basename($orig_dir_path);
  232. $dir_to_copy = array();
  233. if (is_dir($orig_dir_path)) {
  234. mkdir($destination.'/'.$dir_name, api_get_permissions_for_new_directories());
  235. $destination_trail = $destination.'/'.$dir_name;
  236. if (is_dir($destination)) {
  237. chdir ($orig_dir_path) ;
  238. $handle = opendir($orig_dir_path);
  239. while ($element = readdir($handle)) {
  240. if ($element == '.' || $element == '..') {
  241. continue; // Skip the current and parent directories
  242. } elseif (is_file($element)) {
  243. copy($element, $destination_trail.'/'.$element);
  244. if ($move) {
  245. unlink($element) ;
  246. }
  247. } elseif (is_dir($element)) {
  248. $dir_to_copy[] = $orig_dir_path.'/'.$element;
  249. }
  250. }
  251. closedir($handle) ;
  252. if (sizeof($dir_to_copy) > 0) {
  253. foreach ($dir_to_copy as $this_dir) {
  254. copyDirTo($this_dir, $destination_trail, $move); // Recursivity
  255. }
  256. }
  257. if ($move) {
  258. rmdir($orig_dir_path) ;
  259. }
  260. chdir($save_dir);
  261. }
  262. }
  263. }
  264. /* NOTE: These functions batch is used to automatically build HTML forms
  265. * with a list of the directories contained on the course Directory.
  266. *
  267. * From a thechnical point of view, form_dir_lists calls sort_dir wich calls index_dir
  268. */
  269. /**
  270. * Gets all the directories and subdirectories
  271. * contented in a given directory
  272. *
  273. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  274. * @param - path (string) - directory path of the one to index
  275. * @return - an array containing the path of all the subdirectories
  276. */
  277. function index_dir($path) {
  278. $dir_array = array();
  279. $save_dir = getcwd();
  280. if (is_dir($path)){
  281. chdir($path);
  282. $handle = opendir($path);
  283. // Reads directory content end record subdirectoies names in $dir_array
  284. if ($handle !== false) {
  285. while ($element = readdir($handle)) {
  286. if ($element == '.' || $element == '..') continue; // Skip the current and parent directories
  287. if (is_dir($element)) $dir_array[] = $path.'/'.$element;
  288. }
  289. closedir($handle) ;
  290. }
  291. // Recursive operation if subdirectories exist
  292. $dir_number = sizeof($dir_array);
  293. if ($dir_number > 0) {
  294. for ($i = 0 ; $i < $dir_number ; $i++) {
  295. $sub_dir_array = index_dir($dir_array[$i]); // Function recursivity
  296. $dir_array = array_merge((array)$dir_array, (array)$sub_dir_array); // Data merge
  297. }
  298. }
  299. }
  300. chdir($save_dir) ;
  301. return $dir_array ;
  302. }
  303. /**
  304. * Indexes all the directories and subdirectories
  305. * contented in a given directory, and sort them alphabetically
  306. *
  307. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  308. * @param - path (string) - directory path of the one to index
  309. * @return - an array containing the path of all the subdirectories sorted
  310. * false, if there is no directory
  311. * @see - index_and_sort_dir uses the index_dir() function
  312. */
  313. function index_and_sort_dir($path) {
  314. $dir_list = index_dir($path);
  315. if ($dir_list) {
  316. natsort($dir_list);
  317. return $dir_list;
  318. }
  319. return false;
  320. }
  321. /**
  322. * Builds a html form listing all directories of a given directory
  323. *
  324. */
  325. function form_dir_list($source_type, $source_component, $command, $base_work_dir) {
  326. $dir_list = index_and_sort_dir($base_work_dir);
  327. $dialog_box .= "<form action=\"".api_get_self()."\" method=\"post\">\n" ;
  328. $dialog_box .= "<input type=\"hidden\" name=\"".$source_type."\" value=\"".$source_component."\">\n" ;
  329. $dialog_box .= get_lang('Move').' '.$source_component.' '.get_lang('To');
  330. $dialog_box .= "<select name=\"".$command."\">\n" ;
  331. $dialog_box .= "<option value=\"\" style=\"color:#999999\">".get_lang('Root')."\n";
  332. $bwdLen = strlen($base_work_dir) ; // base directories lenght, used under
  333. /* build html form inputs */
  334. if ($dir_list) {
  335. while (list( , $path_value) = each($dir_list) ) {
  336. $path_value = substr ( $path_value , $bwdLen ); // Truncates cunfidential informations confidentielles
  337. $dirname = basename ($path_value); // Extracts $path_value directory name du nom
  338. /* compute de the display tab */
  339. $tab = ""; // $tab reinitialisation
  340. $depth = substr_count($path_value, '/'); // The number of nombre '/' indicates the directory deepness
  341. for ($h = 0; $h < $depth; $h++) {
  342. $tab .= "&nbsp;&nbsp;";
  343. }
  344. $dialog_box .= "<option value=\"$path_value\">$tab>$dirname\n";
  345. }
  346. }
  347. $dialog_box .= "</select>\n";
  348. $dialog_box .= "<input type=\"submit\" value=\"".get_lang('Ok')."\">";
  349. $dialog_box .= "</form>\n";
  350. return $dialog_box;
  351. }
  352. /**
  353. * Extracting extention of a filename
  354. *
  355. * @returns array
  356. * @param string $filename filename
  357. */
  358. function getextension($filename) {
  359. $bouts = explode('.', $filename);
  360. return array(array_pop($bouts), implode('.', $bouts));
  361. }
  362. /**
  363. * Calculation size of a directory
  364. *
  365. * @returns integer size
  366. * @param string $path path to size
  367. * @param boolean $recursive if true , include subdir in total
  368. */
  369. function dirsize($root, $recursive = true) {
  370. $dir = @opendir($root);
  371. $size = 0;
  372. while ($file = @readdir($dir)) {
  373. if (!in_array($file, array('.', '..'))) {
  374. if (is_dir($root.'/'.$file)) {
  375. $size += $recursive ? dirsize($root.'/'.$file) : 0;
  376. } else {
  377. $size += @filesize($root.'/'.$file);
  378. }
  379. }
  380. }
  381. @closedir($dir);
  382. return $size;
  383. }
  384. /* CLASS FileManager */
  385. /**
  386. This class contains functions that you can access statically.
  387. FileManager::list_all_directories($path)
  388. FileManager::list_all_files($dir_array)
  389. FileManager::compat_load_file($file_name)
  390. FileManager::set_default_settings($upload_path, $filename, $filetype="file", $glued_table, $default_visibility='v')
  391. @author Roan Embrechts
  392. @version 1.1, July 2004
  393. * @package chamilo.library
  394. */
  395. class FileManager
  396. {
  397. /**
  398. Returns a list of all directories, except the base dir,
  399. of the current course. This function uses recursion.
  400. Convention: the parameter $path does not end with a slash.
  401. @author Roan Embrechts
  402. @version 1.0.1
  403. */
  404. function list_all_directories($path) {
  405. $result_array = array();
  406. if (is_dir($path)) {
  407. $save_dir = getcwd();
  408. chdir($path);
  409. $handle = opendir($path);
  410. while ($element = readdir($handle)) {
  411. if ($element == '.' || $element == '..') continue; // Skip the current and parent directories
  412. if (is_dir($element)) {
  413. $dir_array[] = $path.'/'.$element;
  414. }
  415. }
  416. closedir($handle);
  417. // Recursive operation if subdirectories exist
  418. $dir_number = sizeof($dir_array);
  419. if ($dir_number > 0) {
  420. for ($i = 0 ; $i < $dir_number ; $i++) {
  421. $sub_dir_array = FileManager::list_all_directories($dir_array[$i]); // Function recursivity
  422. if (is_array($dir_array) && is_array($sub_dir_array)) {
  423. $dir_array = array_merge($dir_array, $sub_dir_array); // Data merge
  424. }
  425. }
  426. }
  427. $result_array = $dir_array;
  428. chdir($save_dir) ;
  429. }
  430. return $result_array ;
  431. }
  432. /**
  433. This function receives a list of directories.
  434. It returns a list of all files in these directories
  435. @author Roan Embrechts
  436. @version 1.0
  437. */
  438. function list_all_files($dir_array) {
  439. $element_array = array();
  440. if (is_dir($dir_array)) {
  441. $save_dir = getcwd();
  442. foreach ($dir_array as $directory) {
  443. chdir($directory);
  444. $handle = opendir($directory);
  445. while ($element = readdir($handle)) {
  446. if ($element == '.' || $element == '..' || $element == '.htaccess') continue; // Skip the current and parent directories
  447. if (!is_dir($element)) {
  448. $element_array[] = $directory.'/'.$element;
  449. }
  450. }
  451. closedir($handle);
  452. chdir('..');
  453. chdir($save_dir);
  454. }
  455. }
  456. return $element_array;
  457. }
  458. /**
  459. Loads contents of file $filename into memory and returns them as a string.
  460. Function kept for compatibility with older PHP versions.
  461. Function is binary safe (is needed on Windows)
  462. */
  463. function compat_load_file($file_name) {
  464. $buffer = '';
  465. if (file_exists($file_name)) {
  466. $fp = fopen($file_name, 'rb');
  467. $buffer = fread ($fp, filesize($file_name));
  468. fclose ($fp);
  469. }
  470. return $buffer;
  471. }
  472. /**
  473. * Adds file/folder to document table in database
  474. * improvement from set_default_settings (see below):
  475. * take all info from function parameters
  476. * no global variables needed
  477. *
  478. * NOTE $glued_table should already have backticks around it
  479. * (get it from the database library, and it is done automatically)
  480. *
  481. * @param path, filename, filetype,
  482. $glued_table, default_visibility
  483. * action: Adds an entry to the document table with the default settings.
  484. * @author Olivier Cauberghe <olivier.cauberghe@ugent.be>
  485. * @author Roan Embrechts
  486. * @version 1.2
  487. */
  488. function set_default_settings($upload_path, $filename, $filetype = 'file', $glued_table, $default_visibility = 'v') {
  489. if (!$default_visibility) $default_visibility = 'v';
  490. // Make sure path is not wrongly formed
  491. $upload_path = !empty($upload_path) ? "/$upload_path" : '';
  492. $endchar = substr($filename, strlen($filename) - 1, 1);
  493. if ($endchar == "\\" || $endchar == '/') {
  494. $filename = substr($filename, 0, strlen($filename) - 1);
  495. }
  496. $full_file_name = $upload_path.'/'.$filename;
  497. //$upload_path = str_replace("//", '/', $upload_path);
  498. $full_file_name = str_replace("//", '/', $full_file_name);
  499. $sql_query = "SELECT count(*) as number_existing FROM $glued_table WHERE path='$full_file_name'";
  500. $sql_result = Database::query($sql_query);
  501. $result = Database::fetch_array($sql_result);
  502. // Determine which query to execute
  503. if ($result['number_existing'] > 0) {
  504. // Entry exists, update
  505. $query = "UPDATE $glued_table SET path='$full_file_name',visibility='$default_visibility', filetype='$filetype' WHERE path='$full_file_name'";
  506. } else {
  507. // No entry exists, create new one
  508. $query = "INSERT INTO $glued_table (path,visibility,filetype) VALUES('$full_file_name','$default_visibility','$filetype')";
  509. }
  510. Database::query($query);
  511. }
  512. } //end class FileManager
  513. /* DEPRECATED FUNCTIONS */
  514. /**
  515. * Like in Java, creates the directory named by this abstract pathname,
  516. * including any necessary but nonexistent parent directories.
  517. *
  518. * @author Hugues Peeters <peeters@ipm.ucl.ac.be>
  519. * @author Christophe Gesche <gesche@ipm.ucl.ac.be>
  520. *
  521. * @param string $path - path to create
  522. * @param string $mode - directory permission (default is '770')
  523. *
  524. * @return boolean TRUE if succeeds FALSE otherwise
  525. */
  526. function mkdirs($path, $mode = '0770') {
  527. if (file_exists($path)) {
  528. return false;
  529. } else {
  530. FileManager :: mkdirs(dirname($path), $mode);
  531. //mkdir($path, $mode);
  532. return true;
  533. }
  534. }
  535. /**
  536. * @deprecated 06-FEB-2010. The function mkdir() is able to create directories recursively.
  537. * @link http://php.net/manual/en/function.mkdir.php
  538. *
  539. * to create missing directory in a gived path
  540. *
  541. * @returns a resource identifier or FALSE if the query was not executed correctly.
  542. * @author KilerCris@Mail.com original function from php manual
  543. * @author Christophe Gesch� gesche@ipm.ucl.ac.be Claroline Team
  544. * @since 28-Aug-2001 09:12
  545. * @param sting $path wanted path
  546. * @param boolean $verbose fix if comments must be printed
  547. * @param string $mode fix if chmod is same of parent or default (Note: This misterious parameter is not used).
  548. * Note string $langCreatedIn string used to say "create in"
  549. */
  550. function mkpath($path, $verbose = false, $mode = 'herit') {
  551. global $langCreatedIn, $_configuration;
  552. $path = str_replace('/', "\\", $path);
  553. $dirs = explode("\\", $path);
  554. $path = $dirs[0];
  555. if ($verbose) {
  556. echo '<ul>';
  557. }
  558. for ($i = 1; $i < sizeof($dirs); $i++) {
  559. $path .= '/'.$dirs[$i];
  560. if (ereg('^'.$path, $_configuration['root_sys']) && strlen($path) < strlen($_configuration['root_sys'])) {
  561. continue;
  562. }
  563. if (!is_dir($path)) {
  564. $ret = mkdir($path, api_get_permissions_for_new_directories());
  565. if ($ret) {
  566. if ($verbose) {
  567. echo '<li><strong>'.basename($path).'</strong><br />'.$langCreatedIn.'<br /><strong>'.realpath($path.'/..').'</strong></li>';
  568. }
  569. } else {
  570. if ($verbose) {
  571. echo '</ul>error : '.$path.' not created';
  572. }
  573. $ret = false;
  574. break;
  575. }
  576. }
  577. }
  578. if ($verbose) {
  579. echo '</ul>';
  580. }
  581. return $ret;
  582. }