if (function_exists('opcache_reset')) {
opcache_reset();
}
+ $cache = 0;
+
+} else {
+ $cache = 1;
}
require('../config.php');
+
+// Invalidate the cache of version.php in any circumstances to help core_component
+// detecting if the version has changed and component cache should be reset.
+if (function_exists('opcache_invalidate')) {
+ opcache_invalidate($CFG->dirroot . '/version.php', true);
+}
+// Make sure the component cache gets rebuilt if necessary, any method that
+// indirectly calls the protected init() method is good here.
+core_component::get_core_subsystems();
+
require_once($CFG->libdir.'/adminlib.php'); // various admin-only functions
require_once($CFG->libdir.'/upgradelib.php'); // general upgrade/install related functions
require_once($CFG->libdir.'/pluginlib.php'); // available updates notifications
$agreelicense = optional_param('agreelicense', 0, PARAM_BOOL);
$fetchupdates = optional_param('fetchupdates', 0, PARAM_BOOL);
$newaddonreq = optional_param('installaddonrequest', null, PARAM_RAW);
-$cache = optional_param('cache', 0, PARAM_BOOL);
// Set up PAGE.
$url = new moodle_url('/admin/index.php');
-if (!is_null($newaddonreq)) {
- // We need to set the eventual add-on installation request in the $PAGE's URL
- // so that it is stored in $SESSION->wantsurl and the admin is redirected
- // correctly once they are logged-in.
- $url->param('installaddonrequest', $newaddonreq);
-}
if ($cache) {
- $url->param('cache', $cache);
+ $url->param('cache', 1);
}
$PAGE->set_url($url);
unset($url);
+// Are we returning from an add-on installation request at moodle.org/plugins?
+if ($newaddonreq and !$cache and empty($CFG->disableonclickaddoninstall)) {
+ $target = new moodle_url('/admin/tool/installaddon/index.php', array(
+ 'installaddonrequest' => $newaddonreq,
+ 'confirm' => 0));
+ if (!isloggedin() or isguestuser()) {
+ // Login and go the the add-on tool page.
+ $SESSION->wantsurl = $target->out();
+ redirect(get_login_url());
+ }
+ redirect($target);
+}
+
$PAGE->set_pagelayout('admin'); // Set a default pagelayout
$documentationlink = '<a href="http://docs.moodle.org/en/Installation">Installation docs</a>';
$PAGE->set_heading($strinstallation);
$PAGE->set_cacheable(false);
+ /** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
echo $output->install_licence_page();
die();
$PAGE->set_heading($strinstallation . ' - Moodle ' . $CFG->target_release);
$PAGE->set_cacheable(false);
+ /** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
echo $output->install_environment_page($maturity, $envstatus, $environment_results, $release);
die();
}
// Detect config cache inconsistency, this happens when you switch branches on dev servers.
-if ($cache) {
- if ($CFG->version != $DB->get_field('config', 'value', array('name'=>'version'))) {
- purge_all_caches();
- redirect(new moodle_url('/admin/index.php'), 'Config cache inconsistency detected, resetting caches...');
- }
+if ($CFG->version != $DB->get_field('config', 'value', array('name'=>'version'))) {
+ purge_all_caches();
+ redirect(new moodle_url('/admin/index.php'), 'Config cache inconsistency detected, resetting caches...');
}
-if ($version > $CFG->version) { // upgrade
+if (!$cache and $version > $CFG->version) { // upgrade
// We purge all of MUC's caches here.
// Caches are disabled for upgrade by CACHE_DISABLE_ALL so we must set the first arg to true.
// This ensures a real config object is loaded and the stores will be purged.
$PAGE->set_title($stradministration);
$PAGE->set_cacheable(false);
+ /** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
echo $output->upgrade_stale_php_files_page();
die();
$PAGE->set_heading($strdatabasechecking);
$PAGE->set_cacheable(false);
+ /** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
echo $output->upgrade_confirm_page($a->newversion, $maturity);
die();
$PAGE->set_heading($strcurrentrelease);
$PAGE->set_cacheable(false);
+ /** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
echo $output->upgrade_environment_page($release, $envstatus, $environment_results);
die();
$reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1));
+ /** @var core_admin_renderer $output */
+ $output = $PAGE->get_renderer('core', 'admin');
+
// check plugin dependencies first
$failed = array();
if (!plugin_manager::instance()->all_plugins_ok($version, $failed)) {
- $output = $PAGE->get_renderer('core', 'admin');
echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl);
die();
}
redirect($reloadurl);
}
- $output = $PAGE->get_renderer('core', 'admin');
-
$deployer = available_update_deployer::instance();
if ($deployer->enabled()) {
$deployer->initialize($reloadurl, $reloadurl);
}
// Updated human-readable release version if necessary
-if ($release <> $CFG->release) { // Update the release version
+if (!$cache and $release <> $CFG->release) { // Update the release version
set_config('release', $release);
}
-if ($branch <> $CFG->branch) { // Update the branch
+if (!$cache and $branch <> $CFG->branch) { // Update the branch
set_config('branch', $branch);
}
-if (moodle_needs_upgrading()) {
+if (!$cache and moodle_needs_upgrading()) {
if (!$PAGE->headerprinted) {
// means core upgrade or installation was not already done
if (!$confirmplugins) {
// Now we can be sure everything was upgraded and caches work fine,
// redirect if necessary to make sure caching is enabled.
-if (!$cache and !optional_param('sesskey', '', PARAM_RAW)) {
- redirect(new moodle_url($PAGE->url, array('cache' => 1)));
+if (!$cache) {
+ redirect(new moodle_url('/admin/index.php', array('cache' => 1)));
}
// Check for valid admin user - no guest autologin
set_config('registered', time());
}
-// Check if we are returning from an add-on installation request at moodle.org/plugins
-if (!is_null($newaddonreq)) {
- if (!empty($CFG->disableonclickaddoninstall)) {
- // The feature is disabled in config.php, ignore the request.
- } else {
- redirect(new moodle_url('/admin/tool/installaddon/index.php', array(
- 'installaddonrequest' => $newaddonreq,
- 'confirm' => 0)));
- }
-}
-
// setup critical warnings before printing admin tree block
$insecuredataroot = is_dataroot_insecure(true);
$SESSION->admin_critical_warning = ($insecuredataroot==INSECURE_DATAROOT_ERROR);
SET visibleold=visible, visible=0
WHERE module=?";
$DB->execute($sql, array($module->id));
- // clear the course modinfo cache for courses
- // where we just uninstalld something
- $sql = "UPDATE {course}
- SET modinfo=''
- WHERE id IN (SELECT DISTINCT course
+ // Increment course.cacherev for courses where we just made something invisible.
+ // This will force cache rebuilding on the next request.
+ increment_revision_number('course', 'cacherev',
+ "id IN (SELECT DISTINCT course
FROM {course_modules}
- WHERE visibleold=1 AND module=?)";
- $DB->execute($sql, array($module->id));
+ WHERE visibleold=1 AND module=?)",
+ array($module->id));
admin_get_root(true, false); // settings not required - only pages
}
}
$DB->set_field("modules", "visible", "1", array("id"=>$module->id)); // Show main module
$DB->set_field('course_modules', 'visible', '1', array('visibleold'=>1, 'module'=>$module->id)); // Get the previous saved visible state for the course module.
- // clear the course modinfo cache for courses
- // where we just made something visible
- $sql = "UPDATE {course}
- SET modinfo = ''
- WHERE id IN (SELECT DISTINCT course
+ // Increment course.cacherev for courses where we just made something visible.
+ // This will force cache rebuilding on the next request.
+ increment_revision_number('course', 'cacherev',
+ "id IN (SELECT DISTINCT course
FROM {course_modules}
- WHERE visible=1 AND module=?)";
- $DB->execute($sql, array($module->id));
+ WHERE visible=1 AND module=?)",
+ array($module->id));
admin_get_root(true, false); // settings not required - only pages
}
require_once($CFG->libdir . '/pluginlib.php');
require_once($CFG->libdir . '/filelib.php');
-admin_externalpage_setup('pluginsoverview');
-require_capability('moodle/site:config', context_system::instance());
-
$fetchremote = optional_param('fetchremote', false, PARAM_BOOL);
$updatesonly = optional_param('updatesonly', false, PARAM_BOOL);
$contribonly = optional_param('contribonly', false, PARAM_BOOL);
$delete = optional_param('delete', '', PARAM_COMPONENT);
$confirmed = optional_param('confirm', false, PARAM_BOOL);
-$output = $PAGE->get_renderer('core', 'admin');
+// NOTE: do not use admin_externalpage_setup() here because it loads
+// full admin tree which is not possible during uninstallation.
+
+require_login();
+$syscontext = context_system::instance();
+require_capability('moodle/site:config', $syscontext);
$pluginman = plugin_manager::instance();
if ($uninstall) {
require_sesskey();
+
+ if (!$confirmed) {
+ admin_externalpage_setup('pluginsoverview');
+ } else {
+ $PAGE->set_url('/admin/plugins.php');
+ $PAGE->set_context($syscontext);
+ $PAGE->set_pagelayout('maintenance');
+ $PAGE->set_popup_notification_allowed(false);
+ }
+
+ /** @var core_admin_renderer $output */
+ $output = $PAGE->get_renderer('core', 'admin');
+
$pluginfo = $pluginman->get_plugin_info($uninstall);
// Make sure we know the plugin.
if ($delete and $confirmed) {
require_sesskey();
+
+ $PAGE->set_url('/admin/plugins.php');
+ $PAGE->set_context($syscontext);
+ $PAGE->set_pagelayout('maintenance');
+ $PAGE->set_popup_notification_allowed(false);
+
+ /** @var core_admin_renderer $output */
+ $output = $PAGE->get_renderer('core', 'admin');
+
$pluginfo = $pluginman->get_plugin_info($delete);
// Make sure we know the plugin.
if (function_exists('opcache_reset')) {
opcache_reset();
}
- redirect($PAGE->url);
+ // We need to execute upgrade to make sure everything including caches is up to date.
+ redirect(new moodle_url('/admin/index.php'));
}
+admin_externalpage_setup('pluginsoverview');
+
+/** @var core_admin_renderer $output */
+$output = $PAGE->get_renderer('core', 'admin');
+
$checker = available_update_checker::instance();
// Filtering options.
public function upgrade_confirm_page($strnewversion, $maturity) {
$output = '';
- $continueurl = new moodle_url('index.php', array('confirmupgrade' => 1));
- $cancelurl = new moodle_url('index.php');
+ $continueurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1));
+ $continue = new single_button($continueurl, get_string('continue'), 'get');
+ $cancelurl = new moodle_url('/admin/index.php');
$output .= $this->header();
$output .= $this->maturity_warning($maturity);
- $output .= $this->confirm(get_string('upgradesure', 'admin', $strnewversion), $continueurl, $cancelurl);
+ $output .= $this->confirm(get_string('upgradesure', 'admin', $strnewversion), $continue, $cancelurl);
$output .= $this->footer();
return $output;
$pluginname = $pluginman->plugin_name($pluginfo->component);
+ // Do not show navigation here, they must click one of the buttons.
+ $this->page->set_pagelayout('maintenance');
+ $this->page->set_cacheable(false);
+
$output .= $this->output->header();
$output .= $this->output->heading(get_string('uninstalling', 'core_plugin', array('name' => $pluginname)));
'uninstalldeleteconfirmexternal');
}
- $output .= $this->output->confirm($confirm, $continueurl, $this->page->url);
+ // After any uninstall we must execute full upgrade to finish the cleanup!
+ $output .= $this->output->confirm($confirm, $continueurl, new moodle_url('/admin/index.php'));
$output .= $this->output->footer();
return $output;
public function plugin_uninstall_results_page(plugin_manager $pluginman, plugininfo_base $pluginfo, progress_trace_buffer $progress) {
$output = '';
- $pluginname = $pluginman->plugin_name($pluginfo->component);
+ $pluginname = $pluginfo->component;
$output .= $this->output->header();
$output .= $this->output->heading(get_string('uninstalling', 'core_plugin', array('name' => $pluginname)));
$output .= $this->output->box(get_string('uninstalldelete', 'core_plugin',
array('name' => $pluginname, 'rootdir' => $pluginfo->rootdir)), 'generalbox uninstalldelete');
- $output .= $this->output->continue_button($this->page->url);
+ $output .= $this->output->continue_button(new moodle_url('/admin/index.php'));
$output .= $this->output->footer();
return $output;
if (!$registered) {
- $registerbutton = $this->single_button(new moodle_url('registration/register.php',
+ $registerbutton = $this->single_button(new moodle_url('/admin/registration/register.php',
array('huburl' => HUB_MOODLEORGHUBURL, 'hubname' => 'Moodle.org')),
get_string('register', 'admin'));
} else {
$str = 'otherplugin';
}
+ $componenturl = new moodle_url('https://moodle.org/plugins/view.php?plugin='.$component);
+ $componenturl = html_writer::tag('a', $component, array('href' => $componenturl->out()));
$requires[] = html_writer::tag('li',
get_string($str, 'core_plugin',
- array('component' => $component, 'version' => $requiredversion)),
+ array('component' => $componenturl, 'version' => $requiredversion)),
array('class' => $class));
}
$success = $repositorytype->update_options($settings);
} else {
$type = new repository_type($plugin, (array)$fromform, $visible);
- $type->create();
$success = true;
+ if (!$repoid = $type->create()) {
+ $success = false;
+ }
$data = data_submitted();
}
if ($success) {
$this->displaypermissions = $this->allpermissions;
$this->strperms[$this->allpermissions[CAP_INHERIT]] = get_string('notset', 'core_role');
- $this->allcontextlevels = array(
- CONTEXT_SYSTEM => get_string('coresystem'),
- CONTEXT_USER => get_string('user'),
- CONTEXT_COURSECAT => get_string('category'),
- CONTEXT_COURSE => get_string('course'),
- CONTEXT_MODULE => get_string('activitymodule'),
- CONTEXT_BLOCK => get_string('block')
- );
+ $this->allcontextlevels = array();
+ $levels = context_helper::get_all_levels();
+ foreach ($levels as $level => $classname) {
+ $this->allcontextlevels[$level] = context_helper::get_level_name($level);
+ }
}
protected function load_current_permissions() {
defined('MOODLE_INTERNAL') || die();
global $CFG;
-require_once($CFG->libdir . '/behat/classes/behat_command.php');
+require_once($CFG->libdir . '/behat/classes/behat_selectors.php');
/**
* Renderer for behat tool web features
// Replace text selector type arguments with a user-friendly select.
$stepsdefinitions = preg_replace_callback('/(TEXT_SELECTOR\d?_STRING)/',
function ($matches) {
- return html_writer::select(behat_command::$allowedtextselectors, uniqid());
+ return html_writer::select(behat_selectors::get_allowed_text_selectors(), uniqid());
},
$stepsdefinitions
);
// Replace selector type arguments with a user-friendly select.
$stepsdefinitions = preg_replace_callback('/(SELECTOR\d?_STRING)/',
function ($matches) {
- return html_writer::select(behat_command::$allowedselectors, uniqid());
+ return html_writer::select(behat_selectors::get_allowed_selectors(), uniqid());
},
$stepsdefinitions
);
| Course 1 | C1 | 0 |
And I log in as "admin"
And I follow "Course 1"
- When I click on "Move this to the dock" "button" in the ".block_settings" "css_element"
+ When I click on "Move this to the dock" "button" in the "Administration" "block"
Then I should not see "Question bank"
And I click on "//div[@id='dock']/descendant::h2[normalize-space(.)='Administration']" "xpath_element"
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And I log in as "admin"
- When I click on "Move this to the dock" "button" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' block_settings ')]" "xpath_element"
+ When I click on "Move this to the dock" "button" in the "Administration" "block"
Then I should not see "Turn editing on"
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+/**
+ * Backend generic code.
+ *
+ * @package tool_generator
+ * @copyright 2013 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
defined('MOODLE_INTERNAL') || die();
/**
- * Backend code for the 'make large course' tool.
+ * Backend generic code for all tool_generator commands.
*
+ * @abstract
* @package tool_generator
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class tool_generator_backend {
+abstract class tool_generator_backend {
/**
* @var int Lowest (smallest) size index
*/
const DEFAULT_SIZE = 3;
/**
- * @var array Number of sections in course
- */
- private static $paramsections = array(1, 10, 100, 500, 1000, 2000);
- /**
- * @var array Number of Page activities in course
- */
- private static $parampages = array(1, 50, 200, 1000, 5000, 10000);
- /**
- * @var array Number of students enrolled in course
- */
- private static $paramusers = array(1, 100, 1000, 10000, 50000, 100000);
- /**
- * Total size of small files: 1KB, 1MB, 10MB, 100MB, 1GB, 2GB.
- *
- * @var array Number of small files created in a single file activity
- */
- private static $paramsmallfilecount = array(1, 64, 128, 1024, 16384, 32768);
- /**
- * @var array Size of small files (to make the totals into nice numbers)
- */
- private static $paramsmallfilesize = array(1024, 16384, 81920, 102400, 65536, 65536);
- /**
- * Total size of big files: 8KB, 8MB, 80MB, 800MB, 8GB, 16GB.
- *
- * @var array Number of big files created as individual file activities
- */
- private static $parambigfilecount = array(1, 2, 5, 10, 10, 10);
- /**
- * @var array Size of each large file
- */
- private static $parambigfilesize = array(8192, 4194304, 16777216, 83886080,
- 858993459, 1717986918);
- /**
- * @var array Number of forum discussions
- */
- private static $paramforumdiscussions = array(1, 10, 100, 500, 1000, 2000);
- /**
- * @var array Number of forum posts per discussion
- */
- private static $paramforumposts = array(2, 2, 5, 10, 10, 10);
-
- /**
- * @var string Course shortname
- */
- private $shortname;
-
- /**
- * @var int Size code (index in the above arrays)
+ * @var bool True if we want a fixed dataset or false to generate random data
*/
- private $size;
+ protected $fixeddataset;
/**
* @var bool True if displaying progress
*/
- private $progress;
-
- /**
- * @var testing_data_generator Data generator
- */
- private $generator;
-
- /**
- * @var stdClass Course object
- */
- private $course;
+ protected $progress;
/**
* @var int Epoch time at which last dot was displayed
*/
- private $lastdot;
+ protected $lastdot;
/**
* @var int Epoch time at which last percentage was displayed
*/
- private $lastpercentage;
+ protected $lastpercentage;
/**
* @var int Epoch time at which current step (current set of dots) started
*/
- private $starttime;
+ protected $starttime;
/**
- * @var array Array from test user number (1...N) to userid in database
+ * @var int Size code (index in the above arrays)
*/
- private $userids;
+ protected $size;
/**
- * Constructs object ready to create course.
+ * Generic generator class
*
- * @param string $shortname Course shortname
* @param int $size Size as numeric index
+ * @param bool $fixeddataset To use fixed or random data
* @param bool $progress True if progress information should be displayed
- * @return int Course id
* @throws coding_exception If parameters are invalid
*/
- public function __construct($shortname, $size, $progress = true) {
+ public function __construct($size, $fixeddataset = false, $progress = true) {
+
// Check parameter.
if ($size < self::MIN_SIZE || $size > self::MAX_SIZE) {
throw new coding_exception('Invalid size');
}
// Set parameters.
- $this->shortname = $shortname;
$this->size = $size;
+ $this->fixeddataset = $fixeddataset;
$this->progress = $progress;
}
- /**
- * Gets a list of size choices supported by this backend.
- *
- * @return array List of size (int) => text description for display
- */
- public static function get_size_choices() {
- $options = array();
- for ($size = self::MIN_SIZE; $size <= self::MAX_SIZE; $size++) {
- $options[$size] = get_string('size_' . $size, 'tool_generator');
- }
- return $options;
- }
-
/**
* Converts a size name into the numeric constant.
*
throw new coding_exception("Unknown size name '$sizename'");
}
- /**
- * Checks that a shortname is available (unused).
- *
- * @param string $shortname Proposed course shortname
- * @return string An error message if the name is unavailable or '' if OK
- */
- public static function check_shortname_available($shortname) {
- global $DB;
- $fullname = $DB->get_field('course', 'fullname',
- array('shortname' => $shortname), IGNORE_MISSING);
- if ($fullname !== false) {
- // I wanted to throw an exception here but it is not possible to
- // use strings from moodle.php in exceptions, and I didn't want
- // to duplicate the string in tool_generator, so I changed this to
- // not use exceptions.
- return get_string('shortnametaken', 'moodle', $fullname);
- }
- return '';
- }
-
- /**
- * Runs the entire 'make' process.
- *
- * @return int Course id
- */
- public function make() {
- global $DB, $CFG;
- require_once($CFG->dirroot . '/lib/phpunit/classes/util.php');
-
- raise_memory_limit(MEMORY_EXTRA);
-
- if ($this->progress && !CLI_SCRIPT) {
- echo html_writer::start_tag('ul');
- }
-
- $entirestart = microtime(true);
-
- // Start transaction.
- $transaction = $DB->start_delegated_transaction();
-
- // Get generator.
- $this->generator = phpunit_util::get_data_generator();
-
- // Make course.
- $this->course = $this->create_course();
- $this->create_users();
- $this->create_pages();
- $this->create_small_files();
- $this->create_big_files();
- $this->create_forum();
-
- // Log total time.
- $this->log('complete', round(microtime(true) - $entirestart, 1));
-
- if ($this->progress && !CLI_SCRIPT) {
- echo html_writer::end_tag('ul');
- }
-
- // Commit transaction and finish.
- $transaction->allow_commit();
- return $this->course->id;
- }
-
- /**
- * Creates the actual course.
- *
- * @return stdClass Course record
- */
- private function create_course() {
- $this->log('createcourse', $this->shortname);
- $courserecord = array('shortname' => $this->shortname,
- 'fullname' => get_string('fullname', 'tool_generator',
- array('size' => get_string('shortsize_' . $this->size, 'tool_generator'))),
- 'numsections' => self::$paramsections[$this->size]);
- return $this->generator->create_course($courserecord, array('createsections' => true));
- }
-
- /**
- * Creates a number of user accounts and enrols them on the course.
- * Note: Existing user accounts that were created by this system are
- * reused if available.
- */
- private function create_users() {
- global $DB;
-
- // Work out total number of users.
- $count = self::$paramusers[$this->size];
-
- // Get existing users in order. We will 'fill up holes' in this up to
- // the required number.
- $this->log('checkaccounts', $count);
- $nextnumber = 1;
- $rs = $DB->get_recordset_select('user', $DB->sql_like('username', '?'),
- array('tool_generator_%'), 'username', 'id, username');
- foreach ($rs as $rec) {
- // Extract number from username.
- $matches = array();
- if (!preg_match('~^tool_generator_([0-9]{6})$~', $rec->username, $matches)) {
- continue;
- }
- $number = (int)$matches[1];
-
- // Create missing users in range up to this.
- if ($number != $nextnumber) {
- $this->create_user_accounts($nextnumber, min($number - 1, $count));
- } else {
- $this->userids[$number] = (int)$rec->id;
- }
-
- // Stop if we've got enough users.
- $nextnumber = $number + 1;
- if ($number >= $count) {
- break;
- }
- }
- $rs->close();
-
- // Create users from end of existing range.
- if ($nextnumber <= $count) {
- $this->create_user_accounts($nextnumber, $count);
- }
-
- // Assign all users to course.
- $this->log('enrol', $count, true);
-
- $enrolplugin = enrol_get_plugin('manual');
- $instances = enrol_get_instances($this->course->id, true);
- foreach ($instances as $instance) {
- if ($instance->enrol === 'manual') {
- break;
- }
- }
- if ($instance->enrol !== 'manual') {
- throw new coding_exception('No manual enrol plugin in course');
- }
- $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
-
- for ($number = 1; $number <= $count; $number++) {
- // Enrol user.
- $enrolplugin->enrol_user($instance, $this->userids[$number], $role->id);
- $this->dot($number, $count);
- }
-
- $this->end_log();
- }
-
- /**
- * Creates user accounts with a numeric range.
- *
- * @param int $first Number of first user
- * @param int $last Number of last user
- */
- private function create_user_accounts($first, $last) {
- $this->log('createaccounts', (object)array('from' => $first, 'to' => $last), true);
- $count = $last - $first + 1;
- $done = 0;
- for ($number = $first; $number <= $last; $number++, $done++) {
- // Work out username with 6-digit number.
- $textnumber = (string)$number;
- while (strlen($textnumber) < 6) {
- $textnumber = '0' . $textnumber;
- }
- $username = 'tool_generator_' . $textnumber;
-
- // Create user account.
- $record = array('firstname' => get_string('firstname', 'tool_generator'),
- 'lastname' => $number, 'username' => $username);
- $user = $this->generator->create_user($record);
- $this->userids[$number] = (int)$user->id;
- $this->dot($done, $count);
- }
- $this->end_log();
- }
-
- /**
- * Creates a number of Page activities.
- */
- private function create_pages() {
- // Set up generator.
- $pagegenerator = $this->generator->get_plugin_generator('mod_page');
-
- // Create pages.
- $number = self::$parampages[$this->size];
- $this->log('createpages', $number, true);
- for ($i=0; $i<$number; $i++) {
- $record = array('course' => $this->course->id);
- $options = array('section' => $this->get_random_section());
- $pagegenerator->create_instance($record, $options);
- $this->dot($i, $number);
- }
-
- $this->end_log();
- }
-
- /**
- * Creates one resource activity with a lot of small files.
- */
- private function create_small_files() {
- $count = self::$paramsmallfilecount[$this->size];
- $this->log('createsmallfiles', $count, true);
-
- // Create resource with default textfile only.
- $resourcegenerator = $this->generator->get_plugin_generator('mod_resource');
- $record = array('course' => $this->course->id,
- 'name' => get_string('smallfiles', 'tool_generator'));
- $options = array('section' => 0);
- $resource = $resourcegenerator->create_instance($record, $options);
-
- // Add files.
- $fs = get_file_storage();
- $context = context_module::instance($resource->cmid);
- $filerecord = array('component' => 'mod_resource', 'filearea' => 'content',
- 'contextid' => $context->id, 'itemid' => 0, 'filepath' => '/');
- for ($i = 0; $i < $count; $i++) {
- $filerecord['filename'] = 'smallfile' . $i . '.dat';
-
- // Generate random binary data (different for each file so it
- // doesn't compress unrealistically).
- $data = self::get_random_binary(self::$paramsmallfilesize[$this->size]);
-
- $fs->create_file_from_string($filerecord, $data);
- $this->dot($i, $count);
- }
-
- $this->end_log();
- }
-
- /**
- * Creates a string of random binary data. The start of the string includes
- * the current time, in an attempt to avoid large-scale repetition.
- *
- * @param int $length Number of bytes
- * @return Random data
- */
- private static function get_random_binary($length) {
- $data = microtime(true);
- if (strlen($data) > $length) {
- // Use last digits of data.
- return substr($data, -$length);
- }
- $length -= strlen($data);
- for ($j=0; $j < $length; $j++) {
- $data .= chr(rand(1, 255));
- }
- return $data;
- }
-
- /**
- * Creates a number of resource activities with one big file each.
- */
- private function create_big_files() {
- global $CFG;
-
- // Work out how many files and how many blocks to use (up to 64KB).
- $count = self::$parambigfilecount[$this->size];
- $blocks = ceil(self::$parambigfilesize[$this->size] / 65536);
- $blocksize = floor(self::$parambigfilesize[$this->size] / $blocks);
-
- $this->log('createbigfiles', $count, true);
-
- // Prepare temp area.
- $tempfolder = make_temp_directory('tool_generator');
- $tempfile = $tempfolder . '/' . rand();
-
- // Create resources and files.
- $fs = get_file_storage();
- $resourcegenerator = $this->generator->get_plugin_generator('mod_resource');
- for ($i = 0; $i < $count; $i++) {
- // Create resource.
- $record = array('course' => $this->course->id,
- 'name' => get_string('bigfile', 'tool_generator', $i));
- $options = array('section' => $this->get_random_section());
- $resource = $resourcegenerator->create_instance($record, $options);
-
- // Write file.
- $handle = fopen($tempfile, 'w');
- if (!$handle) {
- throw new coding_exception('Failed to open temporary file');
- }
- for ($j = 0; $j < $blocks; $j++) {
- $data = self::get_random_binary($blocksize);
- fwrite($handle, $data);
- $this->dot($i * $blocks + $j, $count * $blocks);
- }
- fclose($handle);
-
- // Add file.
- $context = context_module::instance($resource->cmid);
- $filerecord = array('component' => 'mod_resource', 'filearea' => 'content',
- 'contextid' => $context->id, 'itemid' => 0, 'filepath' => '/',
- 'filename' => 'bigfile' . $i . '.dat');
- $fs->create_file_from_pathname($filerecord, $tempfile);
- }
-
- unlink($tempfile);
- $this->end_log();
- }
-
- /**
- * Creates one forum activity with a bunch of posts.
- */
- private function create_forum() {
- global $DB;
-
- $discussions = self::$paramforumdiscussions[$this->size];
- $posts = self::$paramforumposts[$this->size];
- $totalposts = $discussions * $posts;
-
- $this->log('createforum', $totalposts, true);
-
- // Create empty forum.
- $forumgenerator = $this->generator->get_plugin_generator('mod_forum');
- $record = array('course' => $this->course->id,
- 'name' => get_string('pluginname', 'forum'));
- $options = array('section' => 0);
- $forum = $forumgenerator->create_instance($record, $options);
-
- // Add discussions and posts.
- $sofar = 0;
- for ($i=0; $i < $discussions; $i++) {
- $record = array('forum' => $forum->id, 'course' => $this->course->id,
- 'userid' => $this->get_random_user());
- $discussion = $forumgenerator->create_discussion($record);
- $parentid = $DB->get_field('forum_posts', 'id', array('discussion' => $discussion->id), MUST_EXIST);
- $sofar++;
- for ($j=0; $j < $posts - 1; $j++, $sofar++) {
- $record = array('discussion' => $discussion->id,
- 'userid' => $this->get_random_user(), 'parent' => $parentid);
- $forumgenerator->create_post($record);
- $this->dot($sofar, $totalposts);
- }
- }
-
- $this->end_log();
- }
-
- /**
- * Gets a random section number.
- *
- * @return int A section number from 1 to the number of sections
- */
- private function get_random_section() {
- return rand(1, self::$paramsections[$this->size]);
- }
-
- /**
- * Gets a random user id.
- *
- * @return int A user id for a random created user
- */
- private function get_random_user() {
- return $this->userids[rand(1, self::$paramusers[$this->size])];
- }
-
/**
* Displays information as part of progress.
* @param string $langstring Part of langstring (after progress_)
* @param mixed $a Optional lang string parameters
* @param bool $leaveopen If true, doesn't close LI tag (ready for dots)
*/
- private function log($langstring, $a = null, $leaveopen = false) {
+ protected function log($langstring, $a = null, $leaveopen = false) {
if (!$this->progress) {
return;
}
* @param int $number Number of completed items
* @param int $total Total number of items to complete
*/
- private function dot($number, $total) {
+ protected function dot($number, $total) {
if (!$this->progress) {
return;
}
/**
* Ends a log string that was started using log function with $leaveopen.
*/
- private function end_log() {
+ protected function end_log() {
if (!$this->progress) {
return;
}
echo html_writer::end_tag('li');
}
}
+
}
--- /dev/null
+<?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/>.
+
+/**
+ * tool_generator course backend code.
+ *
+ * @package tool_generator
+ * @copyright 2013 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Backend code for the 'make large course' tool.
+ *
+ * @package tool_generator
+ * @copyright 2013 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_generator_course_backend extends tool_generator_backend {
+ /**
+ * @var array Number of sections in course
+ */
+ private static $paramsections = array(1, 10, 100, 500, 1000, 2000);
+ /**
+ * @var array Number of Page activities in course
+ */
+ private static $parampages = array(1, 50, 200, 1000, 5000, 10000);
+ /**
+ * @var array Number of students enrolled in course
+ */
+ private static $paramusers = array(1, 100, 1000, 10000, 50000, 100000);
+ /**
+ * Total size of small files: 1KB, 1MB, 10MB, 100MB, 1GB, 2GB.
+ *
+ * @var array Number of small files created in a single file activity
+ */
+ private static $paramsmallfilecount = array(1, 64, 128, 1024, 16384, 32768);
+ /**
+ * @var array Size of small files (to make the totals into nice numbers)
+ */
+ private static $paramsmallfilesize = array(1024, 16384, 81920, 102400, 65536, 65536);
+ /**
+ * Total size of big files: 8KB, 8MB, 80MB, 800MB, 8GB, 16GB.
+ *
+ * @var array Number of big files created as individual file activities
+ */
+ private static $parambigfilecount = array(1, 2, 5, 10, 10, 10);
+ /**
+ * @var array Size of each large file
+ */
+ private static $parambigfilesize = array(8192, 4194304, 16777216, 83886080,
+ 858993459, 1717986918);
+ /**
+ * @var array Number of forum discussions
+ */
+ private static $paramforumdiscussions = array(1, 10, 100, 500, 1000, 2000);
+ /**
+ * @var array Number of forum posts per discussion
+ */
+ private static $paramforumposts = array(2, 2, 5, 10, 10, 10);
+
+ /**
+ * @var string Course shortname
+ */
+ private $shortname;
+
+ /**
+ * @var testing_data_generator Data generator
+ */
+ protected $generator;
+
+ /**
+ * @var stdClass Course object
+ */
+ private $course;
+
+ /**
+ * @var array Array from test user number (1...N) to userid in database
+ */
+ private $userids;
+
+ /**
+ * Constructs object ready to create course.
+ *
+ * @param string $shortname Course shortname
+ * @param int $size Size as numeric index
+ * @param bool $fixeddataset To use fixed or random data
+ * @param bool $progress True if progress information should be displayed
+ * @return int Course id
+ */
+ public function __construct($shortname, $size, $fixeddataset = false, $progress = true) {
+
+ // Set parameters.
+ $this->shortname = $shortname;
+
+ parent::__construct($size, $fixeddataset, $progress);
+ }
+
+ /**
+ * Gets a list of size choices supported by this backend.
+ *
+ * @return array List of size (int) => text description for display
+ */
+ public static function get_size_choices() {
+ $options = array();
+ for ($size = self::MIN_SIZE; $size <= self::MAX_SIZE; $size++) {
+ $options[$size] = get_string('coursesize_' . $size, 'tool_generator');
+ }
+ return $options;
+ }
+
+ /**
+ * Checks that a shortname is available (unused).
+ *
+ * @param string $shortname Proposed course shortname
+ * @return string An error message if the name is unavailable or '' if OK
+ */
+ public static function check_shortname_available($shortname) {
+ global $DB;
+ $fullname = $DB->get_field('course', 'fullname',
+ array('shortname' => $shortname), IGNORE_MISSING);
+ if ($fullname !== false) {
+ // I wanted to throw an exception here but it is not possible to
+ // use strings from moodle.php in exceptions, and I didn't want
+ // to duplicate the string in tool_generator, so I changed this to
+ // not use exceptions.
+ return get_string('shortnametaken', 'moodle', $fullname);
+ }
+ return '';
+ }
+
+ /**
+ * Runs the entire 'make' process.
+ *
+ * @return int Course id
+ */
+ public function make() {
+ global $DB, $CFG;
+ require_once($CFG->dirroot . '/lib/phpunit/classes/util.php');
+
+ raise_memory_limit(MEMORY_EXTRA);
+
+ if ($this->progress && !CLI_SCRIPT) {
+ echo html_writer::start_tag('ul');
+ }
+
+ $entirestart = microtime(true);
+
+ // Start transaction.
+ $transaction = $DB->start_delegated_transaction();
+
+ // Get generator.
+ $this->generator = phpunit_util::get_data_generator();
+
+ // Make course.
+ $this->course = $this->create_course();
+ $this->create_users();
+ $this->create_pages();
+ $this->create_small_files();
+ $this->create_big_files();
+ $this->create_forum();
+
+ // Log total time.
+ $this->log('coursecompleted', round(microtime(true) - $entirestart, 1));
+
+ if ($this->progress && !CLI_SCRIPT) {
+ echo html_writer::end_tag('ul');
+ }
+
+ // Commit transaction and finish.
+ $transaction->allow_commit();
+ return $this->course->id;
+ }
+
+ /**
+ * Creates the actual course.
+ *
+ * @return stdClass Course record
+ */
+ private function create_course() {
+ $this->log('createcourse', $this->shortname);
+ $courserecord = array('shortname' => $this->shortname,
+ 'fullname' => get_string('fullname', 'tool_generator',
+ array('size' => get_string('shortsize_' . $this->size, 'tool_generator'))),
+ 'numsections' => self::$paramsections[$this->size]);
+ return $this->generator->create_course($courserecord, array('createsections' => true));
+ }
+
+ /**
+ * Creates a number of user accounts and enrols them on the course.
+ * Note: Existing user accounts that were created by this system are
+ * reused if available.
+ */
+ private function create_users() {
+ global $DB;
+
+ // Work out total number of users.
+ $count = self::$paramusers[$this->size];
+
+ // Get existing users in order. We will 'fill up holes' in this up to
+ // the required number.
+ $this->log('checkaccounts', $count);
+ $nextnumber = 1;
+ $rs = $DB->get_recordset_select('user', $DB->sql_like('username', '?'),
+ array('tool_generator_%'), 'username', 'id, username');
+ foreach ($rs as $rec) {
+ // Extract number from username.
+ $matches = array();
+ if (!preg_match('~^tool_generator_([0-9]{6})$~', $rec->username, $matches)) {
+ continue;
+ }
+ $number = (int)$matches[1];
+
+ // Create missing users in range up to this.
+ if ($number != $nextnumber) {
+ $this->create_user_accounts($nextnumber, min($number - 1, $count));
+ } else {
+ $this->userids[$number] = (int)$rec->id;
+ }
+
+ // Stop if we've got enough users.
+ $nextnumber = $number + 1;
+ if ($number >= $count) {
+ break;
+ }
+ }
+ $rs->close();
+
+ // Create users from end of existing range.
+ if ($nextnumber <= $count) {
+ $this->create_user_accounts($nextnumber, $count);
+ }
+
+ // Assign all users to course.
+ $this->log('enrol', $count, true);
+
+ $enrolplugin = enrol_get_plugin('manual');
+ $instances = enrol_get_instances($this->course->id, true);
+ foreach ($instances as $instance) {
+ if ($instance->enrol === 'manual') {
+ break;
+ }
+ }
+ if ($instance->enrol !== 'manual') {
+ throw new coding_exception('No manual enrol plugin in course');
+ }
+ $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+
+ for ($number = 1; $number <= $count; $number++) {
+ // Enrol user.
+ $enrolplugin->enrol_user($instance, $this->userids[$number], $role->id);
+ $this->dot($number, $count);
+ }
+
+ // Sets the pointer at the beginning to be aware of the users we use.
+ reset($this->userids);
+
+ $this->end_log();
+ }
+
+ /**
+ * Creates user accounts with a numeric range.
+ *
+ * @param int $first Number of first user
+ * @param int $last Number of last user
+ */
+ private function create_user_accounts($first, $last) {
+ $this->log('createaccounts', (object)array('from' => $first, 'to' => $last), true);
+ $count = $last - $first + 1;
+ $done = 0;
+ for ($number = $first; $number <= $last; $number++, $done++) {
+ // Work out username with 6-digit number.
+ $textnumber = (string)$number;
+ while (strlen($textnumber) < 6) {
+ $textnumber = '0' . $textnumber;
+ }
+ $username = 'tool_generator_' . $textnumber;
+
+ // Create user account.
+ $record = array('firstname' => get_string('firstname', 'tool_generator'),
+ 'lastname' => $number, 'username' => $username);
+ $user = $this->generator->create_user($record);
+ $this->userids[$number] = (int)$user->id;
+ $this->dot($done, $count);
+ }
+ $this->end_log();
+ }
+
+ /**
+ * Creates a number of Page activities.
+ */
+ private function create_pages() {
+ // Set up generator.
+ $pagegenerator = $this->generator->get_plugin_generator('mod_page');
+
+ // Create pages.
+ $number = self::$parampages[$this->size];
+ $this->log('createpages', $number, true);
+ for ($i=0; $i<$number; $i++) {
+ $record = array('course' => $this->course->id);
+ $options = array('section' => $this->get_target_section());
+ $pagegenerator->create_instance($record, $options);
+ $this->dot($i, $number);
+ }
+
+ $this->end_log();
+ }
+
+ /**
+ * Creates one resource activity with a lot of small files.
+ */
+ private function create_small_files() {
+ $count = self::$paramsmallfilecount[$this->size];
+ $this->log('createsmallfiles', $count, true);
+
+ // Create resource with default textfile only.
+ $resourcegenerator = $this->generator->get_plugin_generator('mod_resource');
+ $record = array('course' => $this->course->id,
+ 'name' => get_string('smallfiles', 'tool_generator'));
+ $options = array('section' => 0);
+ $resource = $resourcegenerator->create_instance($record, $options);
+
+ // Add files.
+ $fs = get_file_storage();
+ $context = context_module::instance($resource->cmid);
+ $filerecord = array('component' => 'mod_resource', 'filearea' => 'content',
+ 'contextid' => $context->id, 'itemid' => 0, 'filepath' => '/');
+ for ($i = 0; $i < $count; $i++) {
+ $filerecord['filename'] = 'smallfile' . $i . '.dat';
+
+ // Generate random binary data (different for each file so it
+ // doesn't compress unrealistically).
+ $data = self::get_random_binary(self::$paramsmallfilesize[$this->size]);
+
+ $fs->create_file_from_string($filerecord, $data);
+ $this->dot($i, $count);
+ }
+
+ $this->end_log();
+ }
+
+ /**
+ * Creates a string of random binary data. The start of the string includes
+ * the current time, in an attempt to avoid large-scale repetition.
+ *
+ * @param int $length Number of bytes
+ * @return Random data
+ */
+ private static function get_random_binary($length) {
+ $data = microtime(true);
+ if (strlen($data) > $length) {
+ // Use last digits of data.
+ return substr($data, -$length);
+ }
+ $length -= strlen($data);
+ for ($j=0; $j < $length; $j++) {
+ $data .= chr(rand(1, 255));
+ }
+ return $data;
+ }
+
+ /**
+ * Creates a number of resource activities with one big file each.
+ */
+ private function create_big_files() {
+ global $CFG;
+
+ // Work out how many files and how many blocks to use (up to 64KB).
+ $count = self::$parambigfilecount[$this->size];
+ $blocks = ceil(self::$parambigfilesize[$this->size] / 65536);
+ $blocksize = floor(self::$parambigfilesize[$this->size] / $blocks);
+
+ $this->log('createbigfiles', $count, true);
+
+ // Prepare temp area.
+ $tempfolder = make_temp_directory('tool_generator');
+ $tempfile = $tempfolder . '/' . rand();
+
+ // Create resources and files.
+ $fs = get_file_storage();
+ $resourcegenerator = $this->generator->get_plugin_generator('mod_resource');
+ for ($i = 0; $i < $count; $i++) {
+ // Create resource.
+ $record = array('course' => $this->course->id,
+ 'name' => get_string('bigfile', 'tool_generator', $i));
+ $options = array('section' => $this->get_target_section());
+ $resource = $resourcegenerator->create_instance($record, $options);
+
+ // Write file.
+ $handle = fopen($tempfile, 'w');
+ if (!$handle) {
+ throw new coding_exception('Failed to open temporary file');
+ }
+ for ($j = 0; $j < $blocks; $j++) {
+ $data = self::get_random_binary($blocksize);
+ fwrite($handle, $data);
+ $this->dot($i * $blocks + $j, $count * $blocks);
+ }
+ fclose($handle);
+
+ // Add file.
+ $context = context_module::instance($resource->cmid);
+ $filerecord = array('component' => 'mod_resource', 'filearea' => 'content',
+ 'contextid' => $context->id, 'itemid' => 0, 'filepath' => '/',
+ 'filename' => 'bigfile' . $i . '.dat');
+ $fs->create_file_from_pathname($filerecord, $tempfile);
+ }
+
+ unlink($tempfile);
+ $this->end_log();
+ }
+
+ /**
+ * Creates one forum activity with a bunch of posts.
+ */
+ private function create_forum() {
+ global $DB;
+
+ $discussions = self::$paramforumdiscussions[$this->size];
+ $posts = self::$paramforumposts[$this->size];
+ $totalposts = $discussions * $posts;
+
+ $this->log('createforum', $totalposts, true);
+
+ // Create empty forum.
+ $forumgenerator = $this->generator->get_plugin_generator('mod_forum');
+ $record = array('course' => $this->course->id,
+ 'name' => get_string('pluginname', 'forum'));
+ $options = array('section' => 0);
+ $forum = $forumgenerator->create_instance($record, $options);
+
+ // Add discussions and posts.
+ $sofar = 0;
+ for ($i=0; $i < $discussions; $i++) {
+ $record = array('forum' => $forum->id, 'course' => $this->course->id,
+ 'userid' => $this->get_target_user());
+ $discussion = $forumgenerator->create_discussion($record);
+ $parentid = $DB->get_field('forum_posts', 'id', array('discussion' => $discussion->id), MUST_EXIST);
+ $sofar++;
+ for ($j=0; $j < $posts - 1; $j++, $sofar++) {
+ $record = array('discussion' => $discussion->id,
+ 'userid' => $this->get_target_user(), 'parent' => $parentid);
+ $forumgenerator->create_post($record);
+ $this->dot($sofar, $totalposts);
+ }
+ }
+
+ $this->end_log();
+ }
+
+ /**
+ * Gets a section number.
+ *
+ * Depends on $this->fixeddataset.
+ *
+ * @return int A section number from 1 to the number of sections
+ */
+ private function get_target_section() {
+
+ if (!$this->fixeddataset) {
+ $key = rand(1, self::$paramsections[$this->size]);
+ } else {
+ // Using section 1.
+ $key = 1;
+ }
+
+ return $key;
+ }
+
+ /**
+ * Gets a user id.
+ *
+ * Depends on $this->fixeddataset.
+ *
+ * @return int A user id for a random created user
+ */
+ private function get_target_user() {
+
+ if (!$this->fixeddataset) {
+ $userid = $this->userids[rand(1, self::$paramusers[$this->size])];
+ } else if ($userid = current($this->userids)) {
+ // Moving pointer to the next user.
+ next($this->userids);
+ } else {
+ // Returning to the beginning if we reached the end.
+ $userid = reset($this->userids);
+ }
+
+ return $userid;
+ }
+
+}
$mform = $this->_form;
$mform->addElement('select', 'size', get_string('size', 'tool_generator'),
- tool_generator_backend::get_size_choices());
- $mform->setDefault('size', tool_generator_backend::DEFAULT_SIZE);
+ tool_generator_course_backend::get_size_choices());
+ $mform->setDefault('size', tool_generator_course_backend::DEFAULT_SIZE);
$mform->addElement('text', 'shortname', get_string('shortnamecourse'));
$mform->addRule('shortname', get_string('missingshortname'), 'required', null, 'client');
// Check course doesn't already exist.
if (!empty($data['shortname'])) {
// Check shortname.
- $error = tool_generator_backend::check_shortname_available($data['shortname']);
+ $error = tool_generator_course_backend::check_shortname_available($data['shortname']);
if ($error) {
$errors['shortname'] = $error;
}
--- /dev/null
+<?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/>.
+
+/**
+ * tool_generator site backend.
+ *
+ * @package tool_generator
+ * @copyright 2013 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Backend code for the site generator.
+ *
+ * @package tool_generator
+ * @copyright 2013 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_generator_site_backend extends tool_generator_backend {
+
+ /**
+ * @var string The course's shortname prefix.
+ */
+ const SHORTNAMEPREFIX = 'testcourse_';
+
+ /**
+ * @var bool If the debugging level checking was skipped.
+ */
+ protected $bypasscheck;
+
+ /**
+ * @var array Multidimensional array where the first level is the course size and the second the site size.
+ */
+ protected static $sitecourses = array(
+ array(2, 8, 64, 256, 1024, 4096),
+ array(1, 4, 8, 16, 32, 64),
+ array(0, 0, 1, 4, 8, 16),
+ array(0, 0, 0, 1, 0, 0),
+ array(0, 0, 0, 0, 1, 0),
+ array(0, 0, 0, 0, 0, 1)
+ );
+
+ /**
+ * Constructs object ready to make the site.
+ *
+ * @param int $size Size as numeric index
+ * @param bool $bypasscheck If debugging level checking was skipped.
+ * @param bool $fixeddataset To use fixed or random data
+ * @param bool $progress True if progress information should be displayed
+ * @return int Course id
+ */
+ public function __construct($size, $bypasscheck, $fixeddataset = false, $progress = true) {
+
+ // Set parameters.
+ $this->bypasscheck = $bypasscheck;
+
+ parent::__construct($size, $fixeddataset, $progress);
+ }
+
+ /**
+ * Gets a list of size choices supported by this backend.
+ *
+ * @return array List of size (int) => text description for display
+ */
+ public static function get_size_choices() {
+ $options = array();
+ for ($size = self::MIN_SIZE; $size <= self::MAX_SIZE; $size++) {
+ $options[$size] = get_string('sitesize_' . $size, 'tool_generator');
+ }
+ return $options;
+ }
+
+ /**
+ * Runs the entire 'make' process.
+ *
+ * @return int Course id
+ */
+ public function make() {
+ global $DB, $CFG;
+
+ raise_memory_limit(MEMORY_EXTRA);
+
+ if ($this->progress && !CLI_SCRIPT) {
+ echo html_writer::start_tag('ul');
+ }
+
+ $entirestart = microtime(true);
+
+ // Create courses.
+ $prevchdir = getcwd();
+ chdir($CFG->dirroot);
+ $ncourse = $this->get_last_testcourse_id();
+ foreach (self::$sitecourses as $coursesize => $ncourses) {
+ for ($i = 1; $i <= $ncourses[$this->size]; $i++) {
+ // Non language-dependant shortname.
+ $ncourse++;
+ $this->run_create_course(self::SHORTNAMEPREFIX . $ncourse, $coursesize);
+ }
+ }
+ chdir($prevchdir);
+
+ // Store last course id to return it (will be the bigger one).
+ $lastcourseid = $DB->get_field('course', 'id', array('shortname' => self::SHORTNAMEPREFIX . $ncourse));
+
+ // Log total time.
+ $this->log('sitecompleted', round(microtime(true) - $entirestart, 1));
+
+ if ($this->progress && !CLI_SCRIPT) {
+ echo html_writer::end_tag('ul');
+ }
+
+ return $lastcourseid;
+ }
+
+ /**
+ * Creates a course with the specified shortname, coursesize and the provided maketestsite options.
+ *
+ * @param string $shortname The course shortname
+ * @param int $coursesize One of the possible course sizes.
+ * @return void
+ */
+ protected function run_create_course($shortname, $coursesize) {
+
+ // We are in $CFG->dirroot.
+ $command = 'php admin/tool/generator/cli/maketestcourse.php';
+
+ $options = array(
+ '--shortname="' . $shortname . '"',
+ '--size="' . get_string('shortsize_' . $coursesize, 'tool_generator') . '"'
+ );
+
+ if (!$this->progress) {
+ $options[] = '--quiet';
+ }
+
+ // Extend options.
+ $optionstoextend = array(
+ 'fixeddataset' => 'fixeddataset',
+ 'bypasscheck' => 'bypasscheck',
+ );
+
+ // Getting an options string.
+ foreach ($optionstoextend as $attribute => $option) {
+ if (!empty($this->{$attribute})) {
+ $options[] = '--' . $option;
+ }
+ }
+ $options = implode(' ', $options);
+ if ($this->progress) {
+ system($command . ' ' . $options, $exitcode);
+ } else {
+ passthru($command . ' ' . $options, $exitcode);
+ }
+
+ if ($exitcode != 0) {
+ exit($exitcode);
+ }
+ }
+
+ /**
+ * Obtains the last unique sufix (numeric) using the test course prefix.
+ *
+ * @return int The last generated numeric value.
+ */
+ protected function get_last_testcourse_id() {
+ global $DB;
+
+ $params = array();
+ $params['shortnameprefix'] = $DB->sql_like_escape(self::SHORTNAMEPREFIX) . '%';
+ $like = $DB->sql_like('shortname', ':shortnameprefix');
+
+ if (!$testcourses = $DB->get_records_select('course', $like, $params, 'shortname DESC')) {
+ return 0;
+ }
+
+ // They come ordered by shortname DESC, so non-numeric values will be the first ones.
+ foreach ($testcourses as $testcourse) {
+ $sufix = substr($testcourse->shortname, strlen(self::SHORTNAMEPREFIX));
+ if (is_numeric($sufix)) {
+ return $sufix;
+ }
+ }
+
+ // If all sufixes are not numeric this is the fist make test site run.
+ return 0;
+ }
+
+}
'help' => false,
'shortname' => false,
'size' => false,
+ 'fixeddataset' => false,
'bypasscheck' => false,
'quiet' => false
),
Options:
--shortname Shortname of course to create (required)
--size Size of course to create XS, S, M, L, XL, or XXL (required)
+--fixeddataset Use a fixed data set instead of randomly generated data
--bypasscheck Bypasses the developer-mode check (be careful!)
--quiet Do not show any output
// Get options.
$shortname = $options['shortname'];
$sizename = $options['size'];
+$fixeddataset = $options['fixeddataset'];
// Check size.
try {
- $size = tool_generator_backend::size_for_name($sizename);
+ $size = tool_generator_course_backend::size_for_name($sizename);
} catch (coding_exception $e) {
cli_error("Invalid size ($sizename). Use --help for help.");
}
// Check shortname.
-if ($error = tool_generator_backend::check_shortname_available($shortname)) {
+if ($error = tool_generator_course_backend::check_shortname_available($shortname)) {
cli_error($error);
}
session_set_user(get_admin());
// Do backend code to generate course.
-$backend = new tool_generator_backend($shortname, $size, empty($options['quiet']));
+$backend = new tool_generator_course_backend($shortname, $size, $fixeddataset, empty($options['quiet']));
$id = $backend->make();
--- /dev/null
+<?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/>.
+
+/**
+ * CLI interface for creating a test site.
+ *
+ * @package tool_generator
+ * @copyright 2013 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('CLI_SCRIPT', true);
+define('NO_OUTPUT_BUFFERING', true);
+
+require(__DIR__ . '/../../../../config.php');
+require_once($CFG->libdir. '/clilib.php');
+
+// CLI options.
+list($options, $unrecognized) = cli_get_params(
+ array(
+ 'help' => false,
+ 'size' => false,
+ 'fixeddataset' => false,
+ 'bypasscheck' => false,
+ 'quiet' => false
+ ),
+ array(
+ 'h' => 'help'
+ )
+);
+
+$sitesizes = '* ' . implode(PHP_EOL . '* ', tool_generator_site_backend::get_size_choices());
+
+// Display help.
+if (!empty($options['help']) || empty($options['size'])) {
+ echo "
+Utility to generate a standard test site data set.
+
+Not for use on live sites; only normally works if debugging is set to DEVELOPER
+level.
+
+Consider that, depending on the size you select, this CLI tool can really generate a lot of data, aproximated sizes:
+
+$sitesizes
+
+Options:
+--size Size of the generated site, this value affects the number of courses and their size. Accepted values: XS, S, M, L, XL, or XXL (required)
+--fixeddataset Use a fixed data set instead of randomly generated data
+--bypasscheck Bypasses the developer-mode check (be careful!)
+--quiet Do not show any output
+
+-h, --help Print out this help
+
+Example from Moodle root directory:
+\$ php admin/tool/generator/cli/maketestsite.php --size=S
+";
+ // Exit with error unless we're showing this because they asked for it.
+ exit(empty($options['help']) ? 1 : 0);
+}
+
+// Check debugging is set to developer level.
+if (empty($options['bypasscheck']) && !$CFG->debugdeveloper) {
+ cli_error(get_string('error_notdebugging', 'tool_generator'));
+}
+
+// Get options.
+$sizename = $options['size'];
+$fixeddataset = $options['fixeddataset'];
+
+// Check size.
+try {
+ $size = tool_generator_site_backend::size_for_name($sizename);
+} catch (coding_exception $e) {
+ cli_error("Invalid size ($sizename). Use --help for help.");
+}
+
+// Switch to admin user account.
+session_set_user(get_admin());
+
+// Do backend code to generate site.
+$backend = new tool_generator_site_backend($size, $options['bypasscheck'], $fixeddataset, empty($options['quiet']));
+$backend->make();
*/
$string['bigfile'] = 'Big file {$a}';
+$string['coursesize_0'] = 'XS (~10KB; create in ~1 second)';
+$string['coursesize_1'] = 'S (~10MB; create in ~30 seconds)';
+$string['coursesize_2'] = 'M (~100MB; create in ~5 minutes)';
+$string['coursesize_3'] = 'L (~1GB; create in ~1 hour)';
+$string['coursesize_4'] = 'XL (~10GB; create in ~4 hours)';
+$string['coursesize_5'] = 'XXL (~20GB; create in ~8 hours)';
$string['createcourse'] = 'Create course';
$string['creating'] = 'Creating course';
$string['done'] = 'done ({$a}s)';
$string['firstname'] = 'Test course user';
$string['fullname'] = 'Test course: {$a->size}';
$string['maketestcourse'] = 'Make test course';
-$string['pluginname'] = 'Random course generator';
+$string['pluginname'] = 'Development data generator';
$string['progress_createcourse'] = 'Creating course {$a}';
$string['progress_checkaccounts'] = 'Checking user accounts ({$a})';
+$string['progress_coursecompleted'] = 'Course completed ({$a}s)';
$string['progress_createaccounts'] = 'Creating user accounts ({$a->from} - {$a->to})';
$string['progress_createbigfiles'] = 'Creating big files ({$a})';
$string['progress_createforum'] = 'Creating forum ({$a} posts)';
$string['progress_createpages'] = 'Creating pages ({$a})';
$string['progress_createsmallfiles'] = 'Creating small files ({$a})';
$string['progress_enrol'] = 'Enrolling users into course ({$a})';
-$string['progress_complete'] = 'Complete ({$a}s)';
+$string['progress_sitecompleted'] = 'Site completed ({$a}s)';
$string['shortsize_0'] = 'XS';
$string['shortsize_1'] = 'S';
$string['shortsize_2'] = 'M';
$string['shortsize_3'] = 'L';
$string['shortsize_4'] = 'XL';
$string['shortsize_5'] = 'XXL';
+$string['sitesize_0'] = 'XS (~10MB; 3 courses, created in ~30 seconds)';
+$string['sitesize_1'] = 'S (~50MB; 8 courses, created in ~2 minutes)';
+$string['sitesize_2'] = 'M (~200MB; 73 courses, created in ~10 minutes)';
+$string['sitesize_3'] = 'L (~1\'5GB; 277 courses, created in ~1\'5 hours)';
+$string['sitesize_4'] = 'XL (~10GB; 1065 courses, created in ~5 hours)';
+$string['sitesize_5'] = 'XXL (~20GB; 4177 courses, created in ~10 hours)';
$string['size'] = 'Size of course';
-$string['size_0'] = 'XS (~10KB; create in ~1 second)';
-$string['size_1'] = 'S (~10MB; create in ~30 seconds)';
-$string['size_2'] = 'M (~100MB; create in ~5 minutes)';
-$string['size_3'] = 'L (~1GB; create in ~1 hour)';
-$string['size_4'] = 'XL (~10GB; create in ~4 hours)';
-$string['size_5'] = 'XXL (~20GB; create in ~8 hours)';
$string['smallfiles'] = 'Small files';
if ($data = $mform->get_data()) {
// Do actual work.
echo $OUTPUT->heading(get_string('creating', 'tool_generator'));
- $backend = new tool_generator_backend($data->shortname, $data->size);
+ $backend = new tool_generator_course_backend($data->shortname, $data->size);
$id = $backend->make();
echo html_writer::div(
$this->setAdminUser();
// Create the XS course.
- $backend = new tool_generator_backend('TOOL_MAKELARGECOURSE_XS', 0, false);
+ $backend = new tool_generator_course_backend('TOOL_MAKELARGECOURSE_XS', 0, false, false);
$courseid = $backend->make();
// Get course details.
fd.forum = ?", array($forum->instance));
$this->assertEquals(2, $posts);
}
+
+ /**
+ * Creates an small test course with fixed data set and checks the used sections and users.
+ */
+ public function test_fixed_data_set() {
+ global $DB;
+
+ $this->resetAfterTest();
+ $this->setAdminUser();
+
+ // Create the S course (more sections and activities than XS).
+ $backend = new tool_generator_course_backend('TOOL_S_COURSE_1', 1, true, false);
+ $courseid = $backend->make();
+
+ // Get course details.
+ $course = get_course($courseid);
+ $modinfo = get_fast_modinfo($course);
+
+ // Check module instances belongs to section 1.
+ $instances = $modinfo->get_instances_of('page');
+ $npageinstances = count($instances);
+ foreach ($instances as $instance) {
+ $this->assertEquals(1, $instance->sectionnum);
+ }
+
+ // Users that started discussions are the same.
+ $forums = $modinfo->get_instances_of('forum');
+ $nforuminstances = count($forums);
+ $discussions = forum_get_discussions(reset($forums), 'd.timemodified ASC');
+ $lastusernumber = 0;
+ $discussionstarters = array();
+ foreach ($discussions as $discussion) {
+ $usernumber = intval($discussion->lastname);
+
+ // Checks that the users are odd numbers.
+ $this->assertEquals(1, $usernumber % 2);
+
+ // Checks that the users follows an increasing order.
+ $this->assertGreaterThan($lastusernumber, $usernumber);
+ $lastusernumber = $usernumber;
+ $discussionstarters[$discussion->userid] = $discussion->subject;
+ }
+
+ }
}
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2013080700;
-$plugin->requires = 2013080200;
+$plugin->version = 2013090200;
+$plugin->requires = 2013090200;
$plugin->component = 'tool_generator';
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-$string['install'] = 'Install selected language pack';
+$string['install'] = 'Install selected language pack(s)';
$string['installedlangs'] = 'Installed language packs';
$string['langimport'] = 'Language import utility';
$string['langimportdisabled'] = 'Language import feature has been disabled. You have to update your language packs manually at the file-system level. Do not forget to purge string caches after you do so.';
$string['pluginname'] = 'Language packs';
$string['purgestringcaches'] = 'Purge string caches';
$string['remotelangnotavailable'] = 'Because Moodle cannot connect to download.moodle.org, it is not possible for language packs to be installed automatically. Please download the appropriate ZIP file(s) from <a href="http://download.moodle.org/langpack/">download.moodle.org/langpack</a>, copy them to your {$a} directory and unzip them manually.';
-$string['uninstall'] = 'Uninstall selected language pack';
+$string['uninstall'] = 'Uninstall selected language pack(s)';
$string['uninstallconfirm'] = 'You are about to completely uninstall language pack {$a}, are you sure?';
$string['updatelangs'] = 'Update all installed language packs';
$string['templatefile'] = 'Restore from this file after upload';
$string['templatefile_help'] = 'Select a file to use as a template for the creation of all courses.';
$string['unknownimportmode'] = 'Unknown import mode';
-$string['updatemissing'] = 'Fill in missing from CSV data and defaults';
+$string['updatemissing'] = 'Fill in missing items from CSV data and defaults';
$string['updatemode'] = 'Update mode';
$string['updatemodedoessettonothing'] = 'Update mode does not allow anything to be updated';
$string['updateonly'] = 'Only update existing courses';
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/csvlib.class.php');
require_once($CFG->dirroot.'/user/profile/lib.php');
+require_once($CFG->dirroot.'/user/lib.php');
require_once($CFG->dirroot.'/group/lib.php');
require_once($CFG->dirroot.'/cohort/lib.php');
require_once('locallib.php');
}
if ($doupdate or $existinguser->password !== $oldpw) {
- // we want only users that were really updated
-
- $DB->update_record('user', $existinguser);
+ // We want only users that were really updated.
+ user_update_user($existinguser, false);
$upt->track('status', $struserupdated);
$usersupdated++;
profile_save_data($existinguser);
}
- events_trigger('user_updated', $existinguser);
-
if ($bulk == UU_BULK_UPDATED or $bulk == UU_BULK_ALL) {
if (!in_array($user->id, $SESSION->bulk_users)) {
$SESSION->bulk_users[] = $user->id;
$upt->track('password', '-', 'normal', false);
}
- // create user - insert_record ignores any extra properties
- $user->id = $DB->insert_record('user', $user);
+ $user->id = user_create_user($user, false);
$upt->track('username', html_writer::link(new moodle_url('/user/profile.php', array('id'=>$user->id)), s($user->username)), 'normal', false);
// pre-process custom profile menu fields data from csv file
// make sure user context exists
context_user::instance($user->id);
- events_trigger('user_created', $user);
-
if ($bulk == UU_BULK_NEW or $bulk == UU_BULK_ALL) {
if (!in_array($user->id, $SESSION->bulk_users)) {
$SESSION->bulk_users[] = $user->id;
$string['confirmdeletekey'] = 'Are you absolutely sure that you want to delete the key:';
$string['confirmdeletetable'] = 'Are you absolutely sure that you want to delete the table:';
$string['confirmdeletexmlfile'] = 'Are you absolutely sure that you want to delete the file:';
-$string['confirmcheckbigints'] = 'This functionality will search for <a href="http://tracker.moodle.org/browse/MDL-11038">potential wrong integer fields</a> in your Moodle server, generating (but not executing!) automatically the needed SQL statements to have all the integers in your DB properly defined.<br /><br />
-Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).<br /><br />
-It\'s highly recommended to be running the latest (+ version) available of your Moodle release (1.8, 1.9, 2.x ...) before executing the search of wrong integers.<br /><br />
+$string['confirmcheckbigints'] = 'This functionality will search for <a href="http://tracker.moodle.org/browse/MDL-11038">potential wrong integer fields</a> in your Moodle server, generating (but not executing!) automatically the needed SQL statements to have all the integers in your DB properly defined.
+
+Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).
+
+It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search of wrong integers.
+
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
-$string['confirmcheckdefaults'] = 'This functionality will search for inconsistent default values in your Moodle server, generating (but not executing!) the needed SQL statements to have all the default values properly defined.<br /><br />
-Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).<br /><br />
-It\'s highly recommended to be running the latest (+ version) available of your Moodle release (1.8, 1.9, 2.x ...) before executing the search of inconsistent default values.<br /><br />
+$string['confirmcheckdefaults'] = 'This functionality will search for inconsistent default values in your Moodle server, generating (but not executing!) the needed SQL statements to have all the default values properly defined.
+
+Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).
+
+It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search of inconsistent default values.
+
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
-$string['confirmcheckforeignkeys'] = 'This functionality will search for potential violations of the foreign keys defined in the install.xml definitions. (Moodle does not currently generate actual foreign key constraints in the database, which is why invalid data may be present.)<br /><br />
-It\'s highly recommended to be running the latest (+ version) available of your Moodle release (1.8, 1.9, 2.x ...) before executing the search of missing indexes.<br /><br />
+$string['confirmcheckforeignkeys'] = 'This functionality will search for potential violations of the foreign keys defined in the install.xml definitions. (Moodle does not currently generate actual foreign key constraints in the database, which is why invalid data may be present.)
+
+It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search of missing indexes.
+
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
-$string['confirmcheckindexes'] = 'This functionality will search for potential missing indexes in your Moodle server, generating (but not executing!) automatically the needed SQL statements to keep everything updated.<br /><br />
-Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).<br /><br />
-It\'s highly recommended to be running the latest (+ version) available of your Moodle release (1.8, 1.9, 2.x ...) before executing the search of missing indexes.<br /><br />
+$string['confirmcheckindexes'] = 'This functionality will search for potential missing indexes in your Moodle server, generating (but not executing!) automatically the needed SQL statements to keep everything updated.
+
+Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).
+
+It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search of missing indexes.
+
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
-$string['confirmcheckoraclesemantics'] = 'This functionality will search for <a href="http://tracker.moodle.org/browse/MDL-29322">Oracle varchar2 columns using BYTE semantics</a> in your Moodle server, generating (but not executing!) automatically the needed SQL statements to have all the columns converted to use CHAR semantics instead (better for cross-db compatibility and increased contents max. length).<br /><br />
-Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).<br /><br />
-It\'s highly recommended to be running the latest (+ version) available of your Moodle release (2.2, 2.3, 2.x ...) before executing the search of BYTE semantics.<br /><br />
+$string['confirmcheckoraclesemantics'] = 'This functionality will search for <a href="http://tracker.moodle.org/browse/MDL-29322">Oracle varchar2 columns using BYTE semantics</a> in your Moodle server, generating (but not executing!) automatically the needed SQL statements to have all the columns converted to use CHAR semantics instead (better for cross-db compatibility and increased contents max. length).
+
+Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).
+
+It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search of BYTE semantics.
+
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
$string['confirmrevertchanges'] = 'Are you absolutely sure that you want to revert changes performed over:';
$string['create'] = 'Create';
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/authlib.php');
require_once($CFG->dirroot.'/user/filters/lib.php');
+ require_once($CFG->dirroot.'/user/lib.php');
$delete = optional_param('delete', 0, PARAM_INT);
$confirm = optional_param('confirm', '', PARAM_ALPHANUM); //md5 confirmation hash
if ($user = $DB->get_record('user', array('id'=>$suspend, 'mnethostid'=>$CFG->mnet_localhost_id, 'deleted'=>0))) {
if (!is_siteadmin($user) and $USER->id != $user->id and $user->suspended != 1) {
$user->suspended = 1;
- $user->timemodified = time();
- $DB->set_field('user', 'suspended', $user->suspended, array('id'=>$user->id));
- $DB->set_field('user', 'timemodified', $user->timemodified, array('id'=>$user->id));
- // force logout
+ // Force logout.
session_kill_user($user->id);
- events_trigger('user_updated', $user);
+ user_update_user($user, false);
}
}
redirect($returnurl);
if ($user = $DB->get_record('user', array('id'=>$unsuspend, 'mnethostid'=>$CFG->mnet_localhost_id, 'deleted'=>0))) {
if ($user->suspended != 0) {
$user->suspended = 0;
- $user->timemodified = time();
- $DB->set_field('user', 'suspended', $user->suspended, array('id'=>$user->id));
- $DB->set_field('user', 'timemodified', $user->timemodified, array('id'=>$user->id));
- events_trigger('user_updated', $user);
+ user_update_user($user, false);
}
}
redirect($returnurl);
// test pgtIou parameter for proxy mode (https connection
// in background from CAS server to the php server)
if ($authCAS != 'CAS' && !isset($_GET['pgtIou'])) {
- $PAGE->set_url('/auth/cas/auth.php');
+ $PAGE->set_url('/login/index.php');
$PAGE->navbar->add($CASform);
$PAGE->set_title("$site->fullname: $CASform");
$PAGE->set_heading($site->fullname);
$remove_users = $DB->get_records_sql($sql, $params);
if (!empty($remove_users)) {
+ require_once($CFG->dirroot.'/user/lib.php');
$trace->output(get_string('auth_dbuserstoremove','auth_db', count($remove_users)));
foreach ($remove_users as $user) {
$updateuser = new stdClass();
$updateuser->id = $user->id;
$updateuser->suspended = 1;
- $updateuser->timemodified = time();
- $DB->update_record('user', $updateuser);
+ user_update_user($updateuser, false);
$trace->output(get_string('auth_dbsuspenduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1);
}
}
$string['auth_dbfieldpass_key'] = 'Password field';
$string['auth_dbfielduser'] = 'Name of the field containing usernames';
$string['auth_dbfielduser_key'] = 'Username field';
-$string['auth_dbhost'] = 'The computer hosting the database server.';
+$string['auth_dbhost'] = 'The computer hosting the database server. Use a system DSN entry if using ODBC.';
$string['auth_dbhost_key'] = 'Host';
$string['auth_dbchangepasswordurl_key'] = 'Password-change URL';
$string['auth_dbinsertuser'] = 'Inserted user {$a->name} id {$a->id}';
$string['auth_dbinsertuserduplicate'] = 'Error inserting user {$a->username} - user with this username was already created through \'{$a->auth}\' plugin.';
$string['auth_dbinsertusererror'] = 'Error inserting user {$a}';
-$string['auth_dbname'] = 'Name of the database itself';
+$string['auth_dbname'] = 'Name of the database itself. Leave empty if using an ODBC DSN.';
$string['auth_dbname_key'] = 'DB name';
$string['auth_dbpass'] = 'Password matching the above username';
$string['auth_dbpass_key'] = 'Password';
function user_signup($user, $notify=true) {
global $CFG, $DB;
require_once($CFG->dirroot.'/user/profile/lib.php');
+ require_once($CFG->dirroot.'/user/lib.php');
$user->password = hash_internal_user_password($user->password);
- $user->id = $DB->insert_record('user', $user);
+ $user->id = user_create_user($user, false);
- /// Save any custom profile field information
+ // Save any custom profile field information.
profile_save_data($user);
- $user = $DB->get_record('user', array('id'=>$user->id));
- events_trigger('user_created', $user);
-
if (! send_confirmation_email($user)) {
print_error('auth_emailnoemail','auth_email');
}
require_once($CFG->libdir.'/authlib.php');
require_once($CFG->libdir.'/ldaplib.php');
+require_once($CFG->dirroot.'/user/lib.php');
/**
* LDAP authentication plugin.
print_error('auth_ldap_create_error', 'auth_ldap');
}
- $user->id = $DB->insert_record('user', $user);
+ $user->id = user_create_user($user, false);
// Save any custom profile field information
profile_save_data($user);
update_internal_user_password($user, $plainslashedpassword);
$user = $DB->get_record('user', array('id'=>$user->id));
- events_trigger('user_created', $user);
if (! send_confirmation_email($user)) {
print_error('noemail', 'auth_ldap');
if (!$this->user_activate($username)) {
return AUTH_CONFIRM_FAIL;
}
- $DB->set_field('user', 'confirmed', 1, array('id'=>$user->id));
+ $user->confirmed = 1;
if ($user->firstaccess == 0) {
- $DB->set_field('user', 'firstaccess', time(), array('id'=>$user->id));
+ $user->firstaccess = time();
}
- $euser = $DB->get_record('user', array('id' => $user->id));
- events_trigger('user_updated', $euser);
+ require_once($CFG->dirroot.'/user/lib.php');
+ user_update_user($user, false);
return AUTH_CONFIRM_OK;
}
} else {
$updateuser = new stdClass();
$updateuser->id = $user->id;
$updateuser->suspended = 1;
- $DB->update_record('user', $updateuser);
+ user_update_user($updateuser, false);
echo "\t"; print_string('auth_dbsuspenduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)); echo "\n";
- $euser = $DB->get_record('user', array('id' => $user->id));
- events_trigger('user_updated', $euser);
session_kill_user($user->id);
}
} else {
$updateuser->id = $user->id;
$updateuser->auth = $this->authtype;
$updateuser->suspended = 0;
- $DB->update_record('user', $updateuser);
+ user_update_user($updateuser, false);
echo "\t"; print_string('auth_dbreviveduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)); echo "\n";
- $euser = $DB->get_record('user', array('id' => $user->id));
- events_trigger('user_updated', $euser);
}
} else {
print_string('nouserentriestorevive', 'auth_ldap');
$user->lang = $CFG->lang;
}
- $id = $DB->insert_record('user', $user);
+ $id = user_create_user($user, false);
echo "\t"; print_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)); echo "\n";
$euser = $DB->get_record('user', array('id' => $id));
- events_trigger('user_created', $euser);
+
if (!empty($this->config->forcechangepassword)) {
set_user_preference('auth_forcepasswordchange', 1, $id);
}
$updatekeys = array_keys($newinfo);
}
- foreach ($updatekeys as $key) {
- if (isset($newinfo[$key])) {
- $value = $newinfo[$key];
- } else {
- $value = '';
- }
+ if (!empty($updatekeys)) {
+ $newuser = new stdClass();
+ $newuser->id = $userid;
- if (!empty($this->config->{'field_updatelocal_' . $key})) {
- if ($user->{$key} != $value) { // only update if it's changed
- $DB->set_field('user', $key, $value, array('id'=>$userid));
+ foreach ($updatekeys as $key) {
+ if (isset($newinfo[$key])) {
+ $value = $newinfo[$key];
+ } else {
+ $value = '';
+ }
+
+ if (!empty($this->config->{'field_updatelocal_' . $key})) {
+ // Only update if it's changed.
+ if ($user->{$key} != $value) {
+ $newuser->$key = $value;
+ }
}
}
- }
- if (!empty($updatekeys)) {
- $euser = $DB->get_record('user', array('id' => $userid));
- events_trigger('user_updated', $euser);
+ user_update_user($newuser, false);
}
} else {
return false;
// Now start the whole NTLM machinery.
if($this->config->ntlmsso_ie_fastpath == AUTH_NTLM_FASTPATH_YESATTEMPT ||
$this->config->ntlmsso_ie_fastpath == AUTH_NTLM_FASTPATH_YESFORM) {
-
- if(check_browser_version('MSIE')) {
+ if (core_useragent::check_ie_version()) {
$sesskey = sesskey();
redirect($CFG->wwwroot.'/auth/ldap/ntlmsso_magic.php?sesskey='.$sesskey);
} else if ($this->config->ntlmsso_ie_fastpath == AUTH_NTLM_FASTPATH_YESFORM) {
if ($authplugin->ntlmsso_magic($sesskey) && file_exists($file)) {
if (!empty($authplugin->config->ntlmsso_ie_fastpath)) {
- if (check_browser_version('MSIE')) {
+ if (core_useragent::check_ie_version()) {
// $PAGE->https_required() up above takes care of what $CFG->httpswwwroot should be.
redirect($CFG->httpswwwroot.'/auth/ldap/ntlmsso_finish.php');
}
global $CFG, $DB;
require_once $CFG->dirroot . '/mnet/xmlrpc/client.php';
require_once $CFG->libdir . '/gdlib.php';
+ require_once($CFG->dirroot.'/user/lib.php');
// verify the remote host is configured locally before attempting RPC call
if (! $remotehost = $DB->get_record('mnet_host', array('wwwroot' => $remotepeer->wwwroot, 'deleted' => 0))) {
if (empty($localuser->firstaccess)) { // Now firstaccess, grab it here
$localuser->firstaccess = time();
}
-
- $DB->update_record('user', $localuser);
+ user_update_user($localuser, false);
if (!$firsttime) {
// repeat customer! let the IDP know about enrolments
}
$mnethostlogssql = "
SELECT
- mhostlogs.remoteid, mhostlogs.time, mhostlogs.userid, mhostlogs.ip,
- mhostlogs.course, mhostlogs.module, mhostlogs.cmid, mhostlogs.action,
- mhostlogs.url, mhostlogs.info, mhostlogs.username, c.fullname as coursename,
- c.modinfo
+ l.id as remoteid, l.time, l.userid, l.ip, l.course, l.module, l.cmid,
+ l.action, l.url, l.info, u.username
FROM
- (
- SELECT
- l.id as remoteid, l.time, l.userid, l.ip, l.course, l.module, l.cmid,
- l.action, l.url, l.info, u.username
- FROM
- {user} u
- INNER JOIN {log} l on l.userid = u.id
- WHERE
- u.mnethostid = ?
- AND l.id > ?
- ORDER BY remoteid ASC
- LIMIT 500
- ) mhostlogs
- INNER JOIN {course} c on c.id = mhostlogs.course
- ORDER by mhostlogs.remoteid ASC";
+ {user} u
+ INNER JOIN {log} l on l.userid = u.id
+ WHERE
+ u.mnethostid = ?
+ AND l.id > ?
+ AND l.course IS NOT NULL
+ ORDER by l.id ASC
+ LIMIT 500";
$mnethostlogs = $DB->get_records_sql($mnethostlogssql, array($mnethostid, $mnet_request->response['last log id']));
$processedlogs = array();
foreach($mnethostlogs as $hostlog) {
- // Extract the name of the relevant module instance from the
- // course modinfo if possible.
- if (!empty($hostlog->modinfo) && !empty($hostlog->cmid)) {
- $modinfo = unserialize($hostlog->modinfo);
- unset($hostlog->modinfo);
- $modulearray = array();
- foreach($modinfo as $module) {
- $modulearray[$module->cm] = $module->name;
+ try {
+ // Get impersonalised course information. If it is cached there will be no DB queries.
+ $modinfo = get_fast_modinfo($hostlog->course, -1);
+ $hostlog->coursename = $modinfo->get_course()->fullname;
+ if (!empty($hostlog->cmid) && isset($modinfo->cms[$hostlog->cmid])) {
+ $hostlog->resource_name = $modinfo->cms[$hostlog->cmid]->name;
+ } else {
+ $hostlog->resource_name = '';
}
- $hostlog->resource_name = $modulearray[$hostlog->cmid];
- } else {
- $hostlog->resource_name = '';
+ } catch (moodle_exception $e) {
+ // Course not found
+ continue;
}
$processedlogs[] = array (
}
$backup = new backup_ui($bc);
$backup->process();
-if ($backup->get_stage() == backup_ui::STAGE_FINAL) {
- $backup->execute();
-} else {
- $backup->save_controller();
-}
$PAGE->set_title($heading.': '.$backup->get_stage_name());
$PAGE->set_heading($heading);
if ($backup->enforce_changed_dependencies()) {
debugging('Your settings have been altered due to unmet dependencies', DEBUG_DEVELOPER);
}
+
+if ($backup->get_stage() == backup_ui::STAGE_FINAL) {
+ // Display an extra progress bar so that we can show the progress first.
+ echo html_writer::start_div('', array('id' => 'executionprogress'));
+ echo $renderer->progress_bar($backup->get_progress_bar());
+ $backup->get_controller()->set_progress(new core_backup_display_progress());
+ $backup->execute();
+ echo html_writer::end_div();
+ echo html_writer::script('document.getElementById("executionprogress").style.display = "none";');
+} else {
+ $backup->save_controller();
+}
+
echo $renderer->progress_bar($backup->get_progress_bar());
echo $backup->display($renderer);
$backup->destroy();
protected $destination; // Destination chain object (fs_moodle, fs_os, db, email...)
protected $logger; // Logging chain object (moodle, inline, fs, db, syslog)
+ /**
+ * @var core_backup_progress Progress reporting object.
+ */
+ protected $progress;
+
protected $checksum; // Cache @checksumable results for lighter @is_checksum_correct() uses
/**
// Default logger chain (based on interactive/execution)
$this->logger = backup_factory::get_logger_chain($this->interactive, $this->execution, $this->backupid);
+ // By default there is no progress reporter. Interfaces that wish to
+ // display progress must set it.
+ $this->progress = new core_backup_null_progress();
+
// Instantiate the output_controller singleton and active it if interactive and inmediate
$oc = output_controller::get_instance();
if ($this->interactive == backup::INTERACTIVE_YES && $this->execution == backup::EXECUTION_INMEDIATE) {
return $this->logger;
}
+ /**
+ * Gets the progress reporter, which can be used to report progress within
+ * the backup or restore process.
+ *
+ * @return core_backup_progress Progress reporting object
+ */
+ public function get_progress() {
+ return $this->progress;
+ }
+
+ /**
+ * Sets the progress reporter.
+ *
+ * @param core_backup_progress $progress Progress reporting object
+ */
+ public function set_progress(core_backup_progress $progress) {
+ $this->progress = $progress;
+ }
+
/**
* Executes the backup
* @return void Throws and exception of completes
protected $logger; // Logging chain object (moodle, inline, fs, db, syslog)
+ /**
+ * @var core_backup_progress Progress reporting object.
+ */
+ protected $progress;
+
protected $checksum; // Cache @checksumable results for lighter @is_checksum_correct() uses
/**
// Default logger chain (based on interactive/execution)
$this->logger = backup_factory::get_logger_chain($this->interactive, $this->execution, $this->restoreid);
+ // By default there is no progress reporter. Interfaces that wish to
+ // display progress must set it.
+ $this->progress = new core_backup_null_progress();
+
// Instantiate the output_controller singleton and active it if interactive and inmediate
$oc = output_controller::get_instance();
if ($this->interactive == backup::INTERACTIVE_YES && $this->execution == backup::EXECUTION_INMEDIATE) {
return $this->logger;
}
+ /**
+ * Gets the progress reporter, which can be used to report progress within
+ * the backup or restore process.
+ *
+ * @return core_backup_progress Progress reporting object
+ */
+ public function get_progress() {
+ return $this->progress;
+ }
+
+ /**
+ * Sets the progress reporter.
+ *
+ * @param core_backup_progress $progress Progress reporting object
+ */
+ public function set_progress(core_backup_progress $progress) {
+ $this->progress = $progress;
+ }
+
public function execute_plan() {
// Basic/initial prevention against time/memory limits
set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
if ($this->status != backup::STATUS_NEED_PRECHECK) {
throw new restore_controller_exception('cannot_precheck_wrong_status', $this->status);
}
+ // Basic/initial prevention against time/memory limits
+ set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
+ raise_memory_limit(MEMORY_EXTRA);
$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);
// If it's the final stage process the import
if ($backup->get_stage() == backup_ui::STAGE_FINAL) {
+ echo $OUTPUT->header();
+
+ // Display an extra progress bar so that we can show the current stage.
+ echo html_writer::start_div('', array('id' => 'executionprogress'));
+ echo $renderer->progress_bar($backup->get_progress_bar());
+
+ // Start the progress display - we split into 2 chunks for backup and restore.
+ $progress = new core_backup_display_progress();
+ $progress->start_progress('', 2);
+ $backup->get_controller()->set_progress($progress);
+
// First execute the backup
$backup->execute();
$backup->destroy();
unset($backup);
+ // Note that we've done that progress.
+ $progress->progress(1);
+
// Check whether the backup directory still exists. If missing, something
// went really wrong in backup, throw error. Note that backup::MODE_IMPORT
// backups don't store resulting files ever
// Prepare the restore controller. We don't need a UI here as we will just use what
// ever the restore has (the user has just chosen).
$rc = new restore_controller($backupid, $course->id, backup::INTERACTIVE_YES, backup::MODE_IMPORT, $USER->id, $restoretarget);
+ $rc->set_progress($progress);
// Convert the backup if required.... it should NEVER happed
if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) {
$rc->convert();
// Delete the temp directory now
fulldelete($tempdestination);
+ // All progress complete. Hide progress area.
+ $progress->end_progress();
+ echo html_writer::end_div();
+ echo html_writer::script('document.getElementById("executionprogress").style.display = "none";');
+
// Display a notification and a continue button
- echo $OUTPUT->header();
if ($warnings) {
echo $OUTPUT->box_start();
echo $OUTPUT->notification(get_string('warning'), 'notifywarning');
$this->built = true;
}
+ public function get_weight() {
+ // The final task takes ages, so give it 20 times the weight of a normal task.
+ return 20;
+ }
+
// Protected API starts here
/**
$data->theme = '';
}
+ // Check if this is an old SCORM course format.
+ if ($data->format == 'scorm') {
+ $data->format = 'singleactivity';
+ $data->activitytype = 'scorm';
+ }
+
// Course record ready, update it
$DB->update_record('course', $data);
// Iterate over aliases in the queue.
foreach ($rs as $record) {
- $info = restore_dbops::decode_backup_temp_info($record->info);
+ $info = backup_controller_dbops::decode_backup_temp_info($record->info);
// Try to pick a repository instance that should serve the alias.
$repository = $this->choose_repository($info);
}
}
+$heading = $course->fullname;
+
+$PAGE->set_title($heading.': '.$restore->get_stage_name());
+$PAGE->set_heading($heading);
+$PAGE->navbar->add($restore->get_stage_name());
+
+$renderer = $PAGE->get_renderer('core','backup');
+echo $OUTPUT->header();
+
+// Prepare a progress bar which can display optionally during long-running
+// operations while setting up the UI.
+$slowprogress = new core_backup_display_progress_if_slow();
+// Depending on the code branch above, $restore may be a restore_ui or it may
+// be a restore_ui_independent_stage. Either way, this function exists.
+$restore->set_progress_reporter($slowprogress);
$outcome = $restore->process();
+
+if (!$restore->is_independent() && $restore->enforce_changed_dependencies()) {
+ debugging('Your settings have been altered due to unmet dependencies', DEBUG_DEVELOPER);
+}
+
if (!$restore->is_independent()) {
if ($restore->get_stage() == restore_ui::STAGE_PROCESS && !$restore->requires_substage()) {
try {
+ // Display an extra progress bar so that we can show the progress first.
+ echo html_writer::start_div('', array('id' => 'executionprogress'));
+ echo $renderer->progress_bar($restore->get_progress_bar());
+ $restore->get_controller()->set_progress(new core_backup_display_progress());
$restore->execute();
+ echo html_writer::end_div();
+ echo html_writer::script('document.getElementById("executionprogress").style.display = "none";');
} catch(Exception $e) {
$restore->cleanup();
throw $e;
$restore->save_controller();
}
}
-$heading = $course->fullname;
-
-$PAGE->set_title($heading.': '.$restore->get_stage_name());
-$PAGE->set_heading($heading);
-$PAGE->navbar->add($restore->get_stage_name());
-$renderer = $PAGE->get_renderer('core','backup');
-echo $OUTPUT->header();
-if (!$restore->is_independent() && $restore->enforce_changed_dependencies()) {
- debugging('Your settings have been altered due to unmet dependencies', DEBUG_DEVELOPER);
-}
echo $renderer->progress_bar($restore->get_progress_bar());
echo $restore->display($renderer);
$restore->destroy();
*/
abstract class backup_controller_dbops extends backup_dbops {
+ /**
+ * @var string Backup id for cached backup_includes_files result.
+ */
+ protected static $includesfilescachebackupid;
+
+ /**
+ * @var int Cached backup_includes_files result
+ */
+ protected static $includesfilescache;
+
/**
* Send one backup controller to DB
*
* @return int Indicates whether files should be included in backups.
*/
public static function backup_includes_files($backupid) {
- // Load controller
+ // This function is called repeatedly in a backup with many files.
+ // Loading the controller is a nontrivial operation (in a large test
+ // backup it took 0.3 seconds), so we do a temporary cache of it within
+ // this request.
+ if (self::$includesfilescachebackupid === $backupid) {
+ return self::$includesfilescache;
+ }
+
+ // Load controller, get value, then destroy controller and return result.
+ self::$includesfilescachebackupid = $backupid;
$bc = self::load_controller($backupid);
- return $bc->get_include_files();
+ self::$includesfilescache = $bc->get_include_files();
+ $bc->destroy();
+ return self::$includesfilescache;
}
/**
require_once($CFG->dirroot . '/backup/util/loggers/file_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/database_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/output_indented_logger.class.php');
+require_once($CFG->dirroot . '/backup/util/progress/core_backup_progress.class.php');
+require_once($CFG->dirroot . '/backup/util/progress/core_backup_null_progress.class.php');
+require_once($CFG->dirroot . '/backup/util/progress/core_backup_display_progress.class.php');
require_once($CFG->dirroot . '/backup/util/settings/setting_dependency.class.php');
require_once($CFG->dirroot . '/backup/util/settings/base_setting.class.php');
require_once($CFG->dirroot . '/backup/util/settings/backup_setting.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/file_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/database_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/output_indented_logger.class.php');
+require_once($CFG->dirroot . '/backup/util/progress/core_backup_progress.class.php');
+require_once($CFG->dirroot . '/backup/util/progress/core_backup_null_progress.class.php');
+require_once($CFG->dirroot . '/backup/util/progress/core_backup_display_progress.class.php');
+require_once($CFG->dirroot . '/backup/util/progress/core_backup_display_progress_if_slow.class.php');
require_once($CFG->dirroot . '/backup/util/factories/backup_factory.class.php');
require_once($CFG->dirroot . '/backup/util/factories/restore_factory.class.php');
require_once($CFG->dirroot . '/backup/util/helper/backup_helper.class.php');
if ($this->levelcol) {
$columns[$this->levelcol] = $level;
}
- $columns[$this->messagecol] = $message; //TODO: should this be cleaned?
+ $columns[$this->messagecol] = clean_param($message, PARAM_NOTAGS);
return $this->insert_log_record($this->logtable, $columns);
}
// to preserve DB logs if the whole backup/restore transaction is
// rollback
global $DB;
- return $DB->insert_record($this->logtable, $columns, false); // Don't return inserted id
+ return $DB->insert_record($table, $columns, false); // Don't return inserted id
}
}
return $this->controller->get_logger();
}
+ /**
+ * Gets the progress reporter, which can be used to report progress within
+ * the backup or restore process.
+ *
+ * @return core_backup_progress Progress reporting object
+ */
+ public function get_progress() {
+ return $this->controller->get_progress();
+ }
+
public function is_excluding_activities() {
return $this->excludingdactivities;
}
if (!$this->built) {
throw new base_plan_exception('base_plan_not_built');
}
+
+ // Calculate the total weight of all tasks and start progress tracking.
+ $progress = $this->get_progress();
+ $totalweight = 0;
+ foreach ($this->tasks as $task) {
+ $totalweight += $task->get_weight();
+ }
+ $progress->start_progress($this->get_name(), $totalweight);
+
+ // Build and execute all tasks.
foreach ($this->tasks as $task) {
$task->build();
$task->execute();
}
+
+ // Finish progress tracking.
+ $progress->end_progress();
}
+ /**
+ * Gets the progress reporter, which can be used to report progress within
+ * the backup or restore process.
+ *
+ * @return core_backup_progress Progress reporting object
+ */
+ public abstract function get_progress();
+
/**
* Destroy all circular references. It helps PHP 5.2 a lot!
*/
return $this->settings;
}
+ /**
+ * Returns the weight of this task, an approximation of the amount of time
+ * it will take. By default this value is 1. It can be increased for longer
+ * tasks.
+ *
+ * @return int Weight
+ */
+ public function get_weight() {
+ return 1;
+ }
+
public function get_setting($name) {
// First look in task settings
$result = null;
return $this->plan->get_logger();
}
+ /**
+ * Gets the progress reporter, which can be used to report progress within
+ * the backup or restore process.
+ *
+ * @return core_backup_progress Progress reporting object
+ */
+ public function get_progress() {
+ return $this->plan->get_progress();
+ }
+
public function log($message, $level, $a = null, $depth = null, $display = false) {
backup_helper::log($message, $level, $a, $depth, $display, $this->get_logger());
}
if ($this->executed) {
throw new base_task_exception('base_task_already_executed', $this->name);
}
+
+ // Starts progress based on the weight of this task and number of steps.
+ $progress = $this->get_progress();
+ $progress->start_progress($this->get_name(), count($this->steps), $this->get_weight());
+ $done = 0;
+
+ // Execute all steps.
foreach ($this->steps as $step) {
$result = $step->execute();
// If step returns array, it will be forwarded to plan