2 /************************************************************************/
4 /************************************************************************/
5 /* Copyright (c) 2002-2010 */
6 /* Inclusive Design Institute */
8 /* This program is free software. You can redistribute it and/or */
9 /* modify it under the terms of the GNU General Public License */
10 /* as published by the Free Software Foundation. */
11 /************************************************************************/
14 define('AT_MODULE_STATUS_DISABLED', 1);
15 define('AT_MODULE_STATUS_ENABLED', 2);
16 define('AT_MODULE_STATUS_MISSING', 4);
17 define('AT_MODULE_STATUS_UNINSTALLED', 8); // not in the db
18 define('AT_MODULE_STATUS_PARTIALLY_UNINSTALLED', 16); // not in the db
20 define('AT_MODULE_TYPE_CORE', 1);
21 define('AT_MODULE_TYPE_STANDARD', 2);
22 define('AT_MODULE_TYPE_EXTRA', 4);
24 define('AT_MODULE_DIR_CORE', '_core');
25 define('AT_MODULE_DIR_STANDARD', '_standard');
27 define('AT_MODULE_PATH', realpath(AT_INCLUDE_PATH.'../mods') . DIRECTORY_SEPARATOR);
33 * @author Joel Kronenberg
38 var $_modules = NULL; // array of module refs
40 function ModuleFactory($auto_load = FALSE) {
43 /* snippit to use when extending Module classes:
44 $sql = "SELECT dir_name, privilege, admin_privilege, status FROM ". TABLE_PREFIX . "modules WHERE status=".AT_MODULE_STATUS_ENABLED;
45 $result = mysql_query($sql, $db);
46 $row = mysql_fetch_assoc($result);
47 require(AT_MODULE_PATH . $row['dir_name'].'/module.php');
48 $module = new PropertiesModule($row);
51 $this->_modules = array();
53 if ($auto_load == TRUE) {
54 // initialise enabled modules
55 $sql = "SELECT dir_name, privilege, admin_privilege, status, cron_interval, cron_last_run FROM ". TABLE_PREFIX . "modules WHERE status=".AT_MODULE_STATUS_ENABLED;
56 $result = mysql_query($sql, $db);
57 while($row = mysql_fetch_assoc($result)) {
58 $module = new Module($row);
59 $this->_modules[$row['dir_name']] = $module;
66 // status := enabled | disabled | uninstalled | missing
67 // type := core | standard | extra
68 // sort := true | false (by name only)
69 // the results of this method are not cached. call sparingly.
70 function getModules($status, $type = 0, $sort = FALSE) {
74 $all_modules = array();
77 $type = AT_MODULE_TYPE_CORE | AT_MODULE_TYPE_STANDARD | AT_MODULE_TYPE_EXTRA;
80 $sql = "SELECT dir_name, privilege, admin_privilege, status, cron_interval, cron_last_run FROM ". TABLE_PREFIX . "modules";
81 $result = mysql_query($sql, $db);
83 while($row = mysql_fetch_assoc($result)) {
84 if (!isset($this->_modules[$row['dir_name']])) {
85 $module = new Module($row);
87 $module = $this->_modules[$row['dir_name']];
89 $all_modules[$row['dir_name']] = $module;
92 // small performance addition:
93 if ($status & AT_MODULE_STATUS_UNINSTALLED) {
94 $dir = opendir(AT_MODULE_PATH);
95 while (false !== ($dir_name = readdir($dir))) {
96 if (($dir_name == '.')
97 || ($dir_name == '..')
98 || ($dir_name == '.svn')
99 || ($dir_name == AT_MODULE_DIR_CORE)
100 || ($dir_name == AT_MODULE_DIR_STANDARD)) {
104 if (is_dir(AT_MODULE_PATH . $dir_name) && !isset($all_modules[$dir_name])) {
105 $module = new Module($dir_name);
106 $all_modules[$dir_name] = $module;
112 $keys = array_keys($all_modules);
113 foreach ($keys as $dir_name) {
114 $module =$all_modules[$dir_name];
115 if ($module->checkStatus($status) && $module->checkType($type)) {
116 $modules[$dir_name] = $module;
121 uasort($modules, array($this, 'compare'));
128 function & getModule($module_dir) {
129 if (!isset($this->_modules[$module_dir])) {
131 $sql = "SELECT dir_name, privilege, admin_privilege, status FROM ". TABLE_PREFIX . "modules WHERE dir_name='$module_dir'";
132 $result = mysql_query($sql, $db);
133 if ($row = mysql_fetch_assoc($result)) {
134 $module = new Module($row);
136 $module = new Module($module_dir);
138 $this->_modules[$module_dir] =& $module;
140 return $this->_modules[$module_dir];
144 // used for sorting modules
145 function compare($a, $b) {
146 return strnatcasecmp($a->getName(), $b->getName());
154 * @author Joel Kronenberg
161 var $_status; // core|enabled|disabled
162 var $_privilege; // priv bit(s) | 0 (in dec form)
163 var $_admin_privilege; // priv bit(s) | 0 (in dec form)
164 var $_display_defaults; // bit(s)
166 var $_type; // core, standard, extra
167 var $_properties; // array from xml
168 var $_cron_interval; // cron interval
169 var $_cron_last_run; // cron last run date stamp
170 var $_content_tools; // content tool icons on "edit content" page
173 function Module($row) {
174 global $_content_tools;
176 if (is_array($row)) {
177 $this->_directoryName = $row['dir_name'];
178 $this->_status = $row['status'];
179 $this->_privilege = $row['privilege'];
180 $this->_admin_privilege = $row['admin_privilege'];
181 $this->_display_defaults= isset($row['display_defaults']) ? $row['display_defaults'] : 0;
182 $this->_cron_interval = $row['cron_interval'];
183 $this->_cron_last_run = $row['cron_last_run'];
185 if (strpos($row['dir_name'], AT_MODULE_DIR_CORE) === 0) {
186 $this->_type = AT_MODULE_TYPE_CORE;
187 } else if (strpos($row['dir_name'], AT_MODULE_DIR_STANDARD) === 0) {
188 $this->_type = AT_MODULE_TYPE_STANDARD;
190 $this->_type = AT_MODULE_TYPE_EXTRA;
193 $this->_directoryName = $row;
194 $this->_status = AT_MODULE_STATUS_UNINSTALLED;
195 $this->_privilege = 0;
196 $this->_admin_privilege = 0;
197 $this->_display_defaults= 0;
198 $this->_type = AT_MODULE_TYPE_EXTRA; // standard/core are installed by default
200 $this->_content_tools = array();
204 function checkStatus($status) { return (bool) ($status & $this->_status); }
205 function isPartiallyUninstalled() { return ($this->_status == AT_MODULE_STATUS_PARTIALLY_UNINSTALLED) ? true : false; }
206 function isUninstalled() { return ($this->_status == AT_MODULE_STATUS_UNINSTALLED) ? true : false; }
207 function isEnabled() { return ($this->_status == AT_MODULE_STATUS_ENABLED) ? true : false; }
208 function isDisabled() { return ($this->_status == AT_MODULE_STATUS_DISABLED) ? true : false; }
209 function isMissing() { return ($this->_status == AT_MODULE_STATUS_MISSING) ? true : false; }
212 function checkType($type) { return (bool) ($type & $this->_type); }
213 function isCore() { return ($this->_type == AT_MODULE_TYPE_CORE) ? true : false; }
214 function isStandard() { return ($this->_type == AT_MODULE_TYPE_STANDARD) ? true : false; }
215 function isExtra() { return ($this->_type == AT_MODULE_TYPE_EXTRA) ? true : false; }
218 function getPrivilege() { return $this->_privilege; }
219 function getAdminPrivilege() { return $this->_admin_privilege; }
222 if (is_file(AT_MODULE_PATH . $this->_directoryName.'/module.php')) {
223 global $_modules, $_pages, $_stacks, $_list, $_tool, $_content_tools, $_callbacks; // $_list is for sublinks on "detail view"
225 require(AT_MODULE_PATH . $this->_directoryName.'/module.php');
227 if (isset($this->_pages)) {
228 $_pages = array_merge_recursive((array) $_pages, $this->_pages);
232 if (isset($this->_stacks)) {
234 $_stacks = array_merge((array)$_stacks, $this->_stacks);
237 // sublinks on "detail view"
238 if(isset($this->_list)) {
239 $_list = array_merge((array)$_list, $this->_list);
242 if(isset($this->_content_tools)) {
243 $_content_tools = array_merge((array)$_content_tools, $this->_content_tools);
246 if(isset($this->_callbacks)) {
247 $_callbacks = array_merge((array)$_callbacks, $this->_callbacks);
250 //TODO***********BOLOGNA***********REMOVE ME***********/
251 //tool manager (content editing)
252 if(isset($this->_tool)) {
253 $_tool = array_merge((array)$_tool, $this->_tool);
257 if (isset($_student_tool)) {
258 $this->_student_tool =& $_student_tool;
259 $_modules[] = $this->_student_tool;
263 if (isset($_group_tool)) {
264 $this->_group_tool =& $_group_tool;
270 function _initModuleProperties() {
271 if (!isset($this->_properties)) {
272 require_once(dirname(__FILE__) . '/ModuleParser.class.php');
273 $moduleParser = new ModuleParser();
274 $moduleParser->parse(@file_get_contents(AT_MODULE_PATH . $this->_directoryName.'/module.xml'));
275 if ($moduleParser->rows[0]) {
276 $this->_properties = $moduleParser->rows[0];
278 $this->_properties = array();
279 $this->setIsMissing(); // the xml file may not be found -> the dir may be missing.
285 * Get the properties of this module as found in the module.xml file
287 * @param array $properties_list list of property names
288 * @return array associative array of property/value pairs
289 * @author Joel Kronenberg
291 function getProperties($properties_list) {
292 $this->_initModuleProperties();
294 if (!$this->_properties) {
297 $properties_list = array_flip($properties_list);
298 foreach ($properties_list as $property => $garbage) {
299 $properties_list[$property] = $this->_properties[$property];
301 return $properties_list;
304 * Get a single property as found in the module.xml file
306 * @param string $property name of the property to return
307 * @return string the value of the property
308 * @author Joel Kronenberg
310 function getProperty($property) {
311 $this->_initModuleProperties();
313 if (!$this->_properties) {
317 return $this->_properties[$property];
320 function getCronInterval() {
321 return $this->_cron_interval;
326 if ($this->isUninstalled()) {
327 $name = $this->getProperty('name');
328 return current($name);
330 return _AT(basename($this->_directoryName));
333 function getDescription($lang = 'en') {
334 $this->_initModuleProperties();
336 if (!$this->_properties) {
340 if (isset($this->_properties['description'][$lang])) {
341 return $this->_properties['description'][$lang];
343 $description = current($this->_properties['description']);
347 function getChildPage($page) {
348 if (!is_array($this->_pages)) {
351 foreach ($this->_pages as $tmp_page => $item) {
352 if (!empty($item['parent']) && $item['parent'] == $page) {
359 * Checks whether or not this module can be backed-up
361 * @return boolean true if this module can be backed-up, false otherwise
362 * @author Joel Kronenberg
364 function isBackupable() {
365 return is_file(AT_MODULE_PATH . $this->_directoryName.'/module_backup.php');
368 function createGroup($group_id) {
369 if (is_file(AT_MODULE_PATH . $this->_directoryName.'/module_groups.php')) {
370 require_once(AT_MODULE_PATH . $this->_directoryName.'/module_groups.php');
371 $fn_name = basename($this->_directoryName) .'_create_group';
376 function deleteGroup($group_id) {
377 $fn_name = basename($this->_directoryName) .'_delete_group';
379 if (!function_exists($fn_name) && is_file(AT_MODULE_PATH . $this->_directoryName.'/module_groups.php')) {
380 require_once(AT_MODULE_PATH . $this->_directoryName.'/module_groups.php');
382 if (function_exists($fn_name)) {
387 function getGroupTool() {
388 if (!isset($this->_group_tool)) {
392 return $this->_group_tool;
395 function isGroupable() {
396 return is_file(AT_MODULE_PATH . $this->_directoryName.'/module_groups.php');
400 * Backup this module for a given course
402 * @param int $course_id ID of the course to backup
403 * @param object $zipfile a reference to a zipfile object
404 * @author Joel Kronenberg
406 function backup($course_id, &$zipfile) {
409 if (!isset($CSVExport)) {
410 require_once(AT_INCLUDE_PATH . 'classes/CSVExport.class.php');
411 $CSVExport = new CSVExport();
415 if ($this->isBackupable()) {
416 require(AT_MODULE_PATH . $this->_directoryName . '/module_backup.php');
418 foreach ($sql as $file_name => $table_sql) {
419 $content = $CSVExport->export($table_sql, $course_id);
421 $zipfile->add_file($content, $file_name . '.csv', $now);
427 foreach ($dirs as $dir => $path) {
428 $path = str_replace('?', $course_id, $path);
430 $zipfile->add_dir($path , $dir);
437 * Restores this module into the given course
439 * @param int $course_id ID of the course to restore into
440 * @param string $version version number of the ATutor installation used to make this backup
441 * @param string $import_dir the path to the import directory
442 * @author Joel Kronenberg
444 function restore($course_id, $version, $import_dir) {
446 if (!file_exists(AT_MODULE_PATH . $this->_directoryName.'/module_backup.php')) {
450 if (!isset($CSVImport)) {
451 require_once(AT_INCLUDE_PATH . 'classes/CSVImport.class.php');
452 $CSVImport = new CSVImport();
455 require(AT_MODULE_PATH . $this->_directoryName.'/module_backup.php');
458 foreach ($sql as $table_name => $table_sql) {
459 $CSVImport->import($table_name, $import_dir, $course_id, $version);
462 if ($this->_directoryName == '_core/content')
464 if (version_compare($version, '1.6.4', '<')) {
465 $this->convertContent164($course_id);
470 foreach ($dirs as $src => $dest) {
471 $dest = str_replace('?', $course_id, $dest);
472 copys($import_dir.$src, $dest);
478 * Delete this module's course content. If $groups is specified then it will
479 * delete all content for the groups specified.
481 * @param int $course_id ID of the course to delete
482 * @param array $groups Array of groups to delete
483 * @author Joel Kronenberg
485 function delete($course_id, $groups) {
486 if (is_file(AT_MODULE_PATH . $this->_directoryName.'/module_delete.php')) {
487 require(AT_MODULE_PATH . $this->_directoryName.'/module_delete.php');
488 if (function_exists(basename($this->_directoryName).'_delete')) {
489 $fnctn = basename($this->_directoryName).'_delete';
494 foreach ($groups as $group_id) {
495 $this->deleteGroup($group_id);
501 * Enables the installed module
503 * @author Joel Kronenberg
508 $sql = 'UPDATE '. TABLE_PREFIX . 'modules SET status='.AT_MODULE_STATUS_ENABLED.' WHERE dir_name="'.$this->_directoryName.'"';
509 $result = mysql_query($sql, $db);
513 * Sets the status to missing if the module dir doesn't exist.
515 * @param boolean $force whether or not to force the module to be missing (used for bundled extra modules upon upgrade)
516 * @author Joel Kronenberg
518 function setIsMissing($force = false) {
520 // if the directory doesn't exist then set the status to MISSING
521 if ($force || !is_dir(AT_MODULE_PATH . $this->_directoryName)) {
522 $sql = 'UPDATE '. TABLE_PREFIX . 'modules SET status='.AT_MODULE_STATUS_MISSING.' WHERE dir_name="'.$this->_directoryName.'"';
523 $result = mysql_query($sql, $db);
528 * Disables the installed module
530 * @author Joel Kronenberg
535 // remove any privileges admins, students
536 if ($this->_privilege > 1) {
537 $sql = 'UPDATE '. TABLE_PREFIX . 'course_enrollment SET `privileges`=`privileges`-'.$this->_privilege.' WHERE `privileges` > 1 AND (`privileges` & '.$this->_privilege.')<>0';
538 $result = mysql_query($sql, $db);
541 if ($this->_admin_privilege > 1) {
542 $sql = 'UPDATE '. TABLE_PREFIX . 'admins SET `privileges`=`privileges`-'.$this->_admin_privilege.' WHERE `privileges` > 1 AND (`privileges` & '.$this->_admin_privilege.')<>0';
543 $result = mysql_query($sql, $db);
546 $sql = 'UPDATE '. TABLE_PREFIX . 'modules SET status='.AT_MODULE_STATUS_DISABLED.' WHERE dir_name="'.$this->_directoryName.'"';
547 $result = mysql_query($sql, $db);
549 if (function_exists(basename($this->_directoryName).'_disable')) {
550 $fn_name = basename($this->_directoryName).'_disable';
556 * Installs the module
558 * @author Joel Kronenberg
563 // should check if this module is already installed...
565 if (file_exists(AT_MODULE_PATH . $this->_directoryName . '/module_install.php')) {
566 require(AT_MODULE_PATH . $this->_directoryName . '/module_install.php');
569 if (!$msg->containsErrors()) {
572 $sql = "SELECT MAX(`privilege`) AS `privilege`, MAX(admin_privilege) AS admin_privilege FROM ".TABLE_PREFIX."modules";
573 $result = mysql_query($sql, $db);
574 $row = mysql_fetch_assoc($result);
576 if (($_course_privilege === TRUE) || ((string) $_course_privilege == 'new')) {
577 $priv = $row['privilege'] * 2;
578 } else if ($_course_privilege == AT_PRIV_ADMIN) {
579 $priv = AT_PRIV_ADMIN;
584 if (($_admin_privilege === TRUE) || ((string) $_admin_privilege == 'new')) {
585 $admin_priv = $row['admin_privilege'] * 2;
587 $admin_priv = AT_ADMIN_PRIV_ADMIN;
590 if (isset($_cron_interval)) {
591 $_cron_interval = abs($_cron_interval);
596 $sql = 'INSERT INTO '. TABLE_PREFIX . 'modules VALUES ("'.$this->_directoryName.'", '.AT_MODULE_STATUS_DISABLED.', '.$priv.', '.$admin_priv.', '.$_cron_interval.', 0)';
597 mysql_query($sql, $db);
598 if (mysql_affected_rows($db) != 1) {
599 // in case this module has to be re-installed (because it was Missing)
600 $sql = 'UPDATE '. TABLE_PREFIX . 'modules SET status='.AT_MODULE_STATUS_DISABLED.' WHERE dir_name="'.$this->_directoryName.'"';
601 mysql_query($sql, $db);
607 * Uninstalls the module
609 * @author Cindy Qi Li
611 function uninstall($del_data='') {
614 if (file_exists(AT_MODULE_PATH . $this->_directoryName . '/module_uninstall.php') && $del_data == 1)
616 require(AT_MODULE_PATH . $this->_directoryName . '/module_uninstall.php');
619 if (!$msg->containsErrors())
621 require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php');
623 if (!clr_dir(AT_MODULE_PATH . $this->_directoryName))
624 $msg->addError(array('MODULE_UNINSTALL', '<li>'.AT_MODULE_PATH . $this->_directoryName.' can not be removed. Please manually remove it.</li>'));
627 if (!$msg->containsErrors())
631 $sql = "DELETE FROM ". TABLE_PREFIX . "modules WHERE dir_name = '".$this->_directoryName."'";
632 mysql_query($sql, $db);
635 if ($msg->containsErrors())
639 $sql = "UPDATE ". TABLE_PREFIX . "modules SET status=".AT_MODULE_STATUS_PARTIALLY_UNINSTALLED." WHERE dir_name='".$this->_directoryName."'";
640 mysql_query($sql, $db);
644 function getStudentTools() {
645 if (!isset($this->_student_tool)) {
649 return $this->_student_tool;
654 if ( ($this->_cron_last_run + ($this->_cron_interval * 60)) < time()) {
655 if (is_file(AT_MODULE_PATH . $this->_directoryName.'/module_cron.php')) {
656 require(AT_MODULE_PATH . $this->_directoryName.'/module_cron.php');
657 if (function_exists(basename($this->_directoryName).'_cron')) {
658 $fnctn = basename($this->_directoryName).'_cron';
662 $this->updateCronLastRun();
666 // i'm private! update the last time the cron was run
667 function updateCronLastRun() {
670 $sql = "UPDATE ".TABLE_PREFIX."modules SET cron_last_run=".time()." WHERE dir_name='$this->_directoryName'";
671 mysql_query($sql, $db);
676 * Get the latest news from the Module.
678 * @author Harris Wong
682 global $msg, $enrolled_courses, $db;
684 if (!isset($enrolled_courses)){
685 $sql = 'SELECT E.approved, E.last_cid, C.* FROM AT_course_enrollment E, AT_courses C WHERE E.member_id='.$_SESSION['member_id'].' AND E.course_id=C.course_id ORDER BY C.title';
686 $result = mysql_query($sql, $db);
688 while($row = mysql_fetch_assoc($result)){
689 $enrolled_courses = $enrolled_courses . $row['course_id'] . ', ';
691 $enrolled_courses = substr($enrolled_courses, 0, -2);
693 if ($enrolled_courses != ''){
694 $enrolled_courses = '(' . $enrolled_courses . ')';
699 if (file_exists(AT_MODULE_PATH . $this->_directoryName . '/module_news.php')) {
700 require(AT_MODULE_PATH . $this->_directoryName . '/module_news.php');
701 if (function_exists(basename($this->_directoryName).'_news')) {
702 $fnctn = basename($this->_directoryName).'_news';
703 return $fnctn($course_id);
709 * Get the output that this module wants to add onto content page.
714 function getContent($cid){
715 if (file_exists(AT_MODULE_PATH . $this->_directoryName.'/ModuleCallbacks.class.php') &&
716 isset($this->_callbacks[basename($this->_directoryName)]))
718 require(AT_MODULE_PATH . $this->_directoryName.'/ModuleCallbacks.class.php');
719 if (method_exists($this->_callbacks[basename($this->_directoryName)], "appendContent")) {
720 eval('$output = '.$this->_callbacks[basename($this->_directoryName)]."::appendContent($cid);");
727 private function convertContent164($course_id) {
730 /* convert all content nodes to the IMS standard. (adds null nodes for all top pages) */
731 /* 1. Convert db to a tree */
732 $sql = 'SELECT * FROM '.TABLE_PREFIX.'content where course_id='.$course_id;
734 $result = mysql_query($sql, $db);
735 $content_array = array();
737 while ($row = mysql_fetch_assoc($result)){
738 $content_array[$row['content_parent_id']][$row['ordering']] = $row['content_id'];
740 $tree = $this->buildTree($content_array[0], $content_array);
742 /* 2. Restructure the tree */
743 $tree = $this->rebuild($tree);
745 /* 3. Update the Db based on this new tree */
746 $this->reconstruct($tree, '', 0, TABLE_PREFIX);
750 * Construct a tree based on table entries
751 * @param array current node, (current parent)
752 * @param mixed a set of parents, where each parents is in the format of [parent]=>children
753 * should remain the same throughout the recursion.
754 * @return A tree structure representation of the content entries.
755 * @author Harris Wong
757 private function buildTree($current, $content_array){
759 foreach($current as $order=>$content_id){
761 if (isset($content_array[$content_id])){
762 $wrapper[$content_id] = $this->buildTree($content_array[$content_id], $content_array);
767 $folder['order_'.$order] = $wrapper;
770 $folder['order_'.$order] = $content_id;
778 * Transverse the content tree structure, and reconstruct it with the IMS spec.
779 * This tree has the structure of [order=>array(id)], so the first layer is its order, second is the id
780 * if param merge is true, if node!=null, merge it to top layer, and + offset to all others
781 * @param mixed Tree from the buildTree() function, or sub-tree
782 * @param mixed the current tree.
783 * @return A new content tree that meets the IMS specification.
784 * @author Harris Wong
786 private function rebuild($tree, $node=''){
789 if (!is_array($tree)){
793 $tree['order_0'] = $node;
796 //go through the tree
797 foreach($tree as $k=>$v){
798 if (preg_match('/order\_([\d]+)/', $k, $match)==1){
799 //if this is the order layer
800 $folder['order_'.($match[1]+$order_offset)] = $this->rebuild($v);
802 //if this is the content layer
804 $folder[$k] = $this->rebuild($v, $k);
812 * Transverse the tree and update/insert entries based on the updated structure.
813 * @param array The tree from rebuild(), and the subtree from the recursion.
814 * @param int the ordering of this subtree respect to its parent.
815 * @param int parent content id
816 * @return null (nothing to return, it updates the db only)
818 private function reconstruct($tree, $order, $content_parent_id, $table_prefix){
822 if (!is_array($tree)){
823 $sql = 'UPDATE '.$table_prefix."content SET ordering=$order, content_parent_id=$content_parent_id WHERE content_id=$tree";
824 if (!mysql_query($sql, $db)){
830 foreach ($tree as $k=>$v){
831 if (preg_match('/order\_([\d]+)/', $k, $match)==1){
833 $this->reconstruct($v, $match[1], $content_parent_id, $table_prefix); //inherit the previous layer id
835 //content folder layer
836 $sql = 'SELECT * FROM '.$table_prefix."content WHERE content_id=$k";
837 $result = mysql_query($sql, $db);
838 $old_content_row = mysql_fetch_assoc($result);
839 $sql = 'INSERT INTO '.$table_prefix.'content (course_id, content_parent_id, ordering, last_modified, revision, formatting, release_date, keywords, content_path, title, use_customized_head, allow_test_export, content_type) VALUES ('
840 .$old_content_row['course_id'] . ', '
841 .$content_parent_id . ', '
843 .'\''. $old_content_row['last_modified'] . '\', '
844 .$old_content_row['revision'] . ', '
845 .$old_content_row['formatting'] . ', '
846 .'\''. $old_content_row['release_date'] . '\', '
847 .'\''. $old_content_row['keywords'] . '\', '
848 .'\''. $old_content_row['content_path'] . '\', '
849 .'\''. $old_content_row['title'] . '\', '
850 .$old_content_row['use_customized_head'] . ', '
851 .$old_content_row['allow_test_export'] . ', '
854 if (mysql_query($sql, $db)){
855 $folder_id = mysql_insert_id();
856 $this->reconstruct($v, '', $folder_id, $table_prefix);