MDL-21432 backup - role load/mappings and integration with UI via controller (MDL-23109)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user