composer.phar
composer.lock
/vendor/
+/behat.yml
<?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));
--- /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');
+
+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']) {
+ 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"/>
--- /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");
+ }
+ }
+ }
+}
--- /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"
--- /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)
$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"');
+ }
+
+}
}
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);
$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);
}
/**
* Performs any necessary clean up when the store instance is being deleted.
+ *
+ * @deprecated since 2.5
*/
- abstract public function cleanup();
+ 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()
+ */
+ 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.
$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, $this->configstores[$name]['configuration']);
+ $store->instance_deleted();
+
unset($this->configstores[$name]);
$this->config_save();
return true;
* @param bool $coreonly If set to true only core definitions will be updated.
*/
public static function update_definitions($coreonly = false) {
- $config = self::instance();
+ $factory = cache_factory::instance();
+ $factory->updating_started();
+ $config = $factory->create_config_instance(true);
$config->write_definitions_to_cache(self::locate_definitions($coreonly));
+ $factory->updating_finished();
}
/**
array(
'text' => get_string('editmappings', 'cache'),
'url' => new moodle_url('/cache/admin.php', array('action' => 'editdefinitionmapping', 'sesskey' => sesskey()))
+ ),
+ array(
+ 'text' => get_string('purge', 'cache'),
+ 'url' => new moodle_url('/cache/admin.php', array('action' => 'purgedefinition', 'sesskey' => sesskey()))
)
);
}
}
$actions[] = array(
'text' => get_string('purge', 'cache'),
- 'url' => new moodle_url($baseurl, array('action' => 'purge'))
+ 'url' => new moodle_url($baseurl, array('action' => 'purgestore'))
);
}
return $actions;
protected $name;
/**
- * The path to use for the file storage.
+ * The path used to store files for this store and the definition it was initialised with.
* @var string
*/
- protected $path = null;
+ protected $path = false;
+
+ /**
+ * The path in which definition specific sub directories will be created for caching.
+ * @var string
+ */
+ protected $filestorepath = false;
/**
* Set to true when a prescan has been performed.
$path = make_cache_directory('cachestore_file/'.preg_replace('#[^a-zA-Z0-9\.\-_]+#', '', $name));
}
$this->isready = $path !== false;
+ $this->filestorepath = $path;
+ // This will be updated once the store has been initialised for a definition.
$this->path = $path;
+
// Check if we should prescan the directory.
if (array_key_exists('prescan', $configuration)) {
$this->prescan = (bool)$configuration['prescan'];
}
}
+ /**
+ * Performs any necessary operation when the file store instance has been created.
+ */
+ public function instance_created() {
+ if ($this->isready && !$this->prescan) {
+ // It is supposed the store instance to expect an empty folder.
+ $this->purge_all_definitions();
+ }
+ }
+
/**
* Returns true if this store instance is ready to be used.
* @return bool
public function initialise(cache_definition $definition) {
$this->definition = $definition;
$hash = preg_replace('#[^a-zA-Z0-9]+#', '_', $this->definition->get_id());
- $this->path .= '/'.$hash;
+ $this->path = $this->filestorepath.'/'.$hash;
make_writable_directory($this->path);
if ($this->prescan && $definition->get_mode() !== self::MODE_REQUEST) {
$this->prescan = false;
}
/**
- * Purges the cache deleting all items within it.
+ * Purges the cache definition deleting all the items within it.
*
* @return boolean True on success. False otherwise.
*/
public function purge() {
- $files = glob($this->glob_keys_pattern(), GLOB_MARK | GLOB_NOSORT);
- if (is_array($files)) {
- foreach ($files as $filename) {
- @unlink($filename);
+ if ($this->isready) {
+ $files = glob($this->glob_keys_pattern(), GLOB_MARK | GLOB_NOSORT);
+ if (is_array($files)) {
+ foreach ($files as $filename) {
+ @unlink($filename);
+ }
}
+ $this->keys = array();
}
- $this->keys = array();
return true;
}
+ /**
+ * Purges all the cache definitions deleting all items within them.
+ *
+ * @return boolean True on success. False otherwise.
+ */
+ protected function purge_all_definitions() {
+ // Warning: limit the deletion to what file store is actually able
+ // to create using the internal {@link purge()} providing the
+ // {@link $path} with a wildcard to perform a purge action over all the definitions.
+ $currpath = $this->path;
+ $this->path = $this->filestorepath.'/*';
+ $result = $this->purge();
+ $this->path = $currpath;
+ return $result;
+ }
+
/**
* Given the data from the add instance form this function creates a configuration array.
*
}
/**
- * Performs any necessary clean up when the store instance is being deleted.
+ * Performs any necessary clean up when the file store instance is being deleted.
*
* 1. Purges the cache directory.
- * 2. Deletes the directory we created for this cache instances data.
+ * 2. Deletes the directory we created for the given definition.
*/
- public function cleanup() {
- $this->purge();
- @rmdir($this->path);
+ public function instance_deleted() {
+ $this->purge_all_definitions();
+ @rmdir($this->filestorepath);
}
/**
/**
* Performs any necessary clean up when the store instance is being deleted.
*/
- public function cleanup() {
- $this->purge();
+ public function instance_deleted() {
+ if ($this->connection) {
+ $connection = $this->connection;
+ } else {
+ $connection = new Memcache;
+ foreach ($this->servers as $server) {
+ $connection->addServer($server[0], $server[1], true, $server[2]);
+ }
+ }
+ $connection->flush();
+ unset($connection);
+ unset($this->connection);
}
/**
/**
* Performs any necessary clean up when the store instance is being deleted.
*/
- public function cleanup() {
- $this->purge();
+ public function instance_deleted() {
+ if ($this->connection) {
+ $connection = $this->connection;
+ } else {
+ $connection = new Memcached(crc32($this->name));
+ $servers = $connection->getServerList();
+ if (empty($servers)) {
+ foreach ($this->options as $key => $value) {
+ $connection->setOption($key, $value);
+ }
+ $connection->addServers($this->servers);
+ }
+ }
+ $connection->flush();
+ unset($connection);
+ unset($this->connection);
}
/**
* The Connection object
* @var Mongo
*/
- protected $connection;
+ protected $connection = false;
/**
* The Database Object
/**
* Performs any necessary clean up when the store instance is being deleted.
*/
- public function cleanup() {
- $this->purge();
+ public function instance_deleted() {
+ // We can't use purge here that acts upon a collection.
+ // Instead we must drop the named database.
+ if ($this->connection) {
+ $connection = $this->connection;
+ } else {
+ $connection = new Mongo($this->server, $this->options);
+ }
+ $database = $connection->selectDB($this->databasename);
+ $database->drop();
+ $connection = null;
+ $database = null;
+ // Explicitly unset things to cause a close.
+ $this->collection = null;
+ $this->database = null;
+ $this->connection = null;
}
/**
/**
* Performs any necessary clean up when the store instance is being deleted.
*/
- public function cleanup() {
+ public function instance_deleted() {
$this->purge();
}
/**
* Performs any necessary clean up when the store instance is being deleted.
*/
- public function cleanup() {
+ public function instance_deleted() {
$this->purge();
}
}
$result[5] = sprintf('%01.4f', microtime(true) - $start);
$applicationtable->data[] = $result;
- $store->cleanup();
+ $store->instance_deleted();
}
}
}
$result[5] = sprintf('%01.4f', microtime(true) - $start);
$sessiontable->data[] = $result;
- $store->cleanup();
+ $store->instance_deleted();
}
}
}
$result[5] = sprintf('%01.4f', microtime(true) - $start);
$requesttable->data[] = $result;
- $store->cleanup();
+ $store->instance_deleted();
}
}
$config = $factory->create_config_instance();
$this->assertEquals('cache_config_phpunittest', get_class($config));
}
+
+ /**
+ * Test that multiple loaders work ok.
+ */
+ public function test_multiple_loaders() {
+ $instance = cache_config_phpunittest::instance(true);
+ $instance->phpunit_add_file_store('phpunittest1');
+ $instance->phpunit_add_file_store('phpunittest2');
+ $instance->phpunit_add_definition('phpunit/multi_loader', array(
+ 'mode' => cache_store::MODE_APPLICATION,
+ 'component' => 'phpunit',
+ 'area' => 'multi_loader'
+ ));
+ $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest1', 3);
+ $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest2', 2);
+
+ $cache = cache::make('phpunit', 'multi_loader');
+ $this->assertInstanceOf('cache_application', $cache);
+ $this->assertFalse($cache->get('test'));
+ $this->assertTrue($cache->set('test', 'test'));
+ $this->assertEquals('test', $cache->get('test'));
+ }
}
\ No newline at end of file
public function phpunit_remove_stores() {
$this->configstores = array();
}
+
+ /**
+ * Forcefully adds a file store.
+ *
+ * @param string $name
+ */
+ public function phpunit_add_file_store($name) {
+ $this->configstores[$name] = array(
+ 'name' => $name,
+ 'plugin' => 'file',
+ 'configuration' => array(
+ 'path' => ''
+ ),
+ 'features' => 6,
+ 'modes' => 3,
+ 'mappingsonly' => false,
+ 'class' => 'cachestore_file',
+ 'default' => false,
+ 'lock' => 'cachelock_file_default'
+ );
+ }
+
+ /**
+ * Forcefully injects a definition => store mapping.
+ *
+ * This function does no validation, you should only be calling if it you know
+ * exactly what to expect.
+ *
+ * @param string $definition
+ * @param string $store
+ * @param int $sort
+ */
+ public function phpunit_add_definition_mapping($definition, $store, $sort) {
+ $this->configdefinitionmappings[] = array(
+ 'store' => $store,
+ 'definition' => $definition,
+ 'sort' => (int)$sort
+ );
+ }
}
/**
--- /dev/null
+This files describes API changes in /cache/stores/* - cache store plugins.
+Information provided here is intended especially for developers.
+
+=== 2.5 ===
+* cleanup method renamed to instance_deleted.
+ It is now called when the store is deleted as all comments suggested anyway.
+* instance_created method added.
+ It is called when the store is created for the very first time.
\ No newline at end of file
if (in_array($id, $courses)) {
$funcparam['courses'][] = $id;
} else {
- $warnings[] = array('item' => $id, 'warningcode' => 'nopermissions', 'message' => 'you donot have permissions to access this course');
+ $warnings[] = array('item' => $id, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to access this course');
}
}
} else {
if (in_array($id, $groups)) {
$funcparam['groups'][] = $id;
} else {
- $warnings[] = array('item' => $id, 'warningcode' => 'nopermissions', 'message' => 'you donot have permissions to access this group');
+ $warnings[] = array('item' => $id, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to access this group');
}
}
} else {
(calendar_edit_event_allowed($eventid))) {
$events[$eventid] = $event;
} else {
- $warnings[] = array('item' => $eventid, 'warningcode' => 'nopermissions', 'message' => 'you donot have permissions to view this event');
+ $warnings[] = array('item' => $eventid, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to view this event');
}
}
}
)
);
}
+
+ /**
+ * Returns description of method parameters.
+ *
+ * @return external_function_parameters.
+ * @since Moodle 2.5
+ */
+ public static function create_calendar_events_parameters() {
+ // Userid is always current user, so no need to get it from client.
+ // Module based calendar events are not allowed here. Hence no need of instance and modulename.
+ // subscription id and uuid is not allowed as this is not an ical api.
+ return new external_function_parameters(
+ array('events' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'name' => new external_value(PARAM_TEXT, 'event name', VALUE_REQUIRED, '', NULL_NOT_ALLOWED),
+ 'description' => new external_value(PARAM_RAW, 'Description', VALUE_DEFAULT, null, NULL_ALLOWED),
+ 'format' => new external_format_value('description', VALUE_DEFAULT),
+ 'courseid' => new external_value(PARAM_INT, 'course id', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
+ 'groupid' => new external_value(PARAM_INT, 'group id', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
+ 'repeats' => new external_value(PARAM_INT, 'number of repeats', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
+ 'eventtype' => new external_value(PARAM_TEXT, 'Event type', VALUE_DEFAULT, 'user', NULL_NOT_ALLOWED),
+ 'timestart' => new external_value(PARAM_INT, 'timestart', VALUE_DEFAULT, time(), NULL_NOT_ALLOWED),
+ 'timeduration' => new external_value(PARAM_INT, 'time duration', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED),
+ 'visible' => new external_value(PARAM_INT, 'visible', VALUE_DEFAULT, 1, NULL_NOT_ALLOWED),
+ 'sequence' => new external_value(PARAM_INT, 'sequence', VALUE_DEFAULT, 1, NULL_NOT_ALLOWED),
+ ), 'event')
+ )
+ )
+ );
+ }
+
+ /**
+ * Delete Calendar events.
+ *
+ * @param array $events A list of events to create.
+ * @return array array of events created.
+ * @since Moodle 2.5
+ * @throws moodle_exception if user doesnt have the permission to create events.
+ */
+ public static function create_calendar_events($events) {
+ global $CFG, $DB, $USER;
+ require_once($CFG->dirroot."/calendar/lib.php");
+
+ // Parameter validation.
+ $params = self::validate_parameters(self::create_calendar_events_parameters(), array('events' => $events));
+
+ $transaction = $DB->start_delegated_transaction();
+ $return = array();
+ $warnings = array();
+
+ foreach ($params['events'] as $event) {
+
+ // Let us set some defaults.
+ $event['userid'] = $USER->id;
+ $event['modulename'] = '';
+ $event['instance'] = 0;
+ $event['subscriptionid'] = null;
+ $event['uuid']= '';
+ $event['format'] = external_validate_format($event['format']);
+ if ($event['repeats'] > 0) {
+ $event['repeat'] = 1;
+ } else {
+ $event['repeat'] = 0;
+ }
+
+ $eventobj = new calendar_event($event);
+
+ // Let's check if the user is allowed to delete an event.
+ if (!calendar_add_event_allowed($eventobj)) {
+ $warnings [] = array('item' => $event['name'], 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to create this event');
+ continue;
+ }
+ // Let's create the event.
+ $var = $eventobj->create($event);
+ $var = (array)$var->properties();
+ if ($event['repeat']) {
+ $children = $DB->get_records('event', array('repeatid' => $var['id']));
+ foreach ($children as $child) {
+ $return[] = (array) $child;
+ }
+ } else {
+ $return[] = $var;
+ }
+ }
+
+ // Everything done smoothly, let's commit.
+ $transaction->allow_commit();
+ return array('events' => $return, 'warnings' => $warnings);
+ }
+
+ /**
+ * Returns description of method result value.
+ *
+ * @return external_description.
+ * @since Moodle 2.5
+ */
+ public static function create_calendar_events_returns() {
+ return new external_single_structure(
+ array(
+ 'events' => new external_multiple_structure( new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'event id'),
+ 'name' => new external_value(PARAM_TEXT, 'event name'),
+ 'description' => new external_value(PARAM_RAW, 'Description', VALUE_OPTIONAL),
+ 'format' => new external_format_value('description'),
+ 'courseid' => new external_value(PARAM_INT, 'course id'),
+ 'groupid' => new external_value(PARAM_INT, 'group id'),
+ 'userid' => new external_value(PARAM_INT, 'user id'),
+ 'repeatid' => new external_value(PARAM_INT, 'repeat id', VALUE_OPTIONAL),
+ 'modulename' => new external_value(PARAM_TEXT, 'module name', VALUE_OPTIONAL),
+ 'instance' => new external_value(PARAM_INT, 'instance id'),
+ 'eventtype' => new external_value(PARAM_TEXT, 'Event type'),
+ 'timestart' => new external_value(PARAM_INT, 'timestart'),
+ 'timeduration' => new external_value(PARAM_INT, 'time duration'),
+ 'visible' => new external_value(PARAM_INT, 'visible'),
+ 'uuid' => new external_value(PARAM_TEXT, 'unique id of ical events', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
+ 'sequence' => new external_value(PARAM_INT, 'sequence'),
+ 'timemodified' => new external_value(PARAM_INT, 'time modified'),
+ 'subscriptionid' => new external_value(PARAM_INT, 'Subscription id', VALUE_OPTIONAL),
+ ), 'event')
+ ),
+ 'warnings' => new external_warnings()
+ )
+ );
+ }
}
* @return string $content return available control for the calender in html
*/
function calendar_top_controls($type, $data) {
- global $CFG;
+ global $CFG, $PAGE;
$content = '';
if(!isset($data['d'])) {
$data['d'] = 1;
$data['m'] = $date['mon'];
$data['y'] = $date['year'];
+ $urlbase = $PAGE->url;
//Accessibility: calendar block controls, replaced <table> with <div>.
//$nexttext = link_arrow_right(get_string('monthnext', 'access'), $url='', $accesshide=true);
case 'frontpage':
list($prevmonth, $prevyear) = calendar_sub_month($data['m'], $data['y']);
list($nextmonth, $nextyear) = calendar_add_month($data['m'], $data['y']);
- $nextlink = calendar_get_link_next(get_string('monthnext', 'access'), 'index.php?', 0, $nextmonth, $nextyear, $accesshide=true);
- $prevlink = calendar_get_link_previous(get_string('monthprev', 'access'), 'index.php?', 0, $prevmonth, $prevyear, true);
+ $nextlink = calendar_get_link_next(get_string('monthnext', 'access'), $urlbase, 0, $nextmonth, $nextyear, true);
+ $prevlink = calendar_get_link_previous(get_string('monthprev', 'access'), $urlbase, 0, $prevmonth, $prevyear, true);
$calendarlink = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', array('view'=>'month')), 1, $data['m'], $data['y']);
if (!empty($data['id'])) {
case 'course':
list($prevmonth, $prevyear) = calendar_sub_month($data['m'], $data['y']);
list($nextmonth, $nextyear) = calendar_add_month($data['m'], $data['y']);
- $nextlink = calendar_get_link_next(get_string('monthnext', 'access'), 'view.php?id='.$data['id'].'&', 0, $nextmonth, $nextyear, $accesshide=true);
- $prevlink = calendar_get_link_previous(get_string('monthprev', 'access'), 'view.php?id='.$data['id'].'&', 0, $prevmonth, $prevyear, true);
+ $nextlink = calendar_get_link_next(get_string('monthnext', 'access'), $urlbase, 0, $nextmonth, $nextyear, true);
+ $prevlink = calendar_get_link_previous(get_string('monthprev', 'access'), $urlbase, 0, $prevmonth, $prevyear, true);
$calendarlink = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', array('view'=>'month')), 1, $data['m'], $data['y']);
if (!empty($data['id'])) {
$this->assertEquals(1, count($events['events'])); // site.
$this->assertEquals(0, count($events['warnings']));
}
+
+ /**
+ * Test core_calendar_external::create_calendar_events
+ */
+ public function test_core_create_calendar_events() {
+ global $DB, $USER, $SITE;
+
+ $this->resetAfterTest(true);
+ $this->setAdminUser();
+
+ // Create a few stuff to test with.
+ $user = $this->getDataGenerator()->create_user();
+ $course = $this->getDataGenerator()->create_course();
+ $record = new stdClass();
+ $record->courseid = $course->id;
+ $group = $this->getDataGenerator()->create_group($record);
+
+ $prevcount = $DB->count_records('event');
+
+ // Let's create a few events.
+ $events = array (
+ array('name' => 'site', 'courseid' => $SITE->id, 'eventtype' => 'site'),
+ array('name' => 'course', 'courseid' => $course->id, 'eventtype' => 'course', 'repeats' => 2),
+ array('name' => 'group', 'courseid' => $course->id, 'groupid' => $group->id, 'eventtype' => 'group'),
+ array('name' => 'user')
+ );
+ $eventsret = core_calendar_external::create_calendar_events($events);
+
+ // Check to see if things were created properly.
+ $aftercount = $DB->count_records('event');
+ $this->assertEquals($prevcount + 5, $aftercount);
+ $this->assertEquals(5, count($eventsret['events']));
+ $this->assertEquals(0, count($eventsret['warnings']));
+
+ $sitecontext = context_system::instance();
+ $coursecontext = context_course::instance($course->id);
+
+ $this->setUser($user);
+ $prevcount = $aftercount;
+ $events = array (
+ array('name' => 'course', 'courseid' => $course->id, 'eventtype' => 'course', 'repeats' => 2),
+ array('name' => 'group', 'courseid' => $course->id, 'groupid' => $group->id, 'eventtype' => 'group'),
+ array('name' => 'user')
+ );
+ $role = $DB->get_record('role', array('shortname' => 'student'));
+ $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
+ groups_add_member($group, $user);
+ $this->assignUserCapability('moodle/calendar:manageentries', $coursecontext->id, $role->id);
+ $this->assignUserCapability('moodle/calendar:managegroupentries', $coursecontext->id, $role->id);
+ $eventsret = core_calendar_external::create_calendar_events($events);
+ $eventsret = external_api::clean_returnvalue(core_calendar_external::create_calendar_events_returns(), $eventsret);
+ // Check to see if things were created properly.
+ $aftercount = $DB->count_records('event');
+ $this->assertEquals($prevcount + 4, $aftercount);
+ $this->assertEquals(4, count($eventsret['events']));
+ $this->assertEquals(0, count($eventsret['warnings']));
+
+ // Check to see nothing was created without proper permission.
+ $this->setGuestUser();
+ $prevcount = $DB->count_records('event');
+ $eventsret = core_calendar_external::create_calendar_events($events);
+ $eventsret = external_api::clean_returnvalue(core_calendar_external::create_calendar_events_returns(), $eventsret);
+ $aftercount = $DB->count_records('event');
+ $this->assertEquals($prevcount, $aftercount);
+ $this->assertEquals(0, count($eventsret['events']));
+ $this->assertEquals(3, count($eventsret['warnings']));
+
+ $this->setUser($user);
+ $this->unassignUserCapability('moodle/calendar:manageentries', $coursecontext->id, $role->id);
+ $this->unassignUserCapability('moodle/calendar:managegroupentries', $coursecontext->id, $role->id);
+ $prevcount = $DB->count_records('event');
+ $eventsret = core_calendar_external::create_calendar_events($events);
+ $eventsret = external_api::clean_returnvalue(core_calendar_external::create_calendar_events_returns(), $eventsret);
+ $aftercount = $DB->count_records('event');
+ $this->assertEquals($prevcount + 1, $aftercount); // User event.
+ $this->assertEquals(1, count($eventsret['events']));
+ $this->assertEquals(2, count($eventsret['warnings']));
+ }
}
{
+ "repositories" : [
+ {
+ "type": "vcs",
+ "url": "https://github.com/moodlehq/moodle-behat-extension"
+ }
+ ],
"require-dev": {
"phpunit/phpunit": "3.7.*",
- "phpunit/dbUnit": "1.2.*"
+ "phpunit/dbUnit": "1.2.*",
+ "moodlehq/behat-extension": "1.0.3"
}
-}
\ No newline at end of file
+}
// Divert all outgoing emails to this address to test and debug emailing features
// $CFG->divertallemailsto = 'root@localhost.local'; // NOT FOR PRODUCTION SERVERS!
//
-// special magic evil developer only wanting to edit the xmldb files manually
-// AND don't use the XMLDBEditor nor the prev/next stuff at all (Mahara and others)
-// Uncomment these if you're lazy like Penny
+// Uncomment if you want to allow empty comments when modifying install.xml files.
// $CFG->xmldbdisablecommentchecking = true; // NOT FOR PRODUCTION SERVERS!
-// $CFG->xmldbdisablenextprevchecking = true; // NOT FOR PRODUCTION SERVERS!
-//
-// Special magic - evil developer only wanting to edit xmldb files manually
-// AND allowing the XMLDBEditor to reconstruct the prev/next elements every
-// time one file is loaded and saved (Moodle).
-// Uncomment this if you're lazy like Petr
-// $CFG->xmldbreconstructprevnext = true; // NOT FOR PRODUCTION SERVERS!
//
// Since 2.0 sql queries are not shown during upgrade by default.
// Please note that this setting may produce very long upgrade page on large sites.
// 'otherplugin' => array('mysetting' => 'myvalue', 'thesetting' => 'thevalue'));
//
//=========================================================================
-// 9. PHPUNIT SUPPORT
+// 10. PHPUNIT SUPPORT
//=========================================================================
// $CFG->phpunit_prefix = 'phpu_';
// $CFG->phpunit_dataroot = '/home/example/phpu_moodledata';
// $CFG->phpunit_directorypermissions = 02777; // optional
+//
+//=========================================================================
+// 11. BEHAT SUPPORT
+//=========================================================================
+// Behat uses http://localhost:8000 as default URL to run
+// the acceptance tests, you can override this value.
+// Example:
+// $CFG->behat_wwwroot = 'http://192.168.1.250:8000';
+//
+// You can override default Moodle configuration for Behat and add your own
+// params; here you can add more profiles, use different Mink drivers than Selenium...
+// These params would be merged with the default Moodle behat.yml, giving priority
+// to the ones specified here. The array format is YAML, following the Behat
+// params hierarchy. More info: http://docs.behat.org/guides/7.config.html
+// Example:
+// $CFG->behat_config = array(
+// 'default' => array(
+// 'formatter' => array(
+// 'name' => 'pretty',
+// 'parameters' => array(
+// 'decorated' => true,
+// 'verbose' => false
+// )
+// )
+// )
+// );
+//
+// You can completely switch to test environment when "php admin/tool/behat/cli/util --enable",
+// this means that all the site accesses will be routed to the test environment instead of
+// the regular one, so NEVER USE THIS SETTING IN PRODUCTION SITES. This setting is useful
+// when working with cloud CI (continous integration) servers which requires public sites to run the
+// tests, or in testing/development installations when you are developing in a pre-PHP 5.4 server.
+// Note that with this setting enabled $CFG->behat_wwwroot is ignored and $CFG->behat_wwwroot
+// value will be the regular $CFG->wwwroot value.
+// Example:
+// $CFG->behat_switchcompletely = true;
+//
//=========================================================================
// ALL DONE! To continue installation, visit your main page with a browser
echo '<form id="movecourses" action="category.php" method="post"><div>';
echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
- echo '<table border="0" cellspacing="2" cellpadding="4" class="generalbox boxaligncenter"><tr>';
+ echo '<table border="0" cellspacing="2" cellpadding="4" class="generaltable boxaligncenter"><tr>';
echo '<th class="header" scope="col">'.get_string('courses').'</th>';
if ($editingon) {
echo '<th class="header" scope="col">'.get_string('edit').'</th>';
echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
$table = new html_table();
- $table->classes = array('logtable','generalbox');
+ $table->classes = array('logtable','generaltable');
$table->align = array('right', 'left', 'left');
$table->head = array(
get_string('time'),
// NOTE: this is legacy stuff, module subtypes are very strongly discouraged!!
$gettypesfunc = $modname.'_get_types';
if (function_exists($gettypesfunc)) {
- if ($types = $gettypesfunc()) {
+ $types = $gettypesfunc();
+ if (is_array($types) && count($types) > 0) {
$group = new stdClass();
$group->name = $modname;
$group->icon = $OUTPUT->pix_icon('icon', '', $modname, array('class' => 'icon'));
$module->archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
$modlist[$course->id][$modname] = $module;
}
- $return[$modname] = $modlist[$course->id][$modname];
+ if (isset($modlist[$course->id][$modname])) {
+ $return[$modname] = $modlist[$course->id][$modname];
+ } else {
+ debugging("Invalid module metadata configuration for {$modname}");
+ }
}
return $return;
)), null, true);
}
- // Include blocks dragdrop
- $params = array(
- 'courseid' => $course->id,
- 'pagetype' => $PAGE->pagetype,
- 'pagelayout' => $PAGE->pagelayout,
- 'subpage' => $PAGE->subpage,
- 'regions' => $PAGE->blocks->get_regions(),
- );
- $PAGE->requires->yui_module('moodle-core-blocks', 'M.core_blocks.init_dragdrop', array($params), null, true);
-
// Require various strings for the command toolbox
$PAGE->requires->strings_for_js(array(
'moveleft',
$defaults = array ('reset_events'=>1, 'reset_logs'=>1, 'reset_roles_local'=>1, 'reset_gradebook_grades'=>1, 'reset_notes'=>1);
+ // Set student as default in unenrol user list, if role with student archetype exist.
+ if ($studentrole = get_archetype_roles('student')) {
+ $defaults['unenrol_users'] = array_keys($studentrole);
+ }
+
if ($allmods = $DB->get_records('modules') ) {
foreach ($allmods as $mod) {
$modname = $mod->name;
} else if (!empty($blocklist) and confirm_sesskey()) {
echo "<input type=\"hidden\" name=\"blocklist\" value=\"$blocklist\" /></div>\n";
}
- echo "<table border=\"0\" cellspacing=\"2\" cellpadding=\"4\" class=\"generalbox boxaligncenter\">\n<tr>\n";
+ echo "<table border=\"0\" cellspacing=\"2\" cellpadding=\"4\" class=\"generaltable boxaligncenter\">\n<tr>\n";
echo "<th scope=\"col\">$strcourses</th>\n";
echo "<th scope=\"col\">$strcategory</th>\n";
echo "<th scope=\"col\">$strselect</th>\n";
--- /dev/null
+@core_course
+Feature: Add activities to courses
+ In order to provide tools for students learning
+ As a teacher
+ I need to add activites to a course
+
+ @javascript
+ Scenario: Add an activity to the 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"
+ When I turn editing mode on
+ And I add a "Database" to section "3" and I fill the form with:
+ | Name | Test name |
+ | Introduction | Test database description |
+ | Required entries | 9 |
+ | Comments | Yes |
+ | ID number | ASD123 |
+ Then I should not see "Adding a new"
+ And I follow "Test name"
+ And I follow "Edit settings"
+ And the "Name" field should match "Test name" value
+ And the "Required entries" field should match "9" value
+ And the "Comments" field should match "Yes" value
+ And the "ID number" field should match "ASD123" value
+
+ @javascript
+ Scenario: Add an activity without the required fields
+ 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"
+ When I turn editing mode on
+ And I add a "Database" to section "3" and I fill the form with:
+ | Name | Test name |
+ Then I should see "Adding a new"
+ And I should see "Required"
--- /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 course-related steps definitions.
+ *
+ * @package core_course
+ * @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,
+ Behat\Gherkin\Node\TableNode as TableNode;
+
+/**
+ * Course-related steps definitions.
+ *
+ * @package core_course
+ * @category test
+ * @copyright 2012 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class behat_course extends behat_base {
+
+ /**
+ * Turns editing mode on.
+ * @Given /^I turn editing mode on$/
+ */
+ public function i_turn_editing_mode_on() {
+ return new Given('I press "Turn editing on"');
+ }
+
+ /**
+ * Turns editing mode off.
+ * @Given /^I turn editing mode off$/
+ */
+ public function i_turn_editing_mode_off() {
+ return new Given('I press "Turn editing off"');
+ }
+
+ /**
+ * Adds the selected activity/resource filling the form data with the specified field/value pairs.
+ *
+ * @When /^I add a "(?P<activity_or_resource_string>(?:[^"]|\\")*)" to section "(?P<section_number>\d+)" and I fill the form with:$/
+ * @param string $activity The activity name
+ * @param string $section The section number
+ * @param TableNode $data The activity field/value data
+ */
+ public function i_add_to_section_and_i_fill_the_form_with($activity, $section, TableNode $data) {
+
+ $activity = $this->fixStepArgument($activity);
+ $section = $this->fixStepArgument($section);
+
+ // The 'I wait until the page is ready' is just in case.
+ return array(
+ new Given('I add a "'.$activity.'" to section "'.$section.'"'),
+ new Given('I fill the moodle form with:', $data),
+ new Given('I press "Save and return to course"')
+ );
+ }
+
+ /**
+ * Opens the activity chooser and opens the activity/resource form page.
+ *
+ * @Given /^I add a "(?P<activity_or_resource_string>(?:[^"]|\\")*)" to section "(?P<section_number>\d+)"$/
+ * @param string $activity
+ * @param string $section
+ */
+ public function i_add_to_section($activity, $section) {
+
+ $activity = $this->fixStepArgument($activity);
+ $section = $this->fixStepArgument($section);
+
+ // Clicks add activity or resource section link.
+ $sectionxpath = "//*[@id='section-" . $section . "']/*/*/*/div[@class='section-modchooser']/*/*";
+ $section = $this->getSession()->getPage()->find('xpath', $sectionxpath);
+ $section->click();
+
+ // Clicks the selected activity if it exists.
+ $activityxpath = ".//label[contains(.,'" . $activity . "')]/input";
+ $activity = $this->getSession()->getPage()->find('xpath', $activityxpath);
+ $activity->doubleClick();
+ }
+
+}
$this->resetAfterTest(true);
+ // Enable course completion.
+ set_config('enablecompletion', 1);
+
// Set the required capabilities by the external function
$contextid = context_system::instance()->id;
$roleid = $this->assignUserCapability('moodle/course:create', $contextid);
foreach ($course3options as $key => $value) {
$course3['courseformatoptions'][] = array('name' => $key, 'value' => $value);
}
- $courses = array($course1, $course2);
+ $courses = array($course1, $course2, $course3);
$createdcourses = core_course_external::create_courses($courses);
$createdcourses = external_api::clean_returnvalue(core_course_external::create_courses_returns(), $createdcourses);
// Check that right number of courses were created.
- $this->assertEquals(2, count($createdcourses));
+ $this->assertEquals(3, count($createdcourses));
// Check that the courses were correctly created.
foreach ($createdcourses as $createdcourse) {
$this->assertEquals($courseinfo->theme, $course2['forcetheme']);
}
- if (completion_info::is_enabled_for_site()) {
- $this->assertEquals($courseinfo->enablecompletion, $course2['enabledcompletion']);
- $this->assertEquals($courseinfo->completionstartonenrol, $course2['completionstartonenrol']);
- } else {
- $this->assertEquals($courseinfo->enablecompletion, 0);
- $this->assertEquals($courseinfo->completionstartonenrol, 0);
- }
+ // We enabled completion at the beginning of the test.
+ $this->assertEquals($courseinfo->enablecompletion, $course2['enablecompletion']);
+ $this->assertEquals($courseinfo->completionstartonenrol, $course2['completionstartonenrol']);
} else if ($createdcourse['shortname'] == $course1['shortname']) {
$courseconfig = get_config('moodlecourse');
// Cancel the edit if we lose focus or the escape key is pressed
thisevent = editor.on('blur', cancel_edittitle);
listenevents.push(thisevent);
- thisevent = Y.one('document').on('keyup', function(e) {
- if (e.keyCode == 27) {
+ thisevent = Y.one('document').on('keydown', function(e) {
+ if (e.keyCode === 27) {
+ e.preventDefault();
cancel_edittitle(e);
}
});
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
- <TABLE NAME="enrol_authorize" COMMENT="Holds all known information about authorize.net transactions" NEXT="enrol_authorize_refunds">
+ <TABLE NAME="enrol_authorize" COMMENT="Holds all known information about authorize.net transactions">
<FIELDS>
- <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="paymentmethod"/>
- <FIELD NAME="paymentmethod" TYPE="char" LENGTH="6" NOTNULL="true" DEFAULT="cc" SEQUENCE="false" PREVIOUS="id" NEXT="refundinfo"/>
- <FIELD NAME="refundinfo" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="paymentmethod" NEXT="ccname"/>
- <FIELD NAME="ccname" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="refundinfo" NEXT="courseid"/>
- <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="ccname" NEXT="instanceid"/>
- <FIELD NAME="instanceid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="courseid" NEXT="userid"/>
- <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="instanceid" NEXT="transid"/>
- <FIELD NAME="transid" TYPE="int" LENGTH="20" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="userid" NEXT="status"/>
- <FIELD NAME="status" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="transid" NEXT="timecreated"/>
- <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="status" NEXT="settletime"/>
- <FIELD NAME="settletime" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timecreated" NEXT="amount"/>
- <FIELD NAME="amount" TYPE="char" LENGTH="10" NOTNULL="true" SEQUENCE="false" PREVIOUS="settletime" NEXT="currency"/>
- <FIELD NAME="currency" TYPE="char" LENGTH="3" NOTNULL="true" DEFAULT="USD" SEQUENCE="false" PREVIOUS="amount"/>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+ <FIELD NAME="paymentmethod" TYPE="char" LENGTH="6" NOTNULL="true" DEFAULT="cc" SEQUENCE="false"/>
+ <FIELD NAME="refundinfo" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="ccname" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="instanceid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="transid" TYPE="int" LENGTH="20" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="status" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="settletime" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="amount" TYPE="char" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="currency" TYPE="char" LENGTH="3" NOTNULL="true" DEFAULT="USD" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
<INDEXES>
- <INDEX NAME="courseid" UNIQUE="false" FIELDS="courseid" NEXT="userid"/>
- <INDEX NAME="userid" UNIQUE="false" FIELDS="userid" PREVIOUS="courseid" NEXT="status"/>
- <INDEX NAME="status" UNIQUE="false" FIELDS="status" PREVIOUS="userid" NEXT="transid"/>
- <INDEX NAME="transid" UNIQUE="false" FIELDS="transid" PREVIOUS="status"/>
+ <INDEX NAME="courseid" UNIQUE="false" FIELDS="courseid"/>
+ <INDEX NAME="userid" UNIQUE="false" FIELDS="userid"/>
+ <INDEX NAME="status" UNIQUE="false" FIELDS="status"/>
+ <INDEX NAME="transid" UNIQUE="false" FIELDS="transid"/>
</INDEXES>
</TABLE>
- <TABLE NAME="enrol_authorize_refunds" COMMENT="Authorize.net refunds" PREVIOUS="enrol_authorize">
+ <TABLE NAME="enrol_authorize_refunds" COMMENT="Authorize.net refunds">
<FIELDS>
- <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="orderid"/>
- <FIELD NAME="orderid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="status"/>
- <FIELD NAME="status" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="orderid" NEXT="amount"/>
- <FIELD NAME="amount" TYPE="char" LENGTH="10" NOTNULL="true" SEQUENCE="false" PREVIOUS="status" NEXT="transid"/>
- <FIELD NAME="transid" TYPE="int" LENGTH="20" NOTNULL="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="amount" NEXT="settletime"/>
- <FIELD NAME="settletime" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="transid"/>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+ <FIELD NAME="orderid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="status" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="amount" TYPE="char" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="transid" TYPE="int" LENGTH="20" NOTNULL="false" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="settletime" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
- <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="orderid"/>
- <KEY NAME="orderid" TYPE="foreign" FIELDS="orderid" REFTABLE="enrol_authorize" REFFIELDS="id" COMMENT="Reference to enrol_authorize.id" PREVIOUS="primary"/>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ <KEY NAME="orderid" TYPE="foreign" FIELDS="orderid" REFTABLE="enrol_authorize" REFFIELDS="id" COMMENT="Reference to enrol_authorize.id"/>
</KEYS>
<INDEXES>
<INDEX NAME="transid" UNIQUE="false" FIELDS="transid"/>
if (!enrol_is_enabled('database')) {
return true;
}
- if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
+ if (!$this->get_config('dbtype') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
return true;
}
global $CFG, $DB;
// We do not create courses here intentionally because it requires full sync and is slow.
- if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
+ if (!$this->get_config('dbtype') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
return;
}
global $CFG, $DB;
// We do not create courses here intentionally because it requires full sync and is slow.
- if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
+ if (!$this->get_config('dbtype') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
$trace->output('User enrolment synchronisation skipped.');
$trace->finished();
return 0;
global $CFG, $DB;
// Make sure we sync either enrolments or courses.
- if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('newcoursetable') or !$this->get_config('newcoursefullname') or !$this->get_config('newcourseshortname')) {
+ if (!$this->get_config('dbtype') or !$this->get_config('newcoursetable') or !$this->get_config('newcoursefullname') or !$this->get_config('newcourseshortname')) {
$trace->output('Course synchronisation skipped.');
$trace->finished();
return 0;
<TABLES>
<TABLE NAME="enrol_flatfile" COMMENT="enrol_flatfile table retrofitted from MySQL">
<FIELDS>
- <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="action"/>
- <FIELD NAME="action" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="roleid"/>
- <FIELD NAME="roleid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" PREVIOUS="action" NEXT="userid"/>
- <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" PREVIOUS="roleid" NEXT="courseid"/>
- <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" PREVIOUS="userid" NEXT="timestart"/>
- <FIELD NAME="timestart" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="courseid" NEXT="timeend"/>
- <FIELD NAME="timeend" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timestart" NEXT="timemodified"/>
- <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timeend"/>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+ <FIELD NAME="action" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="roleid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="timestart" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="timeend" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
- <KEY NAME="id" TYPE="primary" FIELDS="id" COMMENT="primary key" NEXT="courseid-id"/>
- <KEY NAME="courseid-id" TYPE="foreign" FIELDS="courseid" REFTABLE="course" REFFIELDS="id" PREVIOUS="id" NEXT="userid-id"/>
- <KEY NAME="userid-id" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" PREVIOUS="courseid-id" NEXT="roleid-id"/>
- <KEY NAME="roleid-id" TYPE="foreign" FIELDS="roleid" REFTABLE="role" REFFIELDS="id" PREVIOUS="userid-id"/>
+ <KEY NAME="id" TYPE="primary" FIELDS="id" COMMENT="primary key"/>
+ <KEY NAME="courseid-id" TYPE="foreign" FIELDS="courseid" REFTABLE="course" REFFIELDS="id"/>
+ <KEY NAME="userid-id" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
+ <KEY NAME="roleid-id" TYPE="foreign" FIELDS="roleid" REFTABLE="role" REFFIELDS="id"/>
</KEYS>
</TABLE>
</TABLES>
}
}
- /**
- * Do any enrolment expiration processing.
- *
- * @param progress_trace $trace
- * @return bool true if any data processed, false if not
- */
- protected function process_expirations(progress_trace $trace) {
- global $DB;
-
- //TODO: this method should be moved to parent class once we refactor all existing enrols, see MDL-36504.
-
- $processed = false;
- $name = $this->get_name();
-
- // Deal with expired accounts.
- $action = $this->get_config('expiredaction', ENROL_EXT_REMOVED_KEEP);
-
- if ($action == ENROL_EXT_REMOVED_UNENROL) {
- $instances = array();
- $sql = "SELECT ue.*, e.courseid, c.id AS contextid
- FROM {user_enrolments} ue
- JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = :enrol)
- JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)
- WHERE ue.timeend > 0 AND ue.timeend < :now";
- $params = array('now'=>time(), 'courselevel'=>CONTEXT_COURSE, 'enrol'=>$name);
-
- $rs = $DB->get_recordset_sql($sql, $params);
- foreach ($rs as $ue) {
- if (!$processed) {
- $trace->output("Starting processing of enrol_$name expirations...");
- $processed = true;
- }
- if (empty($instances[$ue->enrolid])) {
- $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
- }
- $instance = $instances[$ue->enrolid];
- if (!$this->roles_protected()) {
- // Let's just guess what extra roles are supposed to be removed.
- if ($instance->roleid) {
- role_unassign($instance->roleid, $ue->userid, $ue->contextid);
- }
- }
- // The unenrol cleans up all subcontexts if this is the only course enrolment for this user.
- $this->unenrol_user($instance, $ue->userid);
- $trace->output("Unenrolling expired user $ue->userid from course $instance->courseid", 1);
- }
- $rs->close();
- unset($instances);
-
- } else if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
- $instances = array();
- $sql = "SELECT ue.*, e.courseid, c.id AS contextid
- FROM {user_enrolments} ue
- JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = :enrol)
- JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)
- WHERE ue.timeend > 0 AND ue.timeend < :now
- AND ue.status = :useractive";
- $params = array('now'=>time(), 'courselevel'=>CONTEXT_COURSE, 'useractive'=>ENROL_USER_ACTIVE, 'enrol'=>$name);
- $rs = $DB->get_recordset_sql($sql, $params);
- foreach ($rs as $ue) {
- if (!$processed) {
- $trace->output("Starting processing of enrol_$name expirations...");
- $processed = true;
- }
- if (empty($instances[$ue->enrolid])) {
- $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
- }
- $instance = $instances[$ue->enrolid];
-
- if (!$this->roles_protected()) {
- // Let's just guess what roles should be removed.
- $count = $DB->count_records('role_assignments', array('userid'=>$ue->userid, 'contextid'=>$ue->contextid));
- if ($count == 1) {
- role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'', 'itemid'=>0));
-
- } else if ($count > 1 and $instance->roleid) {
- role_unassign($instance->roleid, $ue->userid, $ue->contextid, '', 0);
- }
- }
- // In any case remove all roles that belong to this instance and user.
- role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'enrol_'.$name, 'itemid'=>$instance->id), true);
- // Final cleanup of subcontexts if there are no more course roles.
- if (0 == $DB->count_records('role_assignments', array('userid'=>$ue->userid, 'contextid'=>$ue->contextid))) {
- role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'', 'itemid'=>0), true);
- }
-
- $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
- $trace->output("Suspending expired user $ue->userid in course $instance->courseid", 1);
- }
- $rs->close();
- unset($instances);
-
- } else {
- // ENROL_EXT_REMOVED_KEEP means no changes.
- }
-
- if ($processed) {
- $trace->output("...finished processing of enrol_$name expirations");
- } else {
- $trace->output("No expired enrol_$name enrolments detected");
- }
- $trace->finished();
-
- return $processed;
- }
-
/**
* Returns the user who is responsible for flatfile enrolments in given curse.
*
// Make sure we can connect the server.
$debuginfo = '';
if (!$connection = ldap_connect_moodle(TEST_ENROL_LDAP_HOST_URL, 3, 'rfc2307', TEST_ENROL_LDAP_BIND_DN, TEST_ENROL_LDAP_BIND_PW, LDAP_DEREF_NEVER, $debuginfo, false)) {
- $this->markTestSkipped('Can not connect to LDAP test server.');
+ $this->markTestSkipped('Can not connect to LDAP test server: '.$debuginfo);
}
$this->enable_plugin();
<TABLES>
<TABLE NAME="enrol_paypal" COMMENT="Holds all known information about PayPal transactions">
<FIELDS>
- <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="business"/>
- <FIELD NAME="business" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="receiver_email"/>
- <FIELD NAME="receiver_email" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="business" NEXT="receiver_id"/>
- <FIELD NAME="receiver_id" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="receiver_email" NEXT="item_name"/>
- <FIELD NAME="item_name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="receiver_id" NEXT="courseid"/>
- <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="item_name" NEXT="userid"/>
- <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="courseid" NEXT="instanceid"/>
- <FIELD NAME="instanceid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="userid" NEXT="memo"/>
- <FIELD NAME="memo" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="instanceid" NEXT="tax"/>
- <FIELD NAME="tax" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="memo" NEXT="option_name1"/>
- <FIELD NAME="option_name1" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="tax" NEXT="option_selection1_x"/>
- <FIELD NAME="option_selection1_x" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="option_name1" NEXT="option_name2"/>
- <FIELD NAME="option_name2" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="option_selection1_x" NEXT="option_selection2_x"/>
- <FIELD NAME="option_selection2_x" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="option_name2" NEXT="payment_status"/>
- <FIELD NAME="payment_status" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="option_selection2_x" NEXT="pending_reason"/>
- <FIELD NAME="pending_reason" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="payment_status" NEXT="reason_code"/>
- <FIELD NAME="reason_code" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false" PREVIOUS="pending_reason" NEXT="txn_id"/>
- <FIELD NAME="txn_id" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="reason_code" NEXT="parent_txn_id"/>
- <FIELD NAME="parent_txn_id" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="txn_id" NEXT="payment_type"/>
- <FIELD NAME="payment_type" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false" PREVIOUS="parent_txn_id" NEXT="timeupdated"/>
- <FIELD NAME="timeupdated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="payment_type"/>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+ <FIELD NAME="business" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="receiver_email" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="receiver_id" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="item_name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="instanceid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+ <FIELD NAME="memo" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="tax" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="option_name1" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="option_selection1_x" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="option_name2" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="option_selection2_x" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="payment_status" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="pending_reason" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="reason_code" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="txn_id" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="parent_txn_id" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="payment_type" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="timeupdated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>