fileUpload.lib.php 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * FILE UPLOAD LIBRARY
  5. *
  6. * This is the file upload library for Chamilo.
  7. * Include/require it in your code to use its functionality.
  8. *
  9. * @package chamilo.library
  10. * @todo test and reorganise
  11. */
  12. /**
  13. * Changes the file name extension from .php to .phps
  14. * Useful for securing a site.
  15. *
  16. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  17. * @param - file_name (string) name of a file
  18. * @return - the filenam phps'ized
  19. */
  20. function php2phps($file_name) {
  21. return preg_replace('/\.(php.?|phtml.?)(\.){0,1}.*$/i', '.phps', $file_name);
  22. }
  23. /**
  24. * Renames .htaccess & .HTACCESS to htaccess.txt
  25. *
  26. * @param string $filename
  27. * @return string
  28. */
  29. function htaccess2txt($filename) {
  30. return str_replace(array('.htaccess', '.HTACCESS'), array('htaccess.txt', 'htaccess.txt'), $filename);
  31. }
  32. /**
  33. * This function executes our safety precautions
  34. * more functions can be added
  35. *
  36. * @param string $filename
  37. * @return string
  38. * @see php2phps()
  39. * @see htaccess2txt()
  40. */
  41. function disable_dangerous_file($filename) {
  42. return htaccess2txt(php2phps($filename));
  43. }
  44. /**
  45. * This function generates a unique name for a file on a given location
  46. * filenames are changed to name_#.ext
  47. *
  48. * @param string $path
  49. * @param string $name
  50. * @return new unique name
  51. */
  52. function unique_name($path, $name) {
  53. $ext = substr(strrchr($name, '.'), 0);
  54. $name_no_ext = substr($name, 0, strlen($name) - strlen(strstr($name, $ext)));
  55. $n = 0;
  56. $unique = '';
  57. while (file_exists($path . $name_no_ext . $unique . $ext)) {
  58. $unique = '_' . ++$n;
  59. }
  60. return $name_no_ext . $unique . $ext;
  61. }
  62. /**
  63. * Returns the name without extension, used for the title
  64. *
  65. * @param string $name
  66. * @return name without the extension
  67. */
  68. function get_document_title($name) {
  69. // If they upload .htaccess...
  70. $name = disable_dangerous_file($name);
  71. $ext = substr(strrchr($name, '.'), 0);
  72. return addslashes(substr($name, 0, strlen($name) - strlen(strstr($name, $ext))));
  73. }
  74. /**
  75. * This function checks if the upload succeeded
  76. *
  77. * @param array $uploaded_file ($_FILES)
  78. * @return true if upload succeeded
  79. */
  80. function process_uploaded_file($uploaded_file) {
  81. // Checking the error code sent with the file upload.
  82. switch ($uploaded_file['error']) {
  83. case 1:
  84. // The uploaded file exceeds the upload_max_filesize directive in php.ini.
  85. Display::display_error_message(get_lang('UplExceedMaxServerUpload').ini_get('upload_max_filesize'));
  86. return false;
  87. case 2:
  88. // The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
  89. // Not used at the moment, but could be handy if we want to limit the size of an upload (e.g. image upload in html editor).
  90. Display::display_error_message(get_lang('UplExceedMaxPostSize'). round($_POST['MAX_FILE_SIZE']/1024) .' KB');
  91. return false;
  92. case 3:
  93. // The uploaded file was only partially uploaded.
  94. Display::display_error_message(get_lang('$UplPartialUpload').' '.get_lang('PleaseTryAgain'));
  95. return false;
  96. case 4:
  97. // No file was uploaded.
  98. Display::display_error_message(get_lang('UplNoFileUploaded').' '. get_lang('UplSelectFileFirst'));
  99. return false;
  100. }
  101. // case 0: default: We assume there is no error, the file uploaded with success.
  102. return true;
  103. }
  104. /**
  105. * This function does the save-work for the documents.
  106. * It handles the uploaded file and adds the properties to the database
  107. * If unzip=1 and the file is a zipfile, it is extracted
  108. * If we decide to save ALL kinds of documents in one database,
  109. * we could extend this with a $type='document', 'scormdocument',...
  110. *
  111. * @param array $_course
  112. * @param array $uploaded_file ($_FILES)
  113. * @param string $base_work_dir
  114. * @param string $upload_path
  115. * @param int $user_id
  116. * @param int $to_group_id, 0 for everybody
  117. * @param int $to_user_id, NULL for everybody
  118. * @param int $maxFilledSpace
  119. * @param int $unzip 1/0
  120. * @param string $what_if_file_exists overwrite, rename or warn if exists (default)
  121. * @param boolean Optional output parameter. So far only use for unzip_uploaded_document function. If no output wanted on success, set to false.
  122. * @return path of the saved file
  123. */
  124. function handle_uploaded_document($_course, $uploaded_file, $base_work_dir, $upload_path, $user_id, $to_group_id = 0, $to_user_id = null, $maxFilledSpace = '', $unzip = 0, $what_if_file_exists = '', $output = true) {
  125. if (!$user_id) die('Not a valid user.');
  126. // Strip slashes
  127. $uploaded_file['name'] = stripslashes($uploaded_file['name']);
  128. // Add extension to files without one (if possible)
  129. $uploaded_file['name'] = add_ext_on_mime($uploaded_file['name'], $uploaded_file['type']);
  130. $current_session_id = api_get_session_id();
  131. // Check if there is enough space to save the file
  132. if (!enough_space($uploaded_file['size'], $maxFilledSpace)) {
  133. Display::display_error_message(get_lang('UplNotEnoughSpace'));
  134. return false;
  135. }
  136. // If the want to unzip, check if the file has a .zip (or ZIP,Zip,ZiP,...) extension
  137. if ($unzip == 1 && preg_match('/.zip$/', strtolower($uploaded_file['name']))) {
  138. return unzip_uploaded_document($uploaded_file, $upload_path, $base_work_dir, $maxFilledSpace, $output, $to_group_id);
  139. //display_message('Unzipping file');
  140. }
  141. // We can only unzip ZIP files (no gz, tar,...)
  142. elseif ($unzip == 1 && !preg_match('/.zip$/', strtolower($uploaded_file['name']))) {
  143. Display::display_error_message(get_lang('UplNotAZip')." ".get_lang('PleaseTryAgain'));
  144. return false;
  145. } else {
  146. // Clean up the name, only ASCII characters should stay. (and strict)
  147. $clean_name = replace_dangerous_char($uploaded_file['name'], 'strict');
  148. // No "dangerous" files
  149. $clean_name = disable_dangerous_file($clean_name);
  150. if (!filter_extension($clean_name)) {
  151. Display::display_error_message(get_lang('UplUnableToSaveFileFilteredExtension'));
  152. return false;
  153. } else {
  154. // Extension is good
  155. //echo '<br />clean name = '.$clean_name;
  156. //echo '<br />upload_path = '.$upload_path;
  157. // If the upload path differs from / (= root) it will need a slash at the end
  158. if ($upload_path != '/') {
  159. $upload_path = $upload_path.'/';
  160. }
  161. //echo '<br />upload_path = '.$upload_path;
  162. $file_path = $upload_path.$clean_name;
  163. //echo '<br />file path = '.$file_path;
  164. // Full path to where we want to store the file with trailing slash
  165. $where_to_save = $base_work_dir.$upload_path;
  166. // At least if the directory doesn't exist, tell so
  167. if (!is_dir($where_to_save)) {
  168. Display::display_error_message(get_lang('DestDirectoryDoesntExist').' ('.$upload_path.')');
  169. return false;
  170. }
  171. //echo '<br />where to save = '.$where_to_save;
  172. // Full path of the destination
  173. $store_path = $where_to_save.$clean_name;
  174. //echo '<br />store path = '.$store_path;
  175. // Name of the document without the extension (for the title)
  176. $document_name = get_document_title($uploaded_file['name']);
  177. // Size of the uploaded file (in bytes)
  178. $file_size = $uploaded_file['size'];
  179. $files_perm = api_get_permissions_for_new_files();
  180. // What to do if the target file exists
  181. switch ($what_if_file_exists) {
  182. // Overwrite the file if it exists
  183. case 'overwrite':
  184. // Check if the target file exists, so we can give another message
  185. $file_exists = file_exists($store_path);
  186. if (@move_uploaded_file($uploaded_file['tmp_name'], $store_path)) {
  187. chmod($store_path, $files_perm);
  188. if ($file_exists) {
  189. // UPDATE DATABASE
  190. $document_id = DocumentManager::get_document_id($_course, $file_path);
  191. if ($document_id) {
  192. // Update filesize
  193. update_existing_document($_course, $document_id, $uploaded_file['size']);
  194. // Update document item_property
  195. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentUpdated', $user_id, $to_group_id, $to_user_id, null, null, $current_session_id);
  196. }
  197. // If the file is in a folder, we need to update all parent folders
  198. item_property_update_on_folder($_course,$upload_path,$user_id);
  199. // Display success message with extra info to user
  200. if ($output){
  201. Display::display_confirmation_message(get_lang('UplUploadSucceeded').'<br />'.$file_path .' '. get_lang('UplFileOverwritten'), false);
  202. }
  203. return $file_path;
  204. } else {
  205. // Put the document data in the database
  206. $document_id = add_document($_course, $file_path, 'file', $file_size, $document_name);
  207. if ($document_id) {
  208. // Put the document in item_property update
  209. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $user_id, $to_group_id, $to_user_id, null, null, $current_session_id);
  210. }
  211. // If the file is in a folder, we need to update all parent folders
  212. item_property_update_on_folder($_course, $upload_path, $user_id);
  213. // Display success message to user
  214. Display::display_confirmation_message(get_lang('UplUploadSucceeded').'<br />'.$file_path, false);
  215. return $file_path;
  216. }
  217. } else {
  218. Display::display_error_message(get_lang('UplUnableToSaveFile'));
  219. return false;
  220. }
  221. break;
  222. // Rename the file if it exists
  223. case 'rename':
  224. $new_name = unique_name($where_to_save, $clean_name);
  225. $store_path = $where_to_save.$new_name;
  226. $new_file_path = $upload_path.$new_name;
  227. if (@move_uploaded_file($uploaded_file['tmp_name'], $store_path)) {
  228. chmod($store_path, $files_perm);
  229. // Put the document data in the database
  230. $document_id = add_document($_course, $new_file_path, 'file', $file_size, $document_name);
  231. if ($document_id) {
  232. // Update document item_property
  233. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $user_id, $to_group_id, $to_user_id, null, null, $current_session_id);
  234. }
  235. // If the file is in a folder, we need to update all parent folders
  236. item_property_update_on_folder($_course, $upload_path, $user_id);
  237. // Display success message to user
  238. if ($output){
  239. Display::display_confirmation_message(get_lang('UplUploadSucceeded').'<br />'.get_lang('UplFileSavedAs').$new_file_path, false);
  240. }
  241. return $new_file_path;
  242. } else {
  243. Display::display_error_message(get_lang('UplUnableToSaveFile'));
  244. return false;
  245. }
  246. break;
  247. // Only save the file if it doesn't exist or warn user if it does exist
  248. default:
  249. if (file_exists($store_path)) {
  250. Display::display_error_message($clean_name.' '.get_lang('UplAlreadyExists'));
  251. } else {
  252. if (@move_uploaded_file($uploaded_file['tmp_name'], $store_path)) {
  253. chmod($store_path, $files_perm);
  254. // Put the document data in the database
  255. $document_id = add_document($_course, $file_path, 'file', $file_size, $document_name);
  256. if ($document_id) {
  257. // Update document item_property
  258. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $user_id, $to_group_id, $to_user_id, null, null, $current_session_id);
  259. }
  260. // If the file is in a folder, we need to update all parent folders
  261. item_property_update_on_folder($_course,$upload_path,$user_id);
  262. // Display success message to user
  263. if ($output){
  264. Display::display_confirmation_message(get_lang('UplUploadSucceeded').'<br />'.$file_path, false);
  265. }
  266. return $file_path;
  267. } else {
  268. Display::display_error_message(get_lang('UplUnableToSaveFile'));
  269. return false;
  270. }
  271. }
  272. break;
  273. }
  274. }
  275. }
  276. }
  277. /**
  278. * Checks if there is enough place to add a file on a directory
  279. * on the base of a maximum directory size allowed
  280. * @deprecated use enough_space instead!
  281. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  282. * @param - file_size (int) - size of the file in byte
  283. * @param - dir (string) - Path of the directory
  284. * whe the file should be added
  285. * @param - max_dir_space (int) - maximum size of the diretory in byte
  286. * @return - boolean true if there is enough space,
  287. * boolean false otherwise
  288. *
  289. * @see - enough_size() uses dir_total_space() function
  290. */
  291. function enough_size($file_size, $dir, $max_dir_space) {
  292. if ($max_dir_space) {
  293. $already_filled_space = dir_total_space($dir);
  294. if (($file_size + $already_filled_space) > $max_dir_space) {
  295. return false;
  296. }
  297. }
  298. return true;
  299. }
  300. /**
  301. * Checks if there is enough place to add a file on a directory
  302. * on the base of a maximum directory size allowed
  303. *
  304. * @author Bert Vanderkimpen
  305. * @param int file_size size of the file in byte
  306. * @param array $_course
  307. * @param int max_dir_space maximum size
  308. * @return boolean true if there is enough space, false otherwise
  309. *
  310. * @see enough_space() uses documents_total_space() function
  311. */
  312. function enough_space($file_size, $max_dir_space) {
  313. if ($max_dir_space) {
  314. $already_filled_space = documents_total_space();
  315. if (($file_size + $already_filled_space) > $max_dir_space) {
  316. return false;
  317. }
  318. }
  319. return true;
  320. }
  321. /**
  322. * Computes the size already occupied by a directory and is subdirectories
  323. *
  324. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  325. * @param - dir_path (string) - size of the file in byte
  326. * @return - int - return the directory size in bytes
  327. */
  328. function dir_total_space($dir_path) {
  329. $save_dir = getcwd();
  330. chdir($dir_path) ;
  331. $handle = opendir($dir_path);
  332. while ($element = readdir($handle)) {
  333. if ( $element == '.' || $element == '..') {
  334. continue; // Skip the current and parent directories
  335. }
  336. if (is_file($element)) {
  337. $sumSize += filesize($element);
  338. }
  339. if (is_dir($element)) {
  340. $dirList[] = $dir_path.'/'.$element;
  341. }
  342. }
  343. closedir($handle) ;
  344. if (sizeof($dirList) > 0) {
  345. foreach ($dirList as $j) {
  346. $sizeDir = dir_total_space($j); // Recursivity
  347. $sumSize += $sizeDir;
  348. }
  349. }
  350. chdir($save_dir); // Return to initial position
  351. return $sumSize;
  352. }
  353. /**
  354. * Calculates the total size of all documents in a course
  355. *
  356. * @author Bert vanderkimpen
  357. * @param int $to_group_id (to calculate group document space)
  358. * @return int total size
  359. */
  360. function documents_total_space($to_group_id = '0') {
  361. $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
  362. $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
  363. $sql = "SELECT SUM(size)
  364. FROM ".$TABLE_ITEMPROPERTY." AS props, ".$TABLE_DOCUMENT." AS docs
  365. WHERE docs.id = props.ref
  366. AND props.tool = '".TOOL_DOCUMENT."'
  367. AND props.to_group_id='".$to_group_id."'
  368. AND props.visibility <> 2";
  369. $result = Database::query($sql);
  370. if ($result && Database::num_rows($result) != 0) {
  371. $row = Database::fetch_row($result);
  372. return $row[0];
  373. } else {
  374. return 0;
  375. }
  376. }
  377. /**
  378. * Tries to add an extension to files without extension
  379. * Some applications on Macintosh computers don't add an extension to the files.
  380. * This subroutine try to fix this on the basis of the MIME type sent
  381. * by the browser.
  382. *
  383. * Note : some browsers don't send the MIME Type (e.g. Netscape 4).
  384. * We don't have solution for this kind of situation
  385. *
  386. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  387. * @author - Bert Vanderkimpen
  388. * @param - file_name (string) - Name of the file
  389. * @param - file_type (string) - Type of the file
  390. * @return - file_name (string)
  391. */
  392. function add_ext_on_mime($file_name, $file_type) {
  393. // Check whether the file has an extension AND whether the browser has sent a MIME Type
  394. if (!preg_match('/^.*\.[a-zA-Z_0-9]+$/', $file_name) && $file_type) {
  395. // Build a "MIME-types / extensions" connection table
  396. static $mime_type = array();
  397. $mime_type[] = 'application/msword'; $extension[] = '.doc';
  398. $mime_type[] = 'application/rtf'; $extension[] = '.rtf';
  399. $mime_type[] = 'application/vnd.ms-powerpoint'; $extension[] = '.ppt';
  400. $mime_type[] = 'application/vnd.ms-excel'; $extension[] = '.xls';
  401. $mime_type[] = 'application/pdf'; $extension[] = '.pdf';
  402. $mime_type[] = 'application/postscript'; $extension[] = '.ps';
  403. $mime_type[] = 'application/mac-binhex40'; $extension[] = '.hqx';
  404. $mime_type[] = 'application/x-gzip'; $extension[] = 'tar.gz';
  405. $mime_type[] = 'application/x-shockwave-flash'; $extension[] = '.swf';
  406. $mime_type[] = 'application/x-stuffit'; $extension[] = '.sit';
  407. $mime_type[] = 'application/x-tar'; $extension[] = '.tar';
  408. $mime_type[] = 'application/zip'; $extension[] = '.zip';
  409. $mime_type[] = 'application/x-tar'; $extension[] = '.tar';
  410. $mime_type[] = 'text/html'; $extension[] = '.html';
  411. $mime_type[] = 'text/plain'; $extension[] = '.txt';
  412. $mime_type[] = 'text/rtf'; $extension[] = '.rtf';
  413. $mime_type[] = 'img/gif'; $extension[] = '.gif';
  414. $mime_type[] = 'img/jpeg'; $extension[] = '.jpg';
  415. $mime_type[] = 'img/png'; $extension[] = '.png';
  416. $mime_type[] = 'audio/midi'; $extension[] = '.mid';
  417. $mime_type[] = 'audio/mpeg'; $extension[] = '.mp3';
  418. $mime_type[] = 'audio/x-aiff'; $extension[] = '.aif';
  419. $mime_type[] = 'audio/x-pn-realaudio'; $extension[] = '.rm';
  420. $mime_type[] = 'audio/x-pn-realaudio-plugin'; $extension[] = '.rpm';
  421. $mime_type[] = 'audio/x-wav'; $extension[] = '.wav';
  422. $mime_type[] = 'video/mpeg'; $extension[] = '.mpg';
  423. $mime_type[] = 'video/mpeg4-generic'; $extension[] = '.mp4';
  424. $mime_type[] = 'video/quicktime'; $extension[] = '.mov';
  425. $mime_type[] = 'video/x-msvideo'; $extension[] = '.avi';
  426. $mime_type[] = 'video/x-ms-wmv'; $extension[] = '.wmv';
  427. $mime_type[] = 'video/x-flv'; $extension[] = '.flv';
  428. $mime_type[] = 'application/vnd.ms-word.document.macroEnabled.12'; $extension[] = '.docm';
  429. $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; $extension[] = '.docx';
  430. $mime_type[] = 'application/vnd.ms-word.template.macroEnabled.12'; $extension[] = '.dotm';
  431. $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.template'; $extension[] = '.dotx';
  432. $mime_type[] = 'application/vnd.ms-powerpoint.template.macroEnabled.12'; $extension[] = '.potm';
  433. $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.template'; $extension[] = '.potx';
  434. $mime_type[] = 'application/vnd.ms-powerpoint.addin.macroEnabled.12'; $extension[] = '.ppam';
  435. $mime_type[] = 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12'; $extension[] = '.ppsm';
  436. $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.slideshow'; $extension[] = '.ppsx';
  437. $mime_type[] = 'application/vnd.ms-powerpoint.presentation.macroEnabled.12'; $extension[] = '.pptm';
  438. $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'; $extension[] = '.pptx';
  439. $mime_type[] = 'application/vnd.ms-excel.addin.macroEnabled.12'; $extension[] = '.xlam';
  440. $mime_type[] = 'application/vnd.ms-excel.sheet.binary.macroEnabled.12'; $extension[] = '.xlsb';
  441. $mime_type[] = 'application/vnd.ms-excel.sheet.macroEnabled.12'; $extension[] = '.xlsm';
  442. $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; $extension[] = '.xlsx';
  443. $mime_type[] = 'application/vnd.ms-excel.template.macroEnabled.12'; $extension[] = '.xltm';
  444. $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.template'; $extension[] = '.xltx';
  445. // Test on PC (files with no extension get application/octet-stream)
  446. //$mime_type[] = 'application/octet-stream'; $extension[] = '.ext';
  447. // Check whether the MIME type sent by the browser is within the table
  448. foreach ($mime_type as $key => & $type) {
  449. if ($type == $file_type) {
  450. $file_name .= $extension[$key];
  451. break;
  452. }
  453. }
  454. unset($mime_type, $extension, $type, $key); // Delete to eschew possible collisions
  455. }
  456. return $file_name;
  457. }
  458. /**
  459. *
  460. * @author Hugues Peeters <hugues.peeters@claroline.net>
  461. *
  462. * @param array $uploaded_file - follows the $_FILES Structure
  463. * @param string $base_work_dir - base working directory of the module
  464. * @param string $upload_path - destination of the upload.
  465. * This path is to append to $base_work_dir
  466. * @param int $max_filled_space - amount of bytes to not exceed in the base
  467. * working directory
  468. *
  469. * @return boolean true if it succeds, false otherwise
  470. */
  471. function treat_uploaded_file($uploaded_file, $base_work_dir, $upload_path, $max_filled_space, $uncompress = '') {
  472. $uploaded_file['name'] = stripslashes($uploaded_file['name']);
  473. if (!enough_size($uploaded_file['size'], $base_work_dir, $max_filled_space)) {
  474. return api_failure::set_failure('not_enough_space');
  475. }
  476. if ($uncompress == 'unzip' && preg_match('/.zip$/', strtolower($uploaded_file['name']))) {
  477. return unzip_uploaded_file($uploaded_file, $upload_path, $base_work_dir, $max_filled_space);
  478. } else {
  479. $file_name = trim($uploaded_file['name']);
  480. // CHECK FOR NO DESIRED CHARACTERS
  481. $file_name = replace_dangerous_char($file_name, 'strict');
  482. // TRY TO ADD AN EXTENSION TO FILES WITOUT EXTENSION
  483. $file_name = add_ext_on_mime($file_name, $uploaded_file['type']);
  484. // HANDLE PHP FILES
  485. $file_name = ($file_name);
  486. // COPY THE FILE TO THE DESIRED DESTINATION
  487. if (move_uploaded_file($uploaded_file['tmp_name'], $base_work_dir.$upload_path.'/'.$file_name)) {
  488. set_default_settings($upload_path, $file_name);
  489. }
  490. return true;
  491. }
  492. }
  493. /**
  494. * Manages all the unzipping process of an uploaded file
  495. *
  496. * @author Hugues Peeters <hugues.peeters@claroline.net>
  497. *
  498. * @param array $uploaded_file - follows the $_FILES Structure
  499. * @param string $upload_path - destination of the upload.
  500. * This path is to append to $base_work_dir
  501. * @param string $base_work_dir - base working directory of the module
  502. * @param int $max_filled_space - amount of bytes to not exceed in the base
  503. * working directory
  504. *
  505. * @return boolean true if it succeeds false otherwise
  506. */
  507. function unzip_uploaded_file($uploaded_file, $upload_path, $base_work_dir, $max_filled_space) {
  508. $zip_file = new pclZip($uploaded_file['tmp_name']);
  509. // Check the zip content (real size and file extension)
  510. if (file_exists($uploaded_file)) {
  511. $zip_content_array = $zip_file->listContent();
  512. $ok_scorm = false;
  513. foreach ($zip_content_array as & $this_content) {
  514. if (preg_match('~.(php.*|phtml)$~i', $this_content['filename'])) {
  515. return api_failure::set_failure('php_file_in_zip_file');
  516. } elseif (stristr($this_content['filename'], 'imsmanifest.xml')) {
  517. $ok_scorm = true;
  518. } elseif (stristr($this_content['filename'], 'LMS')) {
  519. $ok_plantyn_scorm1 = true;
  520. } elseif (stristr($this_content['filename'], 'REF')) {
  521. $ok_plantyn_scorm2 = true;
  522. } elseif (stristr($this_content['filename'], 'SCO')) {
  523. $ok_plantyn_scorm3 = true;
  524. } elseif (stristr($this_content['filename'], 'AICC')) {
  525. $ok_aicc_scorm = true;
  526. }
  527. $realFileSize += $this_content['size'];
  528. }
  529. if (($ok_plantyn_scorm1 && $ok_plantyn_scorm2 && $ok_plantyn_scorm3) || $ok_aicc_scorm) {
  530. $ok_scorm = true;
  531. }
  532. if (!$ok_scorm && defined('CHECK_FOR_SCORM') && CHECK_FOR_SCORM) {
  533. return api_failure::set_failure('not_scorm_content');
  534. }
  535. if (!enough_size($realFileSize, $base_work_dir, $max_filled_space)) {
  536. return api_failure::set_failure('not_enough_space');
  537. }
  538. // It happens on Linux that $upload_path sometimes doesn't start with '/'
  539. if ($upload_path[0] != '/') {
  540. $upload_path = '/'.$upload_path;
  541. }
  542. if ($upload_path[strlen($upload_path) - 1] == '/') {
  543. $upload_path=substr($upload_path, 0, -1);
  544. }
  545. /* Uncompressing phase */
  546. /*
  547. The first version, using OS unzip, is not used anymore
  548. because it does not return enough information.
  549. We need to process each individual file in the zip archive to
  550. - add it to the database
  551. - parse & change relative html links
  552. */
  553. if (PHP_OS == 'Linux' && ! get_cfg_var('safe_mode') && false) { // *** UGent, changed by OC ***
  554. // Shell Method - if this is possible, it gains some speed
  555. exec("unzip -d \"".$base_work_dir.$upload_path."/\"".$uploaded_file['name']." " .$uploaded_file['tmp_name']);
  556. } else {
  557. // PHP method - slower...
  558. $save_dir = getcwd();
  559. chdir($base_work_dir.$upload_path);
  560. $unzippingState = $zip_file->extract();
  561. for ($j=0; $j < count($unzippingState); $j++) {
  562. $state = $unzippingState[$j];
  563. // Fix relative links in html files
  564. $extension = strrchr($state['stored_filename'], '.');
  565. }
  566. if ($dir = @opendir($base_work_dir.$upload_path)) {
  567. while ($file = readdir($dir)) {
  568. if ($file != '.' && $file != '..') {
  569. $filetype = 'file';
  570. if (is_dir($base_work_dir.$upload_path.'/'.$file)) $filetype = 'folder';
  571. $safe_file = replace_dangerous_char($file, 'strict');
  572. @rename($base_work_dir.$upload_path.'/'.$file,$base_work_dir.$upload_path.'/'.$safe_file);
  573. set_default_settings($upload_path, $safe_file,$filetype);
  574. }
  575. }
  576. closedir($dir);
  577. }
  578. chdir($save_dir); // Back to previous dir position
  579. }
  580. }
  581. return true;
  582. }
  583. /**
  584. * Manages all the unzipping process of an uploaded document
  585. * This uses the item_property table for properties of documents
  586. *
  587. * @author Hugues Peeters <hugues.peeters@claroline.net>
  588. * @author Bert Vanderkimpen
  589. *
  590. * @param array $uploaded_file - follows the $_FILES Structure
  591. * @param string $upload_path - destination of the upload.
  592. * This path is to append to $base_work_dir
  593. * @param string $base_work_dir - base working directory of the module
  594. * @param int $max_filled_space - amount of bytes to not exceed in the base
  595. * working directory
  596. * @param boolean Output switch. Optional. If no output not wanted on success, set to false.
  597. *
  598. * @return boolean true if it succeeds false otherwise
  599. */
  600. function unzip_uploaded_document($uploaded_file, $upload_path, $base_work_dir, $max_filled_space, $output = true, $to_group_id = 0) {
  601. global $_course;
  602. global $_user;
  603. global $to_user_id;
  604. global $to_group_id;
  605. $zip_file = new pclZip($uploaded_file['tmp_name']);
  606. // Check the zip content (real size and file extension)
  607. $zip_content_array = (array)$zip_file->listContent();
  608. foreach($zip_content_array as & $this_content) {
  609. $real_filesize += $this_content['size'];
  610. }
  611. if (!enough_space($real_filesize, $max_filled_space)) {
  612. Display::display_error_message(get_lang('UplNotEnoughSpace'));
  613. return false;
  614. }
  615. // It happens on Linux that $upload_path sometimes doesn't start with '/'
  616. if ($upload_path[0] != '/') {
  617. $upload_path='/'.$upload_path;
  618. }
  619. /* Uncompressing phase */
  620. // Get into the right directory
  621. $save_dir = getcwd();
  622. chdir($base_work_dir.$upload_path);
  623. // We extract using a callback function that "cleans" the path
  624. $unzipping_state = $zip_file->extract(PCLZIP_CB_PRE_EXTRACT, 'clean_up_files_in_zip');
  625. // Add all documents in the unzipped folder to the database
  626. add_all_documents_in_folder_to_database($_course, $_user['user_id'], $base_work_dir ,$upload_path == '/' ? '' : $upload_path, $to_group_id);
  627. //Display::display_normal_message(get_lang('UplZipExtractSuccess'));
  628. return true;
  629. }
  630. /**
  631. * This function is a callback function that is used while extracting a zipfile
  632. * http://www.phpconcept.net/pclzip/man/en/index.php?options-pclzip_cb_pre_extract
  633. *
  634. * @param $p_event
  635. * @param $p_header
  636. * @return 1 (If the function returns 1, then the extraction is resumed)
  637. */
  638. function clean_up_files_in_zip($p_event, &$p_header) {
  639. $res = clean_up_path($p_header['filename']);
  640. return $res;
  641. }
  642. /**
  643. * This function cleans up a given path
  644. * by eliminating dangerous file names and cleaning them
  645. *
  646. * @param string $path
  647. * @return $path
  648. * @see disable_dangerous_file()
  649. * @see replace_dangerous_char()
  650. */
  651. function clean_up_path(&$path) {
  652. // Split the path in folders and files
  653. $path_array = explode('/', $path);
  654. // Clean up every foler and filename in the path
  655. foreach ($path_array as $key => & $val) {
  656. // We don't want to lose the dots in ././folder/file (cfr. zipfile)
  657. if ($val != '.') {
  658. $val = disable_dangerous_file(replace_dangerous_char($val));
  659. }
  660. }
  661. // Join the "cleaned" path (modified in-place as passed by reference)
  662. $path = implode('/', $path_array);
  663. $res = filter_extension($path);
  664. return $res;
  665. }
  666. /**
  667. * Checks if the file is dangerous, based on extension and/or mimetype.
  668. * The list of extensions accepted/rejected can be found from
  669. * api_get_setting('upload_extensions_exclude') and api_get_setting('upload_extensions_include')
  670. * @param string filename passed by reference. The filename will be modified if filter rules say so! (you can include path but the filename should look like 'abc.html')
  671. * @return int 0 to skip file, 1 to keep file
  672. */
  673. function filter_extension(&$filename) {
  674. if (substr($filename, -1) == '/') {
  675. return 1; // Authorize directories
  676. }
  677. $blacklist = api_get_setting('upload_extensions_list_type');
  678. if ($blacklist != 'whitelist') { // if = blacklist
  679. $extensions = split(';', strtolower(api_get_setting('upload_extensions_blacklist')));
  680. $skip = api_get_setting('upload_extensions_skip');
  681. $ext = strrchr($filename, '.');
  682. $ext = substr($ext, 1);
  683. if (empty($ext)) {
  684. return 1; // We're in blacklist mode, so accept empty extensions
  685. }
  686. if (in_array(strtolower($ext), $extensions)) {
  687. if ($skip == 'true') {
  688. return 0;
  689. } else {
  690. $new_ext = api_get_setting('upload_extensions_replace_by');
  691. $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
  692. return 1;
  693. }
  694. } else {
  695. return 1;
  696. }
  697. } else {
  698. $extensions = split(';', strtolower(api_get_setting('upload_extensions_whitelist')));
  699. $skip = api_get_setting('upload_extensions_skip');
  700. $ext = strrchr($filename, '.');
  701. $ext = substr($ext, 1);
  702. if (empty($ext)) {
  703. return 1; // Accept empty extensions
  704. }
  705. if (!in_array(strtolower($ext), $extensions)) {
  706. if ($skip == 'true') {
  707. return 0;
  708. } else {
  709. $new_ext = api_get_setting('upload_extensions_replace_by');
  710. $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
  711. return 1;
  712. }
  713. } else {
  714. return 1;
  715. }
  716. }
  717. }
  718. /**
  719. * Adds a new document to the database
  720. *
  721. * @param array $_course
  722. * @param string $path
  723. * @param string $filetype
  724. * @param int $filesize
  725. * @param string $title
  726. * @return id if inserted document
  727. */
  728. function add_document($_course, $path, $filetype, $filesize, $title, $comment = null, $readonly = 0) {
  729. $session_id = api_get_session_id();
  730. $table_document = Database::get_course_table(TABLE_DOCUMENT, $_course['dbName']);
  731. $sql = "INSERT INTO $table_document
  732. (`path`, `filetype`, `size`, `title`, `comment`, `readonly`, `session_id`)
  733. VALUES ('$path','$filetype','$filesize','".
  734. Database::escape_string(htmlspecialchars($title, ENT_QUOTES, api_get_system_encoding()))."', '$comment', $readonly, $session_id)";
  735. if (Database::query($sql)) {
  736. //display_message("Added to database (id ".Database::insert_id().")!");
  737. return Database::insert_id();
  738. } else {
  739. //display_error("The uploaded file could not be added to the database (".Database::error().")!");
  740. return false;
  741. }
  742. }
  743. /**
  744. * Updates an existing document in the database
  745. * as the file exists, we only need to change the size
  746. *
  747. * @param array $_course
  748. * @param int $document_id
  749. * @param int $filesize
  750. * @param int $readonly
  751. * @return boolean true /false
  752. */
  753. function update_existing_document($_course, $document_id, $filesize, $readonly = 0) {
  754. $document_table = Database::get_course_table(TABLE_DOCUMENT, $_course['dbName']);
  755. $sql = "UPDATE $document_table SET size = '$filesize' , readonly = '$readonly' WHERE id='$document_id'";
  756. if (Database::query($sql)) {
  757. return true;
  758. } else {
  759. return false;
  760. }
  761. }
  762. /**
  763. * This function updates the last_edit_date, last edit user id on all folders in a given path
  764. *
  765. * @param array $_course
  766. * @param string $path
  767. * @param int $user_id
  768. */
  769. function item_property_update_on_folder($_course, $path, $user_id) {
  770. //display_message("Start update_lastedit_on_folder");
  771. // If we are in the root, just return... no need to update anything
  772. if ($path == '/') {
  773. return;
  774. }
  775. // If the given path ends with a / we remove it
  776. $endchar = substr($path, strlen($path) - 1, 1);
  777. if ($endchar == '/') {
  778. $path = substr($path, 0, strlen($path) - 1);
  779. }
  780. $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY, $_course['dbName']);
  781. // Get the time
  782. $time = date('Y-m-d H:i:s', time());
  783. // Det all paths in the given path
  784. // /folder/subfolder/subsubfolder/file
  785. // if file is updated, subsubfolder, subfolder and folder are updated
  786. $exploded_path = explode('/', $path);
  787. foreach ($exploded_path as $key => & $value) {
  788. // We don't want a slash before our first slash
  789. if ($key != 0) {
  790. $newpath .= '/'.$value;
  791. //echo 'path= '.$newpath.'<br />';
  792. // Select ID of given folder
  793. $folder_id = DocumentManager::get_document_id($_course, $newpath);
  794. if ($folder_id) {
  795. $sql = "UPDATE $TABLE_ITEMPROPERTY SET `lastedit_date`='$time',`lastedit_type`='DocumentInFolderUpdated', `lastedit_user_id`='$user_id' WHERE tool='".TOOL_DOCUMENT."' AND ref='$folder_id'";
  796. Database::query($sql);
  797. }
  798. }
  799. }
  800. }
  801. /**
  802. * Returns the directory depth of the file.
  803. *
  804. * @author Olivier Cauberghe <olivier.cauberghe@ugent.be>
  805. * @param path+filename eg: /main/document/document.php
  806. * @return The directory depth
  807. */
  808. function get_levels($filename) {
  809. $levels=explode('/', $filename);
  810. if (empty($levels[count($levels) - 1])) {
  811. unset($levels[count($levels) - 1]);
  812. }
  813. return count($levels);
  814. }
  815. /**
  816. * Adds file to document table in database
  817. * @deprecated, use file_set_default_settings instead
  818. *
  819. * @author Olivier Cauberghe <olivier.cauberghe@ugent.be>
  820. * @param path,filename
  821. * @action Adds an entry to the document table with the default settings.
  822. */
  823. function set_default_settings($upload_path, $filename, $filetype = 'file') {
  824. global $dbTable,$_configuration;
  825. global $default_visibility;
  826. if (!$default_visibility) {
  827. $default_visibility = 'v';
  828. }
  829. $upload_path = str_replace('\\', '/', $upload_path);
  830. $upload_path = str_replace('//', '/', $upload_path);
  831. if ($upload_path == '/') {
  832. $upload_path='';
  833. } elseif (!empty($upload_path) && $upload_path[0] != '/') {
  834. $upload_path="/$upload_path";
  835. }
  836. $endchar = substr($filename, strlen($filename) - 1, 1);
  837. if ($endchar == '/') {
  838. $filename = substr($filename, 0, -1);
  839. }
  840. // $dbTable already has `backticks`!
  841. //$query = "select count(*) as bestaat from `$dbTable` where path='$upload_path/$filename'";
  842. $query = "select count(*) as bestaat from $dbTable where path='$upload_path/$filename'";
  843. $result = Database::query($query);
  844. $row = Database::fetch_array($result);
  845. if ($row['bestaat'] > 0) {
  846. //$query = "update `$dbTable` set path='$upload_path/$filename',visibility='$default_visibility', filetype='$filetype' where path='$upload_path/$filename'";
  847. $query = "update $dbTable set path='$upload_path/$filename',visibility='$default_visibility', filetype='$filetype' where path='$upload_path/$filename'";
  848. } else {
  849. //$query = "INSERT INTO `$dbTable` (path,visibility,filetype) VALUES('$upload_path/$filename','$default_visibility','$filetype')";
  850. $query = "INSERT INTO $dbTable (path,visibility,filetype) VALUES('$upload_path/$filename','$default_visibility','$filetype')";
  851. }
  852. Database::query($query);
  853. }
  854. /**
  855. * Retrieves the image path list in a html file
  856. *
  857. * @author Hugues Peeters <hugues.peeters@claroline.net>
  858. * @param string $html_file
  859. * @return array - images path list
  860. */
  861. function search_img_from_html($html_file) {
  862. $img_path_list = array();
  863. if (!$fp = fopen($html_file, 'r')) {
  864. return ;
  865. }
  866. // Aearch and store occurences of the <img> tag in an array
  867. $size_file = (filesize($html_file) === 0) ? 1 : filesize($html_file);
  868. if (isset($fp) && $fp !== false) {
  869. $buffer = fread($fp, $size_file);
  870. if (strlen($buffer) >= 0 && $buffer !== false) {
  871. //
  872. } else {
  873. die('<center>Can not read file.</center>');
  874. }
  875. } else {
  876. die('<center>Can not read file.</center>');
  877. }
  878. $matches = array();
  879. if (preg_match_all('~<[[:space:]]*img[^>]*>~i', $buffer, $matches)) {
  880. $img_tag_list = $matches[0];
  881. }
  882. fclose ($fp);
  883. unset($buffer);
  884. // Search the image file path from all the <IMG> tag detected
  885. if (sizeof($img_tag_list) > 0) {
  886. foreach ($img_tag_list as & $this_img_tag) {
  887. if (preg_match('~src[[:space:]]*=[[:space:]]*[\"]{1}([^\"]+)[\"]{1}~i', $this_img_tag, $matches)) {
  888. $img_path_list[] = $matches[1];
  889. }
  890. }
  891. $img_path_list = array_unique($img_path_list); // Remove duplicate entries
  892. }
  893. return $img_path_list;
  894. }
  895. /**
  896. * Creates a new directory trying to find a directory name
  897. * that doesn't already exist
  898. * (we could use unique_name() here...)
  899. *
  900. * @author Hugues Peeters <hugues.peeters@claroline.net>
  901. * @author Bert Vanderkimpen
  902. * @param array $_course current course information
  903. * @param int $user_id current user id
  904. * @param string $desiredDirName complete path of the desired name
  905. * @return string actual directory name if it succeeds,
  906. * boolean false otherwise
  907. */
  908. function create_unexisting_directory($_course, $user_id, $to_group_id, $to_user_id, $base_work_dir, $desired_dir_name, $title = null, $visibility = '') {
  909. $nb = '';
  910. while (file_exists($base_work_dir.$desired_dir_name.$nb)) {
  911. $nb += 1;
  912. }
  913. if($title == null) {
  914. $title = basename($desired_dir_name);
  915. }
  916. if (mkdir($base_work_dir.$desired_dir_name.$nb, api_get_permissions_for_new_directories(), true)) {
  917. // Check if pathname already exists inside document table
  918. $tbl_document = Database::get_course_table(TABLE_DOCUMENT, $_course['dbName']);
  919. $sql = "SELECT path FROM $tbl_document WHERE path='".$desired_dir_name.$nb."'";
  920. $rs = Database::query($sql);
  921. if (Database::num_rows($rs) == 0) {
  922. $document_id = add_document($_course, $desired_dir_name.$nb, 'folder', 0, $title);
  923. if ($document_id) {
  924. // Update document item_property
  925. $current_session_id = api_get_session_id();
  926. if ($visibility !== '') {
  927. $visibilities = array(0 => 'invisible', 1 => 'visible', 2 => 'delete');
  928. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, $visibilities[$visibility], $user_id, $to_group_id, $to_user_id, null, null, $current_session_id);
  929. } else {
  930. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'FolderCreated', $user_id, $to_group_id, $to_user_id, null, null, $current_session_id);
  931. }
  932. return $desired_dir_name.$nb;
  933. }
  934. } else {
  935. return false;
  936. }
  937. } else {
  938. return false;
  939. }
  940. }
  941. /**
  942. * Handles uploaded missing images
  943. *
  944. * @author Hugues Peeters <hugues.peeters@claroline.net>
  945. * @author Bert Vanderkimpen
  946. * @param array $_course
  947. * @param array $uploaded_file_collection - follows the $_FILES Structure
  948. * @param string $base_work_dir
  949. * @param string $missing_files_dir
  950. * @param int $user_id
  951. * @param int $max_filled_space
  952. */
  953. function move_uploaded_file_collection_into_directory($_course, $uploaded_file_collection, $base_work_dir, $missing_files_dir, $user_id, $to_group_id, $to_user_id, $max_filled_space) {
  954. $number_of_uploaded_images = count($uploaded_file_collection['name']);
  955. for ($i = 0; $i < $number_of_uploaded_images; $i++) {
  956. $missing_file['name'] = $uploaded_file_collection['name'][$i];
  957. $missing_file['type'] = $uploaded_file_collection['type'][$i];
  958. $missing_file['tmp_name'] = $uploaded_file_collection['tmp_name'][$i];
  959. $missing_file['error'] = $uploaded_file_collection['error'][$i];
  960. $missing_file['size'] = $uploaded_file_collection['size'][$i];
  961. $upload_ok = process_uploaded_file($missing_file);
  962. if ($upload_ok) {
  963. $new_file_list[] = handle_uploaded_document($_course, $missing_file, $base_work_dir, $missing_files_dir, $user_id, $to_group_id, $to_user_id, $max_filled_space, 0, 'overwrite');
  964. }
  965. unset($missing_file);
  966. }
  967. return $new_file_list;
  968. }
  969. /**
  970. * Opens the old html file and replace the src path into the img tag
  971. * This also works for files in subdirectories.
  972. * @param $original_img_path is an array
  973. * @param $new_img_path is an array
  974. */
  975. function replace_img_path_in_html_file($original_img_path, $new_img_path, $html_file) {
  976. global $_course;
  977. // Open the file
  978. $fp = fopen($html_file, 'r');
  979. $buffer = fread($fp, filesize($html_file));
  980. // Fix the image tags
  981. for ($i = 0, $fileNb = count($original_img_path); $i < $fileNb ; $i++) {
  982. $replace_what = $original_img_path[$i];
  983. // We only need the directory and the filename /path/to/file_html_files/missing_file.gif -> file_html_files/missing_file.gif
  984. $exploded_file_path = explode('/', $new_img_path[$i]);
  985. $replace_by = $exploded_file_path[count($exploded_file_path) - 2].'/'.$exploded_file_path[count($exploded_file_path) - 1];
  986. //$message .= "Element [$i] <b>" . $replace_what . "</b> replaced by <b>" . $replace_by . "</b><br />"; //debug
  987. //api_display_debug_info($message);
  988. $buffer = str_replace($replace_what, $replace_by, $buffer);
  989. }
  990. $new_html_content .= $buffer;
  991. @fclose($fp);
  992. // Write the resulted new file
  993. if (!$fp = fopen($html_file, 'w')) {
  994. return;
  995. }
  996. if (!fwrite($fp, $new_html_content)) {
  997. return;
  998. }
  999. }
  1000. /**
  1001. * Creates a file containing an html redirection to a given url
  1002. *
  1003. * @author Hugues Peeters <hugues.peeters@claroline.net>
  1004. * @param string $file_path
  1005. * @param string $url
  1006. * @return void
  1007. */
  1008. function create_link_file($file_path, $url) {
  1009. $file_content = '<html>'
  1010. .'<head>'
  1011. .'<meta http-equiv="refresh" content="1;url='.$url.'">'
  1012. .'</head>'
  1013. .'<body>'
  1014. .'</body>'
  1015. .'</html>';
  1016. if (file_exists($file_path)) {
  1017. if (!($fp = fopen ($file_path, 'w'))) {
  1018. return false;
  1019. }
  1020. return fwrite($fp, $file_content);
  1021. }
  1022. }
  1023. /**
  1024. * Opens html file $full_file_name;
  1025. * Parses the hyperlinks; and
  1026. * Writes the result back in the html file.
  1027. *
  1028. * @author Roan Embrechts
  1029. * @version 0.1
  1030. */
  1031. function api_replace_links_in_html($upload_path, $full_file_name) {
  1032. // Open the file
  1033. if (file_exists($full_file_name)) {
  1034. $fp = fopen($full_file_name, 'r');
  1035. $buffer = fread ($fp, filesize ($full_file_name));
  1036. // Parse the contents
  1037. $new_html_content = api_replace_links_in_string($upload_path, $buffer);
  1038. // Write the result
  1039. $fp = fopen($full_file_name, 'w');
  1040. fwrite($fp, $new_html_content);
  1041. }
  1042. }
  1043. /**
  1044. @deprecated, use api_replace_parameter instead
  1045. Parse the buffer string provided as parameter
  1046. Replace the a href tags so they are displayed correctly.
  1047. - works for files in root and subdirectories
  1048. - replace relative hyperlinks to use showinframes.php?file= ...
  1049. - add target="_self" to all absolute hyperlinks
  1050. - leave local anchors untouched (e.g. #CHAPTER1)
  1051. - leave links with download.php and showinframes.php untouched
  1052. @author Roan Embrechts
  1053. @version 0.6
  1054. */
  1055. function api_replace_links_in_string($upload_path, $buffer) {
  1056. // Search for hyperlinks
  1057. $matches = array();
  1058. if (preg_match_all('/<a[\s]*href[^<]*>/i', $buffer, $matches)) {
  1059. $tag_list = $matches[0];
  1060. }
  1061. // Search the filepath of all detected <a href> tags
  1062. if (sizeof($tag_list) > 0) {
  1063. $file_path_list = array();
  1064. $href_list = array();
  1065. foreach ($tag_list as & $this_tag) {
  1066. /* Match case insensitive, the stuff between the two ~ :
  1067. a href = <exactly one quote><one or more non-quotes><exactly one ">
  1068. e.g. a href="www.google.be", A HREF = "info.html"
  1069. to match ["] escape the " or else PHP interprets it
  1070. [\"]{1} --> matches exactly one "
  1071. + 1 or more (like * is 0 or more)
  1072. [\s]* matches whitespace
  1073. $matches contains captured subpatterns
  1074. the only one here is ([^\"]+) --> matches[1]
  1075. */
  1076. if (preg_match("~a href[\s]*=[\s]*[\"]{1}([^\"]+)[\"]{1}~i", $this_tag, $matches)) {
  1077. $file_path_list[] = $matches[1]; // older
  1078. $href_list[] = $matches[0]; // to also add target="_self"
  1079. }
  1080. }
  1081. }
  1082. // Replace the original hyperlinks by the correct ones
  1083. for ($count = 0; $count < sizeof($href_list); $count++) {
  1084. $replace_what[$count] = $href_list[$count];
  1085. $is_absolute_hyperlink = strpos($replace_what[$count], 'http');
  1086. $is_local_anchor = strpos($replace_what[$count], "#");
  1087. if (!$is_absolute_hyperlink && !$is_local_anchor) {
  1088. // This is a relative hyperlink
  1089. if ((strpos($replace_what[$count], 'showinframes.php') === false) && (strpos($replace_what[$count], 'download.php') === false)) {
  1090. // Fix the link to use showinframes.php
  1091. $replace_by[$count] = 'a href = "showinframes.php?file='.$upload_path.'/'.$file_path_list[$count].'" target="_self"';
  1092. } else {
  1093. // URL has been already fixed, leave it as is
  1094. $replace_by[$count] = $replace_what[$count];
  1095. }
  1096. } elseif ($is_absolute_hyperlink) {
  1097. $replace_by[$count] = 'a href="'.$file_path_list[$count].'" target ="_self"';
  1098. } else {
  1099. // Don't change anything
  1100. $replace_by[$count] = $replace_what[$count];
  1101. }
  1102. //Display::display_normal_message('Link replaced by ' . $replace_by[$count]); // debug
  1103. }
  1104. $buffer = str_replace($replace_what, $replace_by, $buffer);
  1105. return $buffer;
  1106. }
  1107. /**
  1108. EXPERIMENTAL - function seems to work, needs more testing
  1109. @param $upload_path is the path where the document is stored, like "/archive/"
  1110. if it is the root level, the function expects "/"
  1111. otherwise "/path/"
  1112. This function parses all tags with $param_name parameters.
  1113. so the tags are displayed correctly.
  1114. --------------
  1115. Algorithm v1.0
  1116. --------------
  1117. given a string and a parameter,
  1118. * OK find all tags in that string with the specified parameter (like href or src)
  1119. * OK for every one of these tags, find the src|href|... part to edit it
  1120. * OK change the src|href|... part to use download.php (or showinframes.php)
  1121. * OK do some special stuff for hyperlinks
  1122. Exceptions
  1123. * OK if download.php or showinframes.php is already in the tag, leave it alone
  1124. * OK if mailto is in the tag, leave it alone
  1125. * OK if the src|href param contains http://, it's absolute --> leave it alone
  1126. Special for hyperlinks (a href...)
  1127. * OK add target="_self"
  1128. * OK use showinframes.php instead of download.php
  1129. @author Roan Embrechts
  1130. @version 1.1
  1131. */
  1132. function api_replace_parameter($upload_path, $buffer, $param_name = 'src') {
  1133. // Search for tags with $param_name as a parameter
  1134. /*
  1135. // [\s]* matches whitespace
  1136. // [\"=a-z] matches ", = and a-z
  1137. // ([\s]*[a-z]*)* matches all whitespace and normal alphabet
  1138. // characters a-z combinations but seems too slow
  1139. // perhaps ([\s]*[a-z]*) a maximum number of times ?
  1140. // [\s]*[a-z]*[\s]* matches many tags
  1141. // the ending "i" means to match case insensitive (a matches a and A)
  1142. */
  1143. $matches = array();
  1144. if (preg_match_all('/<[a-z]+[^<]*'.$param_name.'[^<]*>/i', $buffer, $matches)) {
  1145. $tag_list = $matches[0];
  1146. }
  1147. // Search the filepath of parameter $param_name in all detected tags
  1148. if (sizeof($tag_list) > 0) {
  1149. $file_path_list = array();
  1150. $href_list = array();
  1151. foreach ($tag_list as & $this_tag) {
  1152. //Display::display_normal_message(htmlentities($this_tag)); //debug
  1153. if ( preg_match("~".$param_name."[\s]*=[\s]*[\"]{1}([^\"]+)[\"]{1}~i", $this_tag, $matches)) {
  1154. $file_path_list[] = $matches[1]; // older
  1155. $href_list[] = $matches[0]; // to also add target="_self"
  1156. }
  1157. }
  1158. }
  1159. // Replace the original tags by the correct ones
  1160. for ($count = 0; $count < sizeof($href_list); $count++) {
  1161. $replace_what[$count] = $href_list[$count];
  1162. $is_absolute_hyperlink = strpos($replace_what[$count], 'http');
  1163. $is_local_anchor = strpos($replace_what[$count], '#');
  1164. if (!$is_absolute_hyperlink && !$is_local_anchor) {
  1165. if ((strpos($replace_what[$count], 'showinframes.php') === false)
  1166. && (strpos($replace_what[$count], 'download.php') === false)
  1167. && (strpos($replace_what[$count], 'mailto') === false)) {
  1168. // Fix the link to use download.php or showinframes.php
  1169. if (preg_match("/<a([\s]*[\"\/:'=a-z0-9]*){5}href[^<]*>/i", $tag_list[$count])) {
  1170. $replace_by[$count] = " $param_name =\"showinframes.php?file=" . $upload_path.$file_path_list[$count]."\" target=\"_self\" ";
  1171. } else {
  1172. $replace_by[$count] = " $param_name =\"download.php?doc_url=" . $upload_path.$file_path_list[$count]."\" ";
  1173. }
  1174. } else {
  1175. // "mailto" or url already fixed, leave as is
  1176. //$message .= "Already fixed or contains mailto: ";
  1177. $replace_by[$count] = $replace_what[$count];
  1178. }
  1179. } elseif ($is_absolute_hyperlink) {
  1180. //$message .= "Absolute hyperlink, don't change, add target=_self: ";
  1181. $replace_by[$count] = " $param_name=\"" . $file_path_list[$count] . "\" target =\"_self\"";
  1182. } else {
  1183. // Don't change anything
  1184. //$message .= "Local anchor, don't change: ";
  1185. $replace_by[$count] = $replace_what[$count];
  1186. }
  1187. //$message .= "In tag $count, <b>" . htmlentities($tag_list[$count])
  1188. // . "</b>, parameter <b>" . $replace_what[$count] . "</b> replaced by <b>" . $replace_by[$count] . "</b><br>"; //debug
  1189. }
  1190. //if (isset($message) && $message == true) api_display_debug_info($message); //debug
  1191. $buffer = str_replace($replace_what, $replace_by, $buffer);
  1192. return $buffer;
  1193. }
  1194. /**
  1195. * Checks the extension of a file, if it's .htm or .html
  1196. * we use search_img_from_html to get all image paths in the file
  1197. *
  1198. * @param string $file
  1199. * @return array paths
  1200. * @see check_for_missing_files() uses search_img_from_html()
  1201. */
  1202. function check_for_missing_files($file) {
  1203. if (strrchr($file, '.') == '.htm' || strrchr($file, '.') == '.html') {
  1204. $img_file_path = search_img_from_html($file);
  1205. return $img_file_path;
  1206. }
  1207. return false;
  1208. }
  1209. /**
  1210. * This function builds a form that asks for the missing images in a html file
  1211. * maybe we should do this another way?
  1212. *
  1213. * @param array $missing_files
  1214. * @param string $upload_path
  1215. * @param string $file_name
  1216. * @return string the form
  1217. */
  1218. function build_missing_files_form($missing_files, $upload_path, $file_name) {
  1219. // Do we need a / or not?
  1220. $added_slash = ($upload_path == '/') ? '' : '/';
  1221. // Build the form
  1222. $form .= "<p><strong>".get_lang('MissingImagesDetected')."</strong></p>\n"
  1223. ."<form method=\"post\" action=\"".api_get_self()."\" enctype=\"multipart/form-data\">\n"
  1224. // Related_file is the path to the file that has missing images
  1225. ."<input type=\"hidden\" name=\"related_file\" value=\"".$upload_path.$added_slash.$file_name."\" />\n"
  1226. ."<input type=\"hidden\" name=\"upload_path\" value=\"".$upload_path."\" />\n"
  1227. ."<table border=\"0\">\n";
  1228. foreach ($missing_files as & $this_img_file_path) {
  1229. $form .= "<tr>\n"
  1230. ."<td>".basename($this_img_file_path)." : </td>\n"
  1231. ."<td>"
  1232. ."<input type=\"file\" name=\"img_file[]\"/>"
  1233. ."<input type=\"hidden\" name=\"img_file_path[]\" value=\"".$this_img_file_path."\" />"
  1234. ."</td>\n"
  1235. ."</tr>\n";
  1236. }
  1237. $form .= "</table>\n"
  1238. ."<button type='submit' name=\"cancel_submit_image\" value=\"".get_lang('Cancel')."\" class=\"cancel\">".get_lang('Cancel')."</button>"
  1239. ."<button type='submit' name=\"submit_image\" value=\"".get_lang('Ok')."\" class=\"save\">".get_lang('Ok')."</button>"
  1240. ."</form>\n";
  1241. return $form;
  1242. }
  1243. /**
  1244. * This recursive function can be used during the upgrade process form older versions of Chamilo
  1245. * It crawls the given directory, checks if the file is in the DB and adds it if it's not
  1246. *
  1247. * @param string $base_work_dir
  1248. * @param string $current_path, needed for recursivity
  1249. */
  1250. function add_all_documents_in_folder_to_database($_course, $user_id, $base_work_dir, $current_path = '', $to_group_id = 0) {
  1251. $current_session_id = api_get_session_id();
  1252. $path = $base_work_dir.$current_path;
  1253. // Open dir
  1254. $handle = opendir($path);
  1255. if (is_dir($path)) {
  1256. // Run trough
  1257. while ($file = readdir($handle)) {
  1258. if ($file == '.' || $file == '..') continue;
  1259. $completepath = "$path/$file";
  1260. // Directory?
  1261. if (is_dir($completepath)) {
  1262. $title = get_document_title($file);
  1263. $safe_file = replace_dangerous_char($file);
  1264. @rename($path.'/'.$file, $path.'/'.$safe_file);
  1265. // If we can't find the file, add it
  1266. if (!DocumentManager::get_document_id($_course, $current_path.'/'.$safe_file)) {
  1267. $document_id = add_document($_course, $current_path.'/'.$safe_file, 'folder', 0, $title);
  1268. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $user_id, $to_group_id, null, null, null, $current_session_id);
  1269. //echo $current_path.'/'.$safe_file.' added!<br />';
  1270. }
  1271. // Recursive
  1272. add_all_documents_in_folder_to_database($_course,$user_id,$base_work_dir,$current_path.'/'.$safe_file, $to_group_id);
  1273. }
  1274. // File
  1275. else {
  1276. //Rename
  1277. $safe_file = disable_dangerous_file(replace_dangerous_char($file, 'strict'));
  1278. @rename($base_work_dir.$current_path.'/'.$file, $base_work_dir.$current_path.'/'.$safe_file);
  1279. if (!DocumentManager::get_document_id($_course, $current_path.'/'.$safe_file)) {
  1280. $title = get_document_title($file);
  1281. $size = filesize($base_work_dir.$current_path.'/'.$safe_file);
  1282. $document_id = add_document($_course, $current_path.'/'.$safe_file, 'file', $size, $title);
  1283. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $user_id, $to_group_id, null, null, null, $current_session_id);
  1284. //echo $current_path.'/'.$safe_file.' added!<br />';
  1285. }
  1286. }
  1287. }
  1288. }
  1289. }