CourseArchiver.class.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. require_once 'Course.class.php';
  4. /**
  5. * Some functions to write a course-object to a zip-file and to read a course-
  6. * object from such a zip-file.
  7. * @author Bart Mollet <bart.mollet@hogent.be>
  8. * @package chamilo.backup
  9. *
  10. * @todo Use archive-folder of Chamilo?
  11. */
  12. class CourseArchiver
  13. {
  14. /**
  15. * Delete old temp-dirs
  16. */
  17. public static function clean_backup_dir()
  18. {
  19. $dir = api_get_path(SYS_ARCHIVE_PATH);
  20. if ($handle = @ opendir($dir)) {
  21. while (($file = readdir($handle)) !== false) {
  22. if ($file != "." && $file != ".." && strpos($file, 'CourseArchiver_') === 0 && is_dir($dir . '/' . $file)) {
  23. api_rmdirr($dir . '/' . $file);
  24. }
  25. }
  26. closedir($handle);
  27. }
  28. }
  29. /**
  30. * Write a course and all its resources to a zip-file.
  31. * @return string A pointer to the zip-file
  32. */
  33. public static function write_course($course)
  34. {
  35. $perm_dirs = api_get_permissions_for_new_directories();
  36. CourseArchiver::clean_backup_dir();
  37. // Create a temp directory
  38. $tmp_dir_name = 'CourseArchiver_'.api_get_unique_id();
  39. $backup_dir = api_get_path(SYS_ARCHIVE_PATH).$tmp_dir_name . '/';
  40. // All course-information will be stored in course_info.dat
  41. $course_info_file = $backup_dir.'course_info.dat';
  42. $zip_dir = api_get_path(SYS_ARCHIVE_PATH);
  43. $user = api_get_user_info();
  44. $date = new DateTime(api_get_local_time());
  45. $zip_file = $user['user_id'] . '_' . $course->code . '_' . $date->format('Ymd-His') . '.zip';
  46. $php_errormsg = '';
  47. $res = @mkdir($backup_dir, $perm_dirs);
  48. if ($res === false) {
  49. //TODO set and handle an error message telling the user to review the permissions on the archive directory
  50. error_log(__FILE__ . ' line ' . __LINE__ . ': ' . (ini_get('track_errors') != false ? $php_errormsg : 'error not recorded because track_errors is off in your php.ini') . ' - This error, occuring because your archive directory will not let this script write data into it, will prevent courses backups to be created', 0);
  51. }
  52. // Write the course-object to the file
  53. $fp = @fopen($course_info_file, 'w');
  54. if ($fp === false) {
  55. error_log(__FILE__ . ' line ' . __LINE__ . ': ' . (ini_get('track_errors') != false ? $php_errormsg : 'error not recorded because track_errors is off in your php.ini'), 0);
  56. }
  57. $res = @fwrite($fp, base64_encode(serialize($course)));
  58. if ($res === false) {
  59. error_log(__FILE__ . ' line ' . __LINE__ . ': ' . (ini_get('track_errors') != false ? $php_errormsg : 'error not recorded because track_errors is off in your php.ini'), 0);
  60. }
  61. $res = @fclose($fp);
  62. if ($res === false) {
  63. error_log(__FILE__ . ' line ' . __LINE__ . ': ' . (ini_get('track_errors') != false ? $php_errormsg : 'error not recorded because track_errors is off in your php.ini'), 0);
  64. }
  65. // Copy all documents to the temp-dir
  66. if (isset($course->resources[RESOURCE_DOCUMENT]) && is_array($course->resources[RESOURCE_DOCUMENT])) {
  67. foreach ($course->resources[RESOURCE_DOCUMENT] as $document) {
  68. if ($document->file_type == DOCUMENT) {
  69. $doc_dir = $backup_dir . $document->path;
  70. @mkdir(dirname($doc_dir), $perm_dirs, true);
  71. if (file_exists($course->path . $document->path)) {
  72. copy($course->path . $document->path, $doc_dir);
  73. }
  74. } else {
  75. @mkdir($backup_dir . $document->path, $perm_dirs, true);
  76. }
  77. }
  78. }
  79. // Copy all scorm documents to the temp-dir
  80. if (isset($course->resources[RESOURCE_SCORM]) && is_array($course->resources[RESOURCE_SCORM])) {
  81. foreach ($course->resources[RESOURCE_SCORM] as $document) {
  82. $doc_dir = dirname($backup_dir . $document->path);
  83. @mkdir($doc_dir, $perm_dirs, true);
  84. FileManager::copyDirTo($course->path . $document->path, $doc_dir, false);
  85. }
  86. }
  87. // Copy calendar attachments.
  88. if (isset($course->resources[RESOURCE_EVENT]) && is_array($course->resources[RESOURCE_EVENT])) {
  89. $doc_dir = dirname($backup_dir . '/upload/calendar/');
  90. @mkdir($doc_dir, $perm_dirs, true);
  91. FileManager::copyDirTo($course->path . 'upload/calendar/', $doc_dir, false);
  92. }
  93. // Copy Learning path author image.
  94. if (isset($course->resources[RESOURCE_LEARNPATH]) && is_array($course->resources[RESOURCE_LEARNPATH])) {
  95. $doc_dir = dirname($backup_dir . '/upload/learning_path/');
  96. @mkdir($doc_dir, $perm_dirs, true);
  97. FileManager::copyDirTo($course->path . 'upload/learning_path/', $doc_dir, false);
  98. }
  99. // Copy announcements attachments.
  100. if (isset($course->resources[RESOURCE_ANNOUNCEMENT]) && is_array($course->resources[RESOURCE_ANNOUNCEMENT])) {
  101. $doc_dir = dirname($backup_dir . '/upload/announcements/');
  102. @mkdir($doc_dir, $perm_dirs, true);
  103. FileManager::copyDirTo($course->path . 'upload/announcements/', $doc_dir, false);
  104. }
  105. // Copy work folders (only folders)
  106. if (isset($course->resources[RESOURCE_WORK]) && is_array($course->resources[RESOURCE_WORK])) {
  107. $doc_dir = dirname($backup_dir . '/upload/work/');
  108. @mkdir($doc_dir, $perm_dirs, true);
  109. // @todo: adjust to only create subdirs, but not copy files
  110. FileManager::copyDirTo($course->path . 'upload/work/', $doc_dir, false);
  111. }
  112. // Zip the course-contents
  113. $zip = new PclZip($zip_dir . $zip_file);
  114. $zip->create($zip_dir . $tmp_dir_name, PCLZIP_OPT_REMOVE_PATH, $zip_dir . $tmp_dir_name . '/');
  115. //$zip->deleteByIndex(0);
  116. // Remove the temp-dir.
  117. api_rmdirr($backup_dir);
  118. return $zip_file;
  119. }
  120. /**
  121. * @param int $user_id
  122. * @return array
  123. */
  124. static function get_available_backups($user_id = null) {
  125. $backup_files = array();
  126. $dirname = api_get_path(SYS_ARCHIVE_PATH) . '';
  127. if ($dir = opendir($dirname)) {
  128. while (($file = readdir($dir)) !== false) {
  129. $file_parts = explode('_', $file);
  130. if (count($file_parts) == 3) {
  131. $owner_id = $file_parts[0];
  132. $course_code = $file_parts[1];
  133. $file_parts = explode('.', $file_parts[2]);
  134. $date = $file_parts[0];
  135. $ext = $file_parts[1];
  136. if ($ext == 'zip' && ($user_id != null && $owner_id == $user_id || $user_id == null)) {
  137. $date = substr($date, 0, 4) . '-' . substr($date, 4, 2) . '-' . substr($date, 6, 2) . ' ' . substr($date, 8, 2) . ':' . substr($date, 10, 2) . ':' . substr($date, 12, 2);
  138. $backup_files[] = array('file' => $file, 'date' => $date, 'course_code' => $course_code);
  139. }
  140. }
  141. }
  142. closedir($dir);
  143. }
  144. return $backup_files;
  145. }
  146. /**
  147. * @param array $file
  148. * @return bool|string
  149. */
  150. public static function import_uploaded_file($file)
  151. {
  152. $new_filename = uniqid('') . '.zip';
  153. $new_dir = api_get_path(SYS_ARCHIVE_PATH);
  154. if (is_dir($new_dir) && is_writable($new_dir)) {
  155. move_uploaded_file($file, api_get_path(SYS_ARCHIVE_PATH).$new_filename);
  156. return $new_filename;
  157. }
  158. return false;
  159. }
  160. /**
  161. * Read a course-object from a zip-file
  162. * @param string $filename
  163. * @param boolean $delete Delete the file after reading the course?
  164. *
  165. * @return course The course
  166. * @todo Check if the archive is a correct Chamilo-export
  167. */
  168. public static function read_course($filename, $delete = false)
  169. {
  170. CourseArchiver::clean_backup_dir();
  171. // Create a temp directory
  172. $tmp_dir_name = 'CourseArchiver_' . uniqid('');
  173. $unzip_dir = api_get_path(SYS_ARCHIVE_PATH).$tmp_dir_name;
  174. mkdir($unzip_dir, api_get_permissions_for_new_directories(), true);
  175. copy(api_get_path(SYS_ARCHIVE_PATH).$filename, $unzip_dir.'/backup.zip');
  176. // unzip the archive
  177. $zip = new PclZip($unzip_dir.'/backup.zip');
  178. chdir($unzip_dir);
  179. $list = $zip->extract(PCLZIP_OPT_TEMP_FILE_ON);
  180. if ($list == 0) {
  181. /*global $app;
  182. $errorMessage = $zip->errorInfo(true);
  183. $app['session']->getFlashBag()->add('warning', $errorMessage);*/
  184. }
  185. // remove the archive-file
  186. if ($delete) {
  187. if (file_exists(api_get_path(SYS_ARCHIVE_PATH).$filename)) {
  188. unlink(api_get_path(SYS_ARCHIVE_PATH).$filename);
  189. }
  190. }
  191. // Read the course
  192. if (!is_file('course_info.dat')) {
  193. return new Course();
  194. }
  195. $contents = file_get_contents('course_info.dat');
  196. // CourseCopyLearnpath class appeared in Chamilo 1.8.7, it is the former Learnpath class in the "Copy course" tool.
  197. // For backward comaptibility with archives created on Chamilo 1.8.6.2 or older systems, we have to do the following:
  198. // Before unserialization, if class name "Learnpath" was found, it should be renamed as "CourseCopyLearnpath".
  199. $course = unserialize(str_replace('O:9:"Learnpath":', 'O:19:"CourseCopyLearnpath":', base64_decode($contents)));
  200. if (get_class($course) != 'Course') {
  201. return new Course();
  202. }
  203. $course->backup_path = $unzip_dir;
  204. return $course;
  205. }
  206. }