|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00003 // This file is part of Moodle - http://moodle.org/ 00004 // 00005 // Moodle is free software: you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation, either version 3 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // Moodle is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00017 00033 abstract class backup_controller_dbops extends backup_dbops { 00034 00035 public static function save_controller($controller, $checksum) { 00036 global $DB; 00037 // Check we are going to save one backup_controller 00038 if (! $controller instanceof backup_controller) { 00039 throw new backup_controller_exception('backup_controller_expected'); 00040 } 00041 // Check checksum is ok. Sounds silly but it isn't ;-) 00042 if (!$controller->is_checksum_correct($checksum)) { 00043 throw new backup_dbops_exception('backup_controller_dbops_saving_checksum_mismatch'); 00044 } 00045 // Get all the columns 00046 $rec = new stdclass(); 00047 $rec->backupid = $controller->get_backupid(); 00048 $rec->operation = $controller->get_operation(); 00049 $rec->type = $controller->get_type(); 00050 $rec->itemid = $controller->get_id(); 00051 $rec->format = $controller->get_format(); 00052 $rec->interactive = $controller->get_interactive(); 00053 $rec->purpose = $controller->get_mode(); 00054 $rec->userid = $controller->get_userid(); 00055 $rec->status = $controller->get_status(); 00056 $rec->execution = $controller->get_execution(); 00057 $rec->executiontime= $controller->get_executiontime(); 00058 $rec->checksum = $checksum; 00059 // Serialize information 00060 $rec->controller = base64_encode(serialize($controller)); 00061 // Send it to DB 00062 if ($recexists = $DB->get_record('backup_controllers', array('backupid' => $rec->backupid))) { 00063 $rec->id = $recexists->id; 00064 $rec->timemodified = time(); 00065 $DB->update_record('backup_controllers', $rec); 00066 } else { 00067 $rec->timecreated = time(); 00068 $rec->timemodified = 0; 00069 $rec->id = $DB->insert_record('backup_controllers', $rec); 00070 } 00071 return $rec->id; 00072 } 00073 00074 public static function load_controller($backupid) { 00075 global $DB; 00076 if (! $controllerrec = $DB->get_record('backup_controllers', array('backupid' => $backupid))) { 00077 throw new backup_dbops_exception('backup_controller_dbops_nonexisting'); 00078 } 00079 $controller = unserialize(base64_decode($controllerrec->controller)); 00080 // Check checksum is ok. Sounds silly but it isn't ;-) 00081 if (!$controller->is_checksum_correct($controllerrec->checksum)) { 00082 throw new backup_dbops_exception('backup_controller_dbops_loading_checksum_mismatch'); 00083 } 00084 return $controller; 00085 } 00086 00087 public static function create_backup_ids_temp_table($backupid) { 00088 self::create_temptable_from_real_table($backupid, 'backup_ids_template', 'backup_ids_temp'); 00089 } 00090 00094 public static function create_temptable_from_real_table($backupid, $realtablename, $temptablename) { 00095 global $CFG, $DB; 00096 $dbman = $DB->get_manager(); // We are going to use database_manager services 00097 00098 // As far as xmldb objects use a lot of circular references (prev and next) and we aren't destroying 00099 // them at all, that causes one memory leak of about 3M per backup execution, not problematic for 00100 // individual backups but critical for automated (multiple) ones. 00101 // So we are statically caching the xmldb_table definition here to produce the leak "only" once 00102 static $xmldb_tables = array(); 00103 00104 // Not cached, get it 00105 if (!isset($xmldb_tables[$realtablename])) { 00106 // Note: For now we are going to load the realtablename from core lib/db/install.xml 00107 // that way, any change in the "template" will be applied here automatically. If this causes 00108 // too much slow, we can always forget about the template and keep maintained the xmldb_table 00109 // structure inline - manually - here. 00110 // TODO: Right now, loading the whole lib/db/install.xml is "eating" 10M, we should 00111 // change our way here in order to decrease that memory usage 00112 $templatetablename = $realtablename; 00113 $targettablename = $temptablename; 00114 $xmlfile = $CFG->dirroot . '/lib/db/install.xml'; 00115 $xmldb_file = new xmldb_file($xmlfile); 00116 if (!$xmldb_file->fileExists()) { 00117 throw new ddl_exception('ddlxmlfileerror', null, 'File does not exist'); 00118 } 00119 $loaded = $xmldb_file->loadXMLStructure(); 00120 if (!$loaded || !$xmldb_file->isLoaded()) { 00121 throw new ddl_exception('ddlxmlfileerror', null, 'not loaded??'); 00122 } 00123 $xmldb_structure = $xmldb_file->getStructure(); 00124 $xmldb_table = $xmldb_structure->getTable($templatetablename); 00125 if (is_null($xmldb_table)) { 00126 throw new ddl_exception('ddlunknowntable', null, 'The table ' . $templatetablename . ' is not defined in file ' . $xmlfile); 00127 } 00128 // Clean prev & next, we are alone 00129 $xmldb_table->setNext(null); 00130 $xmldb_table->setPrevious(null); 00131 // Rename 00132 $xmldb_table->setName($targettablename); 00133 // Cache it 00134 $xmldb_tables[$realtablename] = $xmldb_table; 00135 } 00136 // Arrived here, we have the table always in static cache, get it 00137 $xmldb_table = $xmldb_tables[$realtablename]; 00138 // Set default backupid (not needed but this enforce any missing backupid). That's hackery in action! 00139 $xmldb_table->getField('backupid')->setDefault($backupid); 00140 00141 $dbman->create_temp_table($xmldb_table); // And create it 00142 } 00143 00144 public static function drop_backup_ids_temp_table($backupid) { 00145 global $DB; 00146 $dbman = $DB->get_manager(); // We are going to use database_manager services 00147 00148 $targettablename = 'backup_ids_temp'; 00149 $table = new xmldb_table($targettablename); 00150 $dbman->drop_temp_table($table); // And drop it 00151 } 00152 00156 public static function get_courseid_from_type_id($type, $id) { 00157 global $DB; 00158 if ($type == backup::TYPE_1COURSE) { 00159 return $id; // id is the course id 00160 00161 } else if ($type == backup::TYPE_1SECTION) { 00162 if (! $courseid = $DB->get_field('course_sections', 'course', array('id' => $id))) { 00163 throw new backup_dbops_exception('course_not_found_for_section', $id); 00164 } 00165 return $courseid; 00166 } else if ($type == backup::TYPE_1ACTIVITY) { 00167 if (! $courseid = $DB->get_field('course_modules', 'course', array('id' => $id))) { 00168 throw new backup_dbops_exception('course_not_found_for_moduleid', $id); 00169 } 00170 return $courseid; 00171 } 00172 } 00173 00178 private static function get_activity_backup_information($task) { 00179 00180 $contentinfo = array( 00181 'moduleid' => $task->get_moduleid(), 00182 'sectionid' => $task->get_sectionid(), 00183 'modulename' => $task->get_modulename(), 00184 'title' => $task->get_name(), 00185 'directory' => 'activities/' . $task->get_modulename() . '_' . $task->get_moduleid()); 00186 00187 // Now get activity settings 00188 // Calculate prefix to find valid settings 00189 $prefix = basename($contentinfo['directory']); 00190 $settingsinfo = array(); 00191 foreach ($task->get_settings() as $setting) { 00192 // Discard ones without valid prefix 00193 if (strpos($setting->get_name(), $prefix) !== 0) { 00194 continue; 00195 } 00196 // Validate level is correct (activity) 00197 if ($setting->get_level() != backup_setting::ACTIVITY_LEVEL) { 00198 throw new backup_controller_exception('setting_not_activity_level', $setting); 00199 } 00200 $settinginfo = array( 00201 'level' => 'activity', 00202 'activity' => $prefix, 00203 'name' => $setting->get_name(), 00204 'value' => $setting->get_value()); 00205 $settingsinfo[$setting->get_name()] = (object)$settinginfo; 00206 } 00207 return array($contentinfo, $settingsinfo); 00208 } 00209 00214 private static function get_section_backup_information($task) { 00215 00216 $contentinfo = array( 00217 'sectionid' => $task->get_sectionid(), 00218 'title' => $task->get_name(), 00219 'directory' => 'sections/' . 'section_' . $task->get_sectionid()); 00220 00221 // Now get section settings 00222 // Calculate prefix to find valid settings 00223 $prefix = basename($contentinfo['directory']); 00224 $settingsinfo = array(); 00225 foreach ($task->get_settings() as $setting) { 00226 // Discard ones without valid prefix 00227 if (strpos($setting->get_name(), $prefix) !== 0) { 00228 continue; 00229 } 00230 // Validate level is correct (section) 00231 if ($setting->get_level() != backup_setting::SECTION_LEVEL) { 00232 throw new backup_controller_exception('setting_not_section_level', $setting); 00233 } 00234 $settinginfo = array( 00235 'level' => 'section', 00236 'section' => $prefix, 00237 'name' => $setting->get_name(), 00238 'value' => $setting->get_value()); 00239 $settingsinfo[$setting->get_name()] = (object)$settinginfo; 00240 } 00241 return array($contentinfo, $settingsinfo); 00242 } 00243 00248 private static function get_course_backup_information($task) { 00249 00250 $contentinfo = array( 00251 'courseid' => $task->get_courseid(), 00252 'title' => $task->get_name(), 00253 'directory' => 'course'); 00254 00255 // Now get course settings 00256 // Calculate prefix to find valid settings 00257 $prefix = basename($contentinfo['directory']); 00258 $settingsinfo = array(); 00259 foreach ($task->get_settings() as $setting) { 00260 // Discard ones without valid prefix 00261 if (strpos($setting->get_name(), $prefix) !== 0) { 00262 continue; 00263 } 00264 // Validate level is correct (course) 00265 if ($setting->get_level() != backup_setting::COURSE_LEVEL) { 00266 throw new backup_controller_exception('setting_not_course_level', $setting); 00267 } 00268 $settinginfo = array( 00269 'level' => 'course', 00270 'name' => $setting->get_name(), 00271 'value' => $setting->get_value()); 00272 $settingsinfo[$setting->get_name()] = (object)$settinginfo; 00273 } 00274 return array($contentinfo, $settingsinfo); 00275 } 00276 00281 private static function get_root_backup_information($task) { 00282 00283 // Now get root settings 00284 $settingsinfo = array(); 00285 foreach ($task->get_settings() as $setting) { 00286 // Validate level is correct (root) 00287 if ($setting->get_level() != backup_setting::ROOT_LEVEL) { 00288 throw new backup_controller_exception('setting_not_root_level', $setting); 00289 } 00290 $settinginfo = array( 00291 'level' => 'root', 00292 'name' => $setting->get_name(), 00293 'value' => $setting->get_value()); 00294 $settingsinfo[$setting->get_name()] = (object)$settinginfo; 00295 } 00296 return array(null, $settingsinfo); 00297 } 00298 00303 public static function get_moodle_backup_information($backupid) { 00304 00305 $detailsinfo = array(); // Information details 00306 $contentsinfo= array(); // Information about backup contents 00307 $settingsinfo= array(); // Information about backup settings 00308 $bc = self::load_controller($backupid); // Load controller 00309 00310 // Details info 00311 $detailsinfo['id'] = $bc->get_id(); 00312 $detailsinfo['backup_id'] = $bc->get_backupid(); 00313 $detailsinfo['type'] = $bc->get_type(); 00314 $detailsinfo['format'] = $bc->get_format(); 00315 $detailsinfo['interactive'] = $bc->get_interactive(); 00316 $detailsinfo['mode'] = $bc->get_mode(); 00317 $detailsinfo['execution'] = $bc->get_execution(); 00318 $detailsinfo['executiontime'] = $bc->get_executiontime(); 00319 $detailsinfo['userid'] = $bc->get_userid(); 00320 $detailsinfo['courseid'] = $bc->get_courseid(); 00321 00322 00323 // Init content placeholders 00324 $contentsinfo['activities'] = array(); 00325 $contentsinfo['sections'] = array(); 00326 $contentsinfo['course'] = array(); 00327 00328 // Contents info (extract information from tasks) 00329 foreach ($bc->get_plan()->get_tasks() as $task) { 00330 00331 if ($task instanceof backup_activity_task) { // Activity task 00332 00333 if ($task->get_setting_value('included')) { // Only return info about included activities 00334 list($contentinfo, $settings) = self::get_activity_backup_information($task); 00335 $contentsinfo['activities'][] = $contentinfo; 00336 $settingsinfo = array_merge($settingsinfo, $settings); 00337 } 00338 00339 } else if ($task instanceof backup_section_task) { // Section task 00340 00341 if ($task->get_setting_value('included')) { // Only return info about included sections 00342 list($contentinfo, $settings) = self::get_section_backup_information($task); 00343 $contentsinfo['sections'][] = $contentinfo; 00344 $settingsinfo = array_merge($settingsinfo, $settings); 00345 } 00346 00347 } else if ($task instanceof backup_course_task) { // Course task 00348 00349 list($contentinfo, $settings) = self::get_course_backup_information($task); 00350 $contentsinfo['course'][] = $contentinfo; 00351 $settingsinfo = array_merge($settingsinfo, $settings); 00352 00353 } else if ($task instanceof backup_root_task) { // Root task 00354 00355 list($contentinfo, $settings) = self::get_root_backup_information($task); 00356 $settingsinfo = array_merge($settingsinfo, $settings); 00357 } 00358 } 00359 00360 $bc->destroy(); // Always need to destroy controller to handle circular references 00361 00362 return array(array((object)$detailsinfo), $contentsinfo, $settingsinfo); 00363 } 00364 00369 public static function apply_version_and_release() { 00370 global $CFG; 00371 00372 if ($CFG->backup_version < backup::VERSION) { 00373 set_config('backup_version', backup::VERSION); 00374 set_config('backup_release', backup::RELEASE); 00375 } 00376 } 00377 00381 public static function backup_includes_mnet_remote_users($backupid) { 00382 global $CFG, $DB; 00383 00384 $sql = "SELECT COUNT(*) 00385 FROM {backup_ids_temp} b 00386 JOIN {user} u ON u.id = b.itemid 00387 WHERE b.backupid = ? 00388 AND b.itemname = 'userfinal' 00389 AND u.mnethostid != ?"; 00390 $count = $DB->count_records_sql($sql, array($backupid, $CFG->mnet_localhost_id)); 00391 return (int)(bool)$count; 00392 } 00393 00399 public static function backup_get_original_course_info($courseid) { 00400 global $DB; 00401 return $DB->get_record('course', array('id' => $courseid), 'fullname, shortname, startdate'); 00402 } 00403 00413 public static function apply_config_defaults(backup_controller $controller) { 00414 // Based on the mode of the backup (general, automated, import, hub...) 00415 // decide the action to perform to get defaults loaded 00416 $mode = $controller->get_mode(); 00417 00418 switch ($mode) { 00419 case backup::MODE_GENERAL: 00420 // Load the general defaults 00421 self::apply_general_config_defaults($controller); 00422 break; 00423 case backup::MODE_AUTOMATED: 00424 // TODO: Move the loading from automatic stuff to here 00425 break; 00426 default: 00427 // Nothing to do for other modes (IMPORT/HUB...). Some day we 00428 // can define defaults (admin UI...) for them if we want to 00429 } 00430 } 00431 00437 private static function apply_general_config_defaults(backup_controller $controller) { 00438 $settings = array( 00439 // Config name => Setting name 00440 'backup_general_users' => 'users', 00441 'backup_general_anonymize' => 'anonymize', 00442 'backup_general_role_assignments' => 'role_assignments', 00443 'backup_general_user_files' => 'user_files', 00444 'backup_general_activities' => 'activities', 00445 'backup_general_blocks' => 'blocks', 00446 'backup_general_filters' => 'filters', 00447 'backup_general_comments' => 'comments', 00448 'backup_general_userscompletion' => 'userscompletion', 00449 'backup_general_logs' => 'logs', 00450 'backup_general_histories' => 'grade_histories' 00451 ); 00452 $plan = $controller->get_plan(); 00453 foreach ($settings as $config=>$settingname) { 00454 $value = get_config('backup', $config); 00455 $locked = (get_config('backup', $config.'_locked') == true); 00456 if ($plan->setting_exists($settingname)) { 00457 $setting = $plan->get_setting($settingname); 00458 if ($setting->get_value() != $value || 1==1) { 00459 $setting->set_value($value); 00460 if ($locked) { 00461 $setting->set_status(base_setting::LOCKED_BY_CONFIG); 00462 } 00463 } 00464 } 00465 } 00466 } 00467 }