composer.phar
composer.lock
/vendor/
+/behat.yml
--- /dev/null
+{
+ "browser": true,
+ "node": true,
+ "yui": true,
+ "bitwise": true,
+ "curly": true,
+ "eqeqeq": true,
+ "forin": false,
+ "immed": true,
+ "newcap": true,
+ "noarg": true,
+ "noempty": true,
+ "nomen": false,
+ "onevar": false,
+ "plusplus": false,
+ "regexp": false,
+ "strict": false,
+ "trailing": true,
+ "unused": true,
+ "white": false,
+ "asi": false,
+ "boss": false,
+ "debug": false,
+ "eqnull": false,
+ "es5": false,
+ "esnext": false,
+ "evil": false,
+ "expr": false,
+ "funcscope": false,
+ "globalstrict": false,
+ "iterator": false,
+ "lastsemic": false,
+ "laxbreak": true,
+ "laxcomma": false,
+ "loopfunc": false,
+ "multistr": false,
+ "onecase": false,
+ "proto": false,
+ "regexdash": false,
+ "scripturl": false,
+ "shadow": false,
+ "smarttabs": false,
+ "sub": false,
+ "supernew": false,
+ "maxerr": 500,
+ "maxlen": 150,
+ "passfail": false,
+ "latedef": true
+}
<?php
-
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
- * Enable or disable maintenance mode
+ * Enable or disable maintenance mode.
*
* @package core
* @subpackage cli
define('CLI_SCRIPT', true);
-require(dirname(dirname(dirname(__FILE__))).'/config.php');
-require_once($CFG->libdir.'/clilib.php'); // cli only functions
+require(__DIR__.'/../../config.php');
+require_once("$CFG->libdir/clilib.php");
+require_once("$CFG->libdir/adminlib.php");
-// now get cli options
-list($options, $unrecognized) = cli_get_params(array('enable'=>false, 'disable'=>false, 'help'=>false),
+// Now get cli options.
+list($options, $unrecognized) = cli_get_params(array('enable'=>false, 'enablelater'=>0, 'enableold'=>false, 'disable'=>false, 'help'=>false),
array('h'=>'help'));
if ($unrecognized) {
Current status displayed if not option specified.
Options:
---enable Enable maintenance mode
+--enable Enable CLI maintenance mode
+--enablelater=MINUTES Number of minutes before entering CLI maintenance mode
+--enableold Enable legacy half-maintenance mode
--disable Disable maintenance mode
-h, --help Print out this help
Example:
-\$sudo -u www-data /usr/bin/php admin/cli/maintenance.php
+\$ sudo -u www-data /usr/bin/php admin/cli/maintenance.php
"; //TODO: localize - to be translated later when everything is finished
echo $help;
cli_heading(get_string('sitemaintenancemode', 'admin')." ($CFG->wwwroot)");
-if ($options['enable']) {
+if ($options['enablelater']) {
+ if (file_exists("$CFG->dataroot/climaintenance.html")) {
+ // Already enabled, sorry.
+ echo get_string('clistatusenabled', 'admin')."\n";
+ return 1;
+ }
+
+ $time = time() + ($options['enablelater']*60);
+ set_config('maintenance_later', $time);
+
+ echo get_string('clistatusenabledlater', 'admin', userdate($time))."\n";
+ return 0;
+
+} else if ($options['enable']) {
+ if (file_exists("$CFG->dataroot/climaintenance.html")) {
+ // The maintenance is already enabled, nothing to do.
+ } else {
+ enable_cli_maintenance_mode();
+ }
+ set_config('maintenance_enabled', 0);
+ unset_config('maintenance_later');
+ echo get_string('sitemaintenanceoncli', 'admin')."\n";
+ exit(0);
+
+} else if ($options['enableold']) {
set_config('maintenance_enabled', 1);
+ unset_config('maintenance_later');
echo get_string('sitemaintenanceon', 'admin')."\n";
exit(0);
+
} else if ($options['disable']) {
set_config('maintenance_enabled', 0);
+ unset_config('maintenance_later');
+ if (file_exists("$CFG->dataroot/climaintenance.html")) {
+ unlink("$CFG->dataroot/climaintenance.html");
+ }
echo get_string('sitemaintenanceoff', 'admin')."\n";
exit(0);
}
-if (!empty($CFG->maintenance_enabled)) {
+if (!empty($CFG->maintenance_enabled) or file_exists("$CFG->dataroot/climaintenance.html")) {
echo get_string('clistatusenabled', 'admin')."\n";
+
+} else if (isset($CFG->maintenance_later)) {
+ echo get_string('clistatusenabledlater', 'admin', userdate($CFG->maintenance_later))."\n";
+
} else {
echo get_string('clistatusdisabled', 'admin')."\n";
}
public function make_copy() {
$this->roleid = 0;
unset($this->role->id);
- $this->role->name .= ' ' . get_string('copyasnoun');
+ $this->role->name = role_get_name($this->role, null, ROLENAME_ORIGINAL) . ' ' . get_string('copyasnoun');
$this->role->shortname .= 'copy';
}
return $output;
}
+ /**
+ * Returns an array of roles of the allowed type.
+ *
+ * @param string $type Must be one of: assign, switch, or override.
+ * @return array
+ */
+ protected function get_allow_roles_list($type) {
+ global $DB;
+
+ if ($type !== 'assign' and $type !== 'switch' and $type !== 'override') {
+ debugging('Invalid role allowed type specified', DEBUG_DEVELOPER);
+ return array();
+ }
+
+ if (empty($this->roleid)) {
+ return array();
+ }
+
+ $sql = "SELECT r.*
+ FROM {role} r
+ JOIN {role_allow_{$type}} a ON a.allow{$type} = r.id
+ WHERE a.roleid = :roleid
+ ORDER BY r.sortorder ASC";
+ return $DB->get_records_sql($sql, array('roleid'=>$this->roleid));
+ }
+
+ /**
+ * Returns an array of roles with the allowed type.
+ *
+ * @param string $type Must be one of: assign, switch, or override.
+ * @return array Am array of role names with the allowed type
+ */
+ protected function get_allow_role_control($type) {
+ if ($roles = $this->get_allow_roles_list($type)) {
+ $roles = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
+ return implode(', ', $roles);
+ } else {
+ return get_string('none');
+ }
+ }
+
+ /**
+ * Returns information about the risks associated with a role.
+ *
+ * @return string
+ */
+ protected function get_role_risks_info() {
+ return '';
+ }
+
protected function print_field($name, $caption, $field) {
global $OUTPUT;
// Attempt to generate HTML like formslib.
$this->print_field('edit-description', get_string('customroledescription', 'role').' '.$OUTPUT->help_icon('customroledescription', 'role'), $this->get_description_field('description'));
$this->print_field('menuarchetype', get_string('archetype', 'role').' '.$OUTPUT->help_icon('archetype', 'role'), $this->get_archetype_field('archetype'));
$this->print_field('', get_string('maybeassignedin', 'role'), $this->get_assignable_levels_control());
+ $this->print_field('', get_string('allowassign', 'role'), $this->get_allow_role_control('assign'));
+ $this->print_field('', get_string('allowoverride', 'role'), $this->get_allow_role_control('override'));
+ $this->print_field('', get_string('allowswitch', 'role'), $this->get_allow_role_control('switch'));
+ if ($risks = $this->get_role_risks_info()) {
+ $this->print_field('', get_string('rolerisks', 'role'), $risks);
+ }
echo "</div>";
$this->print_show_hide_advanced_button();
// Do nothing.
}
+ /**
+ * Returns HTML risk icons.
+ *
+ * @return string
+ */
+ protected function get_role_risks_info() {
+ global $OUTPUT;
+
+ if (empty($this->roleid)) {
+ return '';
+ }
+
+ $risks = array();
+ $allrisks = get_all_risks();
+ foreach ($this->capabilities as $capability) {
+ $perm = $this->permissions[$capability->name];
+ if ($perm != CAP_ALLOW) {
+ continue;
+ }
+ foreach ($allrisks as $type=>$risk) {
+ if ($risk & (int)$capability->riskbitmask) {
+ $risks[$type] = $risk;
+ }
+ }
+ }
+
+ $risksurl = new moodle_url(get_docs_url(s(get_string('risks', 'role'))));
+ foreach ($risks as $type=>$risk) {
+ $pixicon = new pix_icon('/i/' . str_replace('risk', 'risk_', $type), get_string($type . 'short', 'admin'));
+ $risks[$type] = $OUTPUT->action_icon($risksurl, $pixicon, new popup_action('click', $risksurl));
+ }
+
+ return implode(' ', $risks);
+ }
+
+ /**
+ * Returns true if the row should be skipped.
+ *
+ * @param string $capability
+ * @return bool
+ */
+ protected function skip_row($capability) {
+ $perm = $this->permissions[$capability->name];
+ if ($perm == CAP_INHERIT) {
+ // Do not print empty rows in role overview, admins need to know quickly what is allowed and prohibited,
+ // if they want to see the list of all capabilities they can go to edit role page.
+ return true;
+ }
+ parent::skip_row($capability);
+ }
+
protected function add_permission_cells($capability) {
$perm = $this->permissions[$capability->name];
$permname = $this->allpermissions[$perm];
);
$temp->add(new admin_setting_configselect('defaulthomepage', new lang_string('defaulthomepage', 'admin'), new lang_string('configdefaulthomepage', 'admin'), HOMEPAGE_SITE, $choices));
$temp->add(new admin_setting_configcheckbox('allowguestmymoodle', new lang_string('allowguestmymoodle', 'admin'), new lang_string('configallowguestmymoodle', 'admin'), 1));
+ $temp->add(new admin_setting_configcheckbox('navshowfullcoursenames', new lang_string('navshowfullcoursenames', 'admin'), new lang_string('navshowfullcoursenames_help', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('navshowcategories', new lang_string('navshowcategories', 'admin'), new lang_string('confignavshowcategories', 'admin'), 1));
$temp->add(new admin_setting_configcheckbox('navshowmycoursecategories', new lang_string('navshowmycoursecategories', 'admin'), new lang_string('navshowmycoursecategories_help', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('navshowallcourses', new lang_string('navshowallcourses', 'admin'), new lang_string('confignavshowallcourses', 'admin'), 0));
or has_capability('moodle/course:create', $systemcontext)
or has_capability('moodle/site:approvecourse', $systemcontext)) { // speedup for non-admins, add all caps used on this page
- $ADMIN->add('courses', new admin_externalpage('coursemgmt', new lang_string('coursemgmt', 'admin'), $CFG->wwwroot . '/course/index.php?categoryedit=on',
+ $ADMIN->add('courses', new admin_externalpage('coursemgmt', new lang_string('coursemgmt', 'admin'), $CFG->wwwroot . '/course/manage.php',
array('moodle/category:manage', 'moodle/course:create')));
/// Course Default Settings Page
--- /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 tool with utilities to manage Behat integration in Moodle
+ *
+ * All CLI utilities uses $CFG->behat_dataroot and $CFG->prefix_dataroot as
+ * $CFG->dataroot and $CFG->prefix
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+if (isset($_SERVER['REMOTE_ADDR'])) {
+ die(); // No access from web!.
+}
+
+// Basic functions.
+require_once(__DIR__ . '/../../../../lib/clilib.php');
+require_once(__DIR__ . '/../../../../lib/behat/lib.php');
+
+
+// CLI options.
+list($options, $unrecognized) = cli_get_params(
+ array(
+ 'help' => false,
+ 'install' => false,
+ 'drop' => false,
+ 'enable' => false,
+ 'disable' => false,
+ ),
+ array(
+ 'h' => 'help'
+ )
+);
+
+
+// Checking util.php CLI script usage.
+$help = "
+Behat utilities to manage the test environment
+
+Options:
+--install Installs the test environment for acceptance tests
+--drop Drops the database tables and the dataroot contents
+--enable Enables test environment and updates tests list
+--disable Disables test environment
+
+-h, --help Print out this help
+
+Example from Moodle root directory:
+\$ php admin/tool/behat/cli/util.php --enable
+
+More info in http://docs.moodle.org/dev/Acceptance_testing#Running_tests
+";
+
+if (!empty($options['help'])) {
+ echo $help;
+ exit(0);
+}
+
+
+// Checking $CFG->behat_* vars and values.
+define('BEHAT_UTIL', true);
+define('CLI_SCRIPT', true);
+define('ABORT_AFTER_CONFIG', true);
+define('NO_OUTPUT_BUFFERING', true);
+
+error_reporting(E_ALL | E_STRICT);
+ini_set('display_errors', '1');
+ini_set('log_errors', '1');
+
+require_once(__DIR__ . '/../../../../config.php');
+
+// CFG->behat_prefix must be set and with value different than CFG->prefix and phpunit_prefix.
+if (!isset($CFG->behat_prefix) ||
+ (isset($CFG->behat_prefix) &&
+ ($CFG->behat_prefix == $CFG->prefix ||
+ $CFG->behat_prefix == $CFG->phpunit_prefix))) {
+ behat_error(BEHAT_EXITCODE_CONFIG,
+ 'Define $CFG->behat_prefix in config.php with a value different than $CFG->prefix and $CFG->phpunit_prefix');
+}
+
+// CFG->behat_dataroot must be set and with value different than CFG->dataroot and phpunit_dataroot.
+if (!isset($CFG->behat_dataroot) ||
+ (isset($CFG->behat_dataroot) &&
+ ($CFG->behat_dataroot == $CFG->dataroot ||
+ $CFG->behat_dataroot == $CFG->phpunit_dataroot))) {
+ behat_error(BEHAT_EXITCODE_CONFIG,
+ 'Define $CFG->behat_dataroot in config.php with a value different than $CFG->dataroot and $CFG->phpunit_dataroot');
+}
+
+// Create behat_dataroot if it doesn't exists.
+if (!file_exists($CFG->behat_dataroot)) {
+ if (!mkdir($CFG->behat_dataroot, $CFG->directorypermissions)) {
+ behat_error(BEHAT_EXITCODE_PERMISSIONS, '$CFG->behat_dataroot directory can not be created');
+ }
+}
+if (!is_dir($CFG->behat_dataroot) || !is_writable($CFG->behat_dataroot)) {
+ behat_error(BEHAT_EXITCODE_PERMISSIONS, '$CFG->behat_dataroot directory has no permissions or is not a directory');
+}
+
+// Check that the directory does not contains other things.
+if (!file_exists("$CFG->behat_dataroot/behattestdir.txt")) {
+ if ($dh = opendir($CFG->behat_dataroot)) {
+ while (($file = readdir($dh)) !== false) {
+ if ($file === 'behat' or $file === '.' or $file === '..' or $file === '.DS_Store') {
+ continue;
+ }
+ behat_error(BEHAT_EXITCODE_CONFIG, '$CFG->behat_dataroot directory is not empty, ensure this is the directory where you want to install behat test dataroot');
+ }
+ closedir($dh);
+ unset($dh);
+ unset($file);
+ }
+
+ // Now we create dataroot directory structure for behat tests.
+ testing_initdataroot($CFG->behat_dataroot, 'behat');
+}
+
+// Overrides vars with behat-test ones.
+$vars = array('wwwroot', 'prefix', 'dataroot');
+foreach ($vars as $var) {
+ $CFG->{$var} = $CFG->{'behat_' . $var};
+}
+
+$CFG->noemailever = true;
+$CFG->passwordsaltmain = 'moodle';
+
+// Continues setup.
+define('ABORT_AFTER_CONFIG_CANCEL', true);
+require("$CFG->dirroot/lib/setup.php");
+
+require_once($CFG->libdir.'/adminlib.php');
+require_once($CFG->libdir.'/upgradelib.php');
+require_once($CFG->libdir.'/clilib.php');
+require_once($CFG->libdir.'/pluginlib.php');
+require_once($CFG->libdir.'/installlib.php');
+require_once($CFG->libdir.'/testing/classes/test_lock.php');
+
+if ($unrecognized) {
+ $unrecognized = implode("\n ", $unrecognized);
+ cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
+}
+
+// Behat utilities.
+require_once($CFG->libdir . '/behat/classes/util.php');
+require_once($CFG->libdir . '/behat/classes/behat_command.php');
+
+// Run command (only one per time).
+if ($options['install']) {
+ behat_util::install_site();
+ mtrace("Acceptance tests site installed");
+} else if ($options['drop']) {
+ // Ensure no tests are running.
+ test_lock::acquire('behat');
+ behat_util::drop_site();
+ mtrace("Acceptance tests site dropped");
+} else if ($options['enable']) {
+ behat_util::start_test_mode();
+ $runtestscommand = behat_command::get_behat_command() . ' --config '
+ . $CFG->behat_dataroot . DIRECTORY_SEPARATOR . 'behat' . DIRECTORY_SEPARATOR . 'behat.yml';
+ mtrace("Acceptance tests environment enabled, to run the tests use:\n " . $runtestscommand);
+} else if ($options['disable']) {
+ behat_util::stop_test_mode();
+ mtrace("Acceptance tests environment disabled");
+} else {
+ echo $help;
+}
+
+exit(0);
--- /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/>.
+
+/**
+ * Web interface to list and filter steps
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require(__DIR__ . '/../../../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/behat/locallib.php');
+require_once($CFG->libdir . '/behat/classes/behat_config_manager.php');
+
+$filter = optional_param('filter', '', PARAM_ALPHANUMEXT);
+$type = optional_param('type', false, PARAM_ALPHAEXT);
+$component = optional_param('component', '', PARAM_ALPHAEXT);
+
+admin_externalpage_setup('toolbehat');
+
+// Getting available steps definitions from behat.
+$steps = tool_behat::stepsdefinitions($type, $component, $filter);
+
+// Form.
+$componentswithsteps = array('' => get_string('allavailablesteps', 'tool_behat'));
+
+// Complete the components list with the moodle steps definitions.
+$components = behat_config_manager::get_components_steps_definitions();
+if ($components) {
+ foreach ($components as $component => $filepath) {
+ // TODO Use a class static attribute instead of the class name.
+ $componentswithsteps[$component] = 'Moodle ' . substr($component, 6);
+ }
+}
+$form = new steps_definitions_form(null, array('components' => $componentswithsteps));
+
+// Output contents.
+$renderer = $PAGE->get_renderer('tool_behat');
+echo $renderer->render_stepsdefinitions($steps, $form);
+
--- /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/>.
+
+/**
+ * Strings for tool_behat
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['allavailablesteps'] = 'All the available steps definitions';
+$string['giveninfo'] = 'Given. Processes to set up the environment';
+$string['installinfo'] = 'Read {$a} for installation and tests execution info';
+$string['moreinfoin'] = 'More info in {$a}';
+$string['newstepsinfo'] = 'Read {$a} for info about how to add new steps definitions';
+$string['newtestsinfo'] = 'Read {$a} for info about how to write new tests';
+$string['nostepsdefinitions'] = 'There aren\'t steps definitions matching this filters';
+$string['pluginname'] = 'Acceptance testing';
+$string['runclitool'] = 'To list the steps definitions you need to run the Behat CLI tool to create the $CFG->behat_dataroot directory. Go to your moodle dirroot and run "{$a}"';
+$string['stepsdefinitionscomponent'] = 'Area';
+$string['stepsdefinitionscontains'] = 'Contains';
+$string['stepsdefinitionsfilters'] = 'Steps definitions';
+$string['stepsdefinitionstype'] = 'Type';
+$string['theninfo'] = 'Then. Checkings to ensure the outcomes are the expected ones';
+$string['viewsteps'] = 'Filter';
+$string['wheninfo'] = 'When. Actions that provokes an event';
+$string['wrongbehatsetup'] = 'Something is wrong with the setup, ensure you ran the composer installer and vendor/bin/behat file has execution permissions';
--- /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/>.
+
+/**
+ * Behat commands
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir . '/behat/classes/behat_command.php');
+require_once($CFG->libdir . '/behat/classes/behat_config_manager.php');
+require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/behat/steps_definitions_form.php');
+
+/**
+ * Behat commands manager
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_behat {
+
+ /**
+ * Lists the available steps definitions
+ *
+ * @param string $type
+ * @param string $component
+ * @param string $filter
+ * @return string
+ */
+ public static function stepsdefinitions($type, $component, $filter) {
+
+ // We don't require the test environment to be enabled to list the steps definitions
+ // so test writers can more easily set up the environment.
+ behat_command::check_behat_setup();
+
+ // The loaded steps depends on the component specified.
+ behat_config_manager::update_config_file($component, false);
+
+ // The Moodle\BehatExtension\HelpPrinter\MoodleDefinitionsPrinter will parse this search format.
+ if ($type) {
+ $filter .= '&&' . $type;
+ }
+
+ if ($filter) {
+ $filteroption = ' -d "' . $filter . '"';
+ } else {
+ $filteroption = ' -di';
+ }
+
+ // Get steps definitions from Behat.
+ $options = ' --config="'.behat_config_manager::get_steps_list_config_filepath(). '" '.$filteroption;
+ list($steps, $code) = behat_command::run($options);
+
+ if ($steps) {
+ $stepshtml = implode('', $steps);
+ }
+
+ if (empty($stepshtml)) {
+ $stepshtml = get_string('nostepsdefinitions', 'tool_behat');
+ }
+
+ return $stepshtml;
+ }
+
+}
--- /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/>.
+
+/**
+ * Behat tool renderer
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir . '/behat/classes/behat_command.php');
+
+/**
+ * Renderer for behat tool web features
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_behat_renderer extends plugin_renderer_base {
+
+ /**
+ * Renders the list of available steps according to the submitted filters
+ *
+ * @param string $stepsdefinitions HTML from behat with the available steps
+ * @param moodleform $form
+ * @return string HTML code
+ */
+ public function render_stepsdefinitions($stepsdefinitions, $form) {
+
+ $title = get_string('pluginname', 'tool_behat');
+
+ // Header.
+ $html = $this->output->header();
+ $html .= $this->output->heading($title);
+
+ // Info.
+ $installurl = behat_command::DOCS_URL . '#Installation';
+ $installlink = html_writer::tag('a', $installurl, array('href' => $installurl, 'target' => '_blank'));
+ $writetestsurl = behat_command::DOCS_URL . '#Writting_features';
+ $writetestslink = html_writer::tag('a', $writetestsurl, array('href' => $writetestsurl, 'target' => '_blank'));
+ $writestepsurl = behat_command::DOCS_URL . '#Adding_steps_definitions';
+ $writestepslink = html_writer::tag('a', $writestepsurl, array('href' => $writestepsurl, 'target' => '_blank'));
+ $infos = array(
+ get_string('installinfo', 'tool_behat', $installlink),
+ get_string('newtestsinfo', 'tool_behat', $writetestslink),
+ get_string('newstepsinfo', 'tool_behat', $writestepslink)
+ );
+
+ // List of steps
+ $html .= $this->output->box_start();
+ $html .= html_writer::tag('h1', 'Info');
+ $html .= html_writer::empty_tag('div');
+ $html .= html_writer::empty_tag('ul');
+ $html .= html_writer::empty_tag('li');
+ $html .= implode(html_writer::end_tag('li') . html_writer::empty_tag('li'), $infos);
+ $html .= html_writer::end_tag('li');
+ $html .= html_writer::end_tag('ul');
+ $html .= html_writer::end_tag('div');
+ $html .= $this->output->box_end();
+
+ // Form.
+ ob_start();
+ $form->display();
+ $html .= ob_get_contents();
+ ob_end_clean();
+
+ // Steps definitions.
+ $html .= html_writer::tag('div', $stepsdefinitions, array('class' => 'steps-definitions'));
+
+ $html .= $this->output->footer();
+
+ return $html;
+ }
+}
--- /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/>.
+
+/**
+ * Adds behat tests link in admin tree
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+if ($hassiteconfig) {
+ $url = $CFG->wwwroot . '/' . $CFG->admin . '/tool/behat/index.php';
+ $ADMIN->add('development', new admin_externalpage('toolbehat', get_string('pluginname', 'tool_behat'), $url));
+}
--- /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/>.
+
+/**
+ * Steps definitions form
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir.'/formslib.php');
+
+/**
+ * Form to display the available steps definitions
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class steps_definitions_form extends moodleform {
+
+ /**
+ * Form definition
+ * @return void
+ */
+ public function definition() {
+
+ $mform = $this->_form;
+
+ $mform->addElement('header', 'filters', get_string('stepsdefinitionsfilters', 'tool_behat'));
+
+ $types = array(
+ '' => get_string('allavailablesteps', 'tool_behat'),
+ 'given' => get_string('giveninfo', 'tool_behat'),
+ 'when' => get_string('wheninfo', 'tool_behat'),
+ 'then' => get_string('theninfo', 'tool_behat')
+ );
+ $mform->addElement('select', 'type', get_string('stepsdefinitionstype', 'tool_behat'), $types);
+
+ $mform->addElement(
+ 'select',
+ 'component',
+ get_string('stepsdefinitionscomponent', 'tool_behat'),
+ $this->_customdata['components']
+ );
+
+ $mform->addElement('text', 'filter', get_string('stepsdefinitionscontains', 'tool_behat'));
+
+ $mform->addElement('submit', 'submit', get_string('viewsteps', 'tool_behat'));
+ }
+}
--- /dev/null
+.steps-definitions{border-style:solid;border-width:1px;border-color:#BBB;padding:5px;margin:auto;width:50%;}
+.steps-definitions .step{margin: 10px 0px 10px 0px;}
+.steps-definitions .stepdescription{color:#bf8c12;}
+.steps-definitions .steptype{color:#1467a6;margin-right: 5px;}
+.steps-definitions .stepregex{color:#060;}
--- /dev/null
+@tool_behat
+Feature: Set up contextual data for tests
+ In order to write tests quickly
+ As a moodle developer
+ I need to fill the database with fixtures
+
+ Scenario: Add a bunch of users
+ Given the following "users" exists:
+ | username | password | firstname | lastname |
+ | testuser | testuser | | |
+ | testuser2 | testuser2 | TestFirstname | TestLastname |
+ And I log in as "testuser"
+ And I log out
+ When I log in as "testuser2"
+ Then I should see "TestFirstname"
+
+ @javascript
+ Scenario: Add a bunch of courses and categories
+ Given the following "categories" exists:
+ | name | category | idnumber |
+ | Cat 1 | 0 | CAT1 |
+ | Cat 2 | CAT1 | CAT2 |
+ | Cat 3 | CAT1 | CAT3 |
+ And the following "courses" exists:
+ | fullname | shortname | category |
+ | Course 1 | COURSE1 | CAT3 |
+ | Course 2 | COURSE2 | CAT3 |
+ | Course 3 | COURSE3 | 0 |
+ When I log in as "admin"
+ Then I should see "Course 1"
+ And I should see "Course 2"
+ And I should see "Course 3"
+
+ @javascript
+ Scenario: Add a bunch of groups and groupings
+ Given the following "courses" exists:
+ | fullname | shortname |
+ | Course 1 | C1 |
+ And the following "groups" exists:
+ | name | course | idnumber |
+ | Group 1 | C1 | G1 |
+ | Group 2 | C1 | G2 |
+ And the following "groupings" exists:
+ | name | course | idnumber |
+ | Grouping 1 | C1 | GG1 |
+ | Grouping 2 | C1 | GG2 |
+ When I log in as "admin"
+ And I follow "Course 1"
+ And I expand "Users" node
+ And I follow "Groups"
+ Then I should see "Group 1"
+ And I should see "Group 2"
+ And I follow "Groupings"
+ And I should see "Grouping 1"
+ And I should see "Grouping 2"
+
+ Scenario: Add course enrolments
+ Given the following "users" exists:
+ | username | firstname | lastname | email |
+ | student1 | Student | 1 | student1@asd.com |
+ And the following "courses" exists:
+ | fullname | shortname | format |
+ | Course 1 | C1 | topics |
+ And the following "course enrolments" exists:
+ | user | course | role |
+ | student1 | C1 | student |
+ When I log in as "student1"
+ And I follow "Course 1"
+ Then I should see "Topic 1"
+
+ @javascript
+ Scenario: Add relations between users and groups
+ Given the following "users" exists:
+ | username | firstname | lastname | email |
+ | student1 | Student | 1 | student1@asd.com |
+ | student2 | Student | 2 | student2@asd.com |
+ And the following "courses" exists:
+ | fullname | shortname |
+ | Course 1 | C1 |
+ And the following "groups" exists:
+ | name | course | idnumber |
+ | Group 1 | C1 | G1 |
+ | Group 2 | C1 | G2 |
+ And the following "groupings" exists:
+ | name | course | idnumber |
+ | Grouping 1 | C1 | GG1 |
+ And the following "course enrolments" exists:
+ | user | course | role |
+ | student1 | C1 | student |
+ | student2 | C1 | student |
+ And the following "group members" exists:
+ | user | group |
+ | student1 | G1 |
+ | student2 | G2 |
+ And the following "grouping groups" exists:
+ | grouping | group |
+ | GG1 | G1 |
+ When I log in as "admin"
+ And I follow "Course 1"
+ And I expand "Users" node
+ And I follow "Groups"
+ Then the "groups" select box should contain "Group 1 (1)"
+ And the "groups" select box should contain "Group 2 (1)"
+ And I select "Group 1 (1)" from "groups"
+ And I wait "1" seconds
+ And the "members" select box should contain "Student 1"
+ And I select "Group 2 (1)" from "groups"
+ And I wait "1" seconds
+ And the "members" select box should contain "Student 2"
--- /dev/null
+@tool_behat
+Feature: List the system steps definitions
+ In order to create new tests
+ As a tests writer
+ I need to list and filter the system steps definitions
+
+ Background:
+ Given I am on homepage
+ And I log in as "admin"
+ And I expand "Site administration" node
+ And I expand "Development" node
+ And I follow "Acceptance testing"
+
+ @javascript
+ Scenario: Accessing the list
+ Then I should see "Steps definitions"
+ And I should not see "There aren't steps definitions matching this filter"
+
+ @javascript
+ Scenario: Filtering by type
+ Given I select "Then. Checkings to ensure the outcomes are the expected ones" from "Type"
+ When I press "Filter"
+ Then I should see "Checks, that page contains specified text."
+ And I should not see "Opens Moodle homepage."
+
+ @javascript
+ Scenario: Filtering by keyword
+ Given I fill in "Contains" with "homepage"
+ When I press "Filter"
+ Then I should see "Opens Moodle homepage."
+
--- /dev/null
+@tool_behat
+Feature: Set up the testing environment
+ In order to execute automated acceptance tests
+ As a moodle developer
+ I need to use the test environment instead of the regular environment
+
+ Scenario: Accessing the site
+ When I am on homepage
+ Then I should see "Acceptance test site"
--- /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/>.
+
+/**
+ * Unit tests for admin/tool/behat.
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/' . $CFG->admin .'/tool/behat/locallib.php');
+require_once($CFG->libdir . '/behat/classes/util.php');
+require_once($CFG->libdir . '/behat/classes/behat_config_manager.php');
+
+/**
+ * Allows access to internal methods without exposing them.
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class testable_behat_config_manager extends behat_config_manager {
+
+ /**
+ * Allow access to protected method
+ * @see parent::merge_config()
+ * @param mixed $config
+ * @param mixed $localconfig
+ * @return mixed
+ */
+ public static function merge_config($config, $localconfig) {
+ return parent::merge_config($config, $localconfig);
+ }
+
+ /**
+ * Allow access to protected method
+ * @see parent::get_config_file_contents()
+ * @param string $prefix
+ * @param array $features
+ * @param array $stepsdefinitions
+ * @return string
+ */
+ public static function get_config_file_contents($features, $stepsdefinitions) {
+ return parent::get_config_file_contents($features, $stepsdefinitions);
+ }
+}
+
+/**
+ * Tool behat tests.
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_behat_testcase extends advanced_testcase {
+
+ /**
+ * behat_config_manager tests.
+ */
+ public function test_merge_configs() {
+
+ // Simple default config.
+ $array1 = array(
+ 'the' => 'same',
+ 'simple' => 'value',
+ 'array' => array(
+ 'one' => 'arrayvalue1',
+ 'two' => 'arrayvalue2'
+ )
+ );
+
+ // Simple override.
+ $array2 = array(
+ 'simple' => 'OVERRIDDEN1',
+ 'array' => array(
+ 'one' => 'OVERRIDDEN2'
+ ),
+ 'newprofile' => array(
+ 'anotherlevel' => array(
+ 'andanotherone' => array(
+ 'list1',
+ 'list2'
+ )
+ )
+ )
+ );
+
+ $array = testable_behat_config_manager::merge_config($array1, $array2);
+
+ // Overriddes are applied.
+ $this->assertEquals('OVERRIDDEN1', $array['simple']);
+ $this->assertEquals('OVERRIDDEN2', $array['array']['one']);
+
+ // Other values are respected.
+ $this->assertNotEmpty($array['array']['two']);
+
+ // Completely new nodes are added.
+ $this->assertNotEmpty($array['newprofile']);
+ $this->assertNotEmpty($array['newprofile']['anotherlevel']['andanotherone']);
+ $this->assertEquals('list1', $array['newprofile']['anotherlevel']['andanotherone'][0]);
+ $this->assertEquals('list2', $array['newprofile']['anotherlevel']['andanotherone'][1]);
+
+ // Complex override changing vectors to scalars and scalars to vectors.
+ $array2 = array(
+ 'simple' => array(
+ 'simple' => 'should',
+ 'be' => 'overridden',
+ 'by' => 'this-array'
+ ),
+ 'array' => 'one'
+ );
+
+ $array = testable_behat_config_manager::merge_config($array1, $array2);
+
+ // Overrides applied.
+ $this->assertNotEmpty($array['simple']);
+ $this->assertNotEmpty($array['array']);
+ $this->assertTrue(is_array($array['simple']));
+ $this->assertFalse(is_array($array['array']));
+
+ // Other values are maintained.
+ $this->assertEquals('same', $array['the']);
+ }
+
+ /**
+ * behat_config_manager tests.
+ */
+ public function test_config_file_contents() {
+ global $CFG;
+
+ // Skip tests if behat is not installed.
+ $vendorpath = $CFG->dirroot . '/vendor';
+ if (!file_exists($vendorpath . '/autoload.php') || !is_dir($vendorpath . '/behat')) {
+ $this->markTestSkipped('Behat not installed.');
+ }
+
+ // To avoid user value at config.php level.
+ unset($CFG->behat_config);
+
+ // List.
+ $features = array(
+ 'feature1',
+ 'feature2',
+ 'feature3'
+ );
+
+ // Associative array.
+ $stepsdefinitions = array(
+ 'micarro' => '/me/lo/robaron',
+ 'anoche' => '/cuando/yo/dormia'
+ );
+
+ $contents = testable_behat_config_manager::get_config_file_contents($features, $stepsdefinitions);
+
+ // YAML decides when is is necessary to wrap strings between single quotes, so not controlled
+ // values like paths should not be asserted including the key name as they would depend on the
+ // directories values.
+ $this->assertContains($CFG->dirroot . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'behat' . DIRECTORY_SEPARATOR . 'features', $contents);
+
+ // Not quoted strings.
+ $this->assertContains('micarro: /me/lo/robaron', $contents);
+ $this->assertContains('class: behat_init_context', $contents);
+
+ // YAML uses single quotes to wrap URL strings.
+ $this->assertContains("base_url: '" . $CFG->behat_wwwroot . "'", $contents);
+
+ // Lists.
+ $this->assertContains('- feature1', $contents);
+ $this->assertContains('- feature3', $contents);
+ }
+
+}
+
--- /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_behat version info
+ *
+ * @package tool_behat
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2012120700;
+$plugin->requires = 2012120300; // Requires Moodle 2.5.
+$plugin->component = 'tool_behat';
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
- <TABLE NAME="tool_customlang" COMMENT="Contains the working checkout of all strings and their customization" NEXT="tool_customlang_components">
+ <TABLE NAME="tool_customlang" COMMENT="Contains the working checkout of all strings and their customization">
<FIELDS>
- <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="lang"/>
- <FIELD NAME="lang" TYPE="char" LENGTH="20" NOTNULL="true" SEQUENCE="false" COMMENT="The code of the language this string belongs to. Like en, cs or es" PREVIOUS="id" NEXT="componentid"/>
- <FIELD NAME="componentid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The id of the component" PREVIOUS="lang" NEXT="stringid"/>
- <FIELD NAME="stringid" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The identifier of the string" PREVIOUS="componentid" NEXT="original"/>
- <FIELD NAME="original" TYPE="text" LENGTH="big" NOTNULL="true" SEQUENCE="false" COMMENT="English original of the string" PREVIOUS="stringid" NEXT="master"/>
- <FIELD NAME="master" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false" COMMENT="Master translation of the string as is distributed in the official lang pack, null if not translated" PREVIOUS="original" NEXT="local"/>
- <FIELD NAME="local" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false" COMMENT="Local customization of the string, null if not customized" PREVIOUS="master" NEXT="timemodified"/>
- <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The timestamp of when the original or master was recently modified" PREVIOUS="local" NEXT="timecustomized"/>
- <FIELD NAME="timecustomized" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="The timestamp of when the value of the local translation was recently modified, null if not customized yet" PREVIOUS="timemodified" NEXT="outdated"/>
- <FIELD NAME="outdated" TYPE="int" LENGTH="3" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="Either the English original or the master translation changed and the customization may be outdated" PREVIOUS="timecustomized" NEXT="modified"/>
- <FIELD NAME="modified" TYPE="int" LENGTH="3" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="Has the string been modified via the translator?" PREVIOUS="outdated"/>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+ <FIELD NAME="lang" TYPE="char" LENGTH="20" NOTNULL="true" SEQUENCE="false" COMMENT="The code of the language this string belongs to. Like en, cs or es"/>
+ <FIELD NAME="componentid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The id of the component"/>
+ <FIELD NAME="stringid" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The identifier of the string"/>
+ <FIELD NAME="original" TYPE="text" LENGTH="big" NOTNULL="true" SEQUENCE="false" COMMENT="English original of the string"/>
+ <FIELD NAME="master" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false" COMMENT="Master translation of the string as is distributed in the official lang pack, null if not translated"/>
+ <FIELD NAME="local" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false" COMMENT="Local customization of the string, null if not customized"/>
+ <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The timestamp of when the original or master was recently modified"/>
+ <FIELD NAME="timecustomized" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="The timestamp of when the value of the local translation was recently modified, null if not customized yet"/>
+ <FIELD NAME="outdated" TYPE="int" LENGTH="3" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="Either the English original or the master translation changed and the customization may be outdated"/>
+ <FIELD NAME="modified" TYPE="int" LENGTH="3" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="Has the string been modified via the translator?"/>
</FIELDS>
<KEYS>
- <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="fk_component"/>
- <KEY NAME="fk_component" TYPE="foreign" FIELDS="componentid" REFTABLE="tool_customlang_components" REFFIELDS="id" PREVIOUS="primary"/>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ <KEY NAME="fk_component" TYPE="foreign" FIELDS="componentid" REFTABLE="tool_customlang_components" REFFIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="uq_lang_component_string" UNIQUE="true" FIELDS="lang, componentid, stringid" COMMENT="For a given language and component, string identifiers must be unique"/>
</INDEXES>
</TABLE>
- <TABLE NAME="tool_customlang_components" COMMENT="Contains the list of all installed plugins that provide their own language pack" PREVIOUS="tool_customlang">
+ <TABLE NAME="tool_customlang_components" COMMENT="Contains the list of all installed plugins that provide their own language pack">
<FIELDS>
- <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="name"/>
- <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The normalized name of the plugin" PREVIOUS="id" NEXT="version"/>
- <FIELD NAME="version" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="The checked out version of the plugin, null if the version is unknown" PREVIOUS="name"/>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+ <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The normalized name of the plugin"/>
+ <FIELD NAME="version" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="The checked out version of the plugin, null if the version is unknown"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
}
if (!property_exists($user, $column) or !property_exists($existinguser, $column)) {
// this should never happen
+ debugging("Could not find $column on the user objects", DEBUG_DEVELOPER);
continue;
}
if ($updatetype == UU_UPDATE_MISSING) {
// Do not mess with passwords of remote users.
} else if (!$isinternalauth) {
- $existinguser->password = 'not cached';
+ $existinguser->password = AUTH_PASSWORD_NOT_CACHED;
$upt->track('password', '-', 'normal', false);
// clean up prefs
unset_user_preference('create_password', $existinguser);
} else if (!empty($user->password)) {
if ($updatepasswords) {
+ // Check for passwords that we want to force users to reset next
+ // time they log in.
$errmsg = null;
$weak = !check_password_policy($user->password, $errmsg);
if ($resetpasswords == UU_PWRESET_ALL or ($resetpasswords == UU_PWRESET_WEAK and $weak)) {
unset_user_preference('auth_forcepasswordchange', $existinguser);
}
unset_user_preference('create_password', $existinguser); // no need to create password any more
- $existinguser->password = hash_internal_user_password($user->password);
+
+ // Use a low cost factor when generating bcrypt hash otherwise
+ // hashing would be slow when uploading lots of users. Hashes
+ // will be automatically updated to a higher cost factor the first
+ // time the user logs in.
+ $existinguser->password = hash_internal_user_password($user->password, true);
$upt->track('password', $user->password, 'normal', false);
} else {
// do not print password when not changed
}
$forcechangepassword = true;
}
- $user->password = hash_internal_user_password($user->password);
+ // Use a low cost factor when generating bcrypt hash otherwise
+ // hashing would be slow when uploading lots of users. Hashes
+ // will be automatically updated to a higher cost factor the first
+ // time the user logs in.
+ $user->password = hash_internal_user_password($user->password, true);
}
} else {
- $user->password = 'not cached';
+ $user->password = AUTH_PASSWORD_NOT_CACHED;
$upt->track('password', '-', 'normal', false);
}
switch ($plugintype ) {
case 'lib': // has own savepoint function
$result = XMLDB_LINEFEED .
- ' // Main savepoint reached' . XMLDB_LINEFEED .
+ ' // Main savepoint reached.' . XMLDB_LINEFEED .
' upgrade_main_savepoint(true, XXXXXXXXXX);' . XMLDB_LINEFEED;
break;
case 'mod': // has own savepoint function
$result = XMLDB_LINEFEED .
- ' // ' . $pluginname . ' savepoint reached' . XMLDB_LINEFEED .
+ ' // ' . ucfirst($pluginname) . ' savepoint reached.' . XMLDB_LINEFEED .
' upgrade_mod_savepoint(true, XXXXXXXXXX, ' . "'$pluginname'" . ');' . XMLDB_LINEFEED;
break;
case 'block': // has own savepoint function
$result = XMLDB_LINEFEED .
- ' // ' . $pluginname . ' savepoint reached' . XMLDB_LINEFEED .
+ ' // ' . ucfirst($pluginname) . ' savepoint reached.' . XMLDB_LINEFEED .
' upgrade_block_savepoint(true, XXXXXXXXXX, ' . "'$pluginname'" . ');' . XMLDB_LINEFEED;
break;
default: // rest of plugins
$result = XMLDB_LINEFEED .
- ' // ' . $pluginname . ' savepoint reached' . XMLDB_LINEFEED .
+ ' // ' . ucfirst($pluginname) . ' savepoint reached.' . XMLDB_LINEFEED .
' upgrade_plugin_savepoint(true, XXXXXXXXXX, ' . "'$plugintype'" . ', ' . "'$pluginname'" . ');' . XMLDB_LINEFEED;
}
return $result;
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Define table ' . $table->getName() . ' to be created' . XMLDB_LINEFEED;
+ $result .= ' // Define table ' . $table->getName() . ' to be created.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= XMLDB_LINEFEED;
- $result .= ' // Adding fields to table ' . $table->getName() . XMLDB_LINEFEED;
+ $result .= ' // Adding fields to table ' . $table->getName() . '.' . XMLDB_LINEFEED;
// Iterate over each field
foreach ($table->getFields() as $field) {
// The field header, with name
// Iterate over each key
if ($keys = $table->getKeys()) {
$result .= XMLDB_LINEFEED;
- $result .= ' // Adding keys to table ' . $table->getName() . XMLDB_LINEFEED;
+ $result .= ' // Adding keys to table ' . $table->getName() . '.' . XMLDB_LINEFEED;
foreach ($keys as $key) {
// The key header, with name
$result .= ' $table->add_key(' . "'" . $key->getName() . "', ";
// Iterate over each index
if ($indexes = $table->getIndexes()) {
$result .= XMLDB_LINEFEED;
- $result .= ' // Adding indexes to table ' . $table->getName() . XMLDB_LINEFEED;
+ $result .= ' // Adding indexes to table ' . $table->getName() . '.' . XMLDB_LINEFEED;
foreach ($indexes as $index) {
// The index header, with name
$result .= ' $table->add_index(' . "'" . $index->getName() . "', ";
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Conditionally launch create table for ' . $table->getName() . XMLDB_LINEFEED;
+ $result .= ' // Conditionally launch create table for ' . $table->getName() . '.' . XMLDB_LINEFEED;
$result .= ' if (!$dbman->table_exists($table)) {' . XMLDB_LINEFEED;
$result .= ' $dbman->create_table($table);' . XMLDB_LINEFEED;
$result .= ' }' . XMLDB_LINEFEED;
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Define table ' . $table->getName() . ' to be dropped' . XMLDB_LINEFEED;
+ $result .= ' // Define table ' . $table->getName() . ' to be dropped.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Conditionally launch drop table for ' . $table->getName() . XMLDB_LINEFEED;
+ $result .= ' // Conditionally launch drop table for ' . $table->getName() . '.' . XMLDB_LINEFEED;
$result .= ' if ($dbman->table_exists($table)) {' . XMLDB_LINEFEED;
$result .= ' $dbman->drop_table($table);' . XMLDB_LINEFEED;
$result .= ' }' . XMLDB_LINEFEED;
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Define table ' . $table->getName() . ' to be renamed to NEWNAMEGOESHERE' . XMLDB_LINEFEED;
+ $result .= ' // Define table ' . $table->getName() . ' to be renamed to NEWNAMEGOESHERE.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Launch rename table for ' . $table->getName() . XMLDB_LINEFEED;
+ $result .= ' // Launch rename table for ' . $table->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $dbman->rename_table($table, ' . "'NEWNAMEGOESHERE'" . ');' . XMLDB_LINEFEED;
// Add the proper upgrade_xxxx_savepoint call
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Define field ' . $field->getName() . ' to be added to ' . $table->getName() . XMLDB_LINEFEED;
+ $result .= ' // Define field ' . $field->getName() . ' to be added to ' . $table->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= ' $field = new xmldb_field(' . "'" . $field->getName() . "', " . $field->getPHP(true) . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Conditionally launch add field ' . $field->getName() . XMLDB_LINEFEED;
+ $result .= ' // Conditionally launch add field ' . $field->getName() . '.' . XMLDB_LINEFEED;
$result .= ' if (!$dbman->field_exists($table, $field)) {'. XMLDB_LINEFEED;
$result .= ' $dbman->add_field($table, $field);' . XMLDB_LINEFEED;
$result .= ' }'. XMLDB_LINEFEED;
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Define field ' . $field->getName() . ' to be dropped from ' . $table->getName() . XMLDB_LINEFEED;
+ $result .= ' // Define field ' . $field->getName() . ' to be dropped from ' . $table->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= ' $field = new xmldb_field(' . "'" . $field->getName() . "'" . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Conditionally launch drop field ' . $field->getName() . XMLDB_LINEFEED;
+ $result .= ' // Conditionally launch drop field ' . $field->getName() . '.' . XMLDB_LINEFEED;
$result .= ' if ($dbman->field_exists($table, $field)) {' . XMLDB_LINEFEED;
$result .= ' $dbman->drop_field($table, $field);' . XMLDB_LINEFEED;
$result .= ' }' . XMLDB_LINEFEED;
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Rename field ' . $field->getName() . ' on table ' . $table->getName() . ' to NEWNAMEGOESHERE'. XMLDB_LINEFEED;
+ $result .= ' // Rename field ' . $field->getName() . ' on table ' . $table->getName() . ' to NEWNAMEGOESHERE.'. XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= ' $field = new xmldb_field(' . "'" . $field->getName() . "', " . $field->getPHP(true) . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Launch rename field ' . $field->getName() . XMLDB_LINEFEED;
+ $result .= ' // Launch rename field ' . $field->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $dbman->rename_field($table, $field, ' . "'" . 'NEWNAMEGOESHERE' . "'" . ');' . XMLDB_LINEFEED;
// Add the proper upgrade_xxxx_savepoint call
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Changing type of field ' . $field->getName() . ' on table ' . $table->getName() . ' to ' . $type . XMLDB_LINEFEED;
+ $result .= ' // Changing type of field ' . $field->getName() . ' on table ' . $table->getName() . ' to ' . $type . '.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= ' $field = new xmldb_field(' . "'" . $field->getName() . "', " . $field->getPHP(true) . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Launch change of type for field ' . $field->getName() . XMLDB_LINEFEED;
+ $result .= ' // Launch change of type for field ' . $field->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $dbman->change_field_type($table, $field);' . XMLDB_LINEFEED;
// Add the proper upgrade_xxxx_savepoint call
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Changing precision of field ' . $field->getName() . ' on table ' . $table->getName() . ' to ' . $precision . XMLDB_LINEFEED;
+ $result .= ' // Changing precision of field ' . $field->getName() . ' on table ' . $table->getName() . ' to ' . $precision . '.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
- $result .= ' $field = new xmldb_field(' . "'" . $field->getName() . "', " .$field->getPHP(true) . ');' . XMLDB_LINEFEED;
+ $result .= ' $field = new xmldb_field(' . "'" . $field->getName() . "', " . $field->getPHP(true) . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Launch change of precision for field ' . $field->getName() . XMLDB_LINEFEED;
+ $result .= ' // Launch change of precision for field ' . $field->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $dbman->change_field_precision($table, $field);' . XMLDB_LINEFEED;
// Add the proper upgrade_xxxx_savepoint call
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Changing nullability of field ' . $field->getName() . ' on table ' . $table->getName() . ' to ' . $notnull . XMLDB_LINEFEED;
+ $result .= ' // Changing nullability of field ' . $field->getName() . ' on table ' . $table->getName() . ' to ' . $notnull . '.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= ' $field = new xmldb_field(' . "'" . $field->getName() . "', " . $field->getPHP(true) . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Launch change of nullability for field ' . $field->getName() . XMLDB_LINEFEED;
+ $result .= ' // Launch change of nullability for field ' . $field->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $dbman->change_field_notnull($table, $field);' . XMLDB_LINEFEED;
// Add the proper upgrade_xxxx_savepoint call
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Changing the default of field ' . $field->getName() . ' on table ' . $table->getName() . ' to ' . $default . XMLDB_LINEFEED;
+ $result .= ' // Changing the default of field ' . $field->getName() . ' on table ' . $table->getName() . ' to ' . $default . '.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= ' $field = new xmldb_field(' . "'" . $field->getName() . "', " . $field->getPHP(true) . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Launch change of default for field ' . $field->getName() . XMLDB_LINEFEED;
+ $result .= ' // Launch change of default for field ' . $field->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $dbman->change_field_default($table, $field);' . XMLDB_LINEFEED;
// Add the proper upgrade_xxxx_savepoint call
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Define key ' . $key->getName() . ' ('. $key->getXMLDBKeyName($key->getType()) . ') to be added to ' . $table->getName() . XMLDB_LINEFEED;
+ $result .= ' // Define key ' . $key->getName() . ' ('. $key->getXMLDBKeyName($key->getType()) . ') to be added to ' . $table->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= ' $key = new xmldb_key(' . "'" . $key->getName() . "', " . $key->getPHP(true) . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Launch add key ' . $key->getName() . XMLDB_LINEFEED;
+ $result .= ' // Launch add key ' . $key->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $dbman->add_key($table, $key);' . XMLDB_LINEFEED;
// Add the proper upgrade_xxxx_savepoint call
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Define key ' . $key->getName() . ' ('. $key->getXMLDBKeyName($key->getType()) . ') to be dropped form ' . $table->getName() . XMLDB_LINEFEED;
+ $result .= ' // Define key ' . $key->getName() . ' ('. $key->getXMLDBKeyName($key->getType()) . ') to be dropped form ' . $table->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= ' $key = new xmldb_key(' . "'" . $key->getName() . "', " . $key->getPHP(true) . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Launch drop key ' . $key->getName() . XMLDB_LINEFEED;
+ $result .= ' // Launch drop key ' . $key->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $dbman->drop_key($table, $key);' . XMLDB_LINEFEED;
// Add the proper upgrade_xxxx_savepoint call
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Define key ' . $key->getName() . ' ('. $key->getXMLDBKeyName($key->getType()) . ') to be renamed to NEWNAMEGOESHERE' . XMLDB_LINEFEED;
+ $result .= ' // Define key ' . $key->getName() . ' ('. $key->getXMLDBKeyName($key->getType()) . ') to be renamed to NEWNAMEGOESHERE.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= ' $key = new xmldb_key(' . "'" . $key->getName() . "', " . $key->getPHP(true) . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Launch rename key ' . $key->getName() . XMLDB_LINEFEED;
+ $result .= ' // Launch rename key ' . $key->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $dbman->rename_key($table, $key, ' . "'" . 'NEWNAMEGOESHERE' . "'" . ');' . XMLDB_LINEFEED;
// Add the proper upgrade_xxxx_savepoint call
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Define index ' . $index->getName() . ' ('. ($index->getUnique() ? 'unique' : 'not unique') . ') to be added to ' . $table->getName() . XMLDB_LINEFEED;
+ $result .= ' // Define index ' . $index->getName() . ' ('. ($index->getUnique() ? 'unique' : 'not unique') . ') to be added to ' . $table->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= ' $index = new xmldb_index(' . "'" . $index->getName() . "', " . $index->getPHP(true) . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Conditionally launch add index ' . $index->getName() . XMLDB_LINEFEED;
+ $result .= ' // Conditionally launch add index ' . $index->getName() . '.' . XMLDB_LINEFEED;
$result .= ' if (!$dbman->index_exists($table, $index)) {' . XMLDB_LINEFEED;
$result .= ' $dbman->add_index($table, $index);' . XMLDB_LINEFEED;
$result .= ' }' . XMLDB_LINEFEED;
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Define index ' . $index->getName() . ' ('. ($index->getUnique() ? 'unique' : 'not unique') . ') to be dropped form ' . $table->getName() . XMLDB_LINEFEED;
+ $result .= ' // Define index ' . $index->getName() . ' ('. ($index->getUnique() ? 'unique' : 'not unique') . ') to be dropped form ' . $table->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= ' $index = new xmldb_index(' . "'" . $index->getName() . "', " . $index->getPHP(true) . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Conditionally launch drop index ' . $index->getName() . XMLDB_LINEFEED;
+ $result .= ' // Conditionally launch drop index ' . $index->getName() . '.' . XMLDB_LINEFEED;
$result .= ' if ($dbman->index_exists($table, $index)) {' . XMLDB_LINEFEED;
$result .= ' $dbman->drop_index($table, $index);' . XMLDB_LINEFEED;
$result .= ' }' . XMLDB_LINEFEED;
// Add contents
$result .= XMLDB_LINEFEED;
- $result .= ' // Define index ' . $index->getName() . ' ('. ($index->getUnique() ? 'unique' : 'not unique') . ') to be renamed to NEWNAMEGOESHERE' . XMLDB_LINEFEED;
+ $result .= ' // Define index ' . $index->getName() . ' ('. ($index->getUnique() ? 'unique' : 'not unique') . ') to be renamed to NEWNAMEGOESHERE.' . XMLDB_LINEFEED;
$result .= ' $table = new xmldb_table(' . "'" . $table->getName() . "'" . ');' . XMLDB_LINEFEED;
$result .= ' $index = new xmldb_index(' . "'" . $index->getName() . "', " . $index->getPHP(true) . ');' . XMLDB_LINEFEED;
// Launch the proper DDL
$result .= XMLDB_LINEFEED;
- $result .= ' // Launch rename index ' . $index->getName() . XMLDB_LINEFEED;
+ $result .= ' // Launch rename index ' . $index->getName() . '.' . XMLDB_LINEFEED;
$result .= ' $dbman->rename_index($table, $index, ' . "'" . 'NEWNAMEGOESHERE' . "'" . ');' . XMLDB_LINEFEED;
// Add the proper upgrade_xxxx_savepoint call
if ($this->is_internal()) {
$puser = $DB->get_record('user', array('id'=>$user->id), '*', MUST_EXIST);
+ // This will also update the stored hash to the latest algorithm
+ // if the existing hash is using an out-of-date algorithm (or the
+ // legacy md5 algorithm).
if (update_internal_user_password($puser, $newpassword)) {
$user->password = $puser->password;
return true;
*/
function user_update_password($user, $newpassword) {
$user = get_complete_user_data('id', $user->id);
+ // This will also update the stored hash to the latest algorithm
+ // if the existing hash is using an out-of-date algorithm (or the
+ // legacy md5 algorithm).
return update_internal_user_password($user, $newpassword);
}
profile_save_data($user);
$this->update_user_record($user->username);
+ // This will also update the stored hash to the latest algorithm
+ // if the existing hash is using an out-of-date algorithm (or the
+ // legacy md5 algorithm).
update_internal_user_password($user, $plainslashedpassword);
$user = $DB->get_record('user', array('id'=>$user->id));
--- /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/>.
+
+/**
+ * LDAP authentication plugin tests.
+ *
+ * NOTE: in order to execute this test you need to set up
+ * OpenLDAP server with core, cosine, nis and internet schemas
+ * and add configuration constants to config.php or phpunit.xml configuration file:
+ *
+ * define('TEST_AUTH_LDAP_HOST_URL', 'ldap://127.0.0.1');
+ * define('TEST_AUTH_LDAP_BIND_DN', 'cn=someuser,dc=example,dc=local');
+ * define('TEST_AUTH_LDAP_BIND_PW', 'somepassword');
+ * define('TEST_AUTH_LDAP_DOMAIN', 'dc=example,dc=local');
+ *
+ * @package auth_ldap
+ * @category phpunit
+ * @copyright 2013 Petr Skoda {@link http://skodak.org}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+class auth_ldap_testcase extends advanced_testcase {
+
+ public function test_auth_ldap() {
+ global $CFG, $DB;
+
+ if (!extension_loaded('ldap')) {
+ $this->markTestSkipped('LDAP extension is not loaded.');
+ }
+
+ $this->resetAfterTest();
+
+ require_once($CFG->dirroot.'/auth/ldap/auth.php');
+ require_once($CFG->libdir.'/ldaplib.php');
+
+ if (!defined('TEST_AUTH_LDAP_HOST_URL') or !defined('TEST_AUTH_LDAP_BIND_DN') or !defined('TEST_AUTH_LDAP_BIND_PW') or !defined('TEST_AUTH_LDAP_DOMAIN')) {
+ $this->markTestSkipped('External LDAP test server not configured.');
+ }
+
+ // Make sure we can connect the server.
+ $debuginfo = '';
+ if (!$connection = ldap_connect_moodle(TEST_AUTH_LDAP_HOST_URL, 3, 'rfc2307', TEST_AUTH_LDAP_BIND_DN, TEST_AUTH_LDAP_BIND_PW, LDAP_DEREF_NEVER, $debuginfo, false)) {
+ $this->markTestSkipped('Can not connect to LDAP test server: '.$debuginfo);
+ }
+
+ $this->enable_plugin();
+
+ // Create new empty test container.
+ $topdn = 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN;
+
+ $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
+
+ $o = array();
+ $o['objectClass'] = array('dcObject', 'organizationalUnit');
+ $o['dc'] = 'moodletest';
+ $o['ou'] = 'MOODLETEST';
+ if (!ldap_add($connection, 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN, $o)) {
+ $this->markTestSkipped('Can not create test LDAP container.');
+ }
+
+ // Create a few users.
+ $o = array();
+ $o['objectClass'] = array('organizationalUnit');
+ $o['ou'] = 'users';
+ ldap_add($connection, 'ou='.$o['ou'].','.$topdn, $o);
+
+ for ($i=1; $i<=5; $i++) {
+ $this->create_ldap_user($connection, $topdn, $i);
+ }
+
+ // Set up creators group.
+ $o = array();
+ $o['objectClass'] = array('posixGroup');
+ $o['cn'] = 'creators';
+ $o['gidNumber'] = 1;
+ $o['memberUid'] = array('username1', 'username2');
+ ldap_add($connection, 'cn='.$o['cn'].','.$topdn, $o);
+
+ $creatorrole = $DB->get_record('role', array('shortname'=>'coursecreator'));
+ $this->assertNotEmpty($creatorrole);
+
+
+ // Configure the plugin a bit.
+ set_config('host_url', TEST_AUTH_LDAP_HOST_URL, 'auth/ldap');
+ set_config('start_tls', 0, 'auth/ldap');
+ set_config('ldap_version', 3, 'auth/ldap');
+ set_config('ldapencoding', 'utf-8', 'auth/ldap');
+ set_config('pagesize', '2', 'auth/ldap');
+ set_config('bind_dn', TEST_AUTH_LDAP_BIND_DN, 'auth/ldap');
+ set_config('bind_pw', TEST_AUTH_LDAP_BIND_PW, 'auth/ldap');
+ set_config('user_type', 'rfc2307', 'auth/ldap');
+ set_config('contexts', 'ou=users,'.$topdn, 'auth/ldap');
+ set_config('search_sub', 0, 'auth/ldap');
+ set_config('opt_deref', LDAP_DEREF_NEVER, 'auth/ldap');
+ set_config('user_attribute', 'cn', 'auth/ldap');
+ set_config('memberattribute', 'memberuid', 'auth/ldap');
+ set_config('memberattribute_isdn', 0, 'auth/ldap');
+ set_config('creators', 'cn=creators,'.$topdn, 'auth/ldap');
+ set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth/ldap');
+
+ set_config('field_map_email', 'mail', 'auth/ldap');
+ set_config('field_updatelocal_email', 'oncreate', 'auth/ldap');
+ set_config('field_updateremote_email', '0', 'auth/ldap');
+ set_config('field_lock_email', 'unlocked', 'auth/ldap');
+
+ set_config('field_map_firstname', 'givenName', 'auth/ldap');
+ set_config('field_updatelocal_firstname', 'oncreate', 'auth/ldap');
+ set_config('field_updateremote_firstname', '0', 'auth/ldap');
+ set_config('field_lock_firstname', 'unlocked', 'auth/ldap');
+
+ set_config('field_map_lastname', 'sn', 'auth/ldap');
+ set_config('field_updatelocal_lastname', 'oncreate', 'auth/ldap');
+ set_config('field_updateremote_lastname', '0', 'auth/ldap');
+ set_config('field_lock_lastname', 'unlocked', 'auth/ldap');
+
+
+ $this->assertEquals(2, $DB->count_records('user'));
+ $this->assertEquals(0, $DB->count_records('role_assignments'));
+
+ /** @var auth_plugin_ldap $auth */
+ $auth = get_auth_plugin('ldap');
+
+ ob_start();
+ $auth->sync_users(true);
+ ob_end_clean();
+
+ $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
+ $this->assertEquals(2, $DB->count_records('role_assignments'));
+ $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
+
+ for ($i=1; $i<=5; $i++) {
+ $this->assertTrue($DB->record_exists('user', array('username'=>'username'.$i, 'email'=>'user'.$i.'@example.com', 'firstname'=>'Firstname'.$i, 'lastname'=>'Lastname'.$i)));
+ }
+
+ $this->delete_ldap_user($connection, $topdn, 1);
+
+ ob_start();
+ $auth->sync_users(true);
+ ob_end_clean();
+
+ $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
+ $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
+ $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
+ $this->assertEquals(2, $DB->count_records('role_assignments'));
+ $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
+
+
+ set_config('removeuser', AUTH_REMOVEUSER_SUSPEND, 'auth/ldap');
+
+ /** @var auth_plugin_ldap $auth */
+ $auth = get_auth_plugin('ldap');
+
+ ob_start();
+ $auth->sync_users(true);
+ ob_end_clean();
+
+ $this->assertEquals(4, $DB->count_records('user', array('auth'=>'ldap')));
+ $this->assertEquals(1, $DB->count_records('user', array('auth'=>'nologin', 'username'=>'username1')));
+ $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
+ $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
+ $this->assertEquals(2, $DB->count_records('role_assignments'));
+ $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
+
+ $this->create_ldap_user($connection, $topdn, 1);
+
+ ob_start();
+ $auth->sync_users(true);
+ ob_end_clean();
+
+ $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
+ $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
+ $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
+ $this->assertEquals(2, $DB->count_records('role_assignments'));
+ $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
+
+
+ set_config('removeuser', AUTH_REMOVEUSER_FULLDELETE, 'auth/ldap');
+
+ /** @var auth_plugin_ldap $auth */
+ $auth = get_auth_plugin('ldap');
+
+ $this->delete_ldap_user($connection, $topdn, 1);
+
+ ob_start();
+ $auth->sync_users(true);
+ ob_end_clean();
+
+ $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
+ $this->assertEquals(0, $DB->count_records('user', array('username'=>'username1')));
+ $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
+ $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
+ $this->assertEquals(1, $DB->count_records('role_assignments'));
+ $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
+
+ $this->create_ldap_user($connection, $topdn, 1);
+
+ ob_start();
+ $auth->sync_users(true);
+ ob_end_clean();
+
+ $this->assertEquals(6, $DB->count_records('user', array('auth'=>'ldap')));
+ $this->assertEquals(1, $DB->count_records('user', array('username'=>'username1')));
+ $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
+ $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
+ $this->assertEquals(2, $DB->count_records('role_assignments'));
+ $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
+
+
+ $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
+ ldap_close($connection);
+ }
+
+ protected function create_ldap_user($connection, $topdn, $i) {
+ $o = array();
+ $o['objectClass'] = array('inetOrgPerson', 'organizationalPerson', 'person', 'posixAccount');
+ $o['cn'] = 'username'.$i;
+ $o['sn'] = 'Lastname'.$i;
+ $o['givenName'] = 'Firstname'.$i;
+ $o['uid'] = $o['cn'];
+ $o['uidnumber'] = 2000+$i;
+ $o['gidNumber'] = 1000+$i;
+ $o['homeDirectory'] = '/';
+ $o['mail'] = 'user'.$i.'@example.com';
+ $o['userPassword'] = 'pass'.$i;
+ ldap_add($connection, 'cn='.$o['cn'].',ou=users,'.$topdn, $o);
+ }
+
+ protected function delete_ldap_user($connection, $topdn, $i) {
+ ldap_delete($connection, 'cn=username'.$i.',ou=users,'.$topdn);
+ }
+
+ protected function enable_plugin() {
+ $auths = get_enabled_auth_plugins(true);
+ if (!in_array('ldap', $auths)) {
+ $auths[] = 'ldap';
+
+ }
+ set_config('auth', implode(',', $auths));
+ }
+
+ protected function recursive_delete($connection, $dn, $filter) {
+ if ($res = ldap_list($connection, $dn, $filter, array('dn'))) {
+ $info = ldap_get_entries($connection, $res);
+ ldap_free_result($res);
+ if ($info['count'] > 0) {
+ if ($res = ldap_search($connection, "$filter,$dn", 'cn=*', array('dn'))) {
+ $info = ldap_get_entries($connection, $res);
+ ldap_free_result($res);
+ foreach ($info as $i) {
+ if (isset($i['dn'])) {
+ ldap_delete($connection, $i['dn']);
+ }
+ }
+ }
+ if ($res = ldap_search($connection, "$filter,$dn", 'ou=*', array('dn'))) {
+ $info = ldap_get_entries($connection, $res);
+ ldap_free_result($res);
+ foreach ($info as $i) {
+ if (isset($i['dn']) and $info[0]['dn'] != $i['dn']) {
+ ldap_delete($connection, $i['dn']);
+ }
+ }
+ }
+ ldap_delete($connection, "$filter,$dn");
+ }
+ }
+ }
+}
*/
function user_update_password($user, $newpassword) {
$user = get_complete_user_data('id', $user->id);
+ // This will also update the stored hash to the latest algorithm
+ // if the existing hash is using an out-of-date algorithm (or the
+ // legacy md5 algorithm).
return update_internal_user_password($user, $newpassword);
}
*/
function user_update_password($user, $newpassword) {
$user = get_complete_user_data('id', $user->id);
+ // This will also update the stored hash to the latest algorithm
+ // if the existing hash is using an out-of-date algorithm (or the
+ // legacy md5 algorithm).
return update_internal_user_password($user, $newpassword);
}
--- /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/>.
+
+/**
+ * Basic authentication steps definitions.
+ *
+ * @package core
+ * @category test
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
+
+require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
+
+use Behat\Behat\Context\Step\Given as Given;
+use Behat\Behat\Context\Step\When as When;
+
+/**
+ * Log in log out steps definitions.
+ *
+ * @package core
+ * @category test
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class behat_auth extends behat_base {
+
+ /**
+ * Logs in the user. There should exist a user with the same value as username and password.
+ *
+ * @Given /^I log in as "(?P<username_string>(?:[^"]|\\")*)"$/
+ */
+ public function i_log_in_as($username) {
+
+ return array(new Given('I am on homepage'),
+ new Given('I follow "Login"'),
+ new Given('I fill in "Username" with "'.$username.'"'),
+ new Given('I fill in "Password" with "'.$username.'"'),
+ new When('I press "Login"'),
+ new Given('I should see "You are logged in as"'));
+ }
+
+ /**
+ * Logs out of the system.
+ *
+ * @Given /^I log out$/
+ */
+ public function i_log_out() {
+ return new When('I follow "Logout"');
+ }
+
+}
--- /dev/null
+@auth
+Feature: Authentication
+ In order to validate my credentials in the system
+ As a moodle user
+ I need to log into the system
+
+ Scenario: Login with the predefined admin user
+ Given I log in as "admin"
+
+ Scenario: Login as an existing admin user filling the form
+ Given the following "users" exists:
+ | username | password | firstname | lastname | email |
+ | testuser | testuser | Test | User | moodle@moodlemoodle.com |
+ And I am on homepage
+ When I follow "Login"
+ And I fill in "Username" with "testuser"
+ And I fill in "Password" with "testuser"
+ And I press "Login"
+ Then I should see "You are logged in as"
+
+ Scenario: Login as an unexisting user filling the form
+ Given the following "users" exists:
+ | username | password | firstname | lastname | email |
+ | testuser | testuser | Test | User | moodle@moodlemoodle.com |
+ And I am on homepage
+ When I follow "Login"
+ And I fill in "Username" with "testuser"
+ And I fill in "Password" with "unexisting"
+ And I press "Login"
+ Then I should see "Invalid login, please try again"
+
+ Scenario: Logout
+ Given I log in as "admin"
+ When I log out
+ Then I should see "You are not logged in"
*/
function user_update_password($user, $newpassword) {
$user = get_complete_user_data('id', $user->id);
+ // This will also update the stored hash to the latest algorithm
+ // if the existing hash is using an out-of-date algorithm (or the
+ // legacy md5 algorithm).
return update_internal_user_password($user, $newpassword);
}
*/
public function migrate_file($sourcepath, $filepath = '/', $filename = null, $sortorder = 0, $timecreated = null, $timemodified = null) {
- $sourcefullpath = $this->basepath.'/'.$sourcepath;
+ // Normalise Windows paths a bit.
+ $sourcepath = str_replace('\\', '/', $sourcepath);
- if ($sourcefullpath !== clean_param($sourcefullpath, PARAM_PATH)) {
- throw new moodle1_convert_exception('file_invalid_path', $sourcefullpath);
+ // PARAM_PATH must not be used on full OS path!
+ if ($sourcepath !== clean_param($sourcepath, PARAM_PATH)) {
+ throw new moodle1_convert_exception('file_invalid_path', $sourcepath);
}
+ $sourcefullpath = $this->basepath.'/'.$sourcepath;
+
if (!is_readable($sourcefullpath)) {
throw new moodle1_convert_exception('file_not_readable', $sourcefullpath);
}
*/
public function migrate_directory($rootpath, $relpath='/') {
+ // Check the trailing slash in the $rootpath
+ if (substr($rootpath, -1) === '/') {
+ debugging('moodle1_file_manager::migrate_directory() expects $rootpath without the trailing slash', DEBUG_DEVELOPER);
+ $rootpath = substr($rootpath, 0, strlen($rootpath) - 1);
+ }
+
if (!file_exists($this->basepath.'/'.$rootpath.$relpath)) {
return array();
}
$fileids = $fileman->get_fileids();
$this->assertEquals(gettype($fileids), 'array');
$this->assertEquals(0, count($fileids));
- // try to migrate a non-existing directory
- $returned = $fileman->migrate_directory('not/existing/directory');
- $this->assertEquals(gettype($returned), 'array');
- $this->assertEquals(0, count($returned));
- $fileids = $fileman->get_fileids();
- $this->assertEquals(gettype($fileids), 'array');
- $this->assertEquals(0, count($fileids));
// try to migrate an invalid file
$fileman->itemid = 1;
$thrown = false;
$converter->drop_stash_storage();
}
+ public function test_migrate_directory() {
+ $this->resetAfterTest(true);
+
+ // Set-up the file manager.
+ $converter = convert_factory::get_converter('moodle1', $this->tempdir);
+ $converter->create_stash_storage();
+ $contextid = $converter->get_contextid(CONTEXT_MODULE, 32);
+ $fileman = $converter->get_file_manager($contextid, 'mod_unittest', 'testarea');
+ // This fileman has not converted anything yet.
+ $fileids = $fileman->get_fileids();
+ $this->assertEquals(gettype($fileids), 'array');
+ $this->assertEquals(0, count($fileids));
+ // Try to migrate a non-existing directory.
+ $returned = $fileman->migrate_directory('not/existing/directory');
+ $this->assertEquals(gettype($returned), 'array');
+ $this->assertEquals(0, count($returned));
+ $fileids = $fileman->get_fileids();
+ $this->assertEquals(gettype($fileids), 'array');
+ $this->assertEquals(0, count($fileids));
+ // Try to migrate whole course_files.
+ $returned = $fileman->migrate_directory('course_files');
+ $this->assertEquals(gettype($returned), 'array');
+ $this->assertEquals(4, count($returned)); // Two files, two directories.
+ $fileids = $fileman->get_fileids();
+ $this->assertEquals(gettype($fileids), 'array');
+ $this->assertEquals(4, count($fileids));
+ $subdir = substr($this->iconhash, 0, 2);
+ $this->assertTrue(is_file($converter->get_workdir_path().'/files/'.$subdir.'/'.$this->iconhash));
+
+ // Check the file records.
+ $files = array();
+ $filerecordids = $converter->get_stash_itemids('files');
+ foreach ($filerecordids as $filerecordid) {
+ $filerecord = $converter->get_stash('files', $filerecordid);
+ $files[$filerecord['filepath'].$filerecord['filename']] = $filerecord;
+ }
+ $this->assertEquals('array', gettype($files['/.']));
+ $this->assertEquals('array', gettype($files['/file1.gif']));
+ $this->assertEquals('array', gettype($files['/sub1/.']));
+ $this->assertEquals('array', gettype($files['/sub1/file2.gif']));
+ $this->assertEquals(sha1(''), $files['/.']['contenthash']);
+ $this->assertEquals(sha1(''), $files['/sub1/.']['contenthash']);
+ $this->assertEquals($this->iconhash, $files['/file1.gif']['contenthash']);
+ $this->assertEquals($this->iconhash, $files['/sub1/file2.gif']['contenthash']);
+
+ $converter->drop_stash_storage();
+ }
+
+ public function test_migrate_directory_with_trailing_slash() {
+ $this->resetAfterTest(true);
+
+ // Set-up the file manager.
+ $converter = convert_factory::get_converter('moodle1', $this->tempdir);
+ $converter->create_stash_storage();
+ $contextid = $converter->get_contextid(CONTEXT_MODULE, 32);
+ $fileman = $converter->get_file_manager($contextid, 'mod_unittest', 'testarea');
+ // Try to migrate a subdirectory passed with the trailing slash.
+ $returned = $fileman->migrate_directory('course_files/sub1/');
+ // Debugging message must be thrown in this case.
+ $this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
+ $this->assertEquals(gettype($returned), 'array');
+ $this->assertEquals(2, count($returned)); // One file, one directory.
+
+ $converter->drop_stash_storage();
+ }
+
public function test_convert_path() {
$path = new convert_path('foo_bar', '/ROOT/THINGS/FOO/BAR');
$this->assertEquals('foo_bar', $path->get_name());
// Most external plugins do not store passwords locally
if (!empty($userauth->preventpassindb)) {
- $user->password = 'not cached';
+ $user->password = AUTH_PASSWORD_NOT_CACHED;
// If Moodle is responsible for storing/validating pwd and reset functionality is available, mark
} else if ($userauth->isinternal and $userauth->canresetpwd) {
$this->controller->set_status(backup::STATUS_EXECUTING);
parent::execute();
$this->controller->set_status(backup::STATUS_FINISHED_OK);
+
+ events_trigger('course_restored', (object) array(
+ 'courseid' => $this->get_courseid(), // The new course
+ 'userid' => $this->get_userid(), // User doing the restore
+ 'type' => $this->controller->get_type(), // backup::TYPE_* constant
+ 'target' => $this->controller->get_target(), // backup::TARGET_* constant
+ 'mode' => $this->controller->get_mode(), // backup::MODE_* constant
+ 'operation' => $this->controller->get_operation(), // backup::OPERATION_* constant
+ 'samesite' => $this->controller->is_samesite(),
+ ));
}
/**
function definition() {
$ui = $this->uistage->get_ui();
$mform = $this->_form;
+ $mform->setDisableShortforms();
$stage = $mform->addElement('hidden', 'stage', $this->uistage->get_stage());
$stage = $mform->addElement('hidden', $ui->get_name(), $ui->get_uniqueid());
$params = $this->uistage->get_params();
class progressive_parser_exception extends moodle_exception {
public function __construct($errorcode, $a=NULL, $debuginfo=null) {
- parent::__construct($errorcode, 'error', '', $a, null, $debuginfo);
+ parent::__construct($errorcode, 'error', '', $a, $debuginfo);
}
}
--- /dev/null
+default:
+ paths:
+ features: lib/behat/features
+ bootstrap: lib/behat/features/bootstrap
+ context:
+ class: behat_init_context
+ extensions:
+ Behat\MinkExtension\Extension:
+ base_url: 'http://localhost:8000'
+ goutte: null
+ selenium2: null
+ Moodle\BehatExtension\Extension:
+ features: { }
+ steps_definitions: { }
<TABLES>
<TABLE NAME="block_community" COMMENT="Community block">
<FIELDS>
- <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="userid"/>
- <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="coursename"/>
- <FIELD NAME="coursename" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="userid" NEXT="coursedescription"/>
- <FIELD NAME="coursedescription" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false" PREVIOUS="coursename" NEXT="courseurl"/>
- <FIELD NAME="courseurl" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="coursedescription" NEXT="imageurl"/>
- <FIELD NAME="imageurl" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="courseurl"/>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="coursename" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="coursedescription" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false"/>
+ <FIELD NAME="courseurl" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="imageurl" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
// Display completion status
-echo '<table class="generalbox boxaligncenter"><tbody>';
+echo '<table class="generaltable boxaligncenter"><tbody>';
// If not display logged in user, show user name
if ($USER->id != $user->id) {
echo '</td></tr></tbody></table>';
// Generate markup for criteria statuses
- echo '<table class="generalbox logtable boxaligncenter" id="criteriastatus" width="100%"><tbody>';
+ echo '<table class="generaltable logtable boxaligncenter" id="criteriastatus" width="100%"><tbody>';
echo '<tr class="ccheader">';
echo '<th class="c0 header" scope="col">'.get_string('criteriagroup', 'block_completionstatus').'</th>';
echo '<th class="c1 header" scope="col">'.get_string('criteria', 'completion').'</th>';
// For each course, build category cache.
$this->content->text .= $renderer->course_overview($sortedcourses, $overviews);
$this->content->text .= $renderer->hidden_courses($totalcourses - count($sortedcourses));
- if ($this->page->user_is_editing() && ajaxenabled()) {
- $this->page->requires->js_init_call('M.block_course_overview.add_handles');
- }
}
return $this->content;
$string['hiddencoursecountplural'] = 'You have {$a} hidden courses';
$string['message'] = 'message';
$string['messages'] = 'messages';
+$string['movecourse'] = 'Move course: {$a}';
$string['movecoursehere'] = 'Move course here';
+$string['movetofirst'] = 'Move {$a} course to top';
+$string['moveafterhere'] = 'Move {$a->movingcoursename} course after {$a->currentcoursename}';
+$string['movingcourse'] = 'You are moving: {$a->fullname} ({$a->cancellink})';
$string['numtodisplay'] = 'Number of courses to display: ';
$string['otherexpanded'] = 'Other Courses Expanded';
$string['pluginname'] = 'Course overview';
M.block_course_overview.add_handles = function(Y) {
M.block_course_overview.Y = Y;
+ var MOVEICON = {
+ pix: "i/move_2d",
+ component: 'moodle'
+ };
+
YUI().use('dd-constrain', 'dd-proxy', 'dd-drop', 'dd-plugin', function(Y) {
//Static Vars
var goingUp = false, lastY = 0;
- var list = Y.Node.all('#course_list .coursebox');
+ var list = Y.Node.all('.course_list .coursebox');
list.each(function(v, k) {
+ // Replace move link and image with move_2d image.
+ var imagenode = v.one('.course_title .move a img');
+ imagenode.setAttribute('src', M.util.image_url(MOVEICON.pix, MOVEICON.component));
+ imagenode.addClass('cursor');
+ v.one('.course_title .move a').replace(imagenode);
+
var dd = new Y.DD.Drag({
node: v,
target: {
}).plug(Y.Plugin.DDProxy, {
moveOnEnd: false
}).plug(Y.Plugin.DDConstrained, {
- constrain2node: '#course_list'
+ constrain2node: '.course_list'
});
dd.addHandle('.course_title .move');
});
- var drops = Y.Node.all('#coursebox');
- drops.each(function(v, k) {
- var tar = new Y.DD.Drop({
- node: v
- });
- });
-
Y.DD.DDM.on('drag:start', function(e) {
//Get our drag object
var drag = e.target;
M.block_course_overview.save = function() {
var Y = M.block_course_overview.Y;
- var sortorder = Y.one('#course_list').get('children').getAttribute('id');
+ var sortorder = Y.one('.course_list').get('children').getAttribute('id');
for (var i = 0; i < sortorder.length; i++) {
sortorder[i] = sortorder[i].substring(7);
}
require_sesskey();
require_login();
-$source = required_param('source', PARAM_INT);
-$move = required_param('move', PARAM_INT);
-
-list($courses_sorted, $sitecourses, $coursecount) = block_course_overview_get_sorted_courses();
-$sortorder = array_keys($courses_sorted);
-// Now resort based on new weight for chosen course.
-$neworder = array();
-
-$sourcekey = array_search($source, $sortorder);
-if ($sourcekey === false) {
- print_error("invalidcourseid", null, null, $source);
+$coursetomove = required_param('courseid', PARAM_INT);
+$moveto = required_param('moveto', PARAM_INT);
+
+list($courses, $sitecourses, $coursecount) = block_course_overview_get_sorted_courses();
+$sortedcourses = array_keys($courses);
+
+$currentcourseindex = array_search($coursetomove, $sortedcourses);
+// If coursetomove is not found or moveto < 0 or > count($sortedcourses) then throw error.
+if ($currentcourseindex === false) {
+ print_error("invalidcourseid", null, null, $coursetomove);
+} else if (($moveto < 0) || ($moveto >= count($sortedcourses))) {
+ print_error("invalidaction");
}
-$destination = $sourcekey + $move;
-if ($destination < 0) {
- print_error("listcantmoveup");
-} else if ($destination >= count($courses_sorted)) {
- print_error("listcantmovedown");
+// If current course index is same as destination index then don't do anything.
+if ($currentcourseindex === $moveto) {
+ redirect(new moodle_url('/my/index.php'));
}
// Create neworder list for courses.
-unset($sortorder[$sourcekey]);
-if ($move == -1) {
- if ($destination > 0) {
- $neworder = array_slice($sortorder, 0, $destination, true);
- }
- $neworder[] = $source;
- $remaningcourses = array_slice($sortorder, $destination);
- foreach ($remaningcourses as $courseid) {
- $neworder[] = $courseid;
- }
-} else if (($move == 1)) {
- $neworder = array_slice($sortorder, 0, $destination);
- $neworder[] = $source;
- if (($destination) < count($courses_sorted)) {
- $remaningcourses = array_slice($sortorder, $destination);
- foreach ($remaningcourses as $courseid) {
- $neworder[] = $courseid;
- }
- }
-}
+$neworder = array();
-block_course_overview_update_myorder($neworder);
+unset($sortedcourses[$currentcourseindex]);
+$neworder = array_slice($sortedcourses, 0, $moveto, true);
+$neworder[] = $coursetomove;
+$remaningcourses = array_slice($sortedcourses, $moveto);
+foreach ($remaningcourses as $courseid) {
+ $neworder[] = $courseid;
+}
+block_course_overview_update_myorder(array_values($neworder));
redirect(new moodle_url('/my/index.php'));
public function course_overview($courses, $overviews) {
$html = '';
$config = get_config('block_course_overview');
-
- $html .= html_writer::start_tag('div', array('id' => 'course_list'));
+ $ismovingcourse = false;
$courseordernumber = 0;
$maxcourses = count($courses);
- // Intialize string/icon etc if user is editing.
- $url = null;
- $moveicon = null;
- $moveup[] = null;
- $movedown[] = null;
- if ($this->page->user_is_editing()) {
+ $userediting = false;
+ // Intialise string/icon etc if user is editing and courses > 1
+ if ($this->page->user_is_editing() && (count($courses) > 1)) {
+ $userediting = true;
+ // If ajaxenabled then include DND JS and replace link with move image.
if (ajaxenabled()) {
- $moveicon = html_writer::tag('div',
- html_writer::empty_tag('img',
- array('src' => $this->pix_url('i/move_2d')->out(false),
- 'alt' => get_string('move'), 'class' => 'cursor',
- 'title' => get_string('move'))
- ), array('class' => 'move')
- );
- } else {
- $url = new moodle_url('/blocks/course_overview/move.php', array('sesskey' => sesskey()));
- $moveup['str'] = get_string('moveup');
- $moveup['icon'] = $this->pix_url('t/up');
- $movedown['str'] = get_string('movedown');
- $movedown['icon'] = $this->pix_url('t/down');
+ $this->page->requires->js_init_call('M.block_course_overview.add_handles');
}
+
+ // Check if course is moving
+ $ismovingcourse = optional_param('movecourse', FALSE, PARAM_BOOL);
+ $movingcourseid = optional_param('courseid', 0, PARAM_INT);
+ }
+
+ // Render first movehere icon.
+ if ($ismovingcourse) {
+ // Remove movecourse param from url.
+ $this->page->ensure_param_not_in_url('movecourse');
+
+ // Show moving course notice, so user knows what is being moved.
+ $html .= $this->output->box_start('notice');
+ $a = new stdClass();
+ $a->fullname = $courses[$movingcourseid]->fullname;
+ $a->cancellink = html_writer::link($this->page->url, get_string('cancel'));
+ $html .= get_string('movingcourse', 'block_course_overview', $a);
+ $html .= $this->output->box_end();
+
+ $moveurl = new moodle_url('/blocks/course_overview/move.php',
+ array('sesskey' => sesskey(), 'moveto' => 0, 'courseid' => $movingcourseid));
+ // Create move icon, so it can be used.
+ $movetofirsticon = html_writer::empty_tag('img',
+ array('src' => $this->output->pix_url('movehere'),
+ 'alt' => get_string('movetofirst', 'block_course_overview', $courses[$movingcourseid]->fullname),
+ 'title' => get_string('movehere')));
+ $moveurl = html_writer::link($moveurl, $movetofirsticon);
+ $html .= html_writer::tag('div', $moveurl, array('class' => 'movehere'));
}
foreach ($courses as $key => $course) {
+ // If moving course, then don't show course which needs to be moved.
+ if ($ismovingcourse && ($course->id == $movingcourseid)) {
+ continue;
+ }
$html .= $this->output->box_start('coursebox', "course-{$course->id}");
$html .= html_writer::start_tag('div', array('class' => 'course_title'));
- // Ajax enabled then add moveicon html
- if (!is_null($moveicon)) {
- $html .= $moveicon;
- } else if (!is_null($url)) {
- // Add course id to move link
- $url->param('source', $course->id);
- $html .= html_writer::start_tag('div', array('class' => 'moveicons'));
- // Add an arrow to move course up.
- if ($courseordernumber > 0) {
- $url->param('move', -1);
- $html .= html_writer::link($url,
- html_writer::empty_tag('img', array('src' => $moveup['icon'],
- 'class' => 'up', 'alt' => $moveup['str'])),
- array('title' => $moveup['str'], 'class' => 'moveup'));
- } else {
- // Add a spacer to keep keep down arrow icons at right position.
- $html .= html_writer::empty_tag('img', array('src' => $this->pix_url('spacer'),
- 'class' => 'movedownspacer'));
- }
- // Add an arrow to move course down.
- if ($courseordernumber <= $maxcourses-2) {
- $url->param('move', 1);
- $html .= html_writer::link($url, html_writer::empty_tag('img',
- array('src' => $movedown['icon'], 'class' => 'down', 'alt' => $movedown['str'])),
- array('title' => $movedown['str'], 'class' => 'movedown'));
- } else {
- // Add a spacer to keep keep up arrow icons at right position.
- $html .= html_writer::empty_tag('img', array('src' => $this->pix_url('spacer'),
- 'class' => 'moveupspacer'));
- }
- $html .= html_writer::end_tag('div');
+ // If user is editing, then add move icons.
+ if ($userediting && !$ismovingcourse) {
+ $moveicon = html_writer::empty_tag('img',
+ array('src' => $this->pix_url('t/move')->out(false),
+ 'alt' => get_string('movecourse', 'block_course_overview', $course->fullname),
+ 'title' => get_string('move')));
+ $moveurl = new moodle_url($this->page->url, array('sesskey' => sesskey(), 'movecourse' => 1, 'courseid' => $course->id));
+ $moveurl = html_writer::link($moveurl, $moveicon);
+ $html .= html_writer::tag('div', $moveurl, array('class' => 'move'));
+
}
$attributes = array('title' => s($course->fullname));
}
}
- if (isset($overviews[$course->id])) {
+ // If user is moving courses, then down't show overview.
+ if (isset($overviews[$course->id]) && !$ismovingcourse) {
$html .= $this->activity_display($course->id, $overviews[$course->id]);
}
$html .= $this->output->box('', 'flush');
$html .= $this->output->box_end();
$courseordernumber++;
+ if ($ismovingcourse) {
+ $moveurl = new moodle_url('/blocks/course_overview/move.php',
+ array('sesskey' => sesskey(), 'moveto' => $courseordernumber, 'courseid' => $movingcourseid));
+ $a = new stdClass();
+ $a->movingcoursename = $courses[$movingcourseid]->fullname;
+ $a->currentcoursename = $course->fullname;
+ $movehereicon = html_writer::empty_tag('img',
+ array('src' => $this->output->pix_url('movehere'),
+ 'alt' => get_string('moveafterhere', 'block_course_overview', $a),
+ 'title' => get_string('movehere')));
+ $moveurl = html_writer::link($moveurl, $movehereicon);
+ $html .= html_writer::tag('div', $moveurl, array('class' => 'movehere'));
+ }
}
- $html .= html_writer::end_tag('div');
-
- return $html;
+ // Wrap course list in a div and return.
+ return html_writer::tag('div', $html, array('class' => 'course_list'));
}
/**
padding: 2px 10px;
}
-.editing .block_course_overview .moveicons {
- display: block;
- float: left;
- padding-right: 5px;
-}
-.dir-rtl.editing .block_course_overview .moveicons {
- float:right;
-}
-
-.editing .block_course_overview .moveup {
- padding-right: 5px;
-}
-
-.editing .block_course_overview .movedownspacer {
- float: left;
- width: 14px;
-}
-.dir-rtl.editing .block_course_overview .movedownspacer {
- float:right;
-}
-
-.editing .block_course_overview .moveupspacer {
- float: right;
- width: 14px;
-}
-.dir-rtl.editing .block_course_overview .moveupspacer {
- float:left;
-}
-
-.block_course_overview #course_list {
+.block_course_overview .course_list {
width: 100%;
}
text-align: right;
}
-.block_course_overview .coursemovetarget {
- display: block;
- height: 1em;
- margin-bottom: 1em;
- border-width: 2px;
- border-style: dashed;
+.block_course_overview .content .course_list .movehere{
+ margin-bottom: 15px;
}
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2012121000; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version = 2013012300; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2012112900; // Requires this Moodle version
$plugin->component = 'block_course_overview'; // Full name of the plugin (used for diagnostics)
}
// Must set the image src seperatly of we get an error with XML strict headers
- var movetoimg = Y.Node.create('<img alt="'+M.str.block.undockitem+'" title="'+M.str.block.undockitem+'" />');
+ var movetoimg = Y.Node.create('<img alt="'+M.str.block.undockitem+'" title="'+M.util.get_string('undockblock', 'block', blocktitle.innerHTML)+'" />');
var icon = 't/dock_to_block';
if (right_to_left()) {
icon = 't/dock_to_block_rtl';
$this->title = get_string('pluginname','block_glossary_random');
}
- function applicable_formats() {
- return array('all' => true, 'mod' => false, 'tag' => false, 'my' => false);
- }
-
function specialization() {
global $CFG, $DB;
$capabilities = array(
+ 'block/glossary_random:myaddinstance' => array(
+ 'captype' => 'write',
+ 'contextlevel' => CONTEXT_SYSTEM,
+ 'archetypes' => array(
+ 'user' => CAP_ALLOW
+ ),
+
+ 'clonepermissionsfrom' => 'moodle/my:manageblocks'
+ ),
+
'block/glossary_random:addinstance' => array(
'riskbitmask' => RISK_SPAM | RISK_XSS,
+++ /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/>.
-
-/**
- * This file keeps track of upgrades to the glossary random block
- *
- * Sometimes, changes between versions involve alterations to database structures
- * and other major things that may break installations.
- *
- * The upgrade function in this file will attempt to perform all the necessary
- * actions to upgrade your older installation to the current version.
- *
- * If there's something it cannot do itself, it will tell you what you need to do.
- *
- * The commands in here will all be database-neutral, using the methods of
- * database_manager class
- *
- * Please do not forget to use upgrade_set_timeout()
- * before any action that may take longer time to finish.
- *
- * @since 2.0
- * @package blocks
- * @copyright 2012 Mark Nelson <markn@moodle.com>
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-/**
- * Handles upgrading instances of this block.
- *
- * @param int $oldversion
- * @param object $block
- */
-function xmldb_block_glossary_random_upgrade($oldversion, $block) {
- global $DB;
-
- // Moodle v2.4.0 release upgrade line
- // Put any upgrade step following this.
-
- if ($oldversion < 2012112901) {
- // Get the instances of this block.
- if ($blocks = $DB->get_records('block_instances', array('blockname' => 'glossary_random', 'pagetypepattern' => 'my-index'))) {
- // Loop through and remove them from the My Moodle page.
- foreach ($blocks as $block) {
- blocks_delete_instance($block);
- }
-
- }
-
- // Savepoint reached.
- upgrade_block_savepoint(true, 2012112901, 'glossary_random');
- }
-
-
- return true;
-}
\ No newline at end of file
$string['askinvisible'] = 'When users cannot edit or view the glossary, show this text (without link)';
$string['askviewglossary'] = 'When users can view the glossary but not add entries, show a link with this text';
$string['glossary_random:addinstance'] = 'Add a new random glossary entry block';
+$string['glossary_random:myaddinstance'] = 'Add a new random glossary entry block to the My Moodle page';
$string['intro'] = 'Make sure you have at least one glossary with at least one entry added to this course. Then you can adjust the following settings';
$string['invisible'] = '(to be continued)';
$string['lastmodified'] = 'Last modified entry';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2012112901; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version = 2012112902; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2012112900; // Requires this Moodle version
$plugin->component = 'block_glossary_random'; // Full name of the plugin (used for diagnostics)
}
function applicable_formats() {
- return array('all' => true, 'tag' => false, 'my' => false);
+ return array('all' => true, 'tag' => false);
}
function specialization() {
$capabilities = array(
+ 'block/mentees:myaddinstance' => array(
+ 'captype' => 'write',
+ 'contextlevel' => CONTEXT_SYSTEM,
+ 'archetypes' => array(
+ 'user' => CAP_ALLOW
+ ),
+
+ 'clonepermissionsfrom' => 'moodle/my:manageblocks'
+ ),
+
'block/mentees:addinstance' => array(
'riskbitmask' => RISK_SPAM | RISK_XSS,
+++ /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/>.
-
-/**
- * This file keeps track of upgrades to the mentees block
- *
- * Sometimes, changes between versions involve alterations to database structures
- * and other major things that may break installations.
- *
- * The upgrade function in this file will attempt to perform all the necessary
- * actions to upgrade your older installation to the current version.
- *
- * If there's something it cannot do itself, it will tell you what you need to do.
- *
- * The commands in here will all be database-neutral, using the methods of
- * database_manager class
- *
- * Please do not forget to use upgrade_set_timeout()
- * before any action that may take longer time to finish.
- *
- * @since 2.0
- * @package blocks
- * @copyright 2012 Mark Nelson <markn@moodle.com>
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-/**
- * Handles upgrading instances of this block.
- *
- * @param int $oldversion
- * @param object $block
- */
-function xmldb_block_mentees_upgrade($oldversion, $block) {
- global $DB;
-
- // Moodle v2.4.0 release upgrade line
- // Put any upgrade step following this.
-
- if ($oldversion < 2012112901) {
- // Get the instances of this block.
- if ($blocks = $DB->get_records('block_instances', array('blockname' => 'mentees', 'pagetypepattern' => 'my-index'))) {
- // Loop through and remove them from the My Moodle page.
- foreach ($blocks as $block) {
- blocks_delete_instance($block);
- }
-
- }
-
- // Savepoint reached.
- upgrade_block_savepoint(true, 2012112901, 'mentees');
- }
-
-
- return true;
-}
\ No newline at end of file
$string['configtitleblankhides'] = 'Block title (no title if blank)';
$string['leaveblanktohide'] = 'leave blank to hide the title';
$string['mentees:addinstance'] = 'Add a new mentees block';
+$string['mentees:myaddinstance'] = 'Add a new mentees block to the My Moodle page';
$string['newmenteesblock'] = '(new Mentees block)';
$string['pluginname'] = 'Mentees';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2012112901; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version = 2012112902; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2012112900; // Requires this Moodle version
$plugin->component = 'block_mentees'; // Full name of the plugin (used for diagnostics)
$this->title = get_string('pluginname', 'block_news_items');
}
- function applicable_formats() {
- return array('all' => true, 'mod' => false, 'tag' => false, 'my' => false);
- }
-
function get_content() {
global $CFG, $USER;
$capabilities = array(
+ 'block/news_items:myaddinstance' => array(
+ 'captype' => 'write',
+ 'contextlevel' => CONTEXT_SYSTEM,
+ 'archetypes' => array(
+ 'user' => CAP_ALLOW
+ ),
+
+ 'clonepermissionsfrom' => 'moodle/my:manageblocks'
+ ),
+
'block/news_items:addinstance' => array(
'riskbitmask' => RISK_SPAM | RISK_XSS,
*/
$string['news_items:addinstance'] = 'Add a new latest news block';
+$string['news_items:myaddinstance'] = 'Add a new latest news block to the My Moodle page';
$string['pluginname'] = 'Latest news';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2012112901; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version = 2012112902; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2012112900; // Requires this Moodle version
$plugin->component = 'block_news_items'; // Full name of the plugin (used for diagnostics)
$this->title = get_string('pluginname','block_online_users');
}
- function applicable_formats() {
- return array('all' => true, 'mod' => false, 'tag' => false, 'my' => false);
- }
-
function has_config() {
return true;
}
$capabilities = array(
+ 'block/online_users:myaddinstance' => array(
+ 'captype' => 'write',
+ 'contextlevel' => CONTEXT_SYSTEM,
+ 'archetypes' => array(
+ 'user' => CAP_ALLOW
+ ),
+
+ 'clonepermissionsfrom' => 'moodle/my:manageblocks'
+ ),
+
'block/online_users:addinstance' => array(
'riskbitmask' => RISK_SPAM | RISK_XSS,
+++ /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/>.
-
-/**
- * This file keeps track of upgrades to the online users block
- *
- * Sometimes, changes between versions involve alterations to database structures
- * and other major things that may break installations.
- *
- * The upgrade function in this file will attempt to perform all the necessary
- * actions to upgrade your older installation to the current version.
- *
- * If there's something it cannot do itself, it will tell you what you need to do.
- *
- * The commands in here will all be database-neutral, using the methods of
- * database_manager class
- *
- * Please do not forget to use upgrade_set_timeout()
- * before any action that may take longer time to finish.
- *
- * @since 2.0
- * @package blocks
- * @copyright 2012 Mark Nelson <markn@moodle.com>
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-/**
- * Handles upgrading instances of this block.
- *
- * @param int $oldversion
- * @param object $block
- */
-function xmldb_block_online_users_upgrade($oldversion, $block) {
- global $DB;
-
- // Moodle v2.4.0 release upgrade line
- // Put any upgrade step following this.
-
- if ($oldversion < 2012112901) {
- // Get the instances of this block.
- if ($blocks = $DB->get_records('block_instances', array('blockname' => 'online_users', 'pagetypepattern' => 'my-index'))) {
- // Loop through and remove them from the My Moodle page.
- foreach ($blocks as $block) {
- blocks_delete_instance($block);
- }
-
- }
-
- // Savepoint reached.
- upgrade_block_savepoint(true, 2012112901, 'online_users');
- }
-
-
- return true;
-}
\ No newline at end of file
$string['configtimetosee'] = 'Number of minutes determining the period of inactivity after which a user is no longer considered to be online.';
$string['online_users:addinstance'] = 'Add a new online users block';
+$string['online_users:myaddinstance'] = 'Add a new online users block to the My Moodle page';
$string['online_users:viewlist'] = 'View list of online users';
$string['periodnminutes'] = 'last {$a} minutes';
$string['pluginname'] = 'Online users';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2012112901; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version = 2012112902; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2012112900; // Requires this Moodle version
$plugin->component = 'block_online_users'; // Full name of the plugin (used for diagnostics)
<TABLES>
<TABLE NAME="block_rss_client" COMMENT="Remote news feed information. Contains the news feed id, the userid of the user who added the feed, the title of the feed itself and a description of the feed contents along with the url used to access the remote feed. Preferredtitle is a field for future use - intended to allow for custom titles rather than those found in the feed">
<FIELDS>
- <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="userid"/>
- <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="title"/>
- <FIELD NAME="title" TYPE="text" NOTNULL="true" SEQUENCE="false" PREVIOUS="userid" NEXT="preferredtitle"/>
- <FIELD NAME="preferredtitle" TYPE="char" LENGTH="64" NOTNULL="true" SEQUENCE="false" PREVIOUS="title" NEXT="description"/>
- <FIELD NAME="description" TYPE="text" NOTNULL="true" SEQUENCE="false" PREVIOUS="preferredtitle" NEXT="shared"/>
- <FIELD NAME="shared" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="description" NEXT="url"/>
- <FIELD NAME="url" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="shared"/>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="title" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="preferredtitle" TYPE="char" LENGTH="64" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="description" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="shared" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="url" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" />
$managefeeds = new moodle_url('/blocks/rss_client/managefeeds.php', $urlparams);
$PAGE->set_url('/blocks/rss_client/editfeed.php', $urlparams);
-$PAGE->set_pagelayout('base');
+$PAGE->set_pagelayout('standard');
if ($rssid) {
$isadding = false;
$PAGE->set_title($strtitle);
$PAGE->set_heading($strtitle);
- $settingsurl = new moodle_url('/admin/settings.php?section=blocksettingrss_client');
$PAGE->navbar->add(get_string('blocks'));
- $PAGE->navbar->add(get_string('pluginname', 'block_rss_client'), $settingsurl);
- $PAGE->navbar->add(get_string('managefeeds', 'block_rss_client'));
+ $PAGE->navbar->add(get_string('pluginname', 'block_rss_client'));
+ $PAGE->navbar->add(get_string('managefeeds', 'block_rss_client'), $managefeeds );
$PAGE->navbar->add($strtitle);
echo $OUTPUT->header();
$PAGE->set_title($strmanage);
$PAGE->set_heading($strmanage);
-$settingsurl = new moodle_url('/admin/settings.php?section=blocksettingrss_client');
$managefeeds = new moodle_url('/blocks/rss_client/managefeeds.php', $urlparams);
$PAGE->navbar->add(get_string('blocks'));
-$PAGE->navbar->add(get_string('pluginname', 'block_rss_client'), $settingsurl);
+$PAGE->navbar->add(get_string('pluginname', 'block_rss_client'));
$PAGE->navbar->add(get_string('managefeeds', 'block_rss_client'), $managefeeds);
echo $OUTPUT->header();
$PAGE->set_title($strviewfeed);
$PAGE->set_heading($strviewfeed);
-$settingsurl = new moodle_url('/admin/settings.php?section=blocksettingrss_client');
$managefeeds = new moodle_url('/blocks/rss_client/managefeeds.php', $urlparams);
$PAGE->navbar->add(get_string('blocks'));
-$PAGE->navbar->add(get_string('pluginname', 'block_rss_client'), $settingsurl);
-$PAGE->navbar->add(get_string('managefeeds', 'block_rss_client'));
+$PAGE->navbar->add(get_string('pluginname', 'block_rss_client'));
+$PAGE->navbar->add(get_string('managefeeds', 'block_rss_client'), $managefeeds);
$PAGE->navbar->add($strviewfeed);
echo $OUTPUT->header();
<?php
-
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+/**
+ * This file contains the main class for the section links block.
+ *
+ * @package block_section_links
+ * @copyright Jason Hardin
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
/**
- * Section links block
+ * Section links block class.
*
- * @package moodlecore
+ * @package block_section_links
+ * @copyright Jason Hardin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class block_section_links extends block_base {
- function init() {
+ /**
+ * Initialises the block instance.
+ */
+ public function init() {
$this->title = get_string('pluginname', 'block_section_links');
}
- function instance_config($instance) {
- global $DB;
-
- parent::instance_config($instance);
- $course = $this->page->course;
- if (isset($course->format)) {
- if ($course->format == 'topics') {
- $this->title = get_string('topics', 'block_section_links');
- } else if ($course->format == 'weeks') {
- $this->title = get_string('weeks', 'block_section_links');
- } else {
- $this->title = get_string('pluginname', 'block_section_links');
- }
- }
- }
-
- function applicable_formats() {
- return (array('course-view-weeks' => true, 'course-view-topics' => true));
+ /**
+ * Returns an array of formats for which this block can be used.
+ *
+ * @return array
+ */
+ public function applicable_formats() {
+ return array(
+ 'course-view-weeks' => true,
+ 'course-view-topics' => true
+ );
}
- function get_content() {
- global $CFG, $USER, $DB;
-
- $highlight = 0;
- if(isset($this->config)){
+ /**
+ * Generates the content of the block and returns it.
+ *
+ * If the content has already been generated then the previously generated content is returned.
+ *
+ * @return stdClass
+ */
+ public function get_content() {
+
+ // The config should be loaded by now.
+ // If its empty then we will use the global config for the section links block.
+ if (isset($this->config)){
$config = $this->config;
} else{
- // TODO: Move these config settings to proper ones using component name
- $config = get_config('blocks/section_links');
+ $config = get_config('block_section_links');
}
- if ($this->content !== NULL) {
+ if ($this->content !== null) {
return $this->content;
}
}
$course = $this->page->course;
- $courseformatoptions = course_get_format($course)->get_format_options();
+ $courseformat = course_get_format($course);
+ $courseformatoptions = $courseformat->get_format_options();
$context = context_course::instance($course->id);
- if ($course->format == 'weeks' or $course->format == 'weekscss') {
- $highlight = ceil((time()-$course->startdate)/604800);
- $linktext = get_string('jumptocurrentweek', 'block_section_links');
- $sectionname = 'week';
- }
- else if ($course->format == 'topics') {
+ // Prepare the highlight value.
+ if ($course->format == 'weeks') {
+ $highlight = ceil((time() - $course->startdate) / 604800);
+ } else if ($course->format == 'topics') {
$highlight = $course->marker;
- $linktext = get_string('jumptocurrenttopic', 'block_section_links');
- $sectionname = 'topic';
+ } else {
+ $highlight = 0;
}
- $inc = 1;
- if(!empty($config->numsections1) and ($courseformatoptions['numsections'] > $config->numsections1)) {
+ // Prepare the increment value.
+ if (!empty($config->numsections1) and ($courseformatoptions['numsections'] > $config->numsections1)) {
$inc = $config->incby1;
+ } else if ($courseformatoptions['numsections'] > 22) {
+ $inc = 2;
} else {
- if ($courseformatoptions['numsections'] > 22) {
- $inc = 2;
- }
+ $inc = 1;
}
-
- if(!empty($config->numsections2) and ($courseformatoptions['numsections'] > $config->numsections2)) {
+ if (!empty($config->numsections2) and ($courseformatoptions['numsections'] > $config->numsections2)) {
$inc = $config->incby2;
} else {
if ($courseformatoptions['numsections'] > 40) {
}
}
- $sql = "SELECT section, visible
- FROM {course_sections}
- WHERE course = ? AND
- section < ".($courseformatoptions['numsections']+1)."
- ORDER BY section";
-
- if ($sections = $DB->get_records_sql($sql, array($course->id))) {
- $text = '<ol class="inline-list">';
- for ($i = $inc; $i <= $courseformatoptions['numsections']; $i += $inc) {
- if (!isset($sections[$i])) {
- continue;
- }
- $isvisible = $sections[$i]->visible;
- if (!$isvisible and !has_capability('moodle/course:update', $context)) {
- continue;
- }
- $style = ($isvisible) ? '' : ' class="dimmed"';
- if ($i == $highlight) {
- $text .= '<li><a href="'.course_get_url($course, $i)."\"$style><strong>$i</strong></a></li>\n";
- } else {
- $text .= '<li><a href="'.course_get_url($course, $i)."\"$style>$i</a></li>\n";
- }
+ // Prepare an array of sections to create links for.
+ $sections = array();
+ $canviewhidden = has_capability('moodle/course:update', $context);
+ $coursesections = $courseformat->get_sections();
+ $coursesectionscount = count($coursesections);
+ for ($i = $inc; $i <= $coursesectionscount; $i += $inc) {
+ if ($i > $courseformatoptions['numsections'] || !isset($coursesections[$i])) {
+ continue;
+ }
+ $section = $coursesections[$i];
+ if ($section->section && ($section->visible || $canviewhidden)) {
+ $sections[$i] = (object)array(
+ 'section' => $section->section,
+ 'visible' => $section->visible,
+ 'highlight' => ($section->section == $highlight)
+ );
}
- $text .= '</ol>';
- if ($highlight and isset($sections[$highlight])) {
- $isvisible = $sections[$highlight]->visible;
- if ($isvisible or has_capability('moodle/course:update', $context)) {
- $style = ($isvisible) ? '' : ' class="dimmed"';
- $text .= "\n<a href=\"".course_get_url($course, $highlight)."\"$style>$linktext</a>";
- }
+ }
+
+ if (!empty($sections)) {
+ $sectiontojumpto = false;
+ if ($highlight && isset($sections[$highlight]) && ($sections[$highlight]->visible || $canviewhidden)) {
+ $sectiontojumpto = $highlight;
}
+ // Render the sections.
+ $renderer = $this->page->get_renderer('block_section_links');
+ $this->content->text = $renderer->render_section_links($this->page->course, $sections, $sectiontojumpto);
}
- $this->content->text = $text;
return $this->content;
}
/**
- * Has instance config
- * @return boolean
+ * Returns true if this block has instance config.
+ *
+ * @return bool
**/
- function instance_allow_config() {
+ public function instance_allow_config() {
return true;
}
- function before_delete() {
- global $DB;
- // TODO: Move these config settings to proper ones using component name
- $DB->delete_records('config_plugins', array('plugin' => 'blocks/section_links'));
- // Have to manually purge the cache as well
- cache_helper::invalidate_by_definition('core', 'config', array(), 'blocks/section_links');
- }
- function has_config() {
+ /**
+ * Returns true if this block has global config.
+ *
+ * @return bool
+ */
+ public function has_config() {
return true;
}
}
+++ /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/>.
-
-
-/**
- * Section links block
- *
- * @package moodlecore
- * @Author Jason Hardin
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-$numberofsections = array();
-for ($i = 1; $i < 53; $i++){
- $numberofsections[$i] = $i;
-}
-
-$increments = array();
-
-for ($i = 1; $i < 11; $i++){
- $increments[$i] = $i;
-}
-
-if(isset($this->config)){
- $config = $this->config;
-} else{
- $config = get_config('blocks/section_links');
-}
-
-$selected = array();
-if (!empty($config->numsections1)) {
- if (empty($config->incby1)) {
- $config->incby1 = 2;
- }
- $selected[1] = array($config->numsections1, $config->incby1);
-} else {
- $selected[1] = array(22, 2);
-}
-
-if (!empty($config->numsections2)) {
- if (empty($config->incby1)) {
- $config->incby1 = 5;
- }
- $selected[2] = array($config->numsections2, $config->incby2);
-} else {
- $selected[2] = array(40, 5);
-}
-
-?>
-<table cellpadding="9" cellspacing="0">
-<?php
-for($i = 1; $i < 3; $i++){
-?>
- <tr valign="top">
- <td align="right">
- <label for="menunumsections<?php echo $i; ?>"><?php print_string('numsections'.$i, 'block_section_links'); ?>:</label>
- </td>
- <td>
- <?php choose_from_menu($numberofsections, 'numsections'.$i, $selected[$i][0]); ?>
- </td>
- <td>
- <?php print_string('numsectionsdesc'.$i, 'block_section_links'); ?>
- </td>
- </tr>
- <tr valign="top">
- <td align="right">
- <label for="menuincby<?php echo $i;?>"><?php print_string('incby'.$i, 'block_section_links'); ?>:</label>
- </td>
- <td>
- <?php choose_from_menu($increments, 'incby'.$i, $selected[$i][1]); ?>
- </td>
- <td>
- <?php print_string('incbydesc'.$i, 'block_section_links'); ?>
- </td>
- </tr>
-<?php }
-?>
-<tr>
- <td colspan="3" align="center">
- <input type="hidden" name="sesskey" value="<?php echo sesskey();?>">
- <input type="submit" value="<?php print_string('savechanges') ?>" />
- </td>
-</tr>
-</table>
<?php
-
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
- * This file keeps track of upgrades to the latest news block
+ * This file keeps track of upgrades to the section links block
*
* Sometimes, changes between versions involve alterations to database structures
* and other major things that may break installations.
* Please do not forget to use upgrade_set_timeout()
* before any action that may take longer time to finish.
*
- * @since 2.0
- * @package blocks
- * @copyright 2012 Mark Nelson <markn@moodle.com>
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since 2.5
+ * @package block_section_links
+ * @copyright 2013 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
- * Handles upgrading instances of this block.
+ * Upgrade code for the section links block.
*
+ * @global moodle_database $DB
* @param int $oldversion
* @param object $block
*/
-function xmldb_block_news_items_upgrade($oldversion, $block) {
+function xmldb_block_section_links_upgrade($oldversion, $block) {
global $DB;
+ // Moodle v2.3.0 release upgrade line
+ // Put any upgrade step following this
+
// Moodle v2.4.0 release upgrade line
- // Put any upgrade step following this.
+ // Put any upgrade step following this
- if ($oldversion < 2012112901) {
- // Get the instances of this block.
- if ($blocks = $DB->get_records('block_instances', array('blockname' => 'news_items', 'pagetypepattern' => 'my-index'))) {
- // Loop through and remove them from the My Moodle page.
- foreach ($blocks as $block) {
- blocks_delete_instance($block);
- }
+ // Moodle v2.5.0 release upgrade line
+ // Put any upgrade step following this
+
+ if ($oldversion < 2013012200.00) {
+ // The section links block used to use its own crazy plugin name.
+ // Here we are converting it to the proper component name.
+ $oldplugin = 'blocks/section_links';
+ $newplugin = 'block_section_links';
+
+ // Use the proper API here... thats what we should be doing as it ensures any caches etc are cleared
+ // along the way!
+ // It may be quicker to just write an SQL statement but that would be reckless.
+ $config = get_config($oldplugin);
+ if (!empty($config)) {
+ foreach ($config as $name => $value) {
+ set_config($name, $value, $newplugin);
+ unset_config($name, $oldplugin);
+ }
}
- // Savepoint reached.
- upgrade_block_savepoint(true, 2012112901, 'news_items');
+ // Main savepoint reached.
+ upgrade_block_savepoint(true, 2013012200.00, 'section_links');
}
-
return true;
-}
\ No newline at end of file
+}
--- /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/>.
+
+/**
+ * Instance configuration for the section links block.
+ *
+ * @package block_section_links
+ * @copyright 2013 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Instance configuration form.
+ *
+ * @package block_section_links
+ * @copyright 2013 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class block_section_links_edit_form extends block_edit_form {
+
+ /**
+ * The definition of the fields to use.
+ *
+ * @param MoodleQuickForm $mform
+ */
+ protected function specific_definition($mform) {
+ $mform->addElement('header', 'configheader', get_string('blocksettings', 'block'));
+
+ $numberofsections = array();
+ for ($i = 1; $i < 53; $i++){
+ $numberofsections[$i] = $i;
+ }
+
+ $increments = array();
+
+ for ($i = 1; $i < 11; $i++){
+ $increments[$i] = $i;
+ }
+
+ $config = get_config('block_section_links');
+
+ $selected = array(
+ 1 => array(22, 2),
+ 2 => array(40, 5),
+ );
+ if (!empty($config->numsections1)) {
+ if (empty($config->incby1)) {
+ $config->incby1 = $selected[1][1];
+ }
+ $selected[1] = array($config->numsections1, $config->incby1);
+ }
+
+ if (!empty($config->numsections2)) {
+ if (empty($config->incby1)) {
+ $config->incby1 = $selected[2][1];
+ }
+ $selected[2] = array($config->numsections2, $config->incby2);
+ }
+
+ for ($i = 1; $i < 3; $i++) {
+ $mform->addElement('select', 'config_numsections'.$i, get_string('numsections'.$i, 'block_section_links'), $numberofsections);
+ $mform->setDefault('config_numsections'.$i, $selected[$i][0]);
+ $mform->setType('config_numsections'.$i, PARAM_INT);
+ $mform->addHelpButton('config_numsections'.$i, 'numsections'.$i, 'block_section_links');
+
+ $mform->addElement('select', 'config_incby'.$i, get_string('incby'.$i, 'block_section_links'), $increments);
+ $mform->setDefault('config_incby'.$i, $selected[$i][1]);
+ $mform->setType('config_incby'.$i, PARAM_INT);
+ $mform->addHelpButton('config_incby'.$i, 'incby'.$i, 'block_section_links');
+ }
+
+ }
+}
\ No newline at end of file
<?php
-
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
/**
* Strings for component 'block_section_links', language 'en', branch 'MOODLE_20_STABLE'
*
- * @package block_section_links
- * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package block_section_links
+ * @copyright Jason Hardin
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-$string['incbydesc1'] = 'This is the value the section is incremented each time a section link is displayed starting at 1.';
-$string['incbydesc2'] = 'This is the value the section is incremented each time a section link is displayed starting at 1.';
$string['incby1'] = 'Increase by';
+$string['incby1_help'] = 'This is the value the section is incremented each time a section link is displayed starting at 1.';
$string['incby2'] = 'Alternative increase by';
+$string['incby2_help'] = 'This is the value the section is incremented each time a section link is displayed starting at 1.';
$string['jumptocurrenttopic'] = 'Jump to current topic';
$string['jumptocurrentweek'] = 'Jump to current week';
-$string['numsectionsdesc1'] = 'Once the number of sections in the course reaches this number then the increment by value is used.';
-$string['numsectionsdesc2'] = 'Once the number of sections in the course reaches this number then the Alternative increment by value is used.';
$string['numsections1'] = 'Number of sections';
+$string['numsections1_help'] = 'Once the number of sections in the course reaches this number then the increment by value is used.';
$string['numsections2'] = 'Alternative number of sections';
+$string['numsections2_help'] = 'Once the number of sections in the course reaches this number then the Alternative increment by value is used.';
$string['pluginname'] = 'Section links';
$string['section_links:addinstance'] = 'Add a new section links block';
$string['topics'] = 'Topics';
--- /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/>.
+
+/**
+ * Renderer for the section links block.
+ *
+ * @since 2.5
+ * @package block_section_links
+ * @copyright 2013 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Renderer for the section links block.
+ *
+ * @package block_section_links
+ * @copyright 2013 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class block_section_links_renderer extends plugin_renderer_base {
+
+ /**
+ * Render a series of section links.
+ *
+ * @param stdClass $course The course we are rendering for.
+ * @param array $sections An array of section objects to render.
+ * @param bool|int The section to provide a jump to link for.
+ * @return string The HTML to display.
+ */
+ public function render_section_links(stdClass $course, array $sections, $jumptosection = false) {
+ $html = html_writer::start_tag('ol', array('class' => 'inline-list'));
+ foreach ($sections as $section) {
+ $attributes = array();
+ if (!$section->visible) {
+ $attributes['class'] = 'dimmed';
+ }
+ $html .= html_writer::start_tag('li');
+ $sectiontext = $section->section;
+ if ($section->highlight) {
+ $sectiontext = html_writer::tag('strong', $sectiontext);
+ }
+ $html .= html_writer::link(course_get_url($course, $section->section), $sectiontext, $attributes);
+ $html .= html_writer::end_tag('li').' ';
+ }
+ $html .= html_writer::end_tag('ol');
+ if ($jumptosection && isset($sections[$jumptosection])) {
+
+ if ($course->format == 'weeks') {
+ $linktext = new lang_string('jumptocurrentweek', 'block_section_links');
+ } else if ($course->format == 'topics') {
+ $linktext = new lang_string('jumptocurrenttopic', 'block_section_links');
+ }
+
+ $attributes = array();
+ if (!$sections[$jumptosection]->visible) {
+ $attributes['class'] = 'dimmed';
+ }
+ $html .= html_writer::link(course_get_url($course, $jumptosection), $linktext, $attributes);
+ }
+
+ return $html;
+ }
+}
\ No newline at end of file
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-
/**
* Section links block
*
- * @package moodlecore
+ * @package block_section_links
+ * @copyright Jason Hardin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
if ($ADMIN->fulltree) {
- $configs = array();
-
$numberofsections = array();
for ($i = 1; $i < 53; $i++){
2 => array(40,5));
for($i = 1; $i < 3; $i++){
- $configs[] = new admin_setting_configselect('numsections'.$i, get_string('numsections'.$i, 'block_section_links'),
- get_string('numsectionsdesc'.$i, 'block_section_links'),
- $selected[$i][0], $numberofsections);
-
- $configs[] = new admin_setting_configselect('incby'.$i, get_string('incby'.$i, 'block_section_links'),
- get_string('incbydesc'.$i, 'block_section_links'),
- $selected[$i][1], $increments);
- }
+ $settings->add(new admin_setting_configselect('block_section_links/numsections'.$i, get_string('numsections'.$i, 'block_section_links'),
+ get_string('numsections'.$i.'_help', 'block_section_links'),
+ $selected[$i][0], $numberofsections));
- foreach ($configs as $config) {
- $config->plugin = 'blocks/section_links';
- $settings->add($config);
+ $settings->add(new admin_setting_configselect('block_section_links/incby'.$i, get_string('incby'.$i, 'block_section_links'),
+ get_string('incby'.$i.'_help', 'block_section_links'),
+ $selected[$i][1], $increments));
}
}
\ No newline at end of file
/**
* Version details
*
- * @package block
- * @subpackage section_links
- * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
+ * @package block_section_links
+ * @copyright Jason Hardin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2012112900; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version = 2013012200; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2012112900; // Requires this Moodle version
$plugin->component = 'block_section_links'; // Full name of the plugin (used for diagnostics)
--- /dev/null
+@blocks
+Feature: Add blocks
+ In order to add more functionality to pages
+ As a teacher
+ I need to add blocks to pages
+
+ @javascript
+ Scenario: Add a block to a course
+ Given the following "users" exists:
+ | username | firstname | lastname | email |
+ | student1 | Student | 1 | student1@asd.com |
+ | student2 | Student | 2 | student2@asd.com |
+ And the following "courses" exists:
+ | fullname | shortname | format |
+ | Course 1 | C1 | topics |
+ And the following "course enrolments" exists:
+ | user | course | role |
+ | student1 | C1 | student |
+ | student2 | C1 | student |
+ And I log in as "admin"
+ And I follow "Course 1"
+ And I turn editing mode on
+ When I add the "Blog menu" block
+ Then I should see "View all of my entries"
--- /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/>.
+
+/**
+ * Steps definitions related with blocks.
+ *
+ * @package core
+ * @category test
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
+
+require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
+
+use Behat\Behat\Context\Step\Given as Given;
+
+/**
+ * Blocks management steps definitions.
+ *
+ * @package core
+ * @category test
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class behat_blocks extends behat_base {
+
+ /**
+ * Adds the selected block. Editing mode must be previously enabled.
+ *
+ * @Given /^I add the "(?P<block_name_string>(?:[^"]|\\")*)" block$/
+ * @param string $blockname
+ */
+ public function i_add_the_block($blockname) {
+ return new Given('I select "' . $blockname . '" from "bui_addblock"');
+ }
+
+}
This files describes API changes in /blocks/* - activity modules,
information provided here is intended especially for developers.
+=== 2.4 ===
+
+Created new capability 'blocks/xxx:myaddinstance' that determines whether a user can add
+a specific block to their My Home page. This capability was only defined for blocks where
+the applicable_formats function does not include "'my' => false" in the returned array,
+allowing it be added to the My Home page.
+
=== 2.3 ===
required changes in code:
}
break;
case 'editdefinitionmapping' : // Edit definition mappings.
- $definition = required_param('definition', PARAM_TEXT);
+ $definition = required_param('definition', PARAM_SAFEPATH);
$title = get_string('editdefinitionmappings', 'cache', $definition);
$mform = new cache_definition_mappings_form($PAGE->url, array('definition' => $definition));
if ($mform->is_cancelled()) {
}
break;
+ case 'purgedefinition': // Purge a specific definition.
+ $definition = required_param('definition', PARAM_SAFEPATH);
+ list($component, $area) = explode('/', $definition, 2);
+ cache_helper::purge_by_definition($component, $area);
+ redirect($PAGE->url, get_string('purgedefinitionsuccess', 'cache'), 5);
+ break;
+
+ case 'purgestore':
case 'purge': // Purge a store cache.
$store = required_param('store', PARAM_TEXT);
cache_helper::purge_store($store);
const STATE_SAVING = 2;
/** The cache is ready to use. */
const STATE_READY = 3;
+ /** The cache is currently updating itself */
+ const STATE_UPDATING = 4;
/** The cache encountered an error while initialising. */
const STATE_ERROR_INITIALISING = 9;
/** The cache has been disabled. */
*/
public function create_cache(cache_definition $definition) {
$class = $definition->get_cache_class();
- $stores = cache_helper::get_cache_stores($definition);
+ if ($this->is_initialising()) {
+ // Do nothing we just want the dummy store.
+ $stores = array();
+ } else {
+ $stores = cache_helper::get_cache_stores($definition);
+ }
if (count($stores) === 0) {
// Hmm no stores, better provide a dummy store to mimick functionality. The dev will be none the wiser.
$stores[] = $this->create_dummy_store($definition);
if (!$store->is_ready() || !$store->is_supported_mode($definition->get_mode())) {
return false;
}
- $store = clone($this->stores[$name]);
+ // We always create a clone of the original store.
+ // If we were to clone a store that had already been initialised with a definition then
+ // we'd run into a myriad of issues.
+ // We use a method of the store to create a clone rather than just creating it ourselves
+ // so that if any store out there doesn't handle cloning they can override this method in
+ // order to address the issues.
+ $store = $this->stores[$name]->create_clone($details);
$store->initialise($definition);
return $store;
}
$id .= '::'.$aggregate;
}
if (!array_key_exists($id, $this->definitions)) {
- $instance = $this->create_config_instance();
- $definition = $instance->get_definition_by_id($id);
- if (!$definition) {
- $this->reset();
- $instance = $this->create_config_instance(true);
- $instance->update_definitions();
+ // This is the first time this definition has been requested.
+ if ($this->is_initialising()) {
+ // We're initialising the cache right now. Don't try to create another config instance.
+ // We'll just use an ad-hoc cache for the time being.
+ $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, $component, $area);
+ } else {
+ // Load all the known definitions and find the desired one.
+ $instance = $this->create_config_instance();
$definition = $instance->get_definition_by_id($id);
if (!$definition) {
- throw new coding_exception('The requested cache definition does not exist.'. $id, $id);
+ // Oh-oh the definition doesn't exist.
+ // There are several things that could be going on here.
+ // We may be installing/upgrading a site and have hit a definition that hasn't been used before.
+ // Of the developer may be trying to use a newly created definition.
+ if ($this->is_updating()) {
+ // The cache is presently initialising and the requested cache definition has not been found.
+ // This means that the cache initialisation has requested something from a cache (I had recursive nightmares about this).
+ // To serve this purpose and avoid errors we are going to make use of an ad-hoc cache rather than
+ // search for the definition which would possibly cause an infitite loop trying to initialise the cache.
+ $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, $component, $area);
+ if ($aggregate !== null) {
+ // If you get here you deserve a warning. We have to use an ad-hoc cache here, so we can't find the definition and therefor
+ // can't find any information about the datasource or any of its aggregated.
+ // Best of luck.
+ debugging('An unknown cache was requested during development with an aggregate that could not be loaded. Ad-hoc cache used instead.', DEBUG_DEVELOPER);
+ $aggregate = null;
+ }
+ } else {
+ // Either a typo of the developer has just created the definition and is using it for the first time.
+ $this->reset();
+ $instance = $this->create_config_instance(true);
+ $instance->update_definitions();
+ $definition = $instance->get_definition_by_id($id);
+ if (!$definition) {
+ throw new coding_exception('The requested cache definition does not exist.'. $id, $id);
+ } else {
+ debugging('Cache definitions reparsed causing cache reset in order to locate definition.
+ You should bump the version number to ensure definitions are reprocessed.', DEBUG_DEVELOPER);
+ }
+ $definition = cache_definition::load($id, $definition, $aggregate);
+ }
} else {
- debugging('Cache definitions reparsed causing cache reset in order to locate definition.
- You should bump the version number to ensure definitions are reprocessed.', DEBUG_DEVELOPER);
+ $definition = cache_definition::load($id, $definition, $aggregate);
}
}
- $this->definitions[$id] = cache_definition::load($id, $definition, $aggregate);
+ $this->definitions[$id] = $definition;
}
return $this->definitions[$id];
}
return true;
}
+ /**
+ * Informs the factory that the cache is currently updating itself.
+ *
+ * This forces the state to upgrading and can only be called once the cache is ready to use.
+ * Calling it ensure we don't try to reinstantite things when requesting cache definitions that don't exist yet.
+ */
+ public function updating_started() {
+ if ($this->state !== self::STATE_READY) {
+ return false;
+ }
+ $this->state = self::STATE_UPDATING;
+ return true;
+ }
+
+ /**
+ * Informs the factory that the upgrading has finished.
+ *
+ * This forces the state back to ready.
+ */
+ public function updating_finished() {
+ $this->state = self::STATE_READY;
+ }
+
/**
* Returns true if the cache API has been disabled.
*
return $this->state === self::STATE_DISABLED;
}
+ /**
+ * Returns true if the cache is currently initialising itself.
+ *
+ * This includes both initialisation and saving the cache config file as part of that initialisation.
+ *
+ * @return bool
+ */
+ public function is_initialising() {
+ return $this->state === self::STATE_INITIALISING || $this->state === self::STATE_SAVING;
+ }
+
+ /**
+ * Returns true if the cache is currently updating itself.
+ *
+ * @return bool
+ */
+ public function is_updating() {
+ return $this->state === self::STATE_UPDATING;
+ }
+
/**
* Disables as much of the cache API as possible.
*
cache_helper::record_cache_miss($this->storetype, $this->definition->get_id());
}
if ($this->loader !== false) {
- $result = $this->loader->get($parsedkey);
+ // We must pass the original (unparsed) key to the next loader in the chain.
+ // The next loader will parse the key as it sees fit. It may be parsed differently
+ // depending upon the capabilities of the store associated with the loader.
+ $result = $this->loader->get($key);
} else if ($this->datasource !== false) {
$result = $this->datasource->load_for_cache($key);
}
/**
* Constructs an instance of the cache store.
*
- * This method should not create connections or perform and processing, it should be used
+ * The constructor should be responsible for creating anything needed by the store that is not
+ * specific to a definition.
+ * Tasks such as opening a connection to check it is available are best done here.
+ * Tasks that are definition specific such as creating a storage area for the definition data
+ * or creating key tables and indexs are best done within the initialise method.
+ *
+ * Once a store has been constructed the cache API will check it is ready to be intialised with
+ * a definition by called $this->is_ready().
+ * If the setup of the store failed (connection could not be established for example) then
+ * that method should return false so that the store instance is not selected for use.
*
* @param string $name The name of the cache store
* @param array $configuration The configuration for this store instance.
/**
* Initialises a new instance of the cache store given the definition the instance is to be used for.
*
- * This function should prepare any given connections etc.
+ * This function should be used to run any definition specific setup the store instance requires.
+ * Tasks such as creating storage areas, or creating indexes are best done here.
+ *
+ * Its important to note that the initialise method is expected to always succeed.
+ * If there are setup tasks that may fail they should be done within the __construct method
+ * and should they fail is_ready should return false.
*
* @param cache_definition $definition
*/
/**
* Performs any necessary clean up when the store instance is being deleted.
+ *
+ * @deprecated since 2.5
+ */
+ public function cleanup() {
+ debugging('This function has been renamed to instance_deleted. Please update your calls.', DEBUG_DEVELOPER);
+ }
+
+ /**
+ * Performs any necessary operation when the store instance has been created.
+ *
+ * @since 2.5
+ */
+ public function instance_created() {
+ // By default, do nothing.
+ }
+
+ /**
+ * Performs any necessary operation when the store instance is being deleted.
+ *
+ * This method may be called before the store has been initialised.
+ *
+ * @since 2.5
+ * @see cleanup()
*/
- abstract public function cleanup();
+ public function instance_deleted() {
+ if (method_exists($this, 'cleanup')) {
+ // There used to be a legacy function called cleanup, it was renamed to instance delete.
+ // To be removed in 2.6.
+ $this->cleanup();
+ }
+ }
/**
* Returns true if the user can add an instance of the store plugin.
public function supports_native_ttl() {
return $this::get_supported_features() & self::SUPPORTS_NATIVE_TTL;
}
+
+ /**
+ * Creates a clone of this store instance ready to be initialised.
+ *
+ * This method is used so that a cache store needs only be constructed once.
+ * Future requests for an instance of the store will be given a cloned instance.
+ *
+ * If you are writing a cache store that isn't compatible with the clone operation
+ * you can override this method to handle any situations you want before cloning.
+ *
+ * @param array $details An array containing the details of the store from the cache config.
+ * @return cache_store
+ */
+ public function create_clone(array $details = array()) {
+ // By default we just run clone.
+ // Any stores that have an issue with this will need to override the create_clone method.
+ return clone($this);
+ }
}
$this->configstores[$name]['lock'] = $configuration['lock'];
unset($this->configstores[$name]['configuration']['lock']);
}
+ // Call instance_created()
+ $store = new $class($name, $this->configstores[$name]['configuration']);
+ $store->instance_created();
+
$this->config_save();
return true;
}
throw new cache_exception('You cannot delete a cache store that has definition mappings.');
}
}
+
+ // Call instance_deleted()
+ $class = 'cachestore_'.$this->configstores[$name]['plugin'];
+ $store = new $class($name