fileUpload.lib.php 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971
  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 string $file_name Name of a file
  18. * @return string the filename phps'ized
  19. */
  20. function php2phps($file_name)
  21. {
  22. return preg_replace('/\.(php.?|phtml.?)(\.){0,1}.*$/i', '.phps', $file_name);
  23. }
  24. /**
  25. * Renames .htaccess & .HTACCESS to htaccess.txt
  26. *
  27. * @param string $filename
  28. * @return string
  29. */
  30. function htaccess2txt($filename)
  31. {
  32. return str_replace(array('.htaccess', '.HTACCESS'), array('htaccess.txt', 'htaccess.txt'), $filename);
  33. }
  34. /**
  35. * This function executes our safety precautions
  36. * more functions can be added
  37. *
  38. * @param string $filename
  39. * @return string
  40. * @see php2phps()
  41. * @see htaccess2txt()
  42. */
  43. function disable_dangerous_file($filename)
  44. {
  45. return htaccess2txt(php2phps($filename));
  46. }
  47. /**
  48. * Returns the name without extension, used for the title
  49. *
  50. * @param string $name
  51. * @return name without the extension
  52. */
  53. function get_document_title($name)
  54. {
  55. // If they upload .htaccess...
  56. $name = disable_dangerous_file($name);
  57. $ext = substr(strrchr($name, '.'), 0);
  58. return substr($name, 0, strlen($name) - strlen(strstr($name, $ext)));
  59. }
  60. /**
  61. * This function checks if the upload succeeded
  62. *
  63. * @param array $uploaded_file ($_FILES)
  64. * @return true if upload succeeded
  65. */
  66. function process_uploaded_file($uploaded_file, $show_output = true)
  67. {
  68. // Checking the error code sent with the file upload.
  69. if (isset($uploaded_file['error'])) {
  70. switch ($uploaded_file['error']) {
  71. case 1:
  72. // The uploaded file exceeds the upload_max_filesize directive in php.ini.
  73. if ($show_output) {
  74. Display::addFlash(
  75. Display::return_message(
  76. get_lang('UplExceedMaxServerUpload').ini_get('upload_max_filesize'),
  77. 'error'
  78. )
  79. );
  80. }
  81. return false;
  82. case 2:
  83. // The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
  84. // Not used at the moment, but could be handy if we want to limit the size of an upload
  85. // (e.g. image upload in html editor).
  86. $max_file_size = intval($_POST['MAX_FILE_SIZE']);
  87. if ($show_output) {
  88. Display::addFlash(
  89. Display::return_message(
  90. get_lang('UplExceedMaxPostSize').format_file_size($max_file_size),
  91. 'error'
  92. )
  93. );
  94. }
  95. return false;
  96. case 3:
  97. // The uploaded file was only partially uploaded.
  98. if ($show_output) {
  99. Display::addFlash(
  100. Display::return_message(
  101. get_lang('UplPartialUpload').' '.get_lang('PleaseTryAgain'),
  102. 'error'
  103. )
  104. );
  105. }
  106. return false;
  107. case 4:
  108. // No file was uploaded.
  109. if ($show_output) {
  110. Display::addFlash(
  111. Display::return_message(
  112. get_lang('UplNoFileUploaded').' '.get_lang('UplSelectFileFirst'),
  113. 'error'
  114. )
  115. );
  116. }
  117. return false;
  118. }
  119. }
  120. if (!file_exists($uploaded_file['tmp_name'])) {
  121. // No file was uploaded.
  122. if ($show_output) {
  123. Display::addFlash(Display::return_message(get_lang('UplUploadFailed'), 'error'));
  124. }
  125. return false;
  126. }
  127. if (file_exists($uploaded_file['tmp_name'])) {
  128. $filesize = filesize($uploaded_file['tmp_name']);
  129. if (empty($filesize)) {
  130. // No file was uploaded.
  131. if ($show_output) {
  132. Display::addFlash(
  133. Display::return_message(
  134. get_lang('UplUploadFailedSizeIsZero'),
  135. 'error'
  136. )
  137. );
  138. }
  139. return false;
  140. }
  141. }
  142. $course_id = api_get_course_id();
  143. //Checking course quota if we are in a course
  144. if (!empty($course_id)) {
  145. $max_filled_space = DocumentManager::get_course_quota();
  146. // Check if there is enough space to save the file
  147. if (!DocumentManager::enough_space($uploaded_file['size'], $max_filled_space)) {
  148. if ($show_output) {
  149. Display::addFlash(
  150. Display::return_message(
  151. get_lang('UplNotEnoughSpace'),
  152. 'error'
  153. )
  154. );
  155. }
  156. return false;
  157. }
  158. }
  159. // case 0: default: We assume there is no error, the file uploaded with success.
  160. return true;
  161. }
  162. /**
  163. * This function does the save-work for the documents.
  164. * It handles the uploaded file and adds the properties to the database
  165. * If unzip=1 and the file is a zipfile, it is extracted
  166. * If we decide to save ALL kinds of documents in one database,
  167. * we could extend this with a $type='document', 'scormdocument',...
  168. *
  169. * @param array $courseInfo
  170. * @param array $uploadedFile ($_FILES)
  171. * array(
  172. * 'name' => 'picture.jpg',
  173. * 'tmp_name' => '...', // absolute path
  174. * );
  175. * @param string $documentDir Example: /var/www/chamilo/courses/ABC/document
  176. * @param string $uploadPath Example: /folder1/folder2/
  177. * @param int $userId
  178. * @param int $groupId group.id
  179. * @param int $toUserId, NULL for everybody
  180. * @param int $unzip 1/0
  181. * @param string $whatIfFileExists overwrite, rename or warn if exists (default)
  182. * @param boolean $output Optional output parameter.
  183. * @param bool $onlyUploadFile
  184. * @param string $comment
  185. * @param int $sessionId
  186. * @param bool $treat_spaces_as_hyphens
  187. *
  188. * So far only use for unzip_uploaded_document function.
  189. * If no output wanted on success, set to false.
  190. * @param string $comment
  191. * @return string path of the saved file
  192. */
  193. function handle_uploaded_document(
  194. $courseInfo,
  195. $uploadedFile,
  196. $documentDir,
  197. $uploadPath,
  198. $userId,
  199. $groupId = 0,
  200. $toUserId = null,
  201. $unzip = 0,
  202. $whatIfFileExists = '',
  203. $output = true,
  204. $onlyUploadFile = false,
  205. $comment = null,
  206. $sessionId = null,
  207. $treat_spaces_as_hyphens = true
  208. ) {
  209. if (!$userId) {
  210. return false;
  211. }
  212. $userInfo = api_get_user_info();
  213. $uploadedFile['name'] = stripslashes($uploadedFile['name']);
  214. // Add extension to files without one (if possible)
  215. $uploadedFile['name'] = add_ext_on_mime($uploadedFile['name'], $uploadedFile['type']);
  216. if (empty($sessionId)) {
  217. $sessionId = api_get_session_id();
  218. } else {
  219. $sessionId = intval($sessionId);
  220. }
  221. $groupInfo = [];
  222. if (!empty($groupId)) {
  223. $groupInfo = GroupManager::get_group_properties($groupId);
  224. }
  225. // Just in case process_uploaded_file is not called
  226. $maxSpace = DocumentManager::get_course_quota();
  227. // Check if there is enough space to save the file
  228. if (!DocumentManager::enough_space($uploadedFile['size'], $maxSpace)) {
  229. if ($output) {
  230. Display::addFlash(Display::return_message(get_lang('UplNotEnoughSpace'), 'error'));
  231. }
  232. return false;
  233. }
  234. // If the want to unzip, check if the file has a .zip (or ZIP,Zip,ZiP,...) extension
  235. if ($unzip == 1 && preg_match('/.zip$/', strtolower($uploadedFile['name']))) {
  236. return unzip_uploaded_document(
  237. $courseInfo,
  238. $userInfo,
  239. $uploadedFile,
  240. $uploadPath,
  241. $documentDir,
  242. $maxSpace,
  243. $sessionId,
  244. $groupId,
  245. $output,
  246. $onlyUploadFile
  247. );
  248. } elseif ($unzip == 1 && !preg_match('/.zip$/', strtolower($uploadedFile['name']))) {
  249. // We can only unzip ZIP files (no gz, tar,...)
  250. if ($output) {
  251. Display::addFlash(
  252. Display::return_message(get_lang('UplNotAZip')." ".get_lang('PleaseTryAgain'), 'error')
  253. );
  254. }
  255. return false;
  256. } else {
  257. // Clean up the name, only ASCII characters should stay. (and strict)
  258. $cleanName = api_replace_dangerous_char($uploadedFile['name'], $treat_spaces_as_hyphens);
  259. // No "dangerous" files
  260. $cleanName = disable_dangerous_file($cleanName);
  261. // Checking file extension
  262. if (!filter_extension($cleanName)) {
  263. if ($output) {
  264. Display::addFlash(
  265. Display::return_message(get_lang('UplUnableToSaveFileFilteredExtension'), 'error')
  266. );
  267. }
  268. return false;
  269. } else {
  270. // If the upload path differs from / (= root) it will need a slash at the end
  271. if ($uploadPath != '/') {
  272. $uploadPath = $uploadPath.'/';
  273. }
  274. // Full path to where we want to store the file with trailing slash
  275. $whereToSave = $documentDir.$uploadPath;
  276. // At least if the directory doesn't exist, tell so
  277. if (!is_dir($whereToSave)) {
  278. if (!mkdir($whereToSave, api_get_permissions_for_new_directories())) {
  279. if ($output) {
  280. Display::addFlash(
  281. Display::return_message(
  282. get_lang('DestDirectoryDoesntExist').' ('.$uploadPath.')',
  283. 'error'
  284. )
  285. );
  286. }
  287. return false;
  288. }
  289. }
  290. // Just upload the file "as is"
  291. if ($onlyUploadFile) {
  292. $errorResult = moveUploadedFile($uploadedFile, $whereToSave.$cleanName);
  293. if ($errorResult) {
  294. return $whereToSave.$cleanName;
  295. } else {
  296. return $errorResult;
  297. }
  298. }
  299. /*
  300. Based in the clean name we generate a new filesystem name
  301. Using the session_id and group_id if values are not empty
  302. */
  303. $fileSystemName = DocumentManager::fixDocumentName(
  304. $cleanName,
  305. 'file',
  306. $courseInfo,
  307. $sessionId,
  308. $groupId
  309. );
  310. // Name of the document without the extension (for the title)
  311. $documentTitle = get_document_title($uploadedFile['name']);
  312. // Size of the uploaded file (in bytes)
  313. $fileSize = $uploadedFile['size'];
  314. // File permissions
  315. $filePermissions = api_get_permissions_for_new_files();
  316. // Example: /var/www/chamilo/courses/xxx/document/folder/picture.jpg
  317. $fullPath = $whereToSave.$fileSystemName;
  318. // Example: /folder/picture.jpg
  319. $filePath = $uploadPath.$fileSystemName;
  320. $docId = DocumentManager::get_document_id(
  321. $courseInfo,
  322. $filePath,
  323. $sessionId
  324. );
  325. $documentList = DocumentManager::getDocumentByPathInCourse(
  326. $courseInfo,
  327. $filePath //$filePath
  328. );
  329. // This means that the path already exists in this course.
  330. if (!empty($documentList) && $whatIfFileExists != 'overwrite') {
  331. //$found = false;
  332. // Checking if we are talking about the same course + session
  333. /*foreach ($documentList as $document) {
  334. if ($document['session_id'] == $sessionId) {
  335. $found = true;
  336. break;
  337. }
  338. }*/
  339. //if ($found == false) {
  340. $whatIfFileExists = 'rename';
  341. //}
  342. }
  343. // What to do if the target file exists
  344. switch ($whatIfFileExists) {
  345. // Overwrite the file if it exists
  346. case 'overwrite':
  347. // Check if the target file exists, so we can give another message
  348. $fileExists = file_exists($fullPath);
  349. if (moveUploadedFile($uploadedFile, $fullPath)) {
  350. chmod($fullPath, $filePermissions);
  351. if ($fileExists && $docId) {
  352. // UPDATE DATABASE
  353. $documentId = DocumentManager::get_document_id(
  354. $courseInfo,
  355. $filePath
  356. );
  357. if (is_numeric($documentId)) {
  358. // Update file size
  359. update_existing_document(
  360. $courseInfo,
  361. $documentId,
  362. $uploadedFile['size']
  363. );
  364. // Update document item_property
  365. api_item_property_update(
  366. $courseInfo,
  367. TOOL_DOCUMENT,
  368. $documentId,
  369. 'DocumentUpdated',
  370. $userId,
  371. $groupInfo,
  372. $toUserId,
  373. null,
  374. null,
  375. $sessionId
  376. );
  377. // Redo visibility
  378. api_set_default_visibility(
  379. $documentId,
  380. TOOL_DOCUMENT,
  381. null,
  382. $courseInfo
  383. );
  384. } else {
  385. // There might be cases where the file exists on disk but there is no registration of
  386. // that in the database
  387. // In this case, and if we are in overwrite mode, overwrite and create the db record
  388. $documentId = add_document(
  389. $courseInfo,
  390. $filePath,
  391. 'file',
  392. $fileSize,
  393. $documentTitle,
  394. $comment,
  395. 0,
  396. true,
  397. $groupId,
  398. $sessionId
  399. );
  400. if ($documentId) {
  401. // Put the document in item_property update
  402. api_item_property_update(
  403. $courseInfo,
  404. TOOL_DOCUMENT,
  405. $documentId,
  406. 'DocumentAdded',
  407. $userId,
  408. $groupInfo,
  409. $toUserId,
  410. null,
  411. null,
  412. $sessionId
  413. );
  414. // Redo visibility
  415. api_set_default_visibility(
  416. $documentId,
  417. TOOL_DOCUMENT,
  418. null,
  419. $courseInfo
  420. );
  421. }
  422. }
  423. // If the file is in a folder, we need to update all parent folders
  424. item_property_update_on_folder($courseInfo, $uploadPath, $userId);
  425. // Display success message with extra info to user
  426. if ($output) {
  427. Display::addFlash(
  428. Display::return_message(
  429. get_lang('UplUploadSucceeded').'<br /> '.$documentTitle.' '.get_lang('UplFileOverwritten'),
  430. 'confirmation',
  431. false
  432. )
  433. );
  434. }
  435. return $filePath;
  436. } else {
  437. // Put the document data in the database
  438. $documentId = add_document(
  439. $courseInfo,
  440. $filePath,
  441. 'file',
  442. $fileSize,
  443. $documentTitle,
  444. $comment,
  445. 0,
  446. true,
  447. $groupId,
  448. $sessionId
  449. );
  450. if ($documentId) {
  451. // Put the document in item_property update
  452. api_item_property_update(
  453. $courseInfo,
  454. TOOL_DOCUMENT,
  455. $documentId,
  456. 'DocumentAdded',
  457. $userId,
  458. $groupInfo,
  459. $toUserId,
  460. null,
  461. null,
  462. $sessionId
  463. );
  464. // Redo visibility
  465. api_set_default_visibility($documentId, TOOL_DOCUMENT, null, $courseInfo);
  466. }
  467. // If the file is in a folder, we need to update all parent folders
  468. item_property_update_on_folder($courseInfo, $uploadPath, $userId);
  469. // Display success message to user
  470. if ($output) {
  471. Display::addFlash(
  472. Display::return_message(
  473. get_lang('UplUploadSucceeded').'<br /> '.$documentTitle,
  474. 'confirmation',
  475. false
  476. )
  477. );
  478. }
  479. return $filePath;
  480. }
  481. } else {
  482. if ($output) {
  483. Display::addFlash(
  484. Display::return_message(
  485. get_lang('UplUnableToSaveFile'),
  486. 'error',
  487. false
  488. )
  489. );
  490. }
  491. return false;
  492. }
  493. break;
  494. case 'rename':
  495. // Rename the file if it exists
  496. // Always rename.
  497. $cleanName = DocumentManager::getUniqueFileName(
  498. $uploadPath,
  499. $cleanName,
  500. $courseInfo,
  501. $sessionId,
  502. $groupId
  503. );
  504. $fileSystemName = DocumentManager::fixDocumentName(
  505. $cleanName,
  506. 'file',
  507. $courseInfo,
  508. $sessionId,
  509. $groupId
  510. );
  511. $documentTitle = get_document_title($cleanName);
  512. $fullPath = $whereToSave.$fileSystemName;
  513. $filePath = $uploadPath.$fileSystemName;
  514. if (moveUploadedFile($uploadedFile, $fullPath)) {
  515. chmod($fullPath, $filePermissions);
  516. // Put the document data in the database
  517. $documentId = add_document(
  518. $courseInfo,
  519. $filePath,
  520. 'file',
  521. $fileSize,
  522. $documentTitle,
  523. $comment, // comment
  524. 0, // read only
  525. true, // save visibility
  526. $groupId,
  527. $sessionId
  528. );
  529. if ($documentId) {
  530. // Update document item_property
  531. api_item_property_update(
  532. $courseInfo,
  533. TOOL_DOCUMENT,
  534. $documentId,
  535. 'DocumentAdded',
  536. $userId,
  537. $groupInfo,
  538. $toUserId,
  539. null,
  540. null,
  541. $sessionId
  542. );
  543. // Redo visibility
  544. api_set_default_visibility($documentId, TOOL_DOCUMENT, null, $courseInfo);
  545. }
  546. // If the file is in a folder, we need to update all parent folders
  547. item_property_update_on_folder($courseInfo, $uploadPath, $userId);
  548. // Display success message to user
  549. if ($output) {
  550. Display::addFlash(
  551. Display::return_message(
  552. get_lang('UplUploadSucceeded').'<br />'.get_lang('UplFileSavedAs').' '.$documentTitle,
  553. 'success',
  554. false
  555. )
  556. );
  557. }
  558. return $filePath;
  559. } else {
  560. if ($output) {
  561. Display::addFlash(
  562. Display::return_message(
  563. get_lang('UplUnableToSaveFile'),
  564. 'error',
  565. false
  566. )
  567. );
  568. }
  569. return false;
  570. }
  571. break;
  572. default:
  573. // Only save the file if it doesn't exist or warn user if it does exist
  574. if (file_exists($fullPath) && $docId) {
  575. if ($output) {
  576. Display::addFlash(
  577. Display::return_message($cleanName.' '.get_lang('UplAlreadyExists'), 'error', false)
  578. );
  579. }
  580. } else {
  581. if (moveUploadedFile($uploadedFile, $fullPath)) {
  582. chmod($fullPath, $filePermissions);
  583. // Put the document data in the database
  584. $documentId = add_document(
  585. $courseInfo,
  586. $filePath,
  587. 'file',
  588. $fileSize,
  589. $documentTitle,
  590. $comment,
  591. 0,
  592. true,
  593. $groupId,
  594. $sessionId
  595. );
  596. if ($documentId) {
  597. // Update document item_property
  598. api_item_property_update(
  599. $courseInfo,
  600. TOOL_DOCUMENT,
  601. $documentId,
  602. 'DocumentAdded',
  603. $userId,
  604. $groupInfo,
  605. $toUserId,
  606. null,
  607. null,
  608. $sessionId
  609. );
  610. // Redo visibility
  611. api_set_default_visibility($documentId, TOOL_DOCUMENT, null, $courseInfo);
  612. }
  613. // If the file is in a folder, we need to update all parent folders
  614. item_property_update_on_folder(
  615. $courseInfo,
  616. $uploadPath,
  617. $userId
  618. );
  619. // Display success message to user
  620. if ($output) {
  621. Display::addFlash(
  622. Display::display_confirmation_message(
  623. get_lang('UplUploadSucceeded').'<br /> '.$documentTitle,
  624. false,
  625. true
  626. )
  627. );
  628. }
  629. return $filePath;
  630. } else {
  631. if ($output) {
  632. Display::addFlash(
  633. Display::return_message(
  634. get_lang('UplUnableToSaveFile'),
  635. 'error',
  636. false
  637. )
  638. );
  639. }
  640. return false;
  641. }
  642. }
  643. break;
  644. }
  645. }
  646. }
  647. }
  648. /**
  649. * @param string $file
  650. * @param string $storePath
  651. *
  652. * @return bool
  653. */
  654. function moveUploadedFile($file, $storePath)
  655. {
  656. $handleFromFile = isset($file['from_file']) && $file['from_file'] ? true : false;
  657. $moveFile = isset($file['move_file']) && $file['move_file'] ? true : false;
  658. if ($moveFile) {
  659. copy($file['tmp_name'], $storePath);
  660. }
  661. if ($handleFromFile) {
  662. return file_exists($file['tmp_name']);
  663. } else {
  664. return move_uploaded_file($file['tmp_name'], $storePath);
  665. }
  666. }
  667. /**
  668. * Checks if there is enough place to add a file on a directory
  669. * on the base of a maximum directory size allowed
  670. * deprecated: use enough_space instead!
  671. * @author Hugues Peeters <peeters@ipm.ucl.ac.be>
  672. * @param int $file_size Size of the file in byte
  673. * @param string $dir Path of the directory where the file should be added
  674. * @param int $max_dir_space Maximum size of the diretory in byte
  675. * @return boolean true if there is enough space, false otherwise
  676. * @see enough_size() uses dir_total_space() function
  677. */
  678. function enough_size($file_size, $dir, $max_dir_space)
  679. {
  680. // If the directory is the archive directory, safely ignore the size limit
  681. if (api_get_path(SYS_ARCHIVE_PATH) == $dir) {
  682. return true;
  683. }
  684. if ($max_dir_space) {
  685. $already_filled_space = dir_total_space($dir);
  686. if (($file_size + $already_filled_space) > $max_dir_space) {
  687. return false;
  688. }
  689. }
  690. return true;
  691. }
  692. /**
  693. * Computes the size already occupied by a directory and is subdirectories
  694. *
  695. * @author Hugues Peeters <peeters@ipm.ucl.ac.be>
  696. * @param string $dir_path Size of the file in byte
  697. * @return int Return the directory size in bytes
  698. */
  699. function dir_total_space($dir_path)
  700. {
  701. $save_dir = getcwd();
  702. chdir($dir_path);
  703. $handle = opendir($dir_path);
  704. $sumSize = 0;
  705. $dirList = array();
  706. while ($element = readdir($handle)) {
  707. if ($element == '.' || $element == '..') {
  708. continue; // Skip the current and parent directories
  709. }
  710. if (is_file($element)) {
  711. $sumSize += filesize($element);
  712. }
  713. if (is_dir($element)) {
  714. $dirList[] = $dir_path.'/'.$element;
  715. }
  716. }
  717. closedir($handle);
  718. if (sizeof($dirList) > 0) {
  719. foreach ($dirList as $j) {
  720. $sizeDir = dir_total_space($j); // Recursivity
  721. $sumSize += $sizeDir;
  722. }
  723. }
  724. chdir($save_dir); // Return to initial position
  725. return $sumSize;
  726. }
  727. /**
  728. * Tries to add an extension to files without extension
  729. * Some applications on Macintosh computers don't add an extension to the files.
  730. * This subroutine try to fix this on the basis of the MIME type sent
  731. * by the browser.
  732. *
  733. * Note : some browsers don't send the MIME Type (e.g. Netscape 4).
  734. * We don't have solution for this kind of situation
  735. *
  736. * @author Hugues Peeters <peeters@ipm.ucl.ac.be>
  737. * @author Bert Vanderkimpen
  738. * @param string $file_name Name of the file
  739. * @param string $file_type Type of the file
  740. * @return string File name
  741. */
  742. function add_ext_on_mime($file_name, $file_type)
  743. {
  744. // Check whether the file has an extension AND whether the browser has sent a MIME Type
  745. if (!preg_match('/^.*\.[a-zA-Z_0-9]+$/', $file_name) && $file_type) {
  746. // Build a "MIME-types / extensions" connection table
  747. static $mime_type = array();
  748. $mime_type[] = 'application/msword'; $extension[] = '.doc';
  749. $mime_type[] = 'application/rtf'; $extension[] = '.rtf';
  750. $mime_type[] = 'application/vnd.ms-powerpoint'; $extension[] = '.ppt';
  751. $mime_type[] = 'application/vnd.ms-excel'; $extension[] = '.xls';
  752. $mime_type[] = 'application/pdf'; $extension[] = '.pdf';
  753. $mime_type[] = 'application/postscript'; $extension[] = '.ps';
  754. $mime_type[] = 'application/mac-binhex40'; $extension[] = '.hqx';
  755. $mime_type[] = 'application/x-gzip'; $extension[] = 'tar.gz';
  756. $mime_type[] = 'application/x-shockwave-flash'; $extension[] = '.swf';
  757. $mime_type[] = 'application/x-stuffit'; $extension[] = '.sit';
  758. $mime_type[] = 'application/x-tar'; $extension[] = '.tar';
  759. $mime_type[] = 'application/zip'; $extension[] = '.zip';
  760. $mime_type[] = 'application/x-tar'; $extension[] = '.tar';
  761. $mime_type[] = 'text/html'; $extension[] = '.html';
  762. $mime_type[] = 'text/plain'; $extension[] = '.txt';
  763. $mime_type[] = 'text/rtf'; $extension[] = '.rtf';
  764. $mime_type[] = 'img/gif'; $extension[] = '.gif';
  765. $mime_type[] = 'img/jpeg'; $extension[] = '.jpg';
  766. $mime_type[] = 'img/png'; $extension[] = '.png';
  767. $mime_type[] = 'audio/midi'; $extension[] = '.mid';
  768. $mime_type[] = 'audio/mpeg'; $extension[] = '.mp3';
  769. $mime_type[] = 'audio/x-aiff'; $extension[] = '.aif';
  770. $mime_type[] = 'audio/x-pn-realaudio'; $extension[] = '.rm';
  771. $mime_type[] = 'audio/x-pn-realaudio-plugin'; $extension[] = '.rpm';
  772. $mime_type[] = 'audio/x-wav'; $extension[] = '.wav';
  773. $mime_type[] = 'video/mpeg'; $extension[] = '.mpg';
  774. $mime_type[] = 'video/mpeg4-generic'; $extension[] = '.mp4';
  775. $mime_type[] = 'video/quicktime'; $extension[] = '.mov';
  776. $mime_type[] = 'video/x-msvideo'; $extension[] = '.avi';
  777. $mime_type[] = 'video/x-ms-wmv'; $extension[] = '.wmv';
  778. $mime_type[] = 'video/x-flv'; $extension[] = '.flv';
  779. $mime_type[] = 'image/svg+xml'; $extension[] = '.svg';
  780. $mime_type[] = 'image/svg+xml'; $extension[] = '.svgz';
  781. $mime_type[] = 'video/ogg'; $extension[] = '.ogv';
  782. $mime_type[] = 'audio/ogg'; $extension[] = '.oga';
  783. $mime_type[] = 'application/ogg'; $extension[] = '.ogg';
  784. $mime_type[] = 'application/ogg'; $extension[] = '.ogx';
  785. $mime_type[] = 'application/x-freemind'; $extension[] = '.mm';
  786. $mime_type[] = 'application/vnd.ms-word.document.macroEnabled.12'; $extension[] = '.docm';
  787. $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; $extension[] = '.docx';
  788. $mime_type[] = 'application/vnd.ms-word.template.macroEnabled.12'; $extension[] = '.dotm';
  789. $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.template'; $extension[] = '.dotx';
  790. $mime_type[] = 'application/vnd.ms-powerpoint.template.macroEnabled.12'; $extension[] = '.potm';
  791. $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.template'; $extension[] = '.potx';
  792. $mime_type[] = 'application/vnd.ms-powerpoint.addin.macroEnabled.12'; $extension[] = '.ppam';
  793. $mime_type[] = 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12'; $extension[] = '.ppsm';
  794. $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.slideshow'; $extension[] = '.ppsx';
  795. $mime_type[] = 'application/vnd.ms-powerpoint.presentation.macroEnabled.12'; $extension[] = '.pptm';
  796. $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'; $extension[] = '.pptx';
  797. $mime_type[] = 'application/vnd.ms-excel.addin.macroEnabled.12'; $extension[] = '.xlam';
  798. $mime_type[] = 'application/vnd.ms-excel.sheet.binary.macroEnabled.12'; $extension[] = '.xlsb';
  799. $mime_type[] = 'application/vnd.ms-excel.sheet.macroEnabled.12'; $extension[] = '.xlsm';
  800. $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; $extension[] = '.xlsx';
  801. $mime_type[] = 'application/vnd.ms-excel.template.macroEnabled.12'; $extension[] = '.xltm';
  802. $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.template'; $extension[] = '.xltx';
  803. // Test on PC (files with no extension get application/octet-stream)
  804. //$mime_type[] = 'application/octet-stream'; $extension[] = '.ext';
  805. // Check whether the MIME type sent by the browser is within the table
  806. foreach ($mime_type as $key => & $type) {
  807. if ($type == $file_type) {
  808. $file_name .= $extension[$key];
  809. break;
  810. }
  811. }
  812. unset($mime_type, $extension, $type, $key); // Delete to eschew possible collisions
  813. }
  814. return $file_name;
  815. }
  816. /**
  817. * Manages all the unzipping process of an uploaded file
  818. *
  819. * @author Hugues Peeters <hugues.peeters@claroline.net>
  820. *
  821. * @param array $uploaded_file - follows the $_FILES Structure
  822. * @param string $upload_path - destination of the upload.
  823. * This path is to append to $base_work_dir
  824. * @param string $base_work_dir - base working directory of the module
  825. * @param int $max_filled_space - amount of bytes to not exceed in the base
  826. * working directory
  827. *
  828. * @return boolean true if it succeeds false otherwise
  829. */
  830. function unzip_uploaded_file($uploaded_file, $upload_path, $base_work_dir, $max_filled_space)
  831. {
  832. $zip_file = new PclZip($uploaded_file['tmp_name']);
  833. // Check the zip content (real size and file extension)
  834. if (file_exists($uploaded_file['tmp_name'])) {
  835. $zip_content_array = $zip_file->listContent();
  836. $ok_scorm = false;
  837. $realFileSize = 0;
  838. foreach ($zip_content_array as & $this_content) {
  839. if (preg_match('~.(php.*|phtml)$~i', $this_content['filename'])) {
  840. Display::addFlash(
  841. Display::return_message(get_lang('ZipNoPhp'))
  842. );
  843. return false;
  844. } elseif (stristr($this_content['filename'], 'imsmanifest.xml')) {
  845. $ok_scorm = true;
  846. } elseif (stristr($this_content['filename'], 'LMS')) {
  847. $ok_plantyn_scorm1 = true;
  848. } elseif (stristr($this_content['filename'], 'REF')) {
  849. $ok_plantyn_scorm2 = true;
  850. } elseif (stristr($this_content['filename'], 'SCO')) {
  851. $ok_plantyn_scorm3 = true;
  852. } elseif (stristr($this_content['filename'], 'AICC')) {
  853. $ok_aicc_scorm = true;
  854. }
  855. $realFileSize += $this_content['size'];
  856. }
  857. if (($ok_plantyn_scorm1 && $ok_plantyn_scorm2 && $ok_plantyn_scorm3) || $ok_aicc_scorm) {
  858. $ok_scorm = true;
  859. }
  860. if (!$ok_scorm && defined('CHECK_FOR_SCORM') && CHECK_FOR_SCORM) {
  861. Display::addFlash(
  862. Display::return_message(get_lang('NotScormContent'))
  863. );
  864. return false;
  865. }
  866. if (!enough_size($realFileSize, $base_work_dir, $max_filled_space)) {
  867. Display::addFlash(
  868. Display::return_message(get_lang('NoSpace'))
  869. );
  870. return false;
  871. }
  872. // It happens on Linux that $upload_path sometimes doesn't start with '/'
  873. if ($upload_path[0] != '/' && substr($base_work_dir, -1, 1) != '/') {
  874. $upload_path = '/'.$upload_path;
  875. }
  876. if ($upload_path[strlen($upload_path) - 1] == '/') {
  877. $upload_path = substr($upload_path, 0, -1);
  878. }
  879. /* Uncompressing phase */
  880. /*
  881. The first version, using OS unzip, is not used anymore
  882. because it does not return enough information.
  883. We need to process each individual file in the zip archive to
  884. - add it to the database
  885. - parse & change relative html links
  886. */
  887. if (PHP_OS == 'Linux' && !get_cfg_var('safe_mode') && false) { // *** UGent, changed by OC ***
  888. // Shell Method - if this is possible, it gains some speed
  889. exec("unzip -d \"".$base_work_dir.$upload_path."/\"".$uploaded_file['name']." ".$uploaded_file['tmp_name']);
  890. } else {
  891. // PHP method - slower...
  892. $save_dir = getcwd();
  893. chdir($base_work_dir.$upload_path);
  894. $unzippingState = $zip_file->extract();
  895. for ($j = 0; $j < count($unzippingState); $j++) {
  896. $state = $unzippingState[$j];
  897. // Fix relative links in html files
  898. $extension = strrchr($state['stored_filename'], '.');
  899. }
  900. if ($dir = @opendir($base_work_dir.$upload_path)) {
  901. while ($file = readdir($dir)) {
  902. if ($file != '.' && $file != '..') {
  903. $filetype = 'file';
  904. if (is_dir($base_work_dir.$upload_path.'/'.$file)) $filetype = 'folder';
  905. $safe_file = api_replace_dangerous_char($file);
  906. @rename($base_work_dir.$upload_path.'/'.$file, $base_work_dir.$upload_path.'/'.$safe_file);
  907. set_default_settings($upload_path, $safe_file, $filetype);
  908. }
  909. }
  910. closedir($dir);
  911. } else {
  912. error_log('Could not create directory '.$base_work_dir.$upload_path.' to unzip files');
  913. }
  914. chdir($save_dir); // Back to previous dir position
  915. }
  916. }
  917. return true;
  918. }
  919. /**
  920. * Manages all the unzipping process of an uploaded document
  921. * This uses the item_property table for properties of documents
  922. *
  923. * @author Hugues Peeters <hugues.peeters@claroline.net>
  924. * @author Bert Vanderkimpen
  925. *
  926. * @param array $courseInfo
  927. * @param array $userInfo
  928. * @param array $uploaded_file - follows the $_FILES Structure
  929. * @param string $uploadPath - destination of the upload.
  930. * This path is to append to $base_work_dir
  931. * @param string $base_work_dir - base working directory of the module
  932. * @param int $maxFilledSpace - amount of bytes to not exceed in the base
  933. * working directory
  934. * @param int $sessionId
  935. * @param int $groupId group.id
  936. * @param bool $output Optional. If no output not wanted on success, set to false.
  937. * @param bool $onlyUploadFile
  938. *
  939. * @return boolean true if it succeeds false otherwise
  940. */
  941. function unzip_uploaded_document(
  942. $courseInfo,
  943. $userInfo,
  944. $uploaded_file,
  945. $uploadPath,
  946. $base_work_dir,
  947. $maxFilledSpace,
  948. $sessionId = 0,
  949. $groupId = 0,
  950. $output = true,
  951. $onlyUploadFile = false
  952. ) {
  953. $zip = new PclZip($uploaded_file['tmp_name']);
  954. // Check the zip content (real size and file extension)
  955. $zip_content_array = (array) $zip->listContent();
  956. $realSize = 0;
  957. foreach ($zip_content_array as & $this_content) {
  958. $realSize += $this_content['size'];
  959. }
  960. if (!DocumentManager::enough_space($realSize, $maxFilledSpace)) {
  961. Display::display_error_message(get_lang('UplNotEnoughSpace'));
  962. return false;
  963. }
  964. $folder = api_get_unique_id();
  965. $destinationDir = api_get_path(SYS_ARCHIVE_PATH).$folder;
  966. mkdir($destinationDir, api_get_permissions_for_new_directories(), true);
  967. /* Uncompress zip file*/
  968. // We extract using a callback function that "cleans" the path
  969. $zip->extract(
  970. PCLZIP_OPT_PATH,
  971. $destinationDir,
  972. PCLZIP_CB_PRE_EXTRACT,
  973. 'clean_up_files_in_zip',
  974. PCLZIP_OPT_REPLACE_NEWER
  975. );
  976. if ($onlyUploadFile === false) {
  977. // Add all documents in the unzipped folder to the database
  978. add_all_documents_in_folder_to_database(
  979. $courseInfo,
  980. $userInfo,
  981. $base_work_dir,
  982. $destinationDir,
  983. $sessionId,
  984. $groupId,
  985. $output,
  986. array('path' => $uploadPath)
  987. );
  988. } else {
  989. // Copy result
  990. $fs = new \Symfony\Component\Filesystem\Filesystem();
  991. $fs->mirror($destinationDir, $base_work_dir.$uploadPath, null, ['overwrite']);
  992. }
  993. if (is_dir($destinationDir)) {
  994. rmdirr($destinationDir);
  995. }
  996. return true;
  997. }
  998. /**
  999. * This function is a callback function that is used while extracting a zipfile
  1000. * http://www.phpconcept.net/pclzip/man/en/index.php?options-pclzip_cb_pre_extract
  1001. *
  1002. * @param array $p_event
  1003. * @param array $p_header
  1004. * @return int (If the function returns 1, then the extraction is resumed, if 0 the path was skipped)
  1005. */
  1006. function clean_up_files_in_zip($p_event, &$p_header)
  1007. {
  1008. $originalStoredFileName = $p_header['stored_filename'];
  1009. $modifiedStoredFileName = clean_up_path($originalStoredFileName);
  1010. $p_header['filename'] = str_replace($originalStoredFileName, $modifiedStoredFileName, $p_header['filename']);
  1011. return 1;
  1012. }
  1013. /**
  1014. * This function cleans up a given path
  1015. * by eliminating dangerous file names and cleaning them
  1016. *
  1017. * @param string $path
  1018. *
  1019. * @return string
  1020. *
  1021. * @see disable_dangerous_file()
  1022. * @see api_replace_dangerous_char()
  1023. */
  1024. function clean_up_path($path)
  1025. {
  1026. // Split the path in folders and files
  1027. $path_array = explode('/', $path);
  1028. // Clean up every folder and filename in the path
  1029. foreach ($path_array as $key => & $val) {
  1030. // We don't want to lose the dots in ././folder/file (cfr. zipfile)
  1031. if ($val != '.') {
  1032. $val = disable_dangerous_file(api_replace_dangerous_char($val));
  1033. }
  1034. }
  1035. // Join the "cleaned" path (modified in-place as passed by reference)
  1036. $path = implode('/', $path_array);
  1037. filter_extension($path);
  1038. return $path;
  1039. }
  1040. /**
  1041. * Checks if the file is dangerous, based on extension and/or mimetype.
  1042. * The list of extensions accepted/rejected can be found from
  1043. * api_get_setting('upload_extensions_exclude') and api_get_setting('upload_extensions_include')
  1044. * @param string filename passed by reference. The filename will be modified
  1045. * if filter rules say so! (you can include path but the filename should look like 'abc.html')
  1046. * @return int 0 to skip file, 1 to keep file
  1047. */
  1048. function filter_extension(&$filename)
  1049. {
  1050. if (substr($filename, -1) == '/') {
  1051. return 1; // Authorize directories
  1052. }
  1053. $blacklist = api_get_setting('upload_extensions_list_type');
  1054. if ($blacklist != 'whitelist') { // if = blacklist
  1055. $extensions = explode(';', strtolower(api_get_setting('upload_extensions_blacklist')));
  1056. $skip = api_get_setting('upload_extensions_skip');
  1057. $ext = strrchr($filename, '.');
  1058. $ext = substr($ext, 1);
  1059. if (empty($ext)) {
  1060. return 1; // We're in blacklist mode, so accept empty extensions
  1061. }
  1062. if (in_array(strtolower($ext), $extensions)) {
  1063. if ($skip == 'true') {
  1064. return 0;
  1065. } else {
  1066. $new_ext = api_get_setting('upload_extensions_replace_by');
  1067. $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
  1068. return 1;
  1069. }
  1070. } else {
  1071. return 1;
  1072. }
  1073. } else {
  1074. $extensions = explode(';', strtolower(api_get_setting('upload_extensions_whitelist')));
  1075. $skip = api_get_setting('upload_extensions_skip');
  1076. $ext = strrchr($filename, '.');
  1077. $ext = substr($ext, 1);
  1078. if (empty($ext)) {
  1079. return 1; // Accept empty extensions
  1080. }
  1081. if (!in_array(strtolower($ext), $extensions)) {
  1082. if ($skip == 'true') {
  1083. return 0;
  1084. } else {
  1085. $new_ext = api_get_setting('upload_extensions_replace_by');
  1086. $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
  1087. return 1;
  1088. }
  1089. } else {
  1090. return 1;
  1091. }
  1092. }
  1093. }
  1094. /**
  1095. * Adds a new document to the database
  1096. *
  1097. * @param array $_course
  1098. * @param string $path
  1099. * @param string $filetype
  1100. * @param int $filesize
  1101. * @param string $title
  1102. * @param string $comment
  1103. * @param int $readonly
  1104. * @param bool $save_visibility
  1105. * @param int $group_id group.id
  1106. * @param int $session_id Session ID, if any
  1107. * @param int $userId creator id
  1108. *
  1109. * @return int id if inserted document
  1110. */
  1111. function add_document(
  1112. $_course,
  1113. $path,
  1114. $filetype,
  1115. $filesize,
  1116. $title,
  1117. $comment = null,
  1118. $readonly = 0,
  1119. $save_visibility = true,
  1120. $group_id = null,
  1121. $session_id = 0,
  1122. $userId = 0
  1123. ) {
  1124. $session_id = empty($session_id) ? api_get_session_id() : $session_id;
  1125. $userId = empty($userId) ? api_get_user_id() : $userId;
  1126. $readonly = intval($readonly);
  1127. $c_id = $_course['real_id'];
  1128. $table_document = Database::get_course_table(TABLE_DOCUMENT);
  1129. $params = [
  1130. 'c_id' => $c_id,
  1131. 'path' => $path,
  1132. 'filetype' => $filetype,
  1133. 'size' => $filesize,
  1134. 'title' => $title,
  1135. 'comment' => $comment,
  1136. 'readonly' => $readonly,
  1137. 'session_id' => $session_id,
  1138. ];
  1139. $documentId = Database::insert($table_document, $params);
  1140. if ($documentId) {
  1141. $sql = "UPDATE $table_document SET id = iid WHERE iid = $documentId";
  1142. Database::query($sql);
  1143. if ($save_visibility) {
  1144. api_set_default_visibility(
  1145. $documentId,
  1146. TOOL_DOCUMENT,
  1147. $group_id,
  1148. $_course,
  1149. $session_id,
  1150. $userId
  1151. );
  1152. }
  1153. return $documentId;
  1154. } else {
  1155. return false;
  1156. }
  1157. }
  1158. /**
  1159. * Updates an existing document in the database
  1160. * as the file exists, we only need to change the size
  1161. *
  1162. * @param array $_course
  1163. * @param int $documentId
  1164. * @param int $filesize
  1165. * @param int $readonly
  1166. * @return boolean true /false
  1167. */
  1168. function update_existing_document($_course, $documentId, $filesize, $readonly = 0)
  1169. {
  1170. $document_table = Database::get_course_table(TABLE_DOCUMENT);
  1171. $documentId = intval($documentId);
  1172. $filesize = intval($filesize);
  1173. $readonly = intval($readonly);
  1174. $course_id = $_course['real_id'];
  1175. $sql = "UPDATE $document_table SET
  1176. size = '$filesize',
  1177. readonly = '$readonly'
  1178. WHERE c_id = $course_id AND id = $documentId";
  1179. if (Database::query($sql)) {
  1180. return true;
  1181. } else {
  1182. return false;
  1183. }
  1184. }
  1185. /**
  1186. * This function updates the last_edit_date, last edit user id on all folders in a given path
  1187. *
  1188. * @param array $_course
  1189. * @param string $path
  1190. * @param int $user_id
  1191. */
  1192. function item_property_update_on_folder($_course, $path, $user_id)
  1193. {
  1194. // If we are in the root, just return... no need to update anything
  1195. if ($path == '/') {
  1196. return;
  1197. }
  1198. $user_id = intval($user_id);
  1199. // If the given path ends with a / we remove it
  1200. $endchar = substr($path, strlen($path) - 1, 1);
  1201. if ($endchar == '/') {
  1202. $path = substr($path, 0, strlen($path) - 1);
  1203. }
  1204. $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
  1205. // Get the time
  1206. $time = date('Y-m-d H:i:s', time());
  1207. // Det all paths in the given path
  1208. // /folder/subfolder/subsubfolder/file
  1209. // if file is updated, subsubfolder, subfolder and folder are updated
  1210. $exploded_path = explode('/', $path);
  1211. $course_id = api_get_course_int_id();
  1212. $newpath = '';
  1213. foreach ($exploded_path as $key => & $value) {
  1214. // We don't want a slash before our first slash
  1215. if ($key != 0) {
  1216. $newpath .= '/'.$value;
  1217. //echo 'path= '.$newpath.'<br />';
  1218. // Select ID of given folder
  1219. $folder_id = DocumentManager::get_document_id($_course, $newpath);
  1220. if ($folder_id) {
  1221. $sql = "UPDATE $table SET
  1222. lastedit_date='$time',lastedit_type='DocumentInFolderUpdated', lastedit_user_id='$user_id'
  1223. WHERE c_id = $course_id AND tool='".TOOL_DOCUMENT."' AND ref='$folder_id'";
  1224. Database::query($sql);
  1225. }
  1226. }
  1227. }
  1228. }
  1229. /**
  1230. * Returns the directory depth of the file.
  1231. *
  1232. * @author Olivier Cauberghe <olivier.cauberghe@ugent.be>
  1233. * @param path+filename eg: /main/document/document.php
  1234. * @return The directory depth
  1235. */
  1236. function get_levels($filename) {
  1237. $levels = explode('/', $filename);
  1238. if (empty($levels[count($levels) - 1])) {
  1239. unset($levels[count($levels) - 1]);
  1240. }
  1241. return count($levels);
  1242. }
  1243. /**
  1244. * Adds file to document table in database
  1245. * deprecated: use file_set_default_settings instead
  1246. *
  1247. * @author Olivier Cauberghe <olivier.cauberghe@ugent.be>
  1248. * @param path,filename
  1249. * action: Adds an entry to the document table with the default settings.
  1250. */
  1251. function set_default_settings($upload_path, $filename, $filetype = 'file')
  1252. {
  1253. $dbTable = Database::get_course_table(TABLE_DOCUMENT);
  1254. global $default_visibility;
  1255. if (!$default_visibility) {
  1256. $default_visibility = 'v';
  1257. }
  1258. $filetype = Database::escape_string($filetype);
  1259. $upload_path = str_replace('\\', '/', $upload_path);
  1260. $upload_path = str_replace('//', '/', $upload_path);
  1261. if ($upload_path == '/') {
  1262. $upload_path = '';
  1263. } elseif (!empty($upload_path) && $upload_path[0] != '/') {
  1264. $upload_path = "/$upload_path";
  1265. }
  1266. $endchar = substr($filename, strlen($filename) - 1, 1);
  1267. if ($endchar == '/') {
  1268. $filename = substr($filename, 0, -1);
  1269. }
  1270. $filename = Database::escape_string($filename);
  1271. $query = "SELECT count(*) as bestaat FROM $dbTable
  1272. WHERE path='$upload_path/$filename'";
  1273. $result = Database::query($query);
  1274. $row = Database::fetch_array($result);
  1275. if ($row['bestaat'] > 0) {
  1276. $query = "UPDATE $dbTable SET
  1277. path='$upload_path/$filename',
  1278. visibility='$default_visibility',
  1279. filetype='$filetype'
  1280. WHERE path='$upload_path/$filename'";
  1281. } else {
  1282. $query = "INSERT INTO $dbTable (path,visibility,filetype)
  1283. VALUES('$upload_path/$filename','$default_visibility','$filetype')";
  1284. }
  1285. Database::query($query);
  1286. }
  1287. /**
  1288. * Retrieves the image path list in a html file
  1289. *
  1290. * @author Hugues Peeters <hugues.peeters@claroline.net>
  1291. * @param string $html_file
  1292. * @return array - images path list
  1293. */
  1294. function search_img_from_html($html_file) {
  1295. $img_path_list = array();
  1296. if (!$fp = fopen($html_file, 'r')) {
  1297. return;
  1298. }
  1299. // Aearch and store occurences of the <img> tag in an array
  1300. $size_file = (filesize($html_file) === 0) ? 1 : filesize($html_file);
  1301. if (isset($fp) && $fp !== false) {
  1302. $buffer = fread($fp, $size_file);
  1303. if (strlen($buffer) >= 0 && $buffer !== false) {
  1304. //
  1305. } else {
  1306. die('<center>Can not read file.</center>');
  1307. }
  1308. } else {
  1309. die('<center>Can not read file.</center>');
  1310. }
  1311. $matches = array();
  1312. if (preg_match_all('~<[[:space:]]*img[^>]*>~i', $buffer, $matches)) {
  1313. $img_tag_list = $matches[0];
  1314. }
  1315. fclose($fp);
  1316. unset($buffer);
  1317. // Search the image file path from all the <IMG> tag detected
  1318. if (sizeof($img_tag_list) > 0) {
  1319. foreach ($img_tag_list as & $this_img_tag) {
  1320. if (preg_match('~src[[:space:]]*=[[:space:]]*[\"]{1}([^\"]+)[\"]{1}~i', $this_img_tag, $matches)) {
  1321. $img_path_list[] = $matches[1];
  1322. }
  1323. }
  1324. $img_path_list = array_unique($img_path_list); // Remove duplicate entries
  1325. }
  1326. return $img_path_list;
  1327. }
  1328. /**
  1329. * Creates a new directory trying to find a directory name
  1330. * that doesn't already exist
  1331. *
  1332. * @author Hugues Peeters <hugues.peeters@claroline.net>
  1333. * @author Bert Vanderkimpen
  1334. * @param array $_course current course information
  1335. * @param int $user_id current user id
  1336. * @param int $session_id
  1337. * @param int $to_group_id group.id
  1338. * @param int $to_user_id
  1339. * @param string $base_work_dir /var/www/chamilo/courses/ABC/document
  1340. * @param string $desired_dir_name complete path of the desired name
  1341. * Example: /folder1/folder2
  1342. * @param string $title "folder2"
  1343. * @param int $visibility (0 for invisible, 1 for visible, 2 for deleted)
  1344. * @param bool $generateNewNameIfExists
  1345. * @return string actual directory name if it succeeds,
  1346. * boolean false otherwise
  1347. */
  1348. function create_unexisting_directory(
  1349. $_course,
  1350. $user_id,
  1351. $session_id,
  1352. $to_group_id,
  1353. $to_user_id,
  1354. $base_work_dir,
  1355. $desired_dir_name,
  1356. $title = null,
  1357. $visibility = null,
  1358. $generateNewNameIfExists = false
  1359. ) {
  1360. $course_id = $_course['real_id'];
  1361. $session_id = intval($session_id);
  1362. $folderExists = DocumentManager::folderExists(
  1363. $desired_dir_name,
  1364. $_course,
  1365. $session_id,
  1366. $to_group_id
  1367. );
  1368. if ($folderExists === true) {
  1369. if ($generateNewNameIfExists) {
  1370. $counter = 1;
  1371. while (1) {
  1372. $folderExists = DocumentManager::folderExists(
  1373. $desired_dir_name.'_'.$counter,
  1374. $_course,
  1375. $session_id,
  1376. $to_group_id
  1377. );
  1378. if ($folderExists === false) {
  1379. break;
  1380. }
  1381. $counter++;
  1382. }
  1383. $desired_dir_name = $desired_dir_name.'_'.$counter;
  1384. } else {
  1385. return false;
  1386. }
  1387. }
  1388. $systemFolderName = $desired_dir_name;
  1389. // Adding suffix
  1390. $suffix = DocumentManager::getDocumentSuffix(
  1391. $_course,
  1392. $session_id,
  1393. $to_group_id
  1394. );
  1395. $systemFolderName .= $suffix;
  1396. if ($title == null) {
  1397. $title = basename($desired_dir_name);
  1398. }
  1399. if (!is_dir($base_work_dir.$systemFolderName)) {
  1400. $result = mkdir(
  1401. $base_work_dir.$systemFolderName,
  1402. api_get_permissions_for_new_directories(),
  1403. true
  1404. );
  1405. if ($result) {
  1406. // Check if pathname already exists inside document table
  1407. $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
  1408. $sql = "SELECT id, path FROM $tbl_document
  1409. WHERE
  1410. c_id = $course_id AND
  1411. (
  1412. path = '".Database::escape_string($systemFolderName)."'
  1413. )
  1414. ";
  1415. $groupInfo = [];
  1416. if (!empty($to_group_id)) {
  1417. $groupInfo = GroupManager::get_group_properties($to_group_id);
  1418. }
  1419. $rs = Database::query($sql);
  1420. if (Database::num_rows($rs) == 0) {
  1421. $document_id = add_document(
  1422. $_course,
  1423. $systemFolderName,
  1424. 'folder',
  1425. 0,
  1426. $title,
  1427. null,
  1428. 0,
  1429. true,
  1430. $to_group_id,
  1431. $session_id,
  1432. $user_id
  1433. );
  1434. if ($document_id) {
  1435. // Update document item_property
  1436. if (!empty($visibility)) {
  1437. $visibilities = array(
  1438. 0 => 'invisible',
  1439. 1 => 'visible',
  1440. 2 => 'delete'
  1441. );
  1442. api_item_property_update(
  1443. $_course,
  1444. TOOL_DOCUMENT,
  1445. $document_id,
  1446. $visibilities[$visibility],
  1447. $user_id,
  1448. $groupInfo,
  1449. $to_user_id,
  1450. null,
  1451. null,
  1452. $session_id
  1453. );
  1454. } else {
  1455. api_item_property_update(
  1456. $_course,
  1457. TOOL_DOCUMENT,
  1458. $document_id,
  1459. 'FolderCreated',
  1460. $user_id,
  1461. $groupInfo,
  1462. $to_user_id,
  1463. null,
  1464. null,
  1465. $session_id
  1466. );
  1467. }
  1468. $documentData = DocumentManager::get_document_data_by_id(
  1469. $document_id,
  1470. $_course['code'],
  1471. false,
  1472. $session_id
  1473. );
  1474. return $documentData;
  1475. }
  1476. } else {
  1477. $document = Database::fetch_array($rs);
  1478. $documentData = DocumentManager::get_document_data_by_id(
  1479. $document['id'],
  1480. $_course['code'],
  1481. false,
  1482. $session_id
  1483. );
  1484. /* This means the folder NOT exist in the filesystem
  1485. (now this was created) but there is a record in the Database*/
  1486. return $documentData;
  1487. }
  1488. }
  1489. }
  1490. return false;
  1491. }
  1492. /**
  1493. * Handles uploaded missing images
  1494. *
  1495. * @author Hugues Peeters <hugues.peeters@claroline.net>
  1496. * @author Bert Vanderkimpen
  1497. * @param array $_course
  1498. * @param array $uploaded_file_collection - follows the $_FILES Structure
  1499. * @param string $base_work_dir
  1500. * @param string $missing_files_dir
  1501. * @param int $user_id
  1502. * @param int $to_group_id group.id
  1503. */
  1504. function move_uploaded_file_collection_into_directory(
  1505. $_course,
  1506. $uploaded_file_collection,
  1507. $base_work_dir,
  1508. $missing_files_dir,
  1509. $user_id,
  1510. $to_group_id,
  1511. $to_user_id,
  1512. $max_filled_space
  1513. ) {
  1514. $number_of_uploaded_images = count($uploaded_file_collection['name']);
  1515. $new_file_list = array();
  1516. for ($i = 0; $i < $number_of_uploaded_images; $i++) {
  1517. $missing_file['name'] = $uploaded_file_collection['name'][$i];
  1518. $missing_file['type'] = $uploaded_file_collection['type'][$i];
  1519. $missing_file['tmp_name'] = $uploaded_file_collection['tmp_name'][$i];
  1520. $missing_file['error'] = $uploaded_file_collection['error'][$i];
  1521. $missing_file['size'] = $uploaded_file_collection['size'][$i];
  1522. $upload_ok = process_uploaded_file($missing_file);
  1523. if ($upload_ok) {
  1524. $new_file_list[] = handle_uploaded_document(
  1525. $_course,
  1526. $missing_file,
  1527. $base_work_dir,
  1528. $missing_files_dir,
  1529. $user_id,
  1530. $to_group_id,
  1531. $to_user_id,
  1532. $max_filled_space,
  1533. 0,
  1534. 'overwrite'
  1535. );
  1536. }
  1537. unset($missing_file);
  1538. }
  1539. return $new_file_list;
  1540. }
  1541. /**
  1542. * Opens the old html file and replace the src path into the img tag
  1543. * This also works for files in subdirectories.
  1544. * @param $original_img_path is an array
  1545. * @param $new_img_path is an array
  1546. */
  1547. function replace_img_path_in_html_file($original_img_path, $new_img_path, $html_file)
  1548. {
  1549. // Open the file
  1550. $fp = fopen($html_file, 'r');
  1551. $buffer = fread($fp, filesize($html_file));
  1552. $new_html_content = '';
  1553. // Fix the image tags
  1554. for ($i = 0, $fileNb = count($original_img_path); $i < $fileNb; $i++) {
  1555. $replace_what = $original_img_path[$i];
  1556. // We only need the directory and the filename /path/to/file_html_files/missing_file.gif -> file_html_files/missing_file.gif
  1557. $exploded_file_path = explode('/', $new_img_path[$i]);
  1558. $replace_by = $exploded_file_path[count($exploded_file_path) - 2].'/'.$exploded_file_path[count($exploded_file_path) - 1];
  1559. $buffer = str_replace($replace_what, $replace_by, $buffer);
  1560. }
  1561. $new_html_content .= $buffer;
  1562. @fclose($fp);
  1563. // Write the resulted new file
  1564. if (!$fp = fopen($html_file, 'w')) {
  1565. return;
  1566. }
  1567. if (!fwrite($fp, $new_html_content)) {
  1568. return;
  1569. }
  1570. }
  1571. /**
  1572. * Creates a file containing an html redirection to a given url
  1573. *
  1574. * @author Hugues Peeters <hugues.peeters@claroline.net>
  1575. * @param string $file_path
  1576. * @param string $url
  1577. * @return void
  1578. */
  1579. function create_link_file($file_path, $url)
  1580. {
  1581. $file_content = '<html>'
  1582. .'<head>'
  1583. .'<meta http-equiv="refresh" content="1;url='.$url.'">'
  1584. .'</head>'
  1585. .'<body>'
  1586. .'</body>'
  1587. .'</html>';
  1588. if (file_exists($file_path)) {
  1589. if (!($fp = fopen($file_path, 'w'))) {
  1590. return false;
  1591. }
  1592. return fwrite($fp, $file_content);
  1593. }
  1594. }
  1595. /**
  1596. * Checks the extension of a file, if it's .htm or .html
  1597. * we use search_img_from_html to get all image paths in the file
  1598. *
  1599. * @param string $file
  1600. * @return array paths
  1601. * @see check_for_missing_files() uses search_img_from_html()
  1602. */
  1603. function check_for_missing_files($file)
  1604. {
  1605. if (strrchr($file, '.') == '.htm' || strrchr($file, '.') == '.html') {
  1606. $img_file_path = search_img_from_html($file);
  1607. return $img_file_path;
  1608. }
  1609. return false;
  1610. }
  1611. /**
  1612. * This function builds a form that asks for the missing images in a html file
  1613. * maybe we should do this another way?
  1614. *
  1615. * @param array $missing_files
  1616. * @param string $upload_path
  1617. * @param string $file_name
  1618. * @return string the form
  1619. */
  1620. function build_missing_files_form($missing_files, $upload_path, $file_name)
  1621. {
  1622. // Do we need a / or not?
  1623. $added_slash = ($upload_path == '/') ? '' : '/';
  1624. $folder_id = DocumentManager::get_document_id(api_get_course_info(), $upload_path);
  1625. // Build the form
  1626. $form = "<p><strong>".get_lang('MissingImagesDetected')."</strong></p>"
  1627. ."<form method=\"post\" action=\"".api_get_self()."\" enctype=\"multipart/form-data\">"
  1628. // Related_file is the path to the file that has missing images
  1629. ."<input type=\"hidden\" name=\"related_file\" value=\"".$upload_path.$added_slash.$file_name."\" />"
  1630. ."<input type=\"hidden\" name=\"upload_path\" value=\"".$upload_path."\" />"
  1631. ."<input type=\"hidden\" name=\"id\" value=\"".$folder_id."\" />"
  1632. ."<table border=\"0\">";
  1633. foreach ($missing_files as & $this_img_file_path) {
  1634. $form .= "<tr>"
  1635. ."<td>".basename($this_img_file_path)." : </td>"
  1636. ."<td>"
  1637. ."<input type=\"file\" name=\"img_file[]\"/>"
  1638. ."<input type=\"hidden\" name=\"img_file_path[]\" value=\"".$this_img_file_path."\" />"
  1639. ."</td>"
  1640. ."</tr>";
  1641. }
  1642. $form .= "</table>"
  1643. ."<button type='submit' name=\"cancel_submit_image\" value=\"".get_lang('Cancel')."\" class=\"cancel\">".get_lang('Cancel')."</button>"
  1644. ."<button type='submit' name=\"submit_image\" value=\"".get_lang('Ok')."\" class=\"save\">".get_lang('Ok')."</button>"
  1645. ."</form>";
  1646. return $form;
  1647. }
  1648. /**
  1649. * This recursive function can be used during the upgrade process form older
  1650. * versions of Chamilo
  1651. * It crawls the given directory, checks if the file is in the DB and adds
  1652. * it if it's not
  1653. *
  1654. * @param array $courseInfo
  1655. * @param array $userInfo
  1656. * @param string $base_work_dir
  1657. * @param string $folderPath
  1658. * @param int $sessionId
  1659. * @param int $groupId group.id
  1660. * @param bool $output
  1661. * @param array $parent
  1662. * @param string $uploadPath
  1663. *
  1664. */
  1665. function add_all_documents_in_folder_to_database(
  1666. $courseInfo,
  1667. $userInfo,
  1668. $base_work_dir,
  1669. $folderPath,
  1670. $sessionId = 0,
  1671. $groupId = 0,
  1672. $output = false,
  1673. $parent = array()
  1674. ) {
  1675. if (empty($userInfo) || empty($courseInfo)) {
  1676. return false;
  1677. }
  1678. $userId = $userInfo['user_id'];
  1679. // Open dir
  1680. $handle = opendir($folderPath);
  1681. if (is_dir($folderPath)) {
  1682. // Run trough
  1683. while ($file = readdir($handle)) {
  1684. if ($file == '.' || $file == '..') {
  1685. continue;
  1686. }
  1687. $parentPath = null;
  1688. if (!empty($parent) && isset($parent['path'])) {
  1689. $parentPath = $parent['path'];
  1690. if ($parentPath == '/') {
  1691. $parentPath = null;
  1692. }
  1693. }
  1694. $completePath = $parentPath.'/'.$file;
  1695. $sysFolderPath = $folderPath.'/'.$file;
  1696. // Is directory?
  1697. if (is_dir($sysFolderPath)) {
  1698. $folderExists = DocumentManager::folderExists(
  1699. $completePath,
  1700. $courseInfo,
  1701. $sessionId,
  1702. $groupId
  1703. );
  1704. if ($folderExists === true) {
  1705. $documentId = DocumentManager::get_document_id($courseInfo, $completePath, $sessionId);
  1706. if ($documentId) {
  1707. $newFolderData = DocumentManager::get_document_data_by_id(
  1708. $documentId,
  1709. $courseInfo,
  1710. false,
  1711. $sessionId
  1712. );
  1713. }
  1714. } else {
  1715. $newFolderData = create_unexisting_directory(
  1716. $courseInfo,
  1717. $userId,
  1718. $sessionId,
  1719. $groupId,
  1720. null,
  1721. $base_work_dir,
  1722. $completePath,
  1723. null,
  1724. null,
  1725. false
  1726. );
  1727. }
  1728. // Recursive
  1729. add_all_documents_in_folder_to_database(
  1730. $courseInfo,
  1731. $userInfo,
  1732. $base_work_dir,
  1733. $sysFolderPath,
  1734. $sessionId,
  1735. $groupId,
  1736. $output,
  1737. $newFolderData
  1738. );
  1739. } else {
  1740. // Rename
  1741. $uploadedFile = array(
  1742. 'name' => $file,
  1743. 'tmp_name' => $sysFolderPath,
  1744. 'size' => filesize($sysFolderPath),
  1745. 'type' => null,
  1746. 'from_file' => true,
  1747. 'move_file' => true
  1748. );
  1749. handle_uploaded_document(
  1750. $courseInfo,
  1751. $uploadedFile,
  1752. $base_work_dir,
  1753. $parentPath,
  1754. $userId,
  1755. $groupId,
  1756. null,
  1757. 0,
  1758. 'overwrite',
  1759. $output,
  1760. false,
  1761. null,
  1762. $sessionId
  1763. );
  1764. }
  1765. }
  1766. }
  1767. }