MDL-21432 backup - role load/mappings and integration with UI via controller (MDL-23109)

This commit is contained in:
Eloy Lafuente
2010-07-20 22:51:04 +00:00
parent 1331264bdf
commit 8d4e41f4ec
5 changed files with 119 additions and 16 deletions
+15 -2
View File
@@ -265,14 +265,27 @@ class restore_controller extends backup implements loggable {
return $this->plan->execute();
}
public function execute_precheck() {
/**
* Execute the restore prechecks to detect any problem before proceed with restore
*
* This function checks various parts of the restore (versions, users, roles...)
* returning true if everything was ok or false if any warning/error was detected.
* Any warning/error is returned by the get_precheck_results() method.
* Note: if any problem is found it will, automatically, drop all the restore temp
* tables as far as the next step is to inform about the warning/errors. If no problem
* is found, then default behaviour is to keep the temp tables so, in the same request
* restore will be executed, saving a lot of checks to be executed again.
* Note: If for any reason (UI to show after prechecks...) you want to force temp tables
* to be dropped always, you can pass true to the $droptemptablesafter parameter
*/
public function execute_precheck($droptemptablesafter = false) {
if (is_array($this->precheck)) {
throw new restore_controller_exception('precheck_alredy_executed', $this->status);
}
if ($this->status != backup::STATUS_NEED_PRECHECK) {
throw new restore_controller_exception('cannot_precheck_wrong_status', $this->status);
}
$this->precheck = restore_prechecks_helper::execute_prechecks($this);
$this->precheck = restore_prechecks_helper::execute_prechecks($this, $droptemptablesafter);
if (!array_key_exists('errors', $this->precheck)) { // No errors, can be executed
$this->set_status(backup::STATUS_AWAITING);
}
+7 -2
View File
@@ -131,15 +131,18 @@ class restore_load_included_files extends restore_structure_step {
class restore_load_and_map_roles extends restore_execution_step {
protected function define_execution() {
if ($this->task->get_preloaded_information()) { // if info is already preloaded, nothing to do
if ($this->task->get_preloaded_information()) { // if info is already preloaded
return;
}
$file = $this->get_basepath() . '/roles.xml';
// Load needed toles to temp_ids
restore_dbops::load_roles_to_tempids($this->get_restoreid(), $file);
// Process roles, mapping/skipping. Any error throws exception
restore_dbops::process_included_roles($this->get_restoreid(), $this->task->get_courseid(), $this->task->get_userid(), $this->task->is_samesite());
// Note we pass controller's info because it can contain role mapping information
// about manual mappings performed by UI
restore_dbops::process_included_roles($this->get_restoreid(), $this->task->get_courseid(), $this->task->get_userid(), $this->task->is_samesite(), $this->task->get_info()->role_mappings);
}
}
@@ -416,6 +419,8 @@ class restore_outcomes_structure_step extends restore_structure_step {
// Remap the user
$userid = $this->get_mappingid('user', $data->usermodified);
$data->usermodified = $userid ? $userid : $this->get_userid();
// Remap the scale
$data->scaleid = $this->get_mappingid('scale', $data->scaleid);
// Remap the course if course outcome
$data->courseid = $data->courseid ? $this->get_courseid() : null;
// If global outcome (course=null), check the user has perms to create it
+85 -6
View File
@@ -113,24 +113,103 @@ abstract class restore_dbops {
/**
* Precheck the loaded roles, return empty array if everything is ok, and
* array with 'errors', 'warnings' elements (suitable to be used by restore_prechecks)
* with any problem found
* with any problem found. At the same time, store all the mapping into backup_ids_temp
* and also put the information into $rolemappings (controller->info), so it can be reworked later by
* post-precheck stages while at the same time accept modified info in the same object coming from UI
*/
public static function precheck_included_roles($restoreid, $courseid, $userid, $samesite) {
debugging('implement the roles mapping/skip here. returns errors/warnings array', DEBUG_DEVELOPER);
return array();
public static function precheck_included_roles($restoreid, $courseid, $userid, $samesite, $rolemappings) {
global $DB;
$problems = array(); // To store warnings/errors
// Get loaded roles from backup_ids
$rs = $DB->get_recordset('backup_ids_temp', array('backupid' => $restoreid, 'itemname' => 'role'), '', 'itemid');
foreach ($rs as $recrole) {
// If the rolemappings->modified flag is set, that means that we are coming from
// manually modified mappings (by UI), so accept those mappings an put them to backup_ids
if ($rolemappings->modified) {
$target = $rolemappings->mappings[$recrole->itemid]->targetroleid;
self::set_backup_ids_record($restoreid, 'role', $recrole->itemid, $target);
// Else, we haven't any info coming from UI, let's calculate the mappings, matching
// in multiple ways and checking permissions. Note mapping to 0 means "skip"
} else {
$role = (object)self::get_backup_ids_record($restoreid, 'role', $recrole->itemid)->info;
$match = self::get_best_assignable_role($role, $courseid, $userid, $samesite);
// Send match to backup_ids
self::set_backup_ids_record($restoreid, 'role', $recrole->itemid, $match);
// Build the rolemappings element for controller
unset($role->id);
unset($role->nameincourse);
unset($role->nameincourse);
$role->targetroleid = $match;
$rolemappings->mappings[$recrole->itemid] = $role;
// Prepare warning if no match found
if (!$match) {
$problems['warnings'][] = get_string('cannotfindassignablerole', 'backup', $role->name);
}
}
}
$rs->close();
return $problems;
}
/**
* Given one role, as loaded from XML, perform the best possible matching against the assignable
* roles, using different fallback alternatives (shortname, archetype, editingteacher => teacher, defaultcourseroleid)
* returning the id of the best matching role or 0 if no match is found
*/
protected static function get_best_assignable_role($role, $courseid, $userid, $samesite) {
global $CFG, $DB;
// Gather various information about roles
$coursectx = get_context_instance(CONTEXT_COURSE, $courseid);
$allroles = $DB->get_records('role');
$assignablerolesshortname = get_assignable_roles($coursectx, ROLENAME_SHORT, false, $userid);
// Note: under 1.9 we had one function restore_samerole() that performed one complete
// matching of roles (all caps) and if match was found the mapping was availabe bypassing
// any assignable_roles() security. IMO that was wrong and we must not allow such
// mappings anymore. So we have left that matching strategy out in 2.0
// Empty assignable roles, mean no match possible
if (empty($assignablerolesshortname)) {
return 0;
}
// Match by shortname
if ($match = array_search($role->shortname, $assignablerolesshortname)) {
return $match;
}
// Match by archetype
list($in_sql, $in_params) = $DB->get_in_or_equal(array_keys($assignablerolesshortname));
$params = array_merge(array($role->archetype), $in_params);
if ($rec = $DB->get_record_select('role', "archetype = ? AND id $in_sql", $params, 'id', IGNORE_MULTIPLE)) {
return $rec->id;
}
// Match editingteacher to teacher (happens a lot, from 1.9)
if ($role->shortname == 'editingteacher' && in_array('teacher', $assignablerolesshortname)) {
return array_search('teacher', $assignablerolesshortname);
}
// No match, return 0
return 0;
}
/**
* Process the loaded roles, looking for their best mapping or skipping
* Any error will cause exception. Note this is one wrapper over
* precheck_included_roles, that contains all the logic, but returns
* errors/warnings instead and is executed as part of the restore prechecks
*/
public static function process_included_roles($restoreid, $courseid, $userid, $samesite) {
public static function process_included_roles($restoreid, $courseid, $userid, $samesite, $rolemappings) {
global $DB;
// Just let precheck_included_roles() to do all the hard work
$problems = self::precheck_included_roles($restoreid, $courseid, $userid, $samesite);
$problems = self::precheck_included_roles($restoreid, $courseid, $userid, $samesite, $rolemappings);
// With problems of type error, throw exception, shouldn't happen if prechecks executed
if (array_key_exists('errors', $problems)) {
@@ -145,6 +145,12 @@ abstract class backup_general_helper extends backup_helper {
$info->type = $infoarr['details']['detail'][0]['type'];
$info->format = $infoarr['details']['detail'][0]['format'];
$info->mode = $infoarr['details']['detail'][0]['mode'];
// Build the role mappings custom object
$rolemappings = new stdclass();
$rolemappings->modified = false;
$rolemappings->mappings = array();
$info->role_mappings = $rolemappings;
// Now the contents
$contentsarr = $infoarr['contents'];
@@ -43,7 +43,7 @@ abstract class restore_prechecks_helper {
*
* Returns empty array or warnings/errors array
*/
public static function execute_prechecks($controller) {
public static function execute_prechecks($controller, $droptemptablesafter = false) {
global $CFG;
$errors = array();
@@ -57,6 +57,7 @@ abstract class restore_prechecks_helper {
$courseid = $controller->get_courseid();
$userid = $controller->get_userid();
$inforeffiles = restore_dbops::get_needed_inforef_files($restoreid); // Get all inforef files
$rolemappings = $controller->get_info()->role_mappings;
// Create temp tables
restore_controller_dbops::create_restore_temp_tables($controller->get_restoreid());
@@ -111,11 +112,10 @@ abstract class restore_prechecks_helper {
}
}
// TODO: Perform role_mappings, warning about non-mappable ones being ignored (see 1.9)
// Note: restore won't create roles at all. Only mapping/skip!
$file = $controller->get_plan()->get_basepath() . '/roles.xml';
restore_dbops::load_roles_to_tempids($restoreid, $file); // Load needed users to temp_ids
if ($problems = restore_dbops::precheck_included_roles($restoreid, $courseid, $userid, $samesite)) {
restore_dbops::load_roles_to_tempids($restoreid, $file); // Load needed roles to temp_ids
if ($problems = restore_dbops::precheck_included_roles($restoreid, $courseid, $userid, $samesite, $rolemappings)) {
$errors = array_key_exists('errors', $problems) ? array_merge($errors, $problems['errors']) : $errors;
$warnings = array_key_exists('warnings', $problems) ? array_merge($warnings, $problems['warnings']) : $warnings;
}
@@ -128,8 +128,8 @@ abstract class restore_prechecks_helper {
if (!empty($warnings)) {
$results['warnings'] = $warnings;
}
// Warnings/errors detected, drop temp tables
if (!empty($results)) {
// Warnings/errors detected or want to do so explicitly, drop temp tables
if (!empty($results) || $droptemptablesafter) {
restore_controller_dbops::drop_restore_temp_tables($controller->get_restoreid());
}
return $results;