MDL-35064 add option to keep enrolments when uninstalling plugin

This commit is contained in:
Petr Škoda
2012-08-26 12:17:11 +02:00
parent d71c486507
commit 84bc3773cc
4 changed files with 354 additions and 9 deletions
+44 -7
View File
@@ -23,12 +23,15 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('NO_OUTPUT_BUFFERING', true);
require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');
$action = required_param('action', PARAM_ALPHANUMEXT);
$enrol = required_param('enrol', PARAM_PLUGIN);
$confirm = optional_param('confirm', 0, PARAM_BOOL);
$migrate = optional_param('migrate', 0, PARAM_BOOL);
$PAGE->set_url('/admin/enrol.php');
$PAGE->set_context(context_system::instance());
@@ -94,24 +97,58 @@ switch ($action) {
break;
case 'uninstall':
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('enrolments', 'enrol'));
if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
$strplugin = get_string('pluginname', 'enrol_'.$enrol);
} else {
$strplugin = $enrol;
}
echo $PAGE->set_title($strplugin);
echo $OUTPUT->header();
if (!$confirm) {
$uurl = new moodle_url('/admin/enrol.php', array('action'=>'uninstall', 'enrol'=>$enrol, 'sesskey'=>sesskey(), 'confirm'=>1));
echo $OUTPUT->confirm(get_string('uninstallconfirm', 'enrol', $strplugin), $uurl, $return);
echo $OUTPUT->heading(get_string('enrolments', 'enrol'));
$deleteurl = new moodle_url('/admin/enrol.php', array('action'=>'uninstall', 'enrol'=>$enrol, 'sesskey'=>sesskey(), 'confirm'=>1, 'migrate'=>0));
$migrateurl = new moodle_url('/admin/enrol.php', array('action'=>'uninstall', 'enrol'=>$enrol, 'sesskey'=>sesskey(), 'confirm'=>1, 'migrate'=>1));
$migrate = new single_button($migrateurl, get_string('uninstallmigrate', 'enrol'));
$delete = new single_button($deleteurl, get_string('uninstalldelete', 'enrol'));
$cancel = new single_button($return, get_string('cancel'), 'get');
$buttons = $OUTPUT->render($delete) . $OUTPUT->render($cancel);
if ($enrol !== 'manual') {
$buttons = $OUTPUT->render($migrate) . $buttons;
}
echo $OUTPUT->box_start('generalbox', 'notice');
echo html_writer::tag('p', markdown_to_html(get_string('uninstallconfirm', 'enrol', $strplugin)));
echo html_writer::tag('div', $buttons, array('class' => 'buttons'));
echo $OUTPUT->box_end();
echo $OUTPUT->footer();
exit;
} else { // Delete everything!!
} else {
// This may take a long time.
set_time_limit(0);
// Disable plugin to prevent concurrent cron execution.
unset($enabled[$enrol]);
set_config('enrol_plugins_enabled', implode(',', array_keys($enabled)));
if ($migrate) {
echo $OUTPUT->heading(get_string('uninstallmigrating', 'enrol', 'enrol_'.$enrol));
require_once("$CFG->dirroot/enrol/manual/locallib.php");
enrol_manual_migrate_plugin_enrolments($enrol);
echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
}
// Delete everything!!
uninstall_plugin('enrol', $enrol);
$syscontext->mark_dirty(); // resets all enrol caches
$syscontext->mark_dirty(); // Resets all enrol caches.
$a = new stdClass();
$a->plugin = $strplugin;
+100 -1
View File
@@ -353,4 +353,103 @@ class enrol_manual_deleteselectedusers_operation extends enrol_bulk_enrolment_op
}
return true;
}
}
}
/**
* Migrates all enrolments of the given plugin to enrol_manual plugin,
* this is used for example during plugin uninstallation.
*
* NOTE: this function does not trigger role and enrolment related events.
*
* @param string $enrol
*/
function enrol_manual_migrate_plugin_enrolments($enrol) {
global $DB;
if ($enrol === 'manual') {
// We can not migrate to self.
return;
}
$manualplugin = enrol_get_plugin('manual');
$params = array('enrol'=>$enrol);
$sql = "SELECT e.id, e.courseid, e.status, MIN(me.id) AS mid, COUNT(ue.id) AS cu
FROM {enrol} e
JOIN {user_enrolments} ue ON (ue.enrolid = e.id)
JOIN {course} c ON (c.id = e.courseid)
LEFT JOIN {enrol} me ON (me.courseid = e.courseid AND me.enrol='manual')
WHERE e.enrol = :enrol
GROUP BY e.id, e.courseid, e.status
ORDER BY e.id";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $e) {
$minstance = false;
if (!$e->mid) {
// Manual instance does not exist yet, add a new one.
$course = $DB->get_record('course', array('id'=>$e->courseid), '*', MUST_EXIST);
if ($minstance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>'manual'))) {
// Already created by previous iteration.
$e->mid = $minstance->id;
} else if ($e->mid = $manualplugin->add_default_instance($course)) {
$minstance = $DB->get_record('enrol', array('id'=>$e->mid));
if ($e->status != ENROL_INSTANCE_ENABLED) {
$DB->set_field('enrol', 'status', ENROL_INSTANCE_DISABLED, array('id'=>$e->mid));
$minstance->status = ENROL_INSTANCE_DISABLED;
}
}
} else {
$minstance = $DB->get_record('enrol', array('id'=>$e->mid));
}
if (!$minstance) {
// This should never happen unless adding of default instance fails unexpectedly.
continue;
}
// First delete potential role duplicates.
$params = array('id'=>$e->id, 'component'=>'enrol_'.$enrol, 'empty'=>$DB->sql_empty());
$sql = "SELECT ra.id
FROM {role_assignments} ra
JOIN {role_assignments} mra ON (mra.contextid = ra.contextid AND mra.userid = ra.userid AND mra.roleid = ra.roleid AND mra.component = :empty AND mra.itemid = 0)
WHERE ra.component = :component AND ra.itemid = :id";
$ras = $DB->get_records_sql($sql, $params);
$ras = array_keys($ras);
$DB->delete_records_list('role_assignments', 'id', $ras);
unset($ras);
// Migrate roles.
$sql = "UPDATE {role_assignments}
SET itemid = 0, component = :empty
WHERE itemid = :id AND component = :component";
$params = array('empty'=>$DB->sql_empty(), 'id'=>$e->id, 'component'=>'enrol_'.$enrol);
$DB->execute($sql, $params);
// Delete potential enrol duplicates.
$params = array('id'=>$e->id, 'mid'=>$e->mid);
$sql = "SELECT ue.id
FROM {user_enrolments} ue
JOIN {user_enrolments} mue ON (mue.userid = ue.userid AND mue.enrolid = :mid)
WHERE ue.enrolid = :id";
$ues = $DB->get_records_sql($sql, $params);
$ues = array_keys($ues);
$DB->delete_records_list('user_enrolments', 'id', $ues);
unset($ues);
// Migrate to manual enrol instance.
$params = array('id'=>$e->id, 'mid'=>$e->mid);
if ($e->status != ENROL_INSTANCE_ENABLED and $minstance->status == ENROL_INSTANCE_ENABLED) {
$status = ", status = :disabled";
$params['disabled'] = ENROL_USER_SUSPENDED;
} else {
$status = "";
}
$sql = "UPDATE {user_enrolments}
SET enrolid = :mid $status
WHERE enrolid = :id";
$DB->execute($sql, $params);
}
$rs->close();
}
+204
View File
@@ -0,0 +1,204 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Manual enrolment tests.
*
* @package enrol_manual
* @category phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Manual enrolment tests.
*
* @package enrol_manual
* @category phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol_manual_lib_testcase extends advanced_testcase {
/**
* Test enrol migration function used when uninstalling enrol plugins.
*/
public function test_migrate_plugin_enrolments() {
global $DB, $CFG;
require_once($CFG->dirroot.'/enrol/manual/locallib.php');
$this->resetAfterTest();
$manplugin = enrol_get_plugin('manual');
// Setup a few courses and users.
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
$this->assertNotEmpty($teacherrole);
$course1 = $this->getDataGenerator()->create_course();
$course2 = $this->getDataGenerator()->create_course();
$course3 = $this->getDataGenerator()->create_course();
$course4 = $this->getDataGenerator()->create_course();
$course5 = $this->getDataGenerator()->create_course();
$context1 = context_course::instance($course1->id);
$context2 = context_course::instance($course2->id);
$context3 = context_course::instance($course3->id);
$context4 = context_course::instance($course4->id);
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
// We expect manual, self and guest instances to be created by default.
$this->assertEquals(5, $DB->count_records('enrol', array('enrol'=>'manual')));
$this->assertEquals(5, $DB->count_records('enrol', array('enrol'=>'self')));
$this->assertEquals(5, $DB->count_records('enrol', array('enrol'=>'guest')));
$this->assertEquals(15, $DB->count_records('enrol', array()));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
// Enrol some users to manual instances.
$maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$DB->set_field('enrol', 'status', ENROL_INSTANCE_DISABLED, array('id'=>$maninstance1->id));
$maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$maninstance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$DB->delete_records('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'));
$DB->delete_records('enrol', array('courseid'=>$course4->id, 'enrol'=>'manual'));
$DB->delete_records('enrol', array('courseid'=>$course5->id, 'enrol'=>'manual'));
$manplugin->enrol_user($maninstance1, $user1->id, $studentrole->id);
$manplugin->enrol_user($maninstance1, $user2->id, $studentrole->id);
$manplugin->enrol_user($maninstance1, $user3->id, $teacherrole->id);
$manplugin->enrol_user($maninstance2, $user3->id, $teacherrole->id);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
// Set up some bogus enrol plugin instances and enrolments.
$xxxinstance1 = $DB->insert_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'xxx', 'status'=>ENROL_INSTANCE_ENABLED));
$xxxinstance1 = $DB->get_record('enrol', array('id'=>$xxxinstance1));
$xxxinstance3 = $DB->insert_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'xxx', 'status'=>ENROL_INSTANCE_DISABLED));
$xxxinstance3 = $DB->get_record('enrol', array('id'=>$xxxinstance3));
$xxxinstance4 = $DB->insert_record('enrol', array('courseid'=>$course4->id, 'enrol'=>'xxx', 'status'=>ENROL_INSTANCE_ENABLED));
$xxxinstance4 = $DB->get_record('enrol', array('id'=>$xxxinstance4));
$xxxinstance4b = $DB->insert_record('enrol', array('courseid'=>$course4->id, 'enrol'=>'xxx', 'status'=>ENROL_INSTANCE_DISABLED));
$xxxinstance4b = $DB->get_record('enrol', array('id'=>$xxxinstance4b));
$DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance1->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_SUSPENDED));
role_assign($studentrole->id, $user1->id, $context1->id, 'enrol_xxx', $xxxinstance1->id);
role_assign($teacherrole->id, $user1->id, $context1->id, 'enrol_xxx', $xxxinstance1->id);
$DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance1->id, 'userid'=>$user4->id, 'status'=>ENROL_USER_ACTIVE));
role_assign($studentrole->id, $user4->id, $context1->id, 'enrol_xxx', $xxxinstance1->id);
$this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance1->id)));
$this->assertEquals(6, $DB->count_records('role_assignments', array('contextid'=>$context1->id)));
$DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance3->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_ACTIVE));
role_assign($studentrole->id, $user1->id, $context3->id, 'enrol_xxx', $xxxinstance3->id);
$DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance3->id, 'userid'=>$user2->id, 'status'=>ENROL_USER_SUSPENDED));
$this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance3->id)));
$this->assertEquals(1, $DB->count_records('role_assignments', array('contextid'=>$context3->id)));
$DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance4->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_ACTIVE));
role_assign($studentrole->id, $user1->id, $context4->id, 'enrol_xxx', $xxxinstance4->id);
$DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance4->id, 'userid'=>$user2->id, 'status'=>ENROL_USER_ACTIVE));
role_assign($studentrole->id, $user2->id, $context4->id, 'enrol_xxx', $xxxinstance4->id);
$DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance4b->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_SUSPENDED));
role_assign($teacherrole->id, $user1->id, $context4->id, 'enrol_xxx', $xxxinstance4b->id);
$DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance4b->id, 'userid'=>$user4->id, 'status'=>ENROL_USER_ACTIVE));
role_assign($teacherrole->id, $user4->id, $context4->id, 'enrol_xxx', $xxxinstance4b->id);
$this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance4->id)));
$this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance4b->id)));
$this->assertEquals(4, $DB->count_records('role_assignments', array('contextid'=>$context4->id)));
// Finally do the migration.
enrol_manual_migrate_plugin_enrolments('xxx');
// Verify results.
$this->assertEquals(1, $DB->count_records('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual')));
$this->assertEquals(1, $DB->count_records('enrol', array('courseid'=>$course1->id, 'enrol'=>'xxx')));
$maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$this->assertEquals(ENROL_INSTANCE_DISABLED, $maninstance1->status);
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance1->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_ACTIVE)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance1->id, 'userid'=>$user2->id, 'status'=>ENROL_USER_ACTIVE)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance1->id, 'userid'=>$user3->id, 'status'=>ENROL_USER_ACTIVE)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance1->id, 'userid'=>$user4->id, 'status'=>ENROL_USER_ACTIVE)));
$this->assertEquals(4, $DB->count_records('user_enrolments', array('enrolid'=>$maninstance1->id)));
$this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'contextid'=>$context1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user1->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'contextid'=>$context1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user4->id, 'roleid'=>$studentrole->id, 'contextid'=>$context1->id)));
$this->assertEquals(5, $DB->count_records('role_assignments', array('contextid'=>$context1->id)));
$this->assertEquals(1, $DB->count_records('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual')));
$this->assertEquals(0, $DB->count_records('enrol', array('courseid'=>$course2->id, 'enrol'=>'xxx')));
$maninstance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$this->assertEquals(ENROL_INSTANCE_ENABLED, $maninstance2->status);
$this->assertEquals(1, $DB->count_records('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual')));
$this->assertEquals(1, $DB->count_records('enrol', array('courseid'=>$course3->id, 'enrol'=>'xxx')));
$maninstance3 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$this->assertEquals(ENROL_INSTANCE_DISABLED, $maninstance3->status);
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance3->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_ACTIVE)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance3->id, 'userid'=>$user2->id, 'status'=>ENROL_USER_SUSPENDED)));
$this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid'=>$maninstance3->id)));
$this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance3->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'contextid'=>$context3->id)));
$this->assertEquals(1, $DB->count_records('role_assignments', array('contextid'=>$context3->id)));
$this->assertEquals(1, $DB->count_records('enrol', array('courseid'=>$course4->id, 'enrol'=>'manual')));
$this->assertEquals(2, $DB->count_records('enrol', array('courseid'=>$course4->id, 'enrol'=>'xxx')));
$maninstance4 = $DB->get_record('enrol', array('courseid'=>$course4->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$this->assertEquals(ENROL_INSTANCE_ENABLED, $maninstance4->status);
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance4->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_ACTIVE)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance4->id, 'userid'=>$user2->id, 'status'=>ENROL_USER_ACTIVE)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance4->id, 'userid'=>$user4->id, 'status'=>ENROL_USER_SUSPENDED)));
$this->assertEquals(3, $DB->count_records('user_enrolments', array('enrolid'=>$maninstance4->id)));
$this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance4->id)));
$this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance4b->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'contextid'=>$context4->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user1->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context4->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'contextid'=>$context4->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>$DB->sql_empty(), 'userid'=>$user4->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context4->id)));
$this->assertEquals(4, $DB->count_records('role_assignments', array('contextid'=>$context4->id)));
$this->assertEquals(0, $DB->count_records('enrol', array('courseid'=>$course5->id, 'enrol'=>'manual')));
$this->assertEquals(0, $DB->count_records('enrol', array('courseid'=>$course5->id, 'enrol'=>'xxx')));
// Make sure wrong params are ignored.
enrol_manual_migrate_plugin_enrolments('manual');
enrol_manual_migrate_plugin_enrolments('yyyy');
}
}
+6 -1
View File
@@ -98,8 +98,13 @@ $string['unenrolconfirm'] = 'Do you really want to unenrol user "{$a->user}" fro
$string['unenrolme'] = 'Unenrol me from {$a}';
$string['unenrolnotpermitted'] = 'You do not have permission or can not unenrol this user from this course.';
$string['unenrolroleusers'] = 'Unenrol users';
$string['uninstallconfirm'] = 'You are about to completely delete the enrol plugin \'{$a}\'. This will completely delete everything in the database associated with this enrolment type. Are you SURE you want to continue?';
$string['uninstallconfirm'] = 'You are about to completely uninstall the enrol plugin \'{$a}\'. This will completely delete everything in the database associated with this enrolment type. Deleting of enrolments removes also users\' grades, group membership, subscriptions and other course related data or preferences.
Are you SURE you want to continue?';
$string['uninstalldelete'] = 'Delete all enrolments and uninstall';
$string['uninstalldeletefiles'] = 'All data associated with the enrol plugin \'{$a->plugin}\' has been deleted from the database. To complete the deletion (and prevent the plugin re-installing itself), you should now delete this directory from your server: {$a->directory}';
$string['uninstallmigrate'] = 'Uninstall but keep all enrolments';
$string['uninstallmigrating'] = 'Migrating "{$a}" enrolments';
$string['unknowajaxaction'] = 'Unknown action requested';
$string['unlimitedduration'] = 'Unlimited';
$string['usersearch'] = 'Search ';