// Run automated backups if required.
require_once($CFG->dirroot.'/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot.'/backup/util/helper/backup_cron_helper.class.php');
-backup_cron_automated_helper::run_automated_backup(backup_cron_automated_helper::RUN_IMMEDIATLY);
+backup_cron_automated_helper::run_automated_backup(backup_cron_automated_helper::RUN_IMMEDIATELY);
mtrace("Automated cron backups completed correctly");
}
}
+ /**
+ * Return the site secret for a given hub
+ * site identifier is assigned to Mooch
+ * each hub has a unique and personal site secret.
+ * @param string $huburl
+ * @return string site secret
+ */
+ public function get_site_secret_for_hub($huburl) {
+ global $DB;
+
+ $existingregistration = $DB->get_record('registration_hubs',
+ array('huburl' => $huburl));
+
+ if (!empty($existingregistration)) {
+ return $existingregistration->secret;
+ }
+
+ if ($huburl == HUB_MOODLEORGHUBURL) {
+ $siteidentifier = get_site_identifier();
+ } else {
+ $siteidentifier = random_string(32) . $_SERVER['HTTP_HOST'];
+ }
+
+ return $siteidentifier;
+
+ }
+
/**
* When the site register on a hub, he must call this function
* @param object $hub where the site is registered on
throw new moodle_exception('missingparameter');
}
-/* TO DO
- if DB config plugin table is not good for dealing with token reference and token confirmation
- => create other DB table
- -----------------------------------------------------------------------------
- Local Type | Token | Local WS | Remote Type | Remote URL | Confirmed
- -----------------------------------------------------------------------------
- HUB 4er4e server HUB-DIRECTORY http...moodle.org Yes
- HUB 73j53 client HUB-DIRECTORY http...moodle.org Yes
- SITE dfsd7 server HUB http...hub Yes
- SITE fd8fd client HUB http...hub Yes
- HUB ds78s server SITE http...site.com Yes
- HUB-DIR. d7d8s server HUB http...hub Yes
- -----------------------------------------------------------------------------
- */
-
$registrationmanager = new registration_manager();
$registeredhub = $registrationmanager->get_registeredhub($huburl);
if (empty($unconfirmedhub)) {
//we save the token into the communication table in order to have a reference
$unconfirmedhub = new stdClass();
- $unconfirmedhub->token = get_site_identifier();
+ $unconfirmedhub->token = $registrationmanager->get_site_secret_for_hub($huburl);
+ $unconfirmedhub->secret = $unconfirmedhub->token;
$unconfirmedhub->huburl = $huburl;
$unconfirmedhub->hubname = $hubname;
$unconfirmedhub->confirmed = 0;
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// This file is part of Moodle - http://moodle.org/ //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// //
+// 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/>. //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+/*
+ * @package moodle
+ * @subpackage registration
+ * @author Jerome Mouneyrac <jerome@mouneyrac.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL
+ * @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
+ *
+ * The administrator is redirect to this page from the hub to renew a registration
+ * process because
+ */
+
+require('../../config.php');
+require_once($CFG->libdir . '/adminlib.php');
+require_once($CFG->dirroot . '/' . $CFG->admin . '/registration/lib.php');
+
+$url = optional_param('url', '', PARAM_URL);
+$hubname = optional_param('hubname', '', PARAM_TEXT);
+$token = optional_param('token', '', PARAM_TEXT);
+
+admin_externalpage_setup('registrationindex');
+
+//check that we are waiting a confirmation from this hub, and check that the token is correct
+$registrationmanager = new registration_manager();
+$registeredhub = $registrationmanager->get_unconfirmedhub($url);
+if (!empty($registeredhub) and $registeredhub->token == $token) {
+
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading(get_string('renewregistration', 'hub'), 3, 'main');
+ $hublink = html_writer::tag('a', $hubname, array('href' => $url));
+
+ $registrationmanager->delete_registeredhub($url);
+
+ //Mooch case, need to recreate the siteidentifier
+ if ($url == HUB_MOODLEORGHUBURL) {
+ $CFG->siteidentifier = null;
+ get_site_identifier();
+ }
+
+ $deletedregmsg = get_string('previousregistrationdeleted', 'hub', $hublink);
+
+ $button = new single_button(new moodle_url('/admin/registration/index.php'),
+ get_string('restartregistration', 'hub'));
+ $button->class = 'restartregbutton';
+
+ echo html_writer::tag('div', $deletedregmsg . $OUTPUT->render($button),
+ array('class' => 'mdl-align'));
+
+ echo $OUTPUT->footer();
+} else {
+ throw new moodle_exception('wrongtoken', 'hub',
+ $CFG->wwwroot . '/' . $CFG->admin . '/registration/index.php');
+}
+
+
$options[99] = get_string('timezonenotforced', 'admin');
$temp->add(new admin_setting_configselect('forcetimezone', get_string('forcetimezone', 'admin'), get_string('helpforcetimezone', 'admin'), 99, $options));
$temp->add(new admin_settings_country_select('country', get_string('country', 'admin'), get_string('configcountry', 'admin'), 0));
+ $temp->add(new admin_setting_configtext('defaultcity', get_string('defaultcity', 'admin'), get_string('defaultcity_help', 'admin'), ''));
$temp->add(new admin_setting_heading('iplookup', get_string('iplookup', 'admin'), get_string('iplookupinfo', 'admin')));
$temp->add(new admin_setting_configfile('geoipfile', get_string('geoipfile', 'admin'), get_string('configgeoipfile', 'admin', $CFG->dataroot.'/geoip/'), $CFG->dataroot.'/geoip/GeoLiteCity.dat'));
$ADMIN->add('server', new admin_externalpage('adminregistration', get_string('registration','admin'), "$CFG->wwwroot/$CFG->admin/registration/index.php"));
+$ADMIN->add('root', new admin_externalpage('bloglevelupgrade', get_string('bloglevelupgrade', 'admin'), $CFG->wwwroot.'/'.$CFG->admin.'/blocklevelupgrade.php', 'moodle/site:config', true));
} // end of speedup
$mform->addElement('text', 'city', get_string('city'), 'maxlength="100" size="25"');
$mform->setType('city', PARAM_MULTILANG);
- $mform->setDefault('city', $templateuser->city);
+ if (empty($CFG->defaultcity)) {
+ $mform->setDefault('city', $templateuser->city);
+ } else {
+ $mform->setDefault('city', $CFG->defaultcity);
+ }
$mform->addElement('select', 'country', get_string('selectacountry'), get_string_manager()->get_list_of_countries());
- $mform->setDefault('country', $templateuser->country);
+ if (empty($CFG->country)) {
+ $mform->setDefault('country', $templateuser->country);
+ } else {
+ $mform->setDefault('country', $CFG->country);
+ }
$mform->setAdvanced('country');
$choices = get_list_of_timezones();
// set to defaults if undefined
if (!isset($config->auth_instructions) or empty($config->user_attribute)) {
- $config->auth_instructions = get_string('shibboleth_instructions', 'auth', $CFG->wwwroot.'/auth/shibboleth/index.php');
+ $config->auth_instructions = get_string('auth_shib_instructions', 'auth_shibboleth', $CFG->wwwroot.'/auth/shibboleth/index.php');
}
if (!isset ($config->user_attribute)) {
$config->user_attribute = '';
} else {
// Check if integrated WAYF was enabled and is now turned off
// If it was and only then, reset the Moodle alternate URL
- if ($this->config->alt_login == 'on'){
+ if (isset($this->config->alt_login) and $this->config->alt_login == 'on'){
set_config('alt_login', 'off', 'auth/shibboleth');
set_config('alternateloginurl', '');
}
--- /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/>.
+
+/**
+ * @package moodlecore
+ * @subpackage backup-moodle2
+ * @copyright 2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Class extending standard backup_plugin in order to implement some
+ * helper methods related with the course formats (format plugin)
+ *
+ * TODO: Finish phpdocs
+ */
+abstract class backup_format_plugin extends backup_plugin {
+
+ protected $courseformat; // To store the format (course->format) of the instance
+
+ public function __construct($plugintype, $pluginname, $optigroup, $step) {
+
+ parent::__construct($plugintype, $pluginname, $optigroup, $step);
+
+ $this->courseformat = backup_plan_dbops::get_courseformat_from_courseid($this->task->get_courseid());
+
+ }
+
+ /**
+ * Return the condition encapsulated into sqlparam format
+ * to get evaluated by value, not by path nor processor setting
+ */
+ protected function get_format_condition() {
+ return array('sqlparam' => $this->courseformat);
+ }
+}
require_once($CFG->dirroot . '/backup/moodle2/backup_xml_transformer.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_subplugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_settingslib.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_stepslib.php');
protected $pluginname;
protected $connectionpoint;
protected $optigroup; // Optigroup, parent of all optigroup elements
+ protected $step;
+ protected $task;
- public function __construct($plugintype, $pluginname, $optigroup) {
+ public function __construct($plugintype, $pluginname, $optigroup, $step) {
$this->plugintype = $plugintype;
$this->pluginname = $pluginname;
- $this->optigroup = $optigroup;
+ $this->optigroup = $optigroup;
$this->connectionpoint = '';
+ $this->step = $step;
+ $this->task = $step->get_task();
}
public function define_plugin_structure($connectionpoint) {
}
}
+// Protected API starts here
+
+// backup_step/structure_step/task wrappers
+
+ /**
+ * Returns the value of one (task/plan) setting
+ */
+ protected function get_setting_value($name) {
+ if (is_null($this->task)) {
+ throw new backup_step_exception('not_specified_backup_task');
+ }
+ return $this->task->get_setting_value($name);
+ }
+
+// end of backup_step/structure_step/task wrappers
+
/**
* Factory method that will return one backup_plugin_element (backup_optigroup_element)
* with its name automatically calculated, based one the plugin being handled (type, name)
$backupfile = $subpluginsdir . '/backup/moodle2/' . $classname . '.class.php';
if (file_exists($backupfile)) {
require_once($backupfile);
- $backupsubplugin = new $classname($subplugintype, $name, $optigroup);
+ $backupsubplugin = new $classname($subplugintype, $name, $optigroup, $this);
// Add subplugin returned structure to optigroup
$backupsubplugin->define_subplugin_structure($element->get_name());
}
}
}
+ /**
+ * As far as activity backup steps are implementing backup_subplugin stuff, they need to
+ * have the parent task available for wrapping purposes (get course/context....)
+ */
+ public function get_task() {
+ return $this->task;
+ }
+
/**
* Wraps any activity backup structure within the common 'activity' element
* that will include common to all activities information like id, context...
$availability = new backup_nested_element('availability', array('id'), array(
'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
+ // attach format plugin structure to $module element, only one allowed
+ $this->add_plugin_structure('format', $module, false);
+
// Define the tree
$module->add_child($availinfo);
$availinfo->add_child($availability);
$section = new backup_nested_element('section', array('id'), array(
'number', 'name', 'summary', 'summaryformat', 'sequence', 'visible'));
+ // attach format plugin structure to $section element, only one allowed
+ $this->add_plugin_structure('format', $section, false);
+
// Define sources
$section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
$module = new backup_nested_element('module', array(), array('modulename'));
+ // attach format plugin structure to $course element, only one allowed
+ $this->add_plugin_structure('format', $course, false);
+
// Build the tree
$course->add_child($category);
protected $subpluginname;
protected $connectionpoint;
protected $optigroup; // Optigroup, parent of all optigroup elements
+ protected $step;
+ protected $task;
- public function __construct($subplugintype, $subpluginname, $optigroup) {
+ public function __construct($subplugintype, $subpluginname, $optigroup, $step) {
$this->subplugintype = $subplugintype;
$this->subpluginname = $subpluginname;
$this->optigroup = $optigroup;
$this->connectionpoint = '';
+ $this->step = $step;
+ $this->task = $step->get_task();
}
public function define_subplugin_structure($connectionpoint) {
}
}
+// Protected API starts here
+
+// backup_step/structure_step/task wrappers
+
+ /**
+ * Returns the value of one (task/plan) setting
+ */
+ protected function get_setting_value($name) {
+ if (is_null($this->task)) {
+ throw new backup_step_exception('not_specified_backup_task');
+ }
+ return $this->task->get_setting_value($name);
+ }
+
+// end of backup_step/structure_step/task wrappers
+
/**
* Factory method that will return one backup_subplugin_element (backup_optigroup_element)
* with its name automatically calculated, based one the subplugin being handled (type, name)
--- /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/>.
+
+/**
+ * @package moodlecore
+ * @subpackage backup-moodle2
+ * @copyright 2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Class extending standard restore_plugin in order to implement some
+ * helper methods related with the course formats (format plugin)
+ *
+ * TODO: Finish phpdocs
+ */
+abstract class restore_format_plugin extends restore_plugin {
+ // Love these classes. :-) Nothing special to customize here for now
+}
require_once($CFG->dirroot . '/backup/moodle2/restore_default_block_task.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_qtype_plugin.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/restore_format_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_subplugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_settingslib.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_stepslib.php');
return $this->step->apply_date_offset($value);
}
+ /**
+ * Returns the value of one (task/plan) setting
+ */
+ protected function get_setting_value($name) {
+ if (is_null($this->task)) {
+ throw new restore_step_exception('not_specified_restore_task');
+ }
+ return $this->task->get_setting_value($name);
+ }
+
+// end of restore_step/structure_step/task wrappers
+
/**
* Simple helper function that returns the name for the restore_path_element
* It's not mandatory to use it but recommended ;-)
protected $info; // info related to section gathered from backup file
protected $contextid; // course context id
+ protected $sectionid; // new (target) id of the course section
/**
* Constructor - instantiates one object of this class
*/
public function __construct($name, $info, $plan = null) {
$this->info = $info;
+ $this->sectionid = 0;
parent::__construct($name, $plan);
}
return $this->get_basepath() . '/sections/section_' . $this->info->sectionid;
}
+ public function set_sectionid($sectionid) {
+ $this->sectionid = $sectionid;
+ }
+
public function get_contextid() {
return $this->contextid;
}
+ public function get_sectionid() {
+ return $this->sectionid;
+ }
+
/**
* Create all the steps that will be part of this task
*/
$params = array('courseid' => $this->get_courseid(), 'grname' => $data->name);
if (!empty($data->description)) {
$description_clause = ' AND ' .
- $DB->sql_compare_text('description') . ' = ' . $DB->sql_compare_text(':desc');
- $params['desc'] = $data->description;
+ $DB->sql_compare_text('description') . ' = ' . $DB->sql_compare_text(':description');
+ $params['description'] = $data->description;
}
if (!$groupdb = $DB->get_record_sql("SELECT *
FROM {groups}
$params = array('courseid' => $this->get_courseid(), 'grname' => $data->name);
if (!empty($data->description)) {
$description_clause = ' AND ' .
- $DB->sql_compare_text('description') . ' = ' . $DB->sql_compare_text(':desc');
- $params['desc'] = $data->description;
+ $DB->sql_compare_text('description') . ' = ' . $DB->sql_compare_text(':description');
+ $params['description'] = $data->description;
}
if (!$groupingdb = $DB->get_record_sql("SELECT *
FROM {groupings}
class restore_section_structure_step extends restore_structure_step {
protected function define_structure() {
- return array(new restore_path_element('section', '/section'));
+ $section = new restore_path_element('section', '/section');
+
+ // Apply for 'format' plugins optional paths at section level
+ $this->add_plugin_structure('format', $section);
+
+ return array($section);
}
public function process_section($data) {
// Annotate the section mapping, with restorefiles option if needed
$this->set_mapping('course_section', $oldid, $newitemid, $restorefiles);
+ // set the new course_section id in the task
+ $this->task->set_sectionid($newitemid);
+
+
// Commented out. We never modify course->numsections as far as that is used
// by a lot of people to "hide" sections on purpose (so this remains as used to be in Moodle 1.x)
// Note: We keep the code here, to know about and because of the possibility of making this
protected function define_structure() {
- $course = new restore_path_element('course', '/course', true); // Grouped
+ $course = new restore_path_element('course', '/course');
$category = new restore_path_element('category', '/course/category');
$tag = new restore_path_element('tag', '/course/tags/tag');
- $allowed = new restore_path_element('allowed', '/course/allowed_modules/module');
+ $allowed_module = new restore_path_element('allowed_module', '/course/allowed_modules/module');
- return array($course, $category, $tag, $allowed);
+ // Apply for 'format' plugins optional paths at course level
+ $this->add_plugin_structure('format', $course);
+
+ return array($course, $category, $tag, $allowed_module);
}
/**
$data->shortname= $shortname;
$data->idnumber = '';
- // Category is set by UI when choosing the destination
- unset($data->category);
-
$data->startdate= $this->apply_date_offset($data->startdate);
if ($data->defaultgroupingid) {
$data->defaultgroupingid = $this->get_mappingid('grouping', $data->defaultgroupingid);
// Course record ready, update it
$DB->update_record('course', $data);
- // Course tags
- if (!empty($CFG->usetags) && isset($coursetags)) { // if enabled in server and present in backup
+ // Role name aliases
+ restore_dbops::set_course_role_names($this->get_restoreid(), $this->get_courseid());
+ }
+
+ public function process_category($data) {
+ // Nothing to do with the category. UI sets it before restore starts
+ }
+
+ public function process_tag($data) {
+ global $CFG, $DB;
+
+ $data = (object)$data;
+
+ if (!empty($CFG->usetags)) { // if enabled in server
+ // TODO: This is highly inneficient. Each time we add one tag
+ // we fetch all the existing because tag_set() deletes them
+ // so everything must be reinserted on each call
$tags = array();
- foreach ($coursetags as $coursetag) {
- $coursetag = (object)$coursetag;
- $tags[] = $coursetag->rawname;
+ $existingtags = tag_get_tags('course', $this->get_courseid());
+ // Re-add all the existitng tags
+ foreach ($existingtags as $existingtag) {
+ $tags[] = $existingtag->rawname;
}
+ // Add the one being restored
+ $tags[] = $data->rawname;
+ // Send all the tags back to the course
tag_set('course', $this->get_courseid(), $tags);
}
- // Course allowed modules
- if (!empty($data->restrictmodules) && !empty($coursemodules)) {
+ }
+
+ public function process_allowed_module($data) {
+ global $CFG, $DB;
+
+ $data = (object)$data;
+
+ // only if enabled by admin setting
+ if (!empty($CFG->restrictmodulesfor) && $CFG->restrictmodulesfor == 'all') {
$available = get_plugin_list('mod');
- foreach ($coursemodules as $coursemodule) {
- $mname = $coursemodule['modulename'];
- if (array_key_exists($mname, $available)) {
- if ($module = $DB->get_record('modules', array('name' => $mname, 'visible' => 1))) {
- $rec = new stdclass();
- $rec->course = $this->get_courseid();
- $rec->module = $module->id;
- if (!$DB->record_exists('course_allowed_modules', (array)$rec)) {
- $DB->insert_record('course_allowed_modules', $rec);
- }
+ $mname = $data->modulename;
+ if (array_key_exists($mname, $available)) {
+ if ($module = $DB->get_record('modules', array('name' => $mname, 'visible' => 1))) {
+ $rec = new stdclass();
+ $rec->course = $this->get_courseid();
+ $rec->module = $module->id;
+ if (!$DB->record_exists('course_allowed_modules', (array)$rec)) {
+ $DB->insert_record('course_allowed_modules', $rec);
}
}
}
}
- // Role name aliases
- restore_dbops::set_course_role_names($this->get_restoreid(), $this->get_courseid());
}
protected function after_execute() {
$data->timecreated = $this->apply_date_offset($data->timecreated);
$data->timemodified = $this->apply_date_offset($data->timemodified);
- $gradeitem = new grade_item($data);
+ $gradeitem = new grade_item($data, false);
$gradeitem->insert('restore');
//sortorder is automatically assigned when inserting. Re-instate the previous sortorder
// TODO: Ask, all the rest of locktime/exported... work with time... to be rolled?
$data->overridden = $this->apply_date_offset($data->overridden);
- $grade = new grade_grade($data);
+ $grade = new grade_grade($data, false);
$grade->insert('restore');
// no need to save any grade_grade mapping
}
$paths = array();
- $paths[] = new restore_path_element('module', '/module');
+ $module = new restore_path_element('module', '/module');
+ $paths[] = $module;
if ($CFG->enableavailability) {
$paths[] = new restore_path_element('availability', '/module/availability_info/availability');
}
+ // Apply for 'format' plugins optional paths at module level
+ $this->add_plugin_structure('format', $module);
+
return $paths;
}
return $this->step->apply_date_offset($value);
}
+ /**
+ * Returns the value of one (task/plan) setting
+ */
+ protected function get_setting_value($name) {
+ if (is_null($this->task)) {
+ throw new restore_step_exception('not_specified_restore_task');
+ }
+ return $this->task->get_setting_value($name);
+ }
+
+// end of restore_step/structure_step/task wrappers
+
/**
* Simple helper function that returns the name for the restore_path_element
* It's not mandatory to use it but recommended ;-)
return $sectionsarr;
}
+ /**
+ * Given one course id, return its format in DB
+ */
+ public static function get_courseformat_from_courseid($courseid) {
+ global $DB;
+
+ return $DB->get_field('course', 'format', array('id' => $courseid));
+ }
+
/**
* Return the wwwroot of the $CFG->mnet_localhost_id host
* caching it along the request
* Returns the default backup filename, based in passed params.
*
* Default format is (see MDL-22145)
- * backup word - format - type - name - date - info . mbz
+ * backup word - format - type - name - date - info . mbz
* where name is variable (course shortname, section name/id, activity modulename + cmid)
- * and info can be (nu = no user info, an = anonymized)
+ * and info can be (nu = no user info, an = anonymized). The last param $useidasname,
+ * defaulting to false, allows to replace the course shortname by the course id (used
+ * by automated backups, to avoid non-ascii chars in OS filesystem)
+ *
+ * @param string $format One of backup::FORMAT_
+ * @param string $type One of backup::TYPE_
+ * @param int $courseid/$sectionid/$cmid
+ * @param bool $users Should be true is users were included in the backup
+ * @param bool $anonymised Should be true is user information was anonymized.
+ * @param bool $useidasname true to use id, false to use strings (default)
+ * @return string The filename to use
*/
- public static function get_default_backup_filename($format, $type, $id, $users, $anonymised) {
+ public static function get_default_backup_filename($format, $type, $id, $users, $anonymised, $useidasname = false) {
global $DB;
// Calculate backup word
$backupword = str_replace(' ', '_', moodle_strtolower(get_string('backupfilename')));
$backupword = trim(clean_filename($backupword), '_');
- // Calculate proper name element (based on type)
- switch ($type) {
- case backup::TYPE_1COURSE:
- $shortname = $DB->get_field('course', 'shortname', array('id' => $id));
- break;
- case backup::TYPE_1SECTION:
- if (!$shortname = $DB->get_field('course_sections', 'name', array('id' => $id))) {
- $shortname = $DB->get_field('course_sections', 'section', array('id' => $id));
- }
- break;
- case backup::TYPE_1ACTIVITY:
- $cm = get_coursemodule_from_id(null, $id);
- $shortname = $cm->modname . $id;
- break;
+ $shortname = '';
+ // Not $useidasname, lets calculate it, else $id will be used
+ if (!$useidasname) {
+ // Calculate proper name element (based on type)
+ switch ($type) {
+ case backup::TYPE_1COURSE:
+ $shortname = $DB->get_field('course', 'shortname', array('id' => $id));
+ break;
+ case backup::TYPE_1SECTION:
+ if (!$shortname = $DB->get_field('course_sections', 'name', array('id' => $id))) {
+ $shortname = $DB->get_field('course_sections', 'section', array('id' => $id));
+ }
+ break;
+ case backup::TYPE_1ACTIVITY:
+ $cm = get_coursemodule_from_id(null, $id);
+ $shortname = $cm->modname . $id;
+ break;
+ }
+ $shortname = str_replace(' ', '_', $shortname);
+ $shortname = moodle_strtolower(trim(clean_filename($shortname), '_'));
}
- $shortname = str_replace(' ', '_', $shortname);
- $shortname = moodle_strtolower(trim(clean_filename($shortname), '_'));
+
$name = empty($shortname) ? $id : $shortname;
// Calculate date
/** Run if required by the schedule set in config. Default. **/
const RUN_ON_SCHEDULE = 0;
- /** Run immediatly. **/
- const RUN_IMMEDIATLY = 1;
+ /** Run immediately. **/
+ const RUN_IMMEDIATELY = 1;
const AUTO_BACKUP_DISABLED = 0;
const AUTO_BACKUP_ENABLED = 1;
return $state;
} else if ($state === backup_cron_automated_helper::STATE_RUNNING) {
mtrace('RUNNING');
- if ($rundirective == self::RUN_IMMEDIATLY) {
+ if ($rundirective == self::RUN_IMMEDIATELY) {
mtrace('automated backups are already. If this script is being run by cron this constitues an error. You will need to increase the time between executions within cron.');
} else {
mtrace("automated backup are already running. Execution delayed");
$DB->update_record('backup_courses', $backupcourse);
mtrace('Skipping unchanged course '.$course->fullname);
$skipped = true;
- } else if (($backupcourse->nextstarttime >= 0 && $backupcourse->nextstarttime < $now) || $rundirective == self::RUN_IMMEDIATLY) {
+ } else if (($backupcourse->nextstarttime >= 0 && $backupcourse->nextstarttime < $now) || $rundirective == self::RUN_IMMEDIATELY) {
mtrace('Backing up '.$course->fullname, '...');
//We have to send a email because we have included at least one backup
$emailpending = true;
-
+
//Only make the backup if laststatus isn't 2-UNFINISHED (uncontrolled error)
if ($backupcourse->laststatus != 2) {
//Set laststarttime
$config = get_config('backup');
$midnight = usergetmidnight($now, $timezone);
$date = usergetdate($now, $timezone);
-
+
//Get number of days (from today) to execute backups
$automateddays = substr($config->backup_auto_weekdays,$date['wday']) . $config->backup_auto_weekdays;
$daysfromtoday = strpos($automateddays, "1");
$users = $bc->get_plan()->get_setting('users')->get_value();
$anonymised = $bc->get_plan()->get_setting('anonymize')->get_value();
$bc->get_plan()->get_setting('filename')->set_value(backup_plan_dbops::get_default_backup_filename($format, $type, $id, $users, $anonymised));
-
+
$bc->set_status(backup::STATUS_AWAITING);
$outcome = $bc->execute_plan();
$dir = null;
}
if (!empty($dir) && $storage !== 0) {
- $filename = self::get_external_filename($course->id, $format, $type, $users, $anonymised);
+ $filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised, true);
$outcome = $file->copy_content_to($dir.'/'.$filename);
if ($outcome && $storage === 1) {
$file->delete();
return true;
}
- /**
- * Gets the filename to use for the backup when it is being moved to an
- * external location.
- *
- * Note: we use the course id in the filename rather than the course shortname
- * because it may contain UTF-8 characters that could cause problems for the
- * recieving filesystem.
- *
- * @param int $courseid
- * @param string $format One of backup::FORMAT_
- * @param string $type One of backup::TYPE_
- * @param bool $users Should be true is users were included in the backup
- * @param bool $anonymised Should be true is user information was anonymized.
- * @return string The filename to use
- */
- public static function get_external_filename($courseid, $format, $type, $users, $anonymised) {
- $backupword = str_replace(' ', '_', moodle_strtolower(get_string('backupfilename')));
- $backupword = trim(clean_filename($backupword), '_');
- // Calculate date
- $backupdateformat = str_replace(' ', '_', get_string('backupnameformat', 'langconfig'));
- $date = userdate(time(), $backupdateformat, 99, false);
- $date = moodle_strtolower(trim(clean_filename($date), '_'));
- // Calculate info
- $info = '';
- if (!$users) {
- $info = 'nu';
- } else if ($anonymised) {
- $info = 'an';
- }
- return $backupword.'-'.$format.'-'.$type.'-'.$courseid.'-'.$date.'-'.$info.'.mbz';
- }
-
/**
* Removes deleted courses fromn the backup_courses table so that we don't
* waste time backing them up.
$filearea = 'automated';
$itemid = 0;
$files = array();
+ // Store all the matching files into timemodified => stored_file array
foreach ($fs->get_area_files($context->id, $component, $filearea, $itemid) as $file) {
if (strpos($file->get_filename(), $backupword) !== 0) {
continue;
}
$files[$file->get_timemodified()] = $file;
}
- arsort($files);
+ if (count($files) <= $keep) {
+ // There are less matching files than the desired number to keep
+ // do there is nothing to clean up.
+ return 0;
+ }
+ // Sort by keys descending (newer to older filemodified)
+ krsort($files);
$remove = array_splice($files, $keep);
foreach ($remove as $file) {
$file->delete();
}
- //mtrace('Removed '.count($remove).' old backup file(s) from the data directory');
+ //mtrace('Removed '.count($remove).' old backup file(s) from the automated filearea');
}
// Clean up excess backups in the specified external directory
if (!empty($dir) && ($storage == 1 || $storage == 2)) {
- // Calculate backup filename regex
-
+ // Calculate backup filename regex, ignoring the date/time/info parts that can be
+ // variable, depending of languages, formats and automated backup settings
$filename = $backupword . '-' . backup::FORMAT_MOODLE . '-' . backup::TYPE_1COURSE . '-' .$course->id . '-';
+ $regex = '#^'.preg_quote($filename, '#').'.*\.mbz$#';
- $regex = '#^'.preg_quote($filename, '#').'(\d{8})\-(\d{4})\-[a-z]{2}\.mbz$#S';
-
+ // Store all the matching files into fullpath => timemodified array
$files = array();
foreach (scandir($dir) as $file) {
if (preg_match($regex, $file, $matches)) {
- $files[$file] = $matches[1].$matches[2];
+ $files[$file] = filemtime($dir . '/' . $file);
}
}
if (count($files) <= $keep) {
// do there is nothing to clean up.
return 0;
}
+ // Sort by values descending (newer to older filemodified)
arsort($files);
$remove = array_splice($files, $keep);
foreach (array_keys($remove) as $file) {
- unlink($dir.'/'.$file);
+ unlink($dir . '/' . $file);
}
//mtrace('Removed '.count($remove).' old backup file(s) from external directory');
}
return true;
}
-}
\ No newline at end of file
+}
$structure->destroy();
}
+ /**
+ * As far as backup structure steps are implementing backup_plugin stuff, they need to
+ * have the parent task available for wrapping purposes (get course/context....)
+ */
+ public function get_task() {
+ return $this->task;
+ }
+
// Protected API starts here
/**
$backupfile = $plugindir . '/backup/moodle2/' . $classname . '.class.php';
if (file_exists($backupfile)) {
require_once($backupfile);
- $backupplugin = new $classname($plugintype, $name, $optigroup);
+ $backupplugin = new $classname($plugintype, $name, $optigroup, $this);
// Add plugin returned structure to optigroup
$backupplugin->define_plugin_structure($element->get_name());
}
}
protected function check_and_set_used($element) {
+ // First of all, check the element being added doesn't conflict with own final elements
+ if (array_key_exists($element->get_name(), $this->final_elements)) {
+ throw new base_element_struct_exception('baseelementchildnameconflict', $element->get_name());
+ }
$grandparent = $this->get_grandoptigroupelement_or_grandparent();
if ($existing = array_intersect($grandparent->get_used(), $element->get_used())) { // Check the element isn't being used already
throw new base_element_struct_exception('baseelementexisting', implode($existing));
$this->assertTrue($e instanceof base_element_parent_exception);
}
+ // Add child element already used by own final elements
+ $nested = new mock_base_nested_element('PARENT1', null, array('FINAL1', 'FINAL2'));
+ $child = new mock_base_nested_element('FINAL2', null, array('FINAL3', 'FINAL4'));
+ try {
+ $nested->add_child($child);
+ $this->fail("Expecting base_element_struct_exception exception, none occurred");
+ } catch (Exception $e) {
+ $this->assertTrue($e instanceof base_element_struct_exception);
+ $this->assertEqual($e->errorcode, 'baseelementchildnameconflict');
+ $this->assertEqual($e->a, 'FINAL2');
+ }
}
}
$alltagswhitespace = false;
continue;
}
+
// If the path including the tag name matches another selected path
- // (registered or parent) delete it, another chunk will contain that info
+ // (registered or parent) and is null or begins with linefeed, we know it's part
+ // of another chunk, delete it, another chunk will contain that info
if ($this->path_is_selected($path . '/' . $key) ||
$this->path_is_selected_parent($path . '/' . $key)) {
- unset($data['tags'][$key]);
- continue;
+ if (!isset($value['cdata']) || substr($value['cdata'], 0, 1) === "\n") {
+ unset($data['tags'][$key]);
+ continue;
+ }
}
+
// Convert to simple name => value array
$data['tags'][$key] = isset($value['cdata']) ? $value['cdata'] : null;
// Entering a new inner level, publish all the information available
if ($this->level > $this->prevlevel) {
if (!empty($this->currtag) && (!empty($this->currtag['attrs']) || !empty($this->currtag['cdata']))) {
- $this->topush['tags'][$this->currtag['name']] = $this->currtag;
+ // We always add the last not-empty repetition. Empty ones are ignored.
+ if (isset($this->topush['tags'][$this->currtag['name']]) && trim($this->currtag['cdata']) === '') {
+ // Do nothing, the tag already exists and the repetition is empty
+ } else {
+ $this->topush['tags'][$this->currtag['name']] = $this->currtag;
+ }
}
if (!empty($this->topush['tags'])) {
$this->publish($this->topush);
// Ending rencently started tag, add value to current tag
if ($this->level == $this->prevlevel) {
$this->currtag['cdata'] = $this->postprocess_cdata($this->accum);
- $this->topush['tags'][$this->currtag['name']] = $this->currtag;
+ // We always add the last not-empty repetition. Empty ones are ignored.
+ if (isset($this->topush['tags'][$this->currtag['name']]) && trim($this->currtag['cdata']) === '') {
+ // Do nothing, the tag already exists and the repetition is empty
+ } else {
+ $this->topush['tags'][$this->currtag['name']] = $this->currtag;
+ }
$this->currtag = array();
}
<name>4</name><!-- Only last will be processed. We don't allow repeated final tags in our parser -->
<value>4</value>
<value>5</value><!-- Only last will be processed. We don't allow repeated final tags in our parser -->
+ <value>
+
+ </value><!-- If one tag already is set and the repeated is empty, the original value is kept -->
</othertest>
</glossary>
</activity>
}
/**
- * Return a block_contents oject representing the full contents of this block.
+ * Return a block_contents object representing the full contents of this block.
*
* This internally calls ->get_content(), and then adds the editing controls etc.
*
* {@link html_attributes()}, {@link formatted_contents()} or {@link get_content()},
* {@link hide_header()}, {@link (get_edit_controls)}, etc.
*
- * @return block_contents a represntation of the block, for rendering.
+ * @return block_contents a representation of the block, for rendering.
* @since Moodle 2.0.
*/
public function get_content_for_output($output) {
if ($this->page->user_is_editing()) {
$bc->controls = $this->page->blocks->edit_controls($this);
- }
-
- if ($this->is_empty() && !$bc->controls) {
- return null;
+ } else {
+ // we must not use is_empty on hidden blocks
+ if ($this->is_empty() && !$bc->controls) {
+ return null;
+ }
}
if (empty($CFG->allowuserblockhiding) ||
*/
protected function formatted_contents($output) {
$this->get_content();
+ $this->get_required_javascript();
if (!empty($this->content->text)) {
- $this->get_required_javascript();
return $this->content->text;
} else {
return '';
/**
* Can be overridden by the block to prevent the block from being dockable.
- *
+ *
* @return bool
*/
public function instance_can_be_docked() {
protected function formatted_contents($output) {
$this->get_content();
+ $this->get_required_javascript();
if (!empty($this->content->items)) {
- $this->get_required_javascript();
return $output->list_block_contents($this->content->icons, $this->content->items);
} else {
return '';
}
$rssfeeds = $DB->get_records_sql_menu('
- SELECT id, CASE WHEN preferredtitle = ? THEN title ELSE preferredtitle END AS acutaltitle
+ SELECT id,
+ CASE WHEN preferredtitle = ? THEN ' . $DB->sql_compare_text('title', 64) .' ELSE preferredtitle END
FROM {block_rss_client}
WHERE userid = ? OR shared = 1
- ORDER BY acutaltitle',
- array($DB->sql_empty(), $USER->id));
+ ORDER BY CASE WHEN preferredtitle = ? THEN ' . $DB->sql_compare_text('title', 64) . ' ELSE preferredtitle END ',
+ array($DB->sql_empty(), $USER->id, $DB->sql_empty()));
if ($rssfeeds) {
$select = $mform->addElement('select', 'config_rssid', get_string('choosefeedlabel', 'block_rss_client'), $rssfeeds);
$select->setMultiple(true);
$rssfile = new moodle_simplepie_file($externalblog->url);
$filetest = new SimplePie_Locator($rssfile);
+ $textlib = textlib_get_instance(); // Going to use textlib services
+
if (!$filetest->is_feed($rssfile)) {
$externalblog->failedlastsync = 1;
$DB->update_record('blog_external', $externalblog);
$newentry->uniquehash = $entry->get_permalink();
$newentry->publishstate = 'site';
$newentry->format = FORMAT_HTML;
- $newentry->subject = $entry->get_title();
+ // Clean subject of html, just in case
+ $newentry->subject = clean_param($entry->get_title(), PARAM_TEXT);
+ // Observe 128 max chars in DB
+ // TODO: +1 to raise this to 255
+ if ($textlib->strlen($newentry->subject) > 128) {
+ $newentry->subject = $textlib->substr($newentry->subject, 0, 125) . '...';
+ }
$newentry->summary = $entry->get_description();
//used to decide whether to insert or update
if ($topic != -1) {
$displaysection = course_set_display($course->id, $topic);
} else {
- if (isset($USER->display[$course->id])) {
- $displaysection = $USER->display[$course->id];
- } else {
- $displaysection = course_set_display($course->id, 0);
- }
+ $displaysection = course_get_display($course->id);
}
$context = get_context_instance(CONTEXT_COURSE, $course->id);
if ($week != -1) {
$displaysection = course_set_display($course->id, $week);
} else {
- if (isset($USER->display[$course->id])) {
- $displaysection = $USER->display[$course->id];
- } else {
- $displaysection = course_set_display($course->id, 0);
- }
+ $displaysection = course_get_display($course->id);
}
$streditsummary = get_string('editsummary');
return $coursesections[$courseid];
}
-function course_set_display($courseid, $display=0) {
+/**
+ * Returns the course section to display or 0 meaning show all sections. Returns 0 for guests.
+ * It also sets the $USER->display cache to array($courseid=>return value)
+ *
+ * @param int $courseid The course id
+ * @return int Course section to display, 0 means all
+ */
+function course_get_display($courseid) {
+ global $USER, $DB;
+
+ if (!isloggedin() or isguestuser()) {
+ //do not get settings in db for guests
+ return 0; //return the implicit setting
+ }
+
+ if (!isset($USER->display[$courseid])) {
+ if (!$display = $DB->get_field('course_display', 'display', array('userid' => $USER->id, 'course'=>$courseid))) {
+ $display = 0; // all sections option is not stored in DB, this makes the table much smaller
+ }
+ //use display cache for one course only - we need to keep session small
+ $USER->display = array($courseid => $display);
+ }
+
+ return $USER->display[$courseid];
+}
+
+/**
+ * Show one section only or all sections.
+ *
+ * @param int $courseid The course id
+ * @param mixed $display show only this section, 0 or 'all' means show all sections
+ * @return int Course section to display, 0 means all
+ */
+function course_set_display($courseid, $display) {
global $USER, $DB;
- if ($display == "all" or empty($display)) {
+ if ($display === 'all' or empty($display)) {
$display = 0;
}
if (!isloggedin() or isguestuser()) {
//do not store settings in db for guests
- } else if ($DB->record_exists("course_display", array("userid" => $USER->id, "course"=>$courseid))) {
- $DB->set_field("course_display", "display", $display, array("userid"=>$USER->id, "course"=>$courseid));
+ return 0;
+ }
+
+ if ($display == 0) {
+ //show all, do not store anything in database
+ $DB->delete_records('course_display', array('userid' => $USER->id, 'course' => $courseid));
+
} else {
- $record = new stdClass();
- $record->userid = $USER->id;
- $record->course = $courseid;
- $record->display = $display;
- $DB->insert_record("course_display", $record);
+ if ($DB->record_exists('course_display', array('userid' => $USER->id, 'course' => $courseid))) {
+ $DB->set_field('course_display', 'display', $display, array('userid' => $USER->id, 'course' => $courseid));
+ } else {
+ $record = new stdClass();
+ $record->userid = $USER->id;
+ $record->course = $courseid;
+ $record->display = $display;
+ $DB->insert_record('course_display', $record);
+ }
}
- return $USER->display[$courseid] = $display; // Note: = not ==
+ //use display cache for one course only - we need to keep session small
+ $USER->display = array($courseid => $display);
+
+ return $display;
}
/**
$DB->set_field("course_sections", "section", $section, array("id"=>$sectiondestrecord->id));
// if the focus is on the section that is being moved, then move the focus along
- if (isset($USER->display[$course->id]) and ($USER->display[$course->id] == $section)) {
+ if (course_get_display($course->id) == $section) {
course_set_display($course->id, $sectiondest);
}
}
// if the focus is on the section that is being moved, then move the focus along
- if (isset($USER->display[$course->id]) and ($USER->display[$course->id] == $section)) {
+ if (course_get_display($course->id) == $section) {
course_set_display($course->id, $destination);
}
return true;
// Display icon
$icon = $OUTPUT->pix_url('icon', $criterion->module);
- $iconlink = $CFG->wwwroot.'/mod/'.$criterion->module.'/view.php?id='.$activity->id;
+ $iconlink = $CFG->wwwroot.'/mod/'.$criterion->module.'/view.php?id='.$criterion->moduleinstance;
$icontitle = $activity->name;
$iconalt = get_string('modulename', $criterion->module);
break;
echo $OUTPUT->notification('An error occurred while moving a section');
}
// Clear the navigation cache at this point so that the affects
- // are seen immediatly on the navigation.
+ // are seen immediately on the navigation.
$PAGE->navigation->clear_cache();
}
}
// first of all add necessary enrol instances to all courses
$parentcat = $DB->sql_concat("cat.path", "'/%'");
- $sql = "SELECT DISTINCT c.*
+ // need whole course records to be used by add_instance(), use inner view (ci) to
+ // get distinct records only.
+ // TODO: Moodle 2.1. Improve enrol API to accept courseid / courserec
+ $sql = "SELECT c.*
FROM {course} c
- JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel)
- JOIN (SELECT DISTINCT cctx.path
- FROM {course_categories} cc
- JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
- JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
- ) cat ON (ctx.path LIKE $parentcat)
- LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
- WHERE e.id IS NULL";
+ JOIN (
+ SELECT DISTINCT c.id
+ FROM {course} c
+ JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel)
+ JOIN (SELECT DISTINCT cctx.path
+ FROM {course_categories} cc
+ JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
+ JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
+ ) cat ON (ctx.path LIKE $parentcat)
+ LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
+ WHERE e.id IS NULL) ci ON (c.id = ci.id)";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $course) {
$instances = $this->manager->get_enrolment_instances();
$plugins = $this->manager->get_enrolment_plugins();
+ $manuals = array();
// print enrol link or selection
$links = array();
foreach($instances as $instance) {
$plugin = $plugins[$instance->enrol];
if ($link = $plugin->get_manual_enrol_link($instance)) {
$links[$instance->id] = $link;
+ $manuals[$instance->id] = $instance;
}
}
if (!empty($links)) {
$startdateoptions[3] = get_string('today') . ' (' . userdate($today, $timeformat) . ')' ;
if ($count == 1) {
+ $instance = reset($manuals);
$page->requires->strings_for_js(array(
'ajaxoneuserfound',
'ajaxxusersfound',
'ajaxurl'=>'/enrol/ajax.php',
'url'=>$url->out(false),
'optionsStartDate'=>$startdateoptions,
- 'defaultRole'=>get_config('enrol_manual', 'roleid'));
+ 'defaultRole'=>$instance->roleid);
$page->requires->yui_module($modules, $function, array($arguments));
}
return $control;
// non string data can not be filtered anyway
return $text;
}
+ if (stripos($text, '</a>') === false) {
+ // performance shortcut - all regexes bellow end with the </a> tag,
+ // if not present nothing can match
+ return $text;
+ }
$newtext = $text; // fullclone is slow and not needed here
if (!empty($CFG->filter_mediaplugin_enable_mp3)) {
- $search = '/<a[^>]*?href="([^<]+\.mp3)"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.mp3)"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_mp3_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_ogg)) {
- $search = '/<a[^>]*?href="([^<]+\.ogg)"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.ogg)"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_ogg_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_ogv)) {
- $search = '/<a[^>]*?href="([^<]+\.ogv)"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.ogv)"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_ogv_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_swf)) {
- $search = '/<a[^>]*?href="([^<]+\.swf)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.swf)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_swf_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_flv)) {
- $search = '/<a[^>]*?href="([^<]+\.flv)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.flv)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_flv_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_mov)) {
- $search = '/<a[^>]*?href="([^<]+\.mov)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.mov)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
- $search = '/<a[^>]*?href="([^<]+\.mp4)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.mp4)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
- $search = '/<a[^>]*?href="([^<]+\.m4v)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.m4v)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
- $search = '/<a[^>]*?href="([^<]+\.m4a)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.m4a)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_wmv)) {
- $search = '/<a[^>]*?href="([^<]+\.wmv)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.wmv)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_wmp_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_mpg)) {
- $search = '/<a[^>]*?href="([^<]+\.mpe?g)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.mpe?g)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_avi)) {
- $search = '/<a[^>]*?href="([^<]+\.avi)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.avi)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_wmp_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_ram)) {
- $search = '/<a[^>]*?href="([^<]+\.ram)"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.ram)"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_real_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_rpm)) {
- $search = '/<a[^>]*?href="([^<]+\.rpm)"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.rpm)"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_real_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_rm)) {
- $search = '/<a[^>]*?href="([^<]+\.rm)"[^>]*>.*?<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.rm)"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_real_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_youtube)) {
- //see MDL-23903 for description of recent changes to this regex
- //$search = '/<a.*?href="([^<]*)youtube.com\/watch\?v=([^"]*)"[^>]*>(.*?)<\/a>/is';
- $search = '/<a[^>]*href="([^<]*?)youtube.com\/watch\?v=([^"]*)"[^>]*>(.*?)<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="(([^"]+youtube\.com)\/watch\?v=([A-Za-z0-9\-_]+))[^>]*>(.*?)<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
- $search = '/<a[^>]*href="([^<]*)youtube.com\/v\/([^"]*)"[^>]*>(.*?)<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="(([^"]+youtube\.com)\/v\/([A-Za-z0-9\-_]*))[^>]+>(.*?)<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
- $search = '/<a(\s+[^>]+?)?\s+href="((([^"]+)youtube\.com)\/view_play_list\?p=([^"]*))"[^>]*>(.*?)<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="((([^"]+)youtube\.com)\/view_play_list\?p=([A-Za-z0-9\-_]+))[^>]*>(.*?)<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_playlist_callback', $newtext);
}
if (!empty($CFG->filter_mediaplugin_enable_img)) {
- $search = '/<a[^>]*?href="([^<]+\.jpg)"[^>]*>(.*?)<\/a>/is';
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.jpg)"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_img_callback', $newtext);
- $search = '/<a[^>]*?href="([^<]+\.png)"[^>]*>(.*?)<\/a>/is';
+
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.png)"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_img_callback', $newtext);
- $search = '/<a[^>]*?href="([^<]+\.gif)"[^>]*>(.*?)<\/a>/is';
+
+ $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.gif)"[^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_img_callback', $newtext);
}
$count++;
$id = 'filter_mp3_'.time().$count; //we need something unique because it might be stored in text cache
- $url = addslashes_js($link[1]);
+ $url = addslashes_js($link[2]);
$playerpath = $CFG->wwwroot.'/filter/mediaplugin/mp3player.swf';
$audioplayerpath = $CFG->wwwroot .'/filter/mediaplugin/flowplayer.audio.swf';
$count++;
$id = 'filter_ogg_'.time().$count; //we need something unique because it might be stored in text cache
- $url = addslashes_js($link[1]);
+ $url = $link[2];
$printlink = html_writer::link($url, get_string('oggaudio', 'filter_mediaplugin'));
$unsupportedplugins = get_string('unsupportedplugins', 'filter_mediaplugin', $printlink);
$output = <<<OET
$count++;
$id = 'filter_ogv_'.time().$count; //we need something unique because it might be stored in text cache
- $url = addslashes_js($link[1]);
+ $url = $link[2];
$printlink = html_writer::link($url, get_string('ogvvideo', 'filter_mediaplugin'));
$unsupportedplugins = get_string('unsupportedplugins', 'filter_mediaplugin', $printlink);
$output = <<<OET
$count++;
$id = 'filter_swf_'.time().$count; //we need something unique because it might be stored in text cache
- $width = empty($link[3]) ? '400' : $link[3];
- $height = empty($link[4]) ? '300' : $link[4];
- $url = addslashes_js($link[1]);
+ $width = empty($link[4]) ? '400' : $link[4];
+ $height = empty($link[5]) ? '300' : $link[5];
+ $url = $link[2];
$args = Array();
$args['movie'] = $url;
$count++;
$id = 'filter_flv_'.time().$count; //we need something unique because it might be stored in text cache
- $width = empty($link[3]) ? '480' : $link[3];
- $height = empty($link[4]) ? '360' : $link[4];
- $url = addslashes_js($link[1]);
+ // note: in 1.9.x this used to be 480x360
+ $width = empty($link[4]) ? '800' : $link[4];
+ $height = empty($link[5]) ? '600' : $link[5];
+ $url = addslashes_js($link[2]);
$playerpath = $CFG->wwwroot.'/filter/mediaplugin/flvplayer.swf';
$output = <<<EOT
<span class="mediaplugin mediaplugin_flv" id="$id"></span>
<noscript><div>
- <object width="800" height="600" id="undefined" name="undefined" data="$playerpath" type="application/x-shockwave-flash">
+ <object width="$width" height="$height" id="undefined" name="undefined" data="$playerpath" type="application/x-shockwave-flash">
<param name="movie" value="$playerpath" />
<param name="allowfullscreen" value="true" />
<param name="allowscriptaccess" value="always" />
}
function filter_mediaplugin_real_callback($link, $autostart=false) {
- $url = addslashes_js($link[1]);
+ $url = $link[2];
+ $jsurl = addslashes_js($link[2]);
$mimetype = mimeinfo('type', $url);
$autostart = $autostart ? 'true' : 'false';
<script type="text/javascript">
//<![CDATA[
document.write(\'<object classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" width="240" height="180">\\
- <param name="src" value="'.$url.'" />\\
+ <param name="src" value="'.$jsurl.'" />\\
<param name="autostart" value="'.$autostart.'" />\\
<param name="controls" value="imagewindow" />\\
<param name="console" value="video" />\\
<param name="loop" value="true" />\\
- <embed src="'.$url.'" width="240" height="180" loop="true" type="'.$mimetype.'" controls="imagewindow" console="video" autostart="'.$autostart.'" />\\
+ <embed src="'.$jsurl.'" width="240" height="180" loop="true" type="'.$mimetype.'" controls="imagewindow" console="video" autostart="'.$autostart.'" />\\
</object><br />\\
<object classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" width="240" height="30">\\
- <param name="src" value="'.$url.'" />\\
+ <param name="src" value="'.$jsurl.'" />\\
<param name="autostart" value="'.$autostart.'" />\\
<param name="controls" value="ControlPanel" />\\
<param name="console" value="video" />\\
- <embed src="'.$url.'" width="240" height="30" controls="ControlPanel" type="'.$mimetype.'" console="video" autostart="'.$autostart.'" />\\
+ <embed src="'.$jsurl.'" width="240" height="30" controls="ControlPanel" type="'.$mimetype.'" console="video" autostart="'.$autostart.'" />\\
</object>\');
//]]>
</script></span>';
* Change links to Youtube into embedded Youtube videos
*/
function filter_mediaplugin_youtube_callback($link, $autostart=false) {
+ $site = s($link[3]);
+ $param = $link[4]; // video id
+ $info = s(strip_tags($link[5]));
- $site = addslashes_js($link[1]);
- $url = addslashes_js($link[2]);
- $info = addslashes_js(strip_tags($link[3]));//strip out html tags as they won't work in the title attribute
return '<object title="'.$info.'"
class="mediaplugin mediaplugin_youtube" type="application/x-shockwave-flash"
- data="'.$site.'youtube.com/v/'.$url.'&fs=1&rel=0" width="425" height="344">'.
- '<param name="movie" value="'.$site.'youtube.com/v/'.$url.'&fs=1&rel=0" />'.
+ data="'.$site.'/v/'.$param.'&fs=1&rel=0" width="400" height="320">'.
+ '<param name="movie" value="'.$site.'/v/'.$param.'&fs=1&rel=0" />'.
'<param name="FlashVars" value="playerMode=embedded" />'.
'<param name="wmode" value="transparent" />'.
'<param name="allowFullScreen" value="true" />'.
function filter_mediaplugin_youtube_playlist_callback($link, $autostart=false) {
$site = s($link[4]);
- $param = s($link[5]);
- $info = s($link[6]);
+ $param = $link[5]; // playlist id
+ $info = s(strip_tags($link[6]));
return '<object title="'.$info.'"
class="mediaplugin mediaplugin_youtube" type="application/x-shockwave-flash"
* Change links to images into embedded images
*/
function filter_mediaplugin_img_callback($link, $autostart=false) {
- $url = addslashes_js($link[1]);
- $info = addslashes_js($link[2]);
-
+ $url = $link[2];
+ $info = s(strip_tags($link[2]));
return '<img class="mediaplugin mediaplugin_img" alt="" title="'.$info.'" src="'.$url.'" />';
}
* Embed video using window media player if available
*/
function filter_mediaplugin_wmp_callback($link, $autostart=false) {
- $url = $link[1];
- if (empty($link[3]) or empty($link[4])) {
+ $url = $link[2];
+ if (empty($link[4]) or empty($link[5])) {
$mpsize = '';
$size = 'width="300" height="260"';
$autosize = 'true';
} else {
- $size = 'width="'.$link[3].'" height="'.$link[4].'"';
+ $size = 'width="'.$link[4].'" height="'.$link[5].'"';
$mpsize = $size;
$autosize = 'false';
}
}
function filter_mediaplugin_qt_callback($link, $autostart=false) {
- $url = $link[1];
- if (empty($link[3]) or empty($link[4])) {
+ $url = $link[2];
+ if (empty($link[4]) or empty($link[5])) {
$size = 'width="440" height="315"';
} else {
- $size = 'width="'.$link[3].'" height="'.$link[4].'"';
+ $size = 'width="'.$link[4].'" height="'.$link[5].'"';
}
$mimetype = mimeinfo('type', $url);
$autostart = $autostart ? 'true' : 'false';
--- /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 test for the filter_mediaplugin
+ *
+ * @package filter
+ * @subpackage Mediaplugin
+ * @copyright 2011 Rossiani Wijaya <rwijaya@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot . '/filter/mediaplugin/filter.php'); // Include the code to test
+
+/**
+ * Test cases for filter_mediaplugin class
+ */
+class filter_mediaplugin_test extends UnitTestCase {
+
+ function test_filter_mediaplugin_link() {
+ global $CFG;
+
+ // we need to enable the plugins somehow
+ $oldcfg = clone($CFG); // very, very ugly hack
+ $CFG->filter_mediaplugin_enable_mp3 = 1;
+ $CFG->filter_mediaplugin_enable_ogg = 1;
+ $CFG->filter_mediaplugin_enable_ogv = 1;
+ $CFG->filter_mediaplugin_enable_swf = 1;
+ $CFG->filter_mediaplugin_enable_flv = 1;
+ $CFG->filter_mediaplugin_enable_mov = 1;
+ $CFG->filter_mediaplugin_enable_wmv = 1;
+ $CFG->filter_mediaplugin_enable_mpg = 1;
+ $CFG->filter_mediaplugin_enable_avi = 1;
+ $CFG->filter_mediaplugin_enable_ram = 1;
+ $CFG->filter_mediaplugin_enable_rpm = 1;
+ $CFG->filter_mediaplugin_enable_rm = 1;
+ $CFG->filter_mediaplugin_enable_youtube = 1;
+ $CFG->filter_mediaplugin_enable_img = 1;
+
+
+ $filterplugin = new filter_mediaplugin(null, array());
+
+ $validtexts = array (
+ '<a href="http://moodle.org/testfile/test.mp3">test mp3</a>',
+ '<a href="http://moodle.org/testfile/test.ogg">test ogg</a>',
+ '<a id="movie player" class="center" href="http://moodle.org/testfile/test.mpg">test mpg</a>',
+ '<a href="http://moodle.org/testfile/test.ram">test</a>',
+ '<a href="http://www.youtube.com/watch?v=JghQgA2HMX8" class="href=css">test file</a>',
+ '<a class="youtube" href="http://www.youtube.com/watch?v=JghQgA2HMX8">test file</a>',
+ '<a class="_blanktarget" href="http://moodle.org/testfile/test.flv?d=100x100">test flv</a>',
+ '<a class="hrefcss" href="http://www.youtube.com/watch?v=JghQgA2HMX8">test file</a>',
+ '<a href="http://www.moodle.org/logo.jpg"><img src="http://moodle.org/test.jpg" /></a>',
+ '<a href="http://www.moodle.org/logo.rpm">rpm file</a>',
+ '<a class="content" href="http://moodle.org/testfile/test.avi">test mp3</a>',
+ '<a id="audio" href="http://moodle.org/testfile/test.mp3">test mp3</a>',
+ '<a href="http://moodle.org/testfile/test.mp3">test mp3</a>',
+ '<a href="http://moodle.org/testfile/test.mp3">test mp3</a>',
+ '<a href="http://www.youtube.com/watch?v=JghQgA2HMX8?d=200x200">youtube\'s</a>',
+ '<a
+ href="http://moodle.org/testfile/test.mp3">
+ test mp3</a>',
+ '<a class="content"
+
+
+ href="http://moodle.org/testfile/test.avi">test mp3
+ </a>',
+ '<a href="http://www.youtube.com/watch?v=JghQgA2HMX8?d=200x200" >youtube\'s</a>'
+ );
+
+ //test for valid link
+ foreach ($validtexts as $text) {
+ $msg = "Testing text: ". $text;
+ $filter = $filterplugin->filter($text);
+ $this->assertNotEqual($text, $filter, $msg);
+ }
+
+ $invalidtexts = array(
+ '<a class="_blanktarget">href="http://moodle.org/testfile/test.mp3"</a>',
+ '<a>test test</a>',
+ '<a >test test</a>',
+ '<a >test test</a>',
+ '<a >test test</a>',
+ '<ahref="http://moodle.org/testfile/test.mp3">sample</a>',
+ '<a href="" test></a>',
+ '<a class="_blanktarget" href="http://moodle.org/testfile/test.flv?d=100x">test flv</a>',
+ '<a href="http://www.moodle.com/path/to?#param=29">test</a>',
+ '<a href="http://moodle.org/testfile/test.mp3">test mp3',
+ '<a href="http://moodle.org/testfile/test.mp3"test</a>',
+ '<a href="http://moodle.org/testfile/">test</a>',
+ '<a href="http://www.moodle.org/path/to/test.flv#param2=1?d=100x100">test</a>',
+ '<href="http://moodle.org/testfile/test.avi">test</a>',
+ '<a ahref="http://moodle.org/testfile/test.mp3">test mp3</a>',
+ '<abbr href="http://moodle.org/testfile/test.mp3">test mp3</abbr>',
+ '<ahref="http://moodle.org/testfile/test.mp3">test mp3</a>',
+ '<a class="content"href="http://moodle.org/testfile/test.mp3">test mp3</a>',
+ '<aclass="content" href="http://moodle.org/testfile/test.mp3">test mp3</a>'
+ );
+
+ //test for invalid link
+ foreach ($invalidtexts as $text) {
+ $msg = "Testing text: ". $text;
+ $filter = $filterplugin->filter($text);
+ $this->assertEqual($text, $filter, $msg);
+ }
+
+ $CFG = $oldcfg; // very, very ugly hack
+ }
+}
$disabled = empty($distro->dbhost) ? '' : 'disabled="disabled';
echo '<div class="formrow"><label for="id_dbhost" class="formlabel">'.$strdbhost.'</label>';
- echo '<input id="id_dbhost" name="dbhost" '.$disabled.' type="text" value="'.s($config->dbhost).'" size="30" class="forminput" />';
+ echo '<input id="id_dbhost" name="dbhost" '.$disabled.' type="text" value="'.s($config->dbhost).'" size="50" class="forminput" />';
echo '</div>';
echo '<div class="formrow"><label for="id_dbname" class="formlabel">'.$strdbname.'</label>';
echo '<div class="userinput">';
echo '<div class="formrow"><label for="id_wwwroot" class="formlabel">'.$paths['wwwroot'].'</label>';
- echo '<input id="id_wwwroot" name="wwwroot" type="text" value="'.s($CFG->wwwroot).'" disabled="disabled" size="45" class="forminput" />';
+ echo '<input id="id_wwwroot" name="wwwroot" type="text" value="'.s($CFG->wwwroot).'" disabled="disabled" size="70" class="forminput" />';
echo '</div>';
echo '<div class="formrow"><label for="id_dirroot" class="formlabel">'.$paths['dirroot'].'</label>';
- echo '<input id="id_dirroot" name="dirroot" type="text" value="'.s($CFG->dirroot).'" disabled="disabled" size="45"class="forminput" />';
+ echo '<input id="id_dirroot" name="dirroot" type="text" value="'.s($CFG->dirroot).'" disabled="disabled" size="70"class="forminput" />';
echo '</div>';
echo '<div class="formrow"><label for="id_dataroot" class="formlabel">'.$paths['dataroot'].'</label>';
- echo '<input id="id_dataroot" name="dataroot" type="text" value="'.s($config->dataroot).'" size="45" class="forminput" />';
+ echo '<input id="id_dataroot" name="dataroot" type="text" value="'.s($config->dataroot).'" size="70" class="forminput" />';
if ($hint_dataroot !== '') {
echo '<div class="hint">'.$hint_dataroot.'</div>';
}
$string['pathssubadmindir'] = 'Einige Webserver benutzen /admin als speziellen Link, um auf Einstellungsseiten oder Ähnliches zu verweisen. Unglücklicherweise kollidiert dies mit dem standardmäßigen Verzeichnis für die Moodle-Administration. Sie können dieses Problem beheben, indem Sie das Verzeichnis admin in Ihrer Moodle-Installation umbenennen und den neuen Namen hier eingeben (z.B. <em>moodleadmin</em>). Mit dieser Änderung werden alle Admin-Links korrigiert.';
$string['pathssubdataroot'] = 'Sie benötigen einen Platz, wo Moodle hochgeladene Dateien abspeichern kann. Dieses Verzeichnis muss Lese- und Schreibrechte für das Nutzerkonto besitzen, mit dem Ihr Webservers läuft (üblicherweise \'nobody\', \'apache\' oder \'www\'). Außerdem sollte das Verzeichnis nicht direkt aus dem Internet erreichbar sein. Das Intallationsskript wird versuchen, ein solches Verzeichnis zu erstellen, falls es nicht existiert.</p>';
$string['pathssubdirroot'] = 'Vollständiger Pfad der Moodle-Installation';
-$string['pathssubwwwroot'] = 'Vollständige Webadresse für den Zugriff auf Moodle. Es ist nicht möglich, auf Moodle über unterschiedliche Adressen zuzugreifen. Sollten Sie für Ihre Website mehrere öffentliche Adressen verwenden, so müssen Sie eine Adresse auswählen und für die übrigen Adressen dauerhafte Weiterleitungen einrichten.
-<p>Falls Ihre Website gleichzeitig im Intranet und im Internet erreichbar ist, so tragen Sie hier die öffentliche Adresse ein. Konfigurieren Sie den DNS-Eintrag des Servers so, dass für alle Intranet-Nutzer ebenfalls die öffentliche Adresse erreichbar ist.
+$string['pathssubwwwroot'] = 'Vollständige Webadresse für den Zugriff auf Moodle. Es ist nicht möglich, über unterschiedliche Adressen auf Moodle zuzugreifen. Sollte Ihre Website mehrere öffentliche Adressen verwenden, so müssen Sie eine Adresse festlegen und für die übrigen Adressen dauerhafte Weiterleitungen dorthin einrichten.
+<p>Falls Ihre Website gleichzeitig im Intranet und im Internet erreichbar ist, so tragen Sie die öffentliche Adresse ein. Konfigurieren Sie den DNS so, dass Moodle auch aus dem Intranet über die öffentliche Adresse erreichbar ist.
<p>Führen Sie Ihre Moodle-Installation unbedingt mit der richtigen Adresse durch, weil es andernfalls zu Problemen kommen könnte.';
$string['pathsunsecuredataroot'] = 'Der Speicherort des Verzeichnisses \'dataroot\' ist unsicher';
$string['pathswrongadmindir'] = 'Das Admin-Verzeichnis existiert nicht';
$string['dbprefix'] = 'Prefixo das tabelas';
$string['dirroot'] = 'Diretório Moodle';
$string['environmenthead'] = 'Verificando o ambiente ...';
+$string['environmentsub2'] = 'Cada release do Moodle requer uma versão mÃnima do PHP e diversas extensões do PHP.
+A verificação completa do ambiente é feita antes de cada instalação e atualização.';
$string['errorsinenvironment'] = 'Verificação do Ambiente falhou!';
$string['installation'] = 'Instalação';
$string['langdownloaderror'] = 'Infelizmente o idioma "{$a}" não foi instalado. A instalação vai continuar em Inglês.';
$string['accounts'] = 'Accounts';
$string['additionalhtml'] = 'Additional HTML';
$string['additionalhtml_heading'] = 'Additional HTML to be added to every page.';
-$string['additionalhtml_desc'] = 'These settings allow you to specify HTML that you want added to every page. You can set HTML that will be added within the HEAD tag for the page, immediatly after the BODY tag has been opened, or immediatly before the body tag is closed.<br />Doing this allows you add custom headers or footers on every page, or add support for services like Google Analytics very easily and independent of your chosen theme.';
+$string['additionalhtml_desc'] = 'These settings allow you to specify HTML that you want added to every page. You can set HTML that will be added within the HEAD tag for the page, immediately after the BODY tag has been opened, or immediately before the body tag is closed.<br />Doing this allows you add custom headers or footers on every page, or add support for services like Google Analytics very easily and independent of your chosen theme.';
$string['additionalhtmlhead'] = 'Within HEAD';
$string['additionalhtmlhead_desc'] = 'Content here will be added to the bottom of the HEAD tag for every page.';
$string['additionalhtmltopofbody'] = 'When BODY is opened';
-$string['additionalhtmltopofbody_desc'] = 'Content here will be added in to every page immediatly after the opening body tag.';
+$string['additionalhtmltopofbody_desc'] = 'Content here will be added in to every page immediately after the opening body tag.';
$string['additionalhtmlfooter'] = 'Before BODY is closed';
$string['additionalhtmlfooter_desc'] = 'Content here will be added in to every page right before the body tag is closed.';
$string['adminseesall'] = 'Admins see all';
$string['debugstringids'] = 'Show origin of languages strings';
$string['debugvalidators'] = 'Show validator links';
$string['defaultallowedmodules'] = 'Default allowed modules';
+$string['defaultcity'] = 'Default city';
+$string['defaultcity_help'] = 'A city entered here will be the default city when creating new user accounts.';
$string['defaulthomepage'] = 'Default home page for users';
$string['defaultrequestcategory'] = 'Default category for course requests';
$string['defaultsettinginfo'] = 'Default: {$a}';
$string['postaladdress'] = 'Postal address';
$string['postaladdress_help'] = 'Postal address of this site, or of the entity represented by this site.';
$string['postsnumber'] = 'Number of posts ({$a})';
+$string['previousregistrationdeleted'] = 'The previous registration has been deleted from {$a}. You can restart the registration process. Thank you.';
$string['prioritise'] = 'Prioritise';
$string['privacy'] = 'Privacy';
$string['privacy_help'] = 'The hub may want to display a list of registered sites. If it does then you can choose whether or not you want to appear on that list.';
$string['registrationupdated'] = 'Registration has been updated.';
$string['registrationupdatedfailed'] = 'Registration update failed.';
$string['removefromhub'] = 'Remove from hub';
+$string['renewregistration'] = 'Renew registration';
$string['resourcesnumber'] = 'Number of resources ({$a})';
+$string['restartregistration'] = 'Restart registration';
$string['roleassignmentsnumber'] = 'Number of role assignments ({$a})';
$string['screenshots'] = 'Screenshots';
$string['screenshots_help'] = 'Any screenshots of the course will be displayed in search results.';
$string['sitegeolocation'] = 'Geolocation';
$string['sitegeolocation_help'] = 'In future we may provide location-based searching in the hubs. If you want to specify the location for your site use a latitude/longitude value here (eg: -31.947884,115.871285). One way to find this is to use Google Maps.';
$string['siteemail'] = 'Email address';
-$string['siteemail_help'] = 'You need to provide an email address so the admin can contact you if necessary. This will not be used for any other purpose.';
+$string['siteemail_help'] = 'You need to provide an email address so the hub administrator can contact you if necessary. This will not be used for any other purpose. It is recommended to enter a email address related to a position (example: sitemanager@example.com) and not directly to a person.';
$string['sitelang'] = 'Language';
$string['sitelang_help'] = 'Your site language will be displayed on the site listing.';
$string['sitename'] = 'Name';
* @return int|bool related course id or false
*/
function get_courseid_from_context($context) {
+ if (empty($context->contextlevel)) {
+ debugging('Invalid context object specified in get_courseid_from_context() call');
+ return false;
+ }
if ($context->contextlevel == CONTEXT_COURSE) {
return $context->instanceid;
}
if ($context->contextlevel == CONTEXT_BLOCK) {
$parentcontexts = get_parent_contexts($context, false);
$parent = reset($parentcontexts);
- return get_courseid_from_context($parent);
+ return get_courseid_from_context(get_context_instance_by_id($parent));
}
return false;
*/
class admin_category implements parentable_part_of_admin_tree {
-/** @var mixed An array of part_of_admin_tree objects that are this object's children */
+ /** @var mixed An array of part_of_admin_tree objects that are this object's children */
public $children;
/** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
public $name;
/** @var mixed Either a string or an array or strings */
public $visiblepath;
+ /** @var array fast lookup category cache, all categories of one tree point to one cache */
+ protected $category_cache;
+
/**
* Constructor for an empty admin category
*
* defaults to false
*/
public function locate($name, $findpath=false) {
+ if (is_array($this->category_cache) and !isset($this->category_cache[$this->name])) {
+ // somebody much have purged the cache
+ $this->category_cache[$this->name] = $this;
+ }
+
if ($this->name == $name) {
if ($findpath) {
$this->visiblepath[] = $this->visiblename;
return $this;
}
+ // quick category lookup
+ if (!$findpath and is_array($this->category_cache) and isset($this->category_cache[$name])) {
+ return $this->category_cache[$name];
+ }
+
$return = NULL;
foreach($this->children as $childid=>$unused) {
if ($return = $this->children[$childid]->locate($name, $findpath)) {
foreach($this->children as $precedence => $child) {
if ($child->name == $name) {
- // found it!
+ // clear cache and delete self
+ if (is_array($this->category_cache)) {
+ while($this->category_cache) {
+ // delete the cache, but keep the original array address
+ array_pop($this->category_cache);
+ }
+ }
unset($this->children[$precedence]);
return true;
- }
- if ($this->children[$precedence]->prune($name)) {
+ } else if ($this->children[$precedence]->prune($name)) {
return true;
}
}
return false;
}
$parent->children[] = $something;
+ if (is_array($this->category_cache) and ($something instanceof admin_category)) {
+ if (isset($this->category_cache[$something->name])) {
+ debugging('Duplicate admin catefory name: '.$something->name);
+ } else {
+ $this->category_cache[$something->name] = $something;
+ $something->category_cache =& $this->category_cache;
+ foreach ($something->children as $child) {
+ // just in case somebody already added subcategories
+ if ($child instanceof admin_category) {
+ if (isset($this->category_cache[$child->name])) {
+ debugging('Duplicate admin catefory name: '.$child->name);
+ } else {
+ $this->category_cache[$child->name] = $child;
+ $child->category_cache =& $this->category_cache;
+ }
+ }
+ }
+ }
+ }
return true;
} else {
$this->fulltree = $fulltree;
$this->loaded = false;
+ $this->category_cache = array();
+
// load custom defaults if found
$this->custom_defaults = null;
$defaultsfile = "$CFG->dirroot/local/defaults.php";
$this->children = array();
$this->fulltree = ($requirefulltree || $this->fulltree);
$this->loaded = false;
+ //break circular dependencies - this helps PHP 5.2
+ while($this->category_cache) {
+ array_pop($this->category_cache);
+ }
+ $this->category_cache = array();
}
}
* This function must be called on each admin page before other code.
*
* @global moodle_page $PAGE
- *
+ *
* @param string $section name of page
* @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
* @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
$PAGE->set_cacheable(false);
return;
}
-
+
// Locate the current item on the navigation and make it active when found.
$path = $extpage->path;
$node = $PAGE->settingsnav;
<?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20110206" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20110209" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
<TABLE NAME="registration_hubs" COMMENT="hub where the site is registered on with their associated token" PREVIOUS="license" NEXT="backup_controllers">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="token"/>
- <FIELD NAME="token" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="hubname"/>
+ <FIELD NAME="token" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="the token to communicate with the hub by web service" PREVIOUS="id" NEXT="hubname"/>
<FIELD NAME="hubname" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="token" NEXT="huburl"/>
<FIELD NAME="huburl" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="hubname" NEXT="confirmed"/>
- <FIELD NAME="confirmed" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="huburl"/>
+ <FIELD NAME="confirmed" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="huburl" NEXT="secret"/>
+ <FIELD NAME="secret" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="the unique site identifier for this hub" PREVIOUS="confirmed"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
}
if ($oldversion < 2010061900.10) {
- // migrate existing setup of meta courses
+ // migrate existing setup of meta courses, ignore records referencing invalid courses
$sql = "INSERT INTO {enrol} (enrol, status, courseid, sortorder, customint1)
- SELECT 'meta', 0, parent_course, 5, child_course
- FROM {course_meta}";
+ SELECT 'meta', 0, cm.parent_course, 5, cm.child_course
+ FROM {course_meta} cm
+ JOIN {course} p ON p.id = cm.parent_course
+ JOIN {course} c ON c.id = cm.child_course";
$DB->execute($sql);
upgrade_main_savepoint(true, 2010061900.10);
upgrade_main_savepoint(true, 2011020200.01);
}
+ if ($oldversion < 2011020900.07) {
+ $DB->delete_records('course_display', array('display' => 0));
+ upgrade_main_savepoint(true, 2011020900.07);
+ }
+
+ if ($oldversion < 2011020900.08) {
+ // Define field secret to be added to registration_hubs
+ $table = new xmldb_table('registration_hubs');
+ $field = new xmldb_field('secret', XMLDB_TYPE_CHAR, '255', null, null, null,
+ $CFG->siteidentifier, 'confirmed');
+
+ // Conditionally launch add field secret
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ // Main savepoint reached
+ upgrade_main_savepoint(true, 2011020900.08);
+ }
+
return true;
}
public $rename_key_sql = null; //SQL sentence to rename one key (PostgreSQL doesn't support this!)
//TABLENAME, OLDKEYNAME, NEWKEYNAME are dynamically replaced
+ protected $std_strings = null; // '' or \' quotes
+
/**
* Reset a sequence to the id field of a table.
* @param string $table name of table or xmldb_table object
return $results;
}
+ public function addslashes($s) {
+ // Postgres is gradually switching to ANSI quotes, we need to check what is expected
+ if (!isset($this->std_strings)) {
+ $this->std_strings = ($this->mdb->get_field_sql("select setting from pg_settings where name = 'standard_conforming_strings'") === 'on');
+ }
+
+ if ($this->std_strings) {
+ $s = str_replace("'", "''", $s);
+ } else {
+ // do not use php addslashes() because it depends on PHP quote settings!
+ $s = str_replace('\\','\\\\',$s);
+ $s = str_replace("\0","\\\0", $s);
+ $s = str_replace("'", "\\'", $s);
+ }
+
+ return $s;
+ }
+
/**
* Given one xmldb_table returns one string with the sequence of the table
* in the table (fetched from DB)
throw new dml_exception('dbdriverproblem', $driverstatus);
}
- if (empty($this->dboptions['dbport'])) {
+ if (!empty($dboptions['dbsocket'])
+ and (strpos($dboptions['dbsocket'], '/') !== false or strpos($dboptions['dbsocket'], '\\') !== false)) {
+ $dbsocket = $dboptions['dbsocket'];
+ } else {
+ $dbsocket = ini_get('mysqli.default_socket');
+ }
+ if (empty($dboptions['dbport'])) {
$dbport = (int)ini_get('mysqli.default_port');
} else {
- $dbport = (int)$this->dboptions['dbport'];
+ $dbport = (int)$dboptions['dbport'];
}
// verify ini.get does not return nonsense
if (empty($dbport)) {
$dbport = 3306;
}
ob_start();
- $conn = new mysqli($dbhost, $dbuser, $dbpass, '', $dbport); /// Connect without db
+ $conn = new mysqli($dbhost, $dbuser, $dbpass, '', $dbport, $dbsocket); /// Connect without db
$dberr = ob_get_contents();
ob_end_clean();
$errorno = @$conn->connect_errno;
* @return string sql
*/
private function limit_to_top_n($sql, $offset, $limit) {
- // If there is no limit we can return immediatly
+ // If there is no limit we can return immediately
if ($limit < 1 && $offset < 1) {
return $sql;
}
return null;
}
- // relink embedded files if text submitted - no absolute links allowed in database!
- if ($CFG->slasharguments) {
- $draftbase = "$CFG->wwwroot/draftfile.php/$usercontext->id/user/draft/$draftitemid/";
- } else {
- $draftbase = "$CFG->wwwroot/draftfile.php?file=/$usercontext->id/user/draft/$draftitemid/";
+ $wwwroot = $CFG->wwwroot;
+ if ($forcehttps) {
+ $wwwroot = str_replace('http://', 'https://', $wwwroot);
}
- if ($forcehttps) {
- $draftbase = str_replace('http://', 'https://', $draftbase);
+ // relink embedded files if text submitted - no absolute links allowed in database!
+ $text = str_ireplace("$wwwroot/draftfile.php/$usercontext->id/user/draft/$draftitemid/", '@@PLUGINFILE@@/', $text);
+
+ if (strpos($text, 'draftfile.php?file=') !== false) {
+ $matches = array();
+ preg_match_all("!$wwwroot/draftfile.php\?file=%2F{$usercontext->id}%2Fuser%2Fdraft%2F{$draftitemid}%2F[^'\",&<>|`\s:\\\\]+!iu", $text, $matches);
+ if ($matches) {
+ foreach ($matches[0] as $match) {
+ $replace = str_ireplace('%2F', '/', $match);
+ $text = str_replace($match, $replace, $text);
+ }
+ }
+ $text = str_ireplace("$wwwroot/draftfile.php?file=/$usercontext->id/user/draft/$draftitemid/", '@@PLUGINFILE@@/', $text);
}
- $text = str_ireplace($draftbase, '@@PLUGINFILE@@/', $text);
return $text;
}
$ranges = false;
}
if ($ranges) {
- $handle = fopen($filename, 'rb');
+ $handle = fopen($path, 'rb');
byteserving_send_file($handle, $mimetype, $ranges, $filesize);
}
}
window.close();
}
-/**
- * Close the current browser window, forcing the window/tab that opened this
- * popup to reload itself. */
-function close_window_reloading_opener() {
- if (window.opener) {
- window.opener.location.reload(1);
- close_window({});
- // Intentionally, only try to close the window if there is some evidence we are in a popup.
- }
-}
-
/**
* Used in a couple of modules to hide navigation areas when using AJAX
*/
* contexturlname - the display text for contexturl
*
* @param object $eventdata information about the message (component, userfrom, userto, ...)
- * @return boolean success
+ * @return int|false the ID of the new message or false if there was a problem with a processor
*/
function message_send($eventdata) {
global $CFG, $DB;
+ //new message ID to return
+ $messageid = false;
+
//TODO: we need to solve problems with database transactions here somehow, for now we just prevent transactions - sorry
$DB->transactions_forbidden();
- //flag we'll return indicating that all processors ran successfully
- $success = true;
-
if (is_int($eventdata->userto)) {
mtrace('message_send() userto is a user ID when it should be a user object');
$eventdata->userto = $DB->get_record('user', array('id' => $eventdata->useridto));
if ($processor=='none' && $savemessage->notification) {
//if they have deselected all processors and its a notification mark it read. The user doesnt want to be bothered
$savemessage->timeread = time();
- $DB->insert_record('message_read', $savemessage);
+ $messageid = $DB->insert_record('message_read', $savemessage);
} else { // Process the message
// Store unread message just in case we can not send it
- $savemessage->id = $DB->insert_record('message', $savemessage);
+ $messageid = $savemessage->id = $DB->insert_record('message', $savemessage);
$eventdata->savedmessageid = $savemessage->id;
// Try to deliver the message to each processor
if (!$pclass->send_message($eventdata)) {
debugging('Error calling message processor '.$procname);
- $success = false;
+ $messageid = false;
}
}
} else {
debugging('Error finding message processor '.$procname);
- $success = false;
+ $messageid = false;
}
}
//unread. To prevent this mark the message read if messaging is disabled
if (empty($CFG->messaging)) {
require_once($CFG->dirroot.'/message/lib.php');
- message_mark_message_read($savemessage, time());
+ $messageid = message_mark_message_read($savemessage, time());
} else if ( $DB->count_records('message_working', array('unreadmessageid' => $savemessage->id)) == 0){
//if there is no more processors that want to process this we can move message to message_read
require_once($CFG->dirroot.'/message/lib.php');
- message_mark_message_read($savemessage, time(), true);
+ $messageid = message_mark_message_read($savemessage, time(), true);
}
}
}
- return $success;
+ return $messageid;
}
* @package Minify
*/
+defined('MOODLE_INTERNAL') || die();
/**
- * In 'debug' mode, Minify can combine files with no minification and
- * add comments to indicate line #s of the original files.
- *
- * To allow debugging, set this option to true and add "&debug=1" to
+ * In 'debug' mode, Minify can combine files with no minification and
+ * add comments to indicate line #s of the original files.
+ *
+ * To allow debugging, set this option to true and add "&debug=1" to
* a URI. E.g. /min/?f=script1.js,script2.js&debug=1
*/
$min_allowDebugFlag = ($CFG->debug);
* Set to false for no error logging (Minify may be slightly faster).
* @link http://www.firephp.org/
*
- * If you want to use a custom error logger, set this to your logger
+ * If you want to use a custom error logger, set this to your logger
* instance. Your object should have a method log(string $message).
*
* @todo cache system does not have error logging yet.
/**
- * Allow use of the Minify URI Builder app. If you no longer need
+ * Allow use of the Minify URI Builder app. If you no longer need
* this, set to false.
**/
-$min_enableBuilder = true;
+$min_enableBuilder = false;
/**
/**
* Leave an empty string to use PHP's $_SERVER['DOCUMENT_ROOT'].
*
- * On some servers, this value may be misconfigured or missing. If so, set this
+ * On some servers, this value may be misconfigured or missing. If so, set this
* to your full document root path with no trailing slash.
* E.g. '/home/accountname/public_html' or 'c:\\xampp\\htdocs'
*
- * If /min/ is directly inside your document root, just uncomment the
+ * If /min/ is directly inside your document root, just uncomment the
* second line. The third line might work on some Apache servers.
*/
$min_documentRoot = $CFG->dirroot.'/lib/minify';
/**
- * Cache file locking. Set to false if filesystem is NFS. On at least one
+ * Cache file locking. Set to false if filesystem is NFS. On at least one
* NFS system flock-ing attempts stalled PHP for 30 seconds!
*/
$min_cacheFileLocking = true;
/**
* Combining multiple CSS files can place @import declarations after rules, which
* is invalid. Minify will attempt to detect when this happens and place a
- * warning comment at the top of the CSS output. To resolve this you can either
- * move the @imports within your CSS files, or enable this option, which will
- * move all @imports to the top of the output. Note that moving @imports could
+ * warning comment at the top of the CSS output. To resolve this you can either
+ * move the @imports within your CSS files, or enable this option, which will
+ * move all @imports to the top of the output. Note that moving @imports could
* affect CSS values (which is why this option is disabled by default).
*/
$min_serveOptions['bubbleCssImports'] = false;
* particular directories below DOCUMENT_ROOT, set this here.
* You will still need to include the directory in the
* f or b GET parameters.
- *
- * // = shortcut for DOCUMENT_ROOT
+ *
+ * // = shortcut for DOCUMENT_ROOT
*/
//$min_serveOptions['minApp']['allowDirs'] = array('//js', '//css');
* If you minify CSS files stored in symlink-ed directories, the URI rewriting
* algorithm can fail. To prevent this, provide an array of link paths to
* target paths, where the link paths are within the document root.
- *
- * Because paths need to be normalized for this to work, use "//" to substitute
+ *
+ * Because paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.:
* <code>
* array('//symlink' => '/real/target/path') // unix
/**
* If you upload files from Windows to a non-Windows server, Windows may report
- * incorrect mtimes for the files. This may cause Minify to keep serving stale
+ * incorrect mtimes for the files. This may cause Minify to keep serving stale
* cache files when source file changes are made too frequently (e.g. more than
* once an hour).
- *
- * Immediately after modifying and uploading a file, use the touch command to
+ *
+ * Immediately after modifying and uploading a file, use the touch command to
* update the mtime on the server. If the mtime jumps ahead by a number of hours,
- * set this variable to that number. If the mtime moves back, this should not be
+ * set this variable to that number. If the mtime moves back, this should not be
* needed.
*
- * In the Windows SFTP client WinSCP, there's an option that may fix this
- * issue without changing the variable below. Under login > environment,
+ * In the Windows SFTP client WinSCP, there's an option that may fix this
+ * issue without changing the variable below. Under login > environment,
* select the option "Adjust remote timestamp with DST".
* @link http://winscp.net/eng/docs/ui_login_environment#daylight_saving_time
*/
/**
- * Path to Minify's lib folder. If you happen to move it, change
+ * Path to Minify's lib folder. If you happen to move it, change
* this accordingly.
*/
$min_libPath = $CFG->libdir . '/minify/lib';
/// Get various settings and preferences
- if ($displays = $DB->get_records('course_display', array('userid'=>$user->id))) {
- foreach ($displays as $display) {
- $user->display[$display->course] = $display->display;
- }
- }
-
// preload preference cache
check_user_preferences_loaded($user);
// this is a special hack to speedup calendar display
$user->groupmember = array();
- if ($groups = $DB->get_records_sql($sql, array($user->id))) {
- foreach ($groups as $group) {
- if (!array_key_exists($group->courseid, $user->groupmember)) {
- $user->groupmember[$group->courseid] = array();
+ if (!isguestuser($user)) {
+ if ($groups = $DB->get_records_sql($sql, array($user->id))) {
+ foreach ($groups as $group) {
+ if (!array_key_exists($group->courseid, $user->groupmember)) {
+ $user->groupmember[$group->courseid] = array();
+ }
+ $user->groupmember[$group->courseid][$group->id] = $group->id;
}
- $user->groupmember[$group->courseid][$group->id] = $group->id;
}
}
/// Add the custom profile fields to the user record
- require_once($CFG->dirroot.'/user/profile/lib.php');
- profile_load_custom_fields($user);
+ $user->profile = array();
+ if (!isguestuser($user)) {
+ require_once($CFG->dirroot.'/user/profile/lib.php');
+ profile_load_custom_fields($user);
+ }
/// Rewrite some variables if necessary
if (!empty($user->description)) {
$this->add_course_essentials($coursenode, $course);
// Load the course sections into the page
$sections = $this->load_course_sections($course, $coursenode);
- if ($course->id !== SITEID) {
+ if ($course->id != SITEID) {
// Find the section for the $CM associated with the page and collect
// its section number.
if (isset($cm->sectionnum)) {
}
// Load each extending user into the navigation.
foreach ($this->extendforuser as $user) {
- if ($user->id !== $USER->id) {
+ if ($user->id != $USER->id) {
$this->load_for_user($user);
}
}
$viewhiddensections = has_capability('moodle/course:viewhiddensections', $this->page->context);
- if (isloggedin() && !isguestuser()) {
- $activesection = $DB->get_field("course_display", "display", array("userid"=>$USER->id, "course"=>$course->id));
- } else {
- $activesection = null;
- }
+ $activesection = course_get_display($course->id);
$namingfunction = 'callback_'.$courseformat.'_get_section_name';
$namingfunctionexists = (function_exists($namingfunction));
$sectionnode = $coursenode->add($sectionname, $url, navigation_node::TYPE_SECTION, null, $section->id);
$sectionnode->nodetype = navigation_node::NODETYPE_BRANCH;
$sectionnode->hidden = (!$section->visible);
- if ($this->page->context->contextlevel != CONTEXT_MODULE && ($sectionnode->isactive || ($activesection != null && $section->section == $activesection))) {
+ if ($this->page->context->contextlevel != CONTEXT_MODULE && ($sectionnode->isactive || ($activesection && $section->section == $activesection))) {
$sectionnode->force_open();
$this->load_section_activities($sectionnode, $section->section, $modinfo);
}
*/
protected function load_activity($cm, stdClass $course, navigation_node $activity) {
global $CFG, $DB;
-
+
// make sure we have a $cm from get_fast_modinfo as this contains activity access details
if (!($cm instanceof cm_info)) {
$modinfo = get_fast_modinfo($course);
// Get the course set against the page, by default this will be the site
$course = $this->page->course;
$baseargs = array('id'=>$user->id);
- if ($course->id !== SITEID && (!$iscurrentuser || $forceforcontext)) {
+ if ($course->id != SITEID && (!$iscurrentuser || $forceforcontext)) {
if (array_key_exists($course->id, $this->mycourses)) {
$coursenode = $this->mycourses[$course->id]->coursenode;
} else {
*/
public function add_course(stdClass $course, $forcegeneric = false) {
global $CFG;
- $canviewhidden = has_capability('moodle/course:viewhiddencourses', $this->page->context);
- if ($course->id !== SITEID && !$canviewhidden && !$course->visible) {
- return false;
+
+ if ($course->id != SITEID) {
+ if (!$course->visible) {
+ if (is_role_switched($course->id)) {
+ // user has to be able to access course in order to switch, let's skip the visibility test here
+ } else if (!has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))) {
+ return false;
+ }
+ }
}
$issite = ($course->id == SITEID);
$availableroles = get_switchable_roles($coursecontext);
if (is_array($availableroles)) {
foreach ($availableroles as $key=>$role) {
- if ($assumedrole===(int)$key) {
+ if ($assumedrole == (int)$key) {
continue;
}
$roles[$key] = $role;
return '';
}
if ($item->action instanceof action_link) {
- //TODO: to be replaced with something else
$link = $item->action;
if ($item->hidden) {
$link->add_class('dimmed');
}
- $content = $this->output->render($link);
+ $link->text = $content.$link->text; // add help icon
+ $content = $this->render($link);
} else if ($item->action instanceof moodle_url) {
$attributes = array();
if ($title !== '') {
*
* Typical usage would be
* <pre>
- * $PAGE->requires->init_js_call('M.mod_forum.init_view');
+ * $PAGE->requires->js_init_call('M.mod_forum.init_view');
* </pre>
*
* It also supports obsoleted coding style withouth YUI3 modules.
$user = null;
if (!empty($CFG->opentogoogle) and !NO_MOODLE_COOKIES) {
- if (!empty($_SERVER['HTTP_USER_AGENT'])) {
- // allow web spiders in as guest users
- if (strpos($_SERVER['HTTP_USER_AGENT'], 'Googlebot') !== false ) {
- $user = guest_user();
- } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'google.com') !== false ) { // Google
- $user = guest_user();
- } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Yahoo! Slurp') !== false ) { // Yahoo
- $user = guest_user();
- } else if (strpos($_SERVER['HTTP_USER_AGENT'], '[ZSEBOT]') !== false ) { // Zoomspider
- $user = guest_user();
- } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSNBOT') !== false ) { // MSN Search
- $user = guest_user();
- }
+ if (is_web_crawler()) {
+ $user = guest_user();
}
if (!empty($CFG->guestloginbutton) and !$user and !empty($_SERVER['HTTP_REFERER'])) {
// automaticaly log in users coming from search engine results
return false;
}
+/**
+ * Checks if current user is a web crawler.
+ *
+ * This list can not be made complete, this is not a security
+ * restriction, we make the list only to help these sites
+ * especially when automatic guest login is disabled.
+ *
+ * If admin needs security they should enable forcelogin
+ * and disable guest access!!
+ *
+ * @return bool
+ */
+function is_web_crawler() {
+ if (!empty($_SERVER['HTTP_USER_AGENT'])) {
+ if (strpos($_SERVER['HTTP_USER_AGENT'], 'Googlebot') !== false ) {
+ return true;
+ } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'google.com') !== false ) { // Google
+ return true;
+ } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Yahoo! Slurp') !== false ) { // Yahoo
+ return true;
+ } else if (strpos($_SERVER['HTTP_USER_AGENT'], '[ZSEBOT]') !== false ) { // Zoomspider
+ return true;
+ } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSNBOT') !== false ) { // MSN Search
+ return true;
+ } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Yandex') !== false ) {
+ return true;
+ } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'AltaVista') !== false ) {
+ return true;
+ }
+ }
+ return false;
+}
/**
* This class solves the problem of how to initialise $OUTPUT.
}
if ($reloadopener) {
- $function = 'close_window_reloading_opener';
- } else {
- $function = 'close_window';
+ // Trigger the reload immediately, even if the reload is after a delay.
+ $PAGE->requires->js_function_call('window.opener.location.reload', array(true));
}
- echo '<p class="centerpara">' . get_string('windowclosing') . '</p>';
+ $OUTPUT->notification(get_string('windowclosing'), 'notifysuccess');
- $PAGE->requires->js_function_call($function, null, false, $delay);
+ $PAGE->requires->js_function_call('close_window', array(new stdClass()), false, $delay);
echo $OUTPUT->footer();
exit;
* @return string|void If $return is false, returns nothing, otherwise returns a string of HTML.
*/
function print_collapsible_region($contents, $classes, $id, $caption, $userpref = '', $default = false, $return = false) {
- $output = print_collapsible_region_start($classes, $id, $caption, $userpref, true, true);
+ $output = print_collapsible_region_start($classes, $id, $caption, $userpref, $default, true);
$output .= $contents;
$output .= print_collapsible_region_end(true);
* be clicked to expand or collapse the region. If JavaScript is off, then the region
* will always be expanded.
*
- * @global object
* @param string $classes class names added to the div that is output.
* @param string $id id added to the div that is output. Must not be blank.
* @param string $caption text displayed at the top. Clicking on this will cause the region to expand or contract.
- * @param boolean $userpref the name of the user preference that stores the user's preferred default state.
+ * @param string $userpref the name of the user preference that stores the user's preferred default state.
* (May be blank if you do not wish the state to be persisted.
* @param boolean $default Initial collapsed state to use if the user_preference it not set.
* @param boolean $return if true, return the HTML as a string, rather than printing it.
* @return string|void if $return is false, returns nothing, otherwise returns a string of HTML.
*/
-function print_collapsible_region_start($classes, $id, $caption, $userpref = false, $default = false, $return = false) {
+function print_collapsible_region_start($classes, $id, $caption, $userpref = '', $default = false, $return = false) {
global $CFG, $PAGE, $OUTPUT;
// Work out the initial state.
- if (is_string($userpref)) {
+ if (!empty($userpref) and is_string($userpref)) {
user_preference_allow_ajax_update($userpref, PARAM_BOOL);
$collapsed = get_user_preferences($userpref, $default);
} else {
$urltogo = $CFG->wwwroot.'/user/edit.php';
// We don't delete $SESSION->wantsurl yet, so we get there later
- } else if (isset($SESSION->wantsurl) and (strpos($SESSION->wantsurl, $CFG->wwwroot) === 0)) {
+ } else if (isset($SESSION->wantsurl) and (strpos($SESSION->wantsurl, $CFG->wwwroot) === 0 or strpos($SESSION->wantsurl, str_replace('http://', 'https://', $CFG->wwwroot)) === 0)) {
$urltogo = $SESSION->wantsurl; /// Because it's an address in this site
unset($SESSION->wantsurl);
$mform->addElement('text', 'city', get_string('city'), 'maxlength="120" size="20"');
$mform->setType('city', PARAM_TEXT);
$mform->addRule('city', get_string('missingcity'), 'required', null, 'server');
+ if (!empty($CFG->defaultcity)) {
+ $mform->setDefault('city', $CFG->defaultcity);
+ }
$country = get_string_manager()->get_list_of_countries();
$default_country[''] = get_string('selectacountry');
$history = optional_param('history', MESSAGE_HISTORY_SHORT, PARAM_INT);
$search = optional_param('search', '', PARAM_CLEAN); //TODO: use PARAM_RAW, but make sure we use s() and p() properly
-$user1id = optional_param('user', $USER->id, PARAM_INT);
-$user2id = optional_param('id', 0, PARAM_INT);
+//the same param as 1.9 and the param we have been logging. Use this parameter.
+$user1id = optional_param(MESSAGE_USER1_PARAM, $USER->id, PARAM_INT);
+//2.0 shipped using this param. Retaining it only for compatibility. It should be removed.
+$user1id = optional_param('user', $user1id, PARAM_INT);
+
+//the same param as 1.9 and the param we have been logging. Use this parameter.
+$user2id = optional_param(MESSAGE_USER2_PARAM, 0, PARAM_INT);
+//2.0 shipped using this param. Retaining it only for compatibility. It should be removed.
+$user2id = optional_param('id', $user2id, PARAM_INT);
$addcontact = optional_param('addcontact', 0, PARAM_INT); // adding a contact
$removecontact = optional_param('removecontact', 0, PARAM_INT); // removing a contact
$messageid = message_post_message($user1, $user2, $data->message, FORMAT_MOODLE, 'direct');
if (!empty($messageid)) {
+ //including the id of the user sending the message in the logged URL so the URL works for admins
+ //note message ID may be misleading as the message may potentially get a different ID when moved from message to message_read
+ add_to_log(SITEID, 'message', 'write', 'index.php?user='.$user1->id.'&id='.$user2->id.'&history=1#m'.$messageid, $user1->id);
redirect($CFG->wwwroot . '/message/index.php?usergroup='.$usergroup.'&id='.$user2->id);
}
}
define('VIEW_COURSE','course_');
define('VIEW_SEARCH','search');
+define('MESSAGE_USER1_PARAM','user1');
+define('MESSAGE_USER2_PARAM','user2');
+
define('SHOW_ACTION_LINKS_IN_CONTACT_LIST', true);
define('MESSAGE_SEARCH_MAX_RESULTS', 200);
/**
* Inserts a message into the database, but also forwards it
* via other means if appropriate.
+ * @return int|false the ID of the new message or false
*/
function message_post_message($userfrom, $userto, $message, $format, $messagetype) {
global $SITE, $CFG, $USER;
* @param message an object with an object property ie $message->id which is an id in the message table
* @param int $timeread the timestamp for when the message should be marked read. Usually time().
* @param bool $messageworkingempty Is the message_working table already confirmed empty for this message?
-* @return void
+* @return int the ID of the message in the message_read table
*/
function message_mark_message_read($message, $timeread, $messageworkingempty=false) {
global $DB;
if (!$messageworkingempty) {
$DB->delete_records('message_working', array('unreadmessageid' => $messageid));
}
- $DB->insert_record('message_read', $message);
+ $messagereadid = $DB->insert_record('message_read', $message);
$DB->delete_records('message', array('id' => $messageid));
+ return $messagereadid;
}
}
}
$this->add_checkbox_controller(1, null, null, 1);
- $this->add_action_buttons(true, get_string('exportdatabaserecords', 'data'));
+ $this->add_action_buttons(true, get_string('exportentries', 'data'));
}
}
if ($id) {
$url->param('id', $id);
$PAGE->set_url($url);
- if (! $cm = get_coursemodule_from_id('data', $id)) {
- print_error('invalidcoursemodule');
- }
- if (! $course = $DB->get_record('course', array('id'=>$cm->course))) {
- print_error('coursemisconf');
- }
- if (! $data = $DB->get_record('data', array('id'=>$cm->instance))) {
- print_error('invalidcoursemodule');
- }
+ $cm = get_coursemodule_from_id('data', $id, 0, false, MUST_EXIST);
+ $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST);
+ $data = $DB->get_record('data', array('id'=>$cm->instance), '*', MUST_EXIST);
} else {
$url->param('d', $d);
$PAGE->set_url($url);
- if (! $data = $DB->get_record('data', array('id'=>$d))) {
- print_error('invalidid', 'data');
- }
- if (! $course = $DB->get_record('course', array('id'=>$data->course))) {
- print_error('coursemisconf');
- }
- if (! $cm = get_coursemodule_from_instance('data', $data->id, $course->id)) {
- print_error('coursemisconf');
- }
+ $data = $DB->get_record('data', array('id'=>$d), '*', MUST_EXIST);
+ $course = $DB->get_record('course', array('id'=>$data->course), '*', MUST_EXIST);
+ $cm = get_coursemodule_from_instance('data', $data->id, $course->id, false, MUST_EXIST);
}
require_login($course, false, $cm);
$PAGE->navbar->add(get_string('add', 'data'));
$PAGE->set_title($data->name);
$PAGE->set_heading($course->fullname);
+navigation_node::override_active_url(new moodle_url('/mod/data/import.php', array('d' => $data->id)));
echo $OUTPUT->header();
-echo $OUTPUT->heading(format_string($data->name));
+echo $OUTPUT->heading_with_help(get_string('uploadrecords', 'mod_data'), 'uploadrecords', 'mod_data');
/// Groups needed for Add entry tab
$currentgroup = groups_get_activity_group($cm);
/// Upload records section. Only for teachers and the admin.
echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide');
require_once('import_form.php');
- echo $OUTPUT->heading(get_string('uploadrecords', 'data'), 3);
$form = new mod_data_import_form(new moodle_url('/mod/data/import.php'));
$formdata = new stdClass();
$formdata->d = $data->id;
$string['export'] = 'Export';
$string['exportaszip'] = 'Export as zip';
$string['exportaszip_help'] = 'The export as zip feature allows you to save the templates and fields as a preset zip for download. The zip may then be imported to another course.';
-$string['exportdatabaserecords'] = 'Export database records';
$string['exportedtozip'] = 'Exported to temporary zip...';
+$string['exportentries'] = 'Export entries';
$string['exportownentries'] = 'Export your own entries only? ({$a->mine}/{$a->all})';
$string['failedpresetdelete'] = 'Error deleting a preset!';
$string['fieldadded'] = 'Field added';
$string['chooseexportformat'] = 'Choose the format you wish to export to:';
$string['chooseorupload'] = 'Choose file';
$string['expired'] = 'Sorry, this activity closed on {$a} and is no longer available';
+$string['importentries'] = 'Import entries';
$string['importsuccess'] = 'The preset has been successfully applied.';
$string['insufficiententries'] = 'more entries needed to view this database';
$string['intro'] = 'Introduction';
$string['updatefield'] = 'Update an existing field';
$string['uploadfile'] = 'Upload file';
$string['uploadrecords'] = 'Upload entries from a file';
+$string['uploadrecords_help'] = 'Entries may be uploaded via text file. The format of the file should be as follows:
+
+* Each line of the file contains one record
+* Each record is a series of data separated by commas (or other delimiters)
+* The first record contains a list of fieldnames defining the format of the rest of the file
+
+The field enclosure is a character that surrounds each field in each record. It can normally be left unset.';
+$string['uploadrecords_link'] = 'mod/data/import';
$string['url'] = 'Url';
$string['usestandard'] = 'Use a preset';
$string['usestandard_help'] = 'To use a preset available to the whole site, select it from the list. (If you have added a preset to the list using the save as preset feature then you have the option of deleting it.)';
if (has_capability(DATA_CAP_EXPORT, $PAGE->cm->context)) {
// The capability required to Export database records is centrally defined in 'lib.php'
// and should be weaker than those required to edit Templates, Fields and Presets.
- $datanode->add(get_string('export', 'data'), new moodle_url('/mod/data/export.php', array('d'=>$data->id)));
+ $datanode->add(get_string('exportentries', 'data'), new moodle_url('/mod/data/export.php', array('d'=>$data->id)));
}
if (has_capability('mod/data:manageentries', $PAGE->cm->context)) {
- $datanode->add(get_string('import'), new moodle_url('/mod/data/import.php', array('d'=>$data->id)));
+ $datanode->add(get_string('importentries', 'data'), new moodle_url('/mod/data/import.php', array('d'=>$data->id)));
}
if (has_capability('mod/data:managetemplates', $PAGE->cm->context)) {
} else {
// If no course is specified, then the user can see SITE + his courses.
$courses1 = $DB->get_records('course', array('id' => SITEID));
- $courses2 = enrol_get_users_courses($userid, true);
+ $courses2 = enrol_get_users_courses($userid, true, array('modinfo'));
$courses = array_merge($courses1, $courses2);
}
if (!$courses) {
$select = array();
if (!$forum->viewhiddentimedposts) {
- $select[] = "(d.userid = :userid OR (d.timestart < : AND (d.timeend = 0 OR d.timeend > :timeend)))";
- $params = array('userid'=>$USER->id, 'timestart'=>$now, 'timeend'=>$now);
+ $select[] = "(d.userid = :userid{$forumid} OR (d.timestart < :timestart{$forumid} AND (d.timeend = 0 OR d.timeend > :timeend{$forumid})))";
+ $params = array_merge($params, array('userid'.$forumid=>$USER->id, 'timestart'.$forumid=>$now, 'timeend'.$forumid=>$now));
}
$cm = $forum->cm;
if ($forum->type == 'qanda'
&& !has_capability('mod/forum:viewqandawithoutposting', $context)) {
if (!empty($forum->onlydiscussions)) {
- list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forum->onlydiscussions, SQL_PARAMS_NAMED, 'qanda0');
+ list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forum->onlydiscussions, SQL_PARAMS_NAMED, 'qanda'.$forumid.'_0000');
$params = array_merge($params, $discussionid_params);
$select[] = "(d.id $discussionid_sql OR p.parent = 0)";
} else {
}
if (!empty($forum->onlygroups)) {
- list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($forum->onlygroups, SQL_PARAMS_NAMED, 'grps0');
+ list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($forum->onlygroups, SQL_PARAMS_NAMED, 'grps'.$forumid.'_0000');
$params = array_merge($params, $groupid_params);
$select[] = "d.groupid $groupid_sql";
}
if ($select) {
$selects = implode(" AND ", $select);
- $where[] = "(d.forum = :forum AND $selects)";
- $params['forum'] = $forumid;
+ $where[] = "(d.forum = :forum{$forumid} AND $selects)";
+ $params['forum'.$forumid] = $forumid;
} else {
$fullaccess[] = $forumid;
}
echo '<div class="user-content">';
if ($course->id == SITEID) {
- if (empty($CFG->forceloginforprofiles) || isloggedin()) {
+ $searchcourse = SITEID;
+ if (empty($CFG->forceloginforprofiles) or (isloggedin() and !isguestuser() and !is_web_crawler())) {
// Search throughout the whole site.
$searchcourse = 0;
- } else {
- $searchcourse = SITEID;
}
} else {
// Search only for posts the user made in this course.
$string['allowduplicatedentries_help'] = 'If enabled, multiple entries can have the same concept name.';
$string['allowprintview'] = 'Allow print view';
$string['allowprintview_help'] = 'If enabled, students are provided with a link to a printer-friendly version of the glossary. The link is always available to teachers.';
+$string['andmorenewentries'] = 'and {$a} more new entries.';
$string['answer'] = 'Answer';
$string['approve'] = 'Approve';
$string['areyousuredelete'] = 'Are you sure you want to delete this entry?';
global $CFG, $USER, $DB, $OUTPUT;
//TODO: use timestamp in approved field instead of changing timemodified when approving in 2.0
+ if (!defined('GLOSSARY_RECENT_ACTIVITY_LIMIT')) {
+ define('GLOSSARY_RECENT_ACTIVITY_LIMIT', 50);
+ }
$modinfo = get_fast_modinfo($course);
$ids = array();
+
foreach ($modinfo->cms as $cm) {
if ($cm->modname != 'glossary') {
continue;
if (!$cm->uservisible) {
continue;
}
- $ids[$cm->instance] = $cm->instance;
+ $ids[$cm->instance] = $cm->id;
}
if (!$ids) {
return false;
}
- $glist = implode(',', $ids); // there should not be hundreds of glossaries in one course, right?
-
- if (!$entries = $DB->get_records_sql("SELECT ge.id, ge.concept, ge.approved, ge.timemodified, ge.glossaryid,
- ge.userid, u.firstname, u.lastname, u.email, u.picture
- FROM {glossary_entries} ge
- JOIN {user} u ON u.id = ge.userid
- WHERE ge.glossaryid IN ($glist) AND ge.timemodified > ?
- ORDER BY ge.timemodified ASC", array($timestart))) {
- return false;
+ // generate list of approval capabilities for all glossaries in the course.
+ $approvals = array();
+ foreach ($ids as $glinstanceid => $glcmid) {
+ $context = get_context_instance(CONTEXT_MODULE, $glcmid);
+ // get records glossary entries that are approved if user has no capability to approve entries.
+ if (has_capability('mod/glossary:approve', $context)) {
+ $approvals[] = ' ge.glossaryid = :glsid'.$glinstanceid.' ';
+ } else {
+ $approvals[] = ' (ge.approved = 1 AND ge.glossaryid = :glsid'.$glinstanceid.') ';
+ }
+ $params['glsid'.$glinstanceid] = $glinstanceid;
}
- $editor = array();
+ $selectsql = 'SELECT ge.id, ge.concept, ge.approved, ge.timemodified, ge.glossaryid,
+ ge.userid, '.user_picture::fields('u',null,'uid');
+ $countsql = 'SELECT COUNT(*)';
- foreach ($entries as $entryid=>$entry) {
- if ($entry->approved) {
- continue;
- }
+ $joins = array(' FROM {glossary_entries} ge ');
+ $joins[] = 'JOIN {user} u ON u.id = ge.userid ';
+ $fromsql = implode($joins, "\n");
- if (!isset($editor[$entry->glossaryid])) {
- $editor[$entry->glossaryid] = has_capability('mod/glossary:approve', get_context_instance(CONTEXT_MODULE, $modinfo->instances['glossary'][$entry->glossaryid]->id));
- }
+ $params['timestart'] = $timestart;
+ $clausesql = ' WHERE ge.timemodified > :timestart AND (';
+ $approvalsql = implode($approvals, ' OR ');
- if (!$editor[$entry->glossaryid]) {
- unset($entries[$entryid]);
- }
- }
+ $ordersql = ') ORDER BY ge.timemodified ASC';
+
+ $entries = $DB->get_records_sql($selectsql.$fromsql.$clausesql.$approvalsql.$ordersql, $params, 0, (GLOSSARY_RECENT_ACTIVITY_LIMIT+1));
- if (!$entries) {
+ if (empty($entries)) {
return false;
}
+
echo $OUTPUT->heading(get_string('newentries', 'glossary').':');
$strftimerecent = get_string('strftimerecent');
+ $entrycount = 0;
foreach ($entries as $entry) {
- $link = $CFG->wwwroot.'/mod/glossary/view.php?g='.$entry->glossaryid.'&mode=entry&hook='.$entry->id;
- if ($entry->approved) {
- $dimmed = '';
+ if ($entrycount < GLOSSARY_RECENT_ACTIVITY_LIMIT) {
+ if ($entry->approved) {
+ $dimmed = '';
+ $urlparams = array('g' => $entry->glossaryid, 'mode' => 'entry', 'hook' => $entry->id);
+ } else {
+ $dimmed = ' dimmed_text';
+ $urlparams = array('id' => $ids[$entry->glossaryid], 'mode' => 'approval', 'hook' => format_text($entry->concept, true));
+ }
+ $link = new moodle_url($CFG->wwwroot.'/mod/glossary/view.php' , $urlparams);
+ echo '<div class="head'.$dimmed.'">';
+ echo '<div class="date">'.userdate($entry->timemodified, $strftimerecent).'</div>';
+ echo '<div class="name">'.fullname($entry, $viewfullnames).'</div>';
+ echo '</div>';
+ echo '<div class="info"><a href="'.$link.'">'.format_text($entry->concept, true).'</a></div>';
+ $entrycount += 1;
} else {
- $dimmed = ' dimmed_text';
+ $numnewentries = $DB->count_records_sql($countsql.$joins[0].$clausesql.$approvalsql.')', $params);
+ echo '<div class="head"><div class="activityhead">'.get_string('andmorenewentries', 'glossary', $numnewentries - GLOSSARY_RECENT_ACTIVITY_LIMIT).'</div></div>';
+ break;
}
- echo '<div class="head'.$dimmed.'">';
- echo '<div class="date">'.userdate($entry->timemodified, $strftimerecent).'</div>';
- echo '<div class="name">'.fullname($entry, $viewfullnames).'</div>';
- echo '</div>';
- echo '<div class="info"><a href="'.$link.'">'.format_text($entry->concept, true).'</a></div>';
}
return true;
$sqlsortkey = NULL;
$textlib = textlib_get_instance();
+ // For cases needing inner view
+ $sqlwrapheader = '';
+ $sqlwrapfooter = '';
+
/// Calculate the SQL sortkey to be used by the SQL statements later
switch ( $sortkey ) {
case "CREATION":
} else {
$searchcond = implode(" AND ", $searchcond);
- $sqlselect = "SELECT DISTINCT ge.*, ge.concept AS glossarypivot";
+ // Need one inner view here to avoid distinct + text
+ $sqlwrapheader = 'SELECT ge.*, ge.concept AS glossarypivot
+ FROM {glossary_entries} ge
+ JOIN ( ';
+ $sqlwrapfooter = ' ) gei ON (ge.id = gei.id)';
+
+ $sqlselect = "SELECT DISTINCT ge.id";
$sqlfrom = "FROM {glossary_entries} ge
LEFT JOIN {glossary_alias} al ON al.entryid = ge.id";
$where = "AND ($searchcond)";
$limitnum = $entriesbypage;
}
- $allentries = $DB->get_records_sql("$sqlselect $sqlfrom $sqlwhere $sqlorderby", $params, $limitfrom, $limitnum);
+ $query = "$sqlwrapheader $sqlselect $sqlfrom $sqlwhere $sqlwrapfooter $sqlorderby";
+ $allentries = $DB->get_records_sql($query, $params, $limitfrom, $limitnum);
'minquestions','maxpages','timed','maxtime','retake','activitylink',
'mediafile','mediaheight','mediawidth','mediaclose','slideshow',
'width','height','bgcolor','displayleft','displayleftif','progressbar',
- 'highscores','maxhighscores','available','deadline','timemodified'
+ 'showhighscores','maxhighscores','available','deadline','timemodified'
));
+ // Tell the lesson element about the showhighscores elements mapping to the highscores
+ // database field.
+ $lesson->set_source_alias('highscores', 'showhighscores');
// The lesson_pages table
// Grouped within a `pages` element, important to note that page is relational
$data->deadline = $this->apply_date_offset($data->deadline);
$data->timemodified = $this->apply_date_offset($data->timemodified);
+ // lesson->highscores can come both in data->highscores and
+ // data->showhighscores, handle both. MDL-26229
+ if (isset($data->showhighscores)) {
+ $data->highscores = $data->showhighscores;
+ unset($data->showhighscores);
+ }
+
// insert the lesson record
$newitemid = $DB->insert_record('lesson', $data);
// immediately after inserting "activity" record, call this
}
$rs->close();
+ // Remap all the restored 'jumpto' fields now that we have all the pages and their mappings
+ $rs = $DB->get_recordset('lesson_answers', array('lessonid' => $this->task->get_activityid()),
+ '', 'id, jumpto');
+ foreach ($rs as $answer) {
+ if ($answer->jumpto > 0) {
+ $answer->jumpto = $this->get_mappingid('lesson_page', $answer->jumpto);
+ $DB->update_record('lesson_answers', $answer);
+ }
+ }
+ $rs->close();
+
// TODO: somewhere at the end of the restore... when all the activities have been restored
// TODO: we need to decode the lesson->activitylink that points to another activity in the course
// TODO: great functionality that breaks self-contained principles, grrr
} else {
$queryadd = '';
$params = array ("lessonid" => $lesson->id);
- if (!$users = $DB->get_records_sql("SELECT DISTINCT u.id, u.*
- FROM {user} u,
- {lesson_attempts} a
- WHERE a.lessonid = :lessonid and
- u.id = a.userid
- ORDER BY u.lastname", $params)) {
+ // Need to use inner view to avoid distinct + text
+ if (!$users = $DB->get_records_sql("
+ SELECT u.*
+ FROM {user} u
+ JOIN (
+ SELECT DISTINCT u.id
+ FROM {user} u,
+ {lesson_attempts} a
+ WHERE a.lessonid = :lessonid and
+ u.id = a.userid) ui ON (u.id = ui.id)
+ ORDER BY u.lastname", $params)) {
print_error('cannotfinduser', 'lesson');
}
}
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
require_capability('mod/lesson:manage', $context);
+$ufields = user_picture::fields('u'); // These fields are enough
$params = array("lessonid" => $lesson->id);
+// TODO: Improve this. Fetching all students always is crazy!
if (!empty($cm->groupingid)) {
$params["groupid"] = $cm->groupingid;
- $sql = "SELECT DISTINCT u.id, u.*
+ $sql = "SELECT DISTINCT $ufields
FROM {lesson_attempts} a
INNER JOIN {user} u ON u.id = a.userid
INNER JOIN {groups_members} gm ON gm.userid = u.id
WHERE a.lessonid = :lessonid
ORDER BY u.lastname";
} else {
- $sql = "SELECT DISTINCT u.id, u.*
+ $sql = "SELECT DISTINCT $ufields
FROM {user} u,
{lesson_attempts} a
WHERE a.lessonid = :lessonid and
// Define each element separated
$quiz = new backup_nested_element('quiz', array('id'), array(
'name', 'intro', 'introformat', 'timeopen',
- 'timeclose', 'optionflags', 'penaltyscheme', 'attempts',
+ 'timeclose', 'optionflags', 'penaltyscheme', 'attempts_number',
'attemptonlast', 'grademethod', 'decimalpoints', 'questiondecimalpoints',
'review', 'questionsperpage', 'shufflequestions', 'shuffleanswers',
'questions', 'sumgrades', 'grade', 'timecreated',
}
// Define source alias
+ $quiz->set_source_alias('attempts', 'attempts_number');
$grade->set_source_alias('grade', 'gradeval');
$attempt->set_source_alias('attempt', 'attemptnum');
$data->questions = $this->questions_recode_layout($data->questions);
+ // quiz->attempts can come both in data->attempts and
+ // data->attempts_number, handle both. MDL-26229
+ if (isset($data->attempts_number)) {
+ $data->attempts = $data->attempts_number;
+ unset($data->attempts_number);
+ }
+
// insert the quiz record
$newitemid = $DB->insert_record('quiz', $data);
// immediately after inserting "activity" record, call this
}
// Show a link to the comment box only for closed attempts
- if ($attempt->timefinish && has_capability('mod/quiz:grade', $context)) {
+ if (!empty($attempt->id) && $attempt->timefinish &&
+ has_capability('mod/quiz:grade', $context)) {
$options->questioncommentlink = new moodle_url('/mod/quiz/comment.php', array('attempt' => $attempt->id));
}
*
* @param stdClass $a associative array of replaceable fields for the templates
*
- * @return bool|string result of events_triger
+ * @return bool
*/
function quiz_send_confirmation($a) {
$eventdata->contexturl = $a->quizurl;
$eventdata->contexturlname = $a->quizname;
- return message_send($eventdata);
+ return (bool)message_send($eventdata); // returns message id or false
}
/**
* @param object $recipient user object of the intended recipient
* @param stdClass $a associative array of replaceable fields for the templates
*
- * @return bool|string result of events_triger()
+ * @return bool
*/
function quiz_send_notification($recipient, $a) {
$eventdata->contexturl = $a->quizreviewurl;
$eventdata->contexturlname = $a->quizname;
- return message_send($eventdata);
+ return (bool)message_send($eventdata);
}
/**
if (!$nostudents || ($attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL)) {
// Construct the SQL
- $fields = $DB->sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, \'0\')').' AS uniqueid, ';
+ $fields = $DB->sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, 0)').' AS uniqueid, ';
if ($qmsubselect) {
$fields .=
"(CASE " .
$hasfeedback = quiz_has_feedback($quiz);
// Construct the SQL
- $fields = $DB->sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, \'0\')').' AS concattedid, ';
+ $fields = $DB->sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, 0)').' AS concattedid, ';
if ($qmsubselect) {
$fields .=
"(CASE " .
if (alsowidth) {
scorm_layout_widget.setStyle('width', '');
var newwidth = scorm_get_htmlelement_size('content', 'width');
- if (newwidth > 600) {
- scorm_layout_widget.setStyle('width', newwidth+'px');
- }
- else {
- scorm_layout_widget.setStyle('width', '600px');
- }
}
// make sure that the max width of the TOC doesn't go to far
/////////////////////////////////////////////////////////////////////////////////
-$module->version = 2011013100; // The (date) version of this module
+$module->version = 2011021400; // The (date) version of this module
$module->requires = 2010080300; // The version of Moodle that is required
$module->cron = 300; // How often should cron check this module (seconds)?
upgrade_mod_savepoint(true, 2010111200, 'workshop');
}
+ /**
+ * Check the course_module integrity - see MDL-26312 for details
+ *
+ * Because of a bug in Workshop upgrade code, multiple workshop course_modules can
+ * potentially point to a single workshop instance. The chance is pretty low as in most cases,
+ * the upgrade failed. But under certain circumstances, workshop could be upgraded with
+ * this data integrity issue. We want to detect it now and let the admin know.
+ */
+ if ($oldversion < 2011021100) {
+ $sql = "SELECT cm.id, cm.course, cm.instance
+ FROM {course_modules} cm
+ WHERE cm.module IN (SELECT id
+ FROM {modules}
+ WHERE name = ?)";
+ $rs = $DB->get_recordset_sql($sql, array('workshop'));
+ $map = array(); // returned stdClasses by instance id
+ foreach ($rs as $cm) {
+ $map[$cm->instance][$cm->id] = $cm;
+ }
+ $rs->close();
+
+ $problems = array();
+ foreach ($map as $instanceid => $cms) {
+ if (count($cms) > 1) {
+ $problems[] = 'workshop instance ' . $instanceid . ' referenced by course_modules ' . implode(', ', array_keys($cms));
+ }
+ }
+ if ($problems) {
+ echo $OUTPUT->notification('¡Ay, caramba! Data integrity corruption has been detected in your workshop ' . PHP_EOL .
+ 'module database tables. This might be caused by a bug in workshop upgrade code. ' . PHP_EOL .
+ 'Please report this issue immediately in workshop module support forum at ' . PHP_EOL .
+ 'http://moodle.org so that we can help to fix this problem. Please copy and keep ' . PHP_EOL .
+ 'following information for future reference:');
+ foreach ($problems as $problem) {
+ echo $OUTPUT->notification($problem);
+ upgrade_log(UPGRADE_LOG_NOTICE, 'mod_workshop', 'course_modules integrity problem', $problem);
+ }
+ }
+
+ unset($problems);
+ unset($map);
+ upgrade_mod_savepoint(true, 2011021100, 'workshop');
+ }
+
return true;
}
upgrade_set_timeout();
$moduleid = $DB->get_field('modules', 'id', array('name' => 'workshop'), MUST_EXIST);
- $rs = $DB->get_recordset_select('workshop_old', 'newid IS NULL');
+ $rs = $DB->get_recordset_select('workshop_old', 'newid IS NULL', null, 'id');
foreach ($rs as $old) {
$new = workshop_upgrade_transform_instance($old);
- $newid = $DB->insert_record('workshop', $new, true, true);
- $DB->set_field('course_modules', 'instance', $newid, array('module' => $moduleid, 'instance' => $old->id));
+ $new->id = $old->id;
+ $DB->import_record('workshop', $new);
$DB->set_field('workshop_old', 'newplugin', 'workshop', array('id' => $old->id));
- $DB->set_field('workshop_old', 'newid', $newid, array('id' => $old->id));
+ $DB->set_field('workshop_old', 'newid', $new->id, array('id' => $old->id));
}
$rs->close();
}
return $new;
}
-/**
- * Returns the list of new workshop instances ids
- *
- * @return array (int)oldid => (int)newid
- */
-function workshop_upgrade_workshop_id_mappings() {
- global $DB;
-
- $oldrecords = $DB->get_records('workshop_old', null, 'id', 'id,newid');
- $newids = array();
- foreach ($oldrecords as $oldid => $oldrecord) {
- if ($oldrecord->id and $oldrecord->newid) {
- $newids[$oldid] = $oldrecord->newid;
- }
- }
- return $newids;
-}
-
/**
* Copies records from workshop_submissions_old into workshop_submissions. Can be called after all workshop module instances
* were correctly migrated and new ids are filled in workshop_old
global $CFG, $DB;
upgrade_set_timeout();
- $newworkshopids = workshop_upgrade_workshop_id_mappings();
- // list of teachers in every workshop: array of (int)oldworkshopid => array of (int)userid => notused
+ // list of teachers in every workshop: array of (int)workshopid => array of (int)userid => notused
$workshopteachers = array();
$rs = $DB->get_recordset_select('workshop_submissions_old', 'newid IS NULL');
foreach ($rs as $old) {
if (!isset($workshopteachers[$old->workshopid])) {
- $cm = get_coursemodule_from_instance('workshop', $newworkshopids[$old->workshopid], 0, false, MUST_EXIST);
+ $cm = get_coursemodule_from_instance('workshop', $old->workshopid, 0, false, MUST_EXIST);
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
$workshopteachers[$old->workshopid] = get_users_by_capability($context, 'mod/workshop:manage', 'u.id');
}
- $new = workshop_upgrade_transform_submission($old, $newworkshopids[$old->workshopid], $workshopteachers[$old->workshopid]);
+ $new = workshop_upgrade_transform_submission($old, $old->workshopid, $workshopteachers[$old->workshopid]);
$newid = $DB->insert_record('workshop_submissions', $new, true, true);
$DB->set_field('workshop_submissions_old', 'newplugin', 'submissions', array('id' => $old->id));
$DB->set_field('workshop_submissions_old', 'newid', $newid, array('id' => $old->id));
global $CFG, $DB, $OUTPUT;
upgrade_set_timeout();
- $newworkshopids = workshop_upgrade_workshop_id_mappings();
+
$newsubmissionids = workshop_upgrade_submission_id_mappings();
$teacherweights = workshop_upgrade_legacy_teacher_weights();
- // list of teachers in every workshop: array of (int)oldworkshopid => array of (int)userid => notused
+ // list of teachers in every workshop: array of (int)workshopid => array of (int)userid => notused
$workshopteachers = array();
$rs = $DB->get_recordset_select('workshop_assessments_old', 'newid IS NULL');
foreach ($rs as $old) {
if (!isset($workshopteachers[$old->workshopid])) {
- $cm = get_coursemodule_from_instance('workshop', $newworkshopids[$old->workshopid], 0, false, MUST_EXIST);
+ $cm = get_coursemodule_from_instance('workshop', $old->workshopid, 0, false, MUST_EXIST);
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
$workshopteachers[$old->workshopid] = get_users_by_capability($context, 'mod/workshop:manage', 'u.id');
}
WHERE workshopid $workshopids
AND newid IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
- $newworkshopids = workshop_upgrade_workshop_id_mappings();
// prepare system (global) scales to replace the legacy in-built ones
$newscaleids = workshopform_accumulative_upgrade_scales();
foreach ($rs as $old) {
- $new = workshopform_accumulative_upgrade_element($old, $newscaleids, $newworkshopids[$old->workshopid]);
+ $new = workshopform_accumulative_upgrade_element($old, $newscaleids, $old->workshopid);
$newid = $DB->insert_record('workshopform_accumulative', $new);
$DB->set_field('workshop_elements_old', 'newplugin', 'accumulative', array('id' => $old->id));
$DB->set_field('workshop_elements_old', 'newid', $newid, array('id' => $old->id));
WHERE workshopid $workshopids
AND newid IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
- $newworkshopids = workshop_upgrade_workshop_id_mappings();
foreach ($rs as $old) {
- $new = workshopform_comments_upgrade_element($old, $newworkshopids[$old->workshopid]);
+ $new = workshopform_comments_upgrade_element($old, $old->workshopid);
$newid = $DB->insert_record('workshopform_comments', $new);
$DB->set_field('workshop_elements_old', 'newplugin', 'comments', array('id' => $old->id));
$DB->set_field('workshop_elements_old', 'newid', $newid, array('id' => $old->id));
WHERE workshopid $workshopids
AND newid IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
- $newworkshopids = workshop_upgrade_workshop_id_mappings();
foreach ($rs as $old) {
// process the information about mapping
$newmapping = new stdclass();
- $newmapping->workshopid = $newworkshopids[$old->workshopid];
+ $newmapping->workshopid = $old->workshopid;
$newmapping->nonegative = $old->elementno;
$newmapping->grade = $old->maxscore;
if ($old->maxscore > 0) {
$DB->insert_record('workshopform_numerrors_map', $newmapping);
// process the information about the element itself
if (trim($old->description) and $old->description <> '@@ GRADE_MAPPING_ELEMENT @@') {
- $new = workshopform_numerrors_upgrade_element($old, $newworkshopids[$old->workshopid]);
+ $new = workshopform_numerrors_upgrade_element($old, $old->workshopid);
$newid = $DB->insert_record('workshopform_numerrors', $new);
} else {
$newid = 0;
WHERE workshopid $workshopids
AND newid IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
- $newworkshopids = workshop_upgrade_workshop_id_mappings();
- $newdimensionids = array(); // (int)oldworkshopid => (int)dimensionid
+ $newdimensionids = array(); // (int)workshopid => (int)dimensionid
foreach ($rs as $old) {
// create rubric criterion and the configuration if necessary
if (!isset($newdimensionids[$old->workshopid])) {
- if (!$DB->record_exists('workshopform_rubric', array('workshopid' => $newworkshopids[$old->workshopid], 'sort' => 1))) {
+ if (!$DB->record_exists('workshopform_rubric', array('workshopid' => $old->workshopid, 'sort' => 1))) {
$newdimension = new stdclass();
- $newdimension->workshopid = $newworkshopids[$old->workshopid];
+ $newdimension->workshopid = $old->workshopid;
$newdimension->sort = 1;
$newdimension->description = trim(get_string('dimensionnumber', 'workshopform_rubric', ''));
$newdimension->descriptionformat = FORMAT_HTML;
$newdimensionids[$old->workshopid] = $DB->insert_record('workshopform_rubric', $newdimension);
} else {
$newdimensionids[$old->workshopid] = $DB->get_field('workshopform_rubric', 'id',
- array('workshopid' => $newworkshopids[$old->workshopid], 'sort' => 1));
+ array('workshopid' => $old->workshopid, 'sort' => 1));
}
- if (!$DB->record_exists('workshopform_rubric_config', array('workshopid' => $newworkshopids[$old->workshopid]))) {
+ if (!$DB->record_exists('workshopform_rubric_config', array('workshopid' => $old->workshopid))) {
$newconfig = new stdclass();
- $newconfig->workshopid = $newworkshopids[$old->workshopid];
+ $newconfig->workshopid = $old->workshopid;
$newconfig->layout = 'list';
$DB->insert_record('workshopform_rubric_config', $newconfig);
}
AND r.newid IS NULL
ORDER BY e.workshopid, e.elementno, r.rubricno";
$rs = $DB->get_recordset_sql($sql, $params);
- $newworkshopids = workshop_upgrade_workshop_id_mappings(); // array of (int)oldworkshopid => (int)newworkshopid
- $newdimensionids = array(); // (int)oldworkshopid => (int)elementno => (int)dimensionid
+ $newdimensionids = array(); // (int)workshopid => (int)elementno => (int)dimensionid
$newlevelids = array(); // (int)oldrubricid => (int)newlevelid
$prevelement = null;
foreach ($rs as $old) {
// create rubric criterion and the configuration if necessary
if (!isset($newdimensionids[$old->workshopid]) or !isset($newdimensionids[$old->workshopid][$old->esort])) {
- if (!$DB->record_exists('workshopform_rubric', array('workshopid' => $newworkshopids[$old->workshopid], 'sort' => $old->esort))) {
+ if (!$DB->record_exists('workshopform_rubric', array('workshopid' => $old->workshopid, 'sort' => $old->esort))) {
$newdimension = new stdclass();
- $newdimension->workshopid = $newworkshopids[$old->workshopid];
+ $newdimension->workshopid = $old->workshopid;
$newdimension->sort = $old->esort;
$newdimension->description = $old->edesc;
$newdimension->descriptionformat = FORMAT_HTML;
$newdimensionids[$old->workshopid][$old->esort] = $DB->insert_record('workshopform_rubric', $newdimension);
} else {
$newdimensionids[$old->workshopid][$old->esort] = $DB->get_field('workshopform_rubric', 'id',
- array('workshopid' => $newworkshopids[$old->workshopid], 'sort' => $old->esort));
+ array('workshopid' => $old->workshopid, 'sort' => $old->esort));
}
- if (!$DB->record_exists('workshopform_rubric_config', array('workshopid' => $newworkshopids[$old->workshopid]))) {
+ if (!$DB->record_exists('workshopform_rubric_config', array('workshopid' => $old->workshopid))) {
$newconfig = new stdclass();
- $newconfig->workshopid = $newworkshopids[$old->workshopid];
+ $newconfig->workshopid = $old->workshopid;
$newconfig->layout = 'grid';
$DB->insert_record('workshopform_rubric_config', $newconfig);
}
defined('MOODLE_INTERNAL') || die();
-$module->version = 2010111200;
-$module->requires = 2010111002; // Requires this Moodle version
+$module->version = 2011021100;
+$module->requires = 2011020900; // Requires this Moodle version
//$module->cron = 60;
$this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
}
+ public function test_import_multichoice_multi() {
+ $gift = "
+// multiple choice, multiple response with specified feedback for right and wrong answers
+::colours:: What's between orange and green in the spectrum?
+{
+ ~%50%yellow # right; good!
+ ~%-100%red # [html]wrong
+ ~%50%off-beige # right; good!
+ ~%-100%[plain]blue # wrong
+}";
+ $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
+
+ $importer = new qformat_gift();
+ $q = $importer->readquestion($lines);
+
+ $expectedq = (object) array(
+ 'name' => 'colours',
+ 'questiontext' => "What's between orange and green in the spectrum?",
+ 'questiontextformat' => FORMAT_MOODLE,
+ 'generalfeedback' => '',
+ 'generalfeedbackformat' => FORMAT_MOODLE,
+ 'qtype' => 'multichoice',
+ 'defaultgrade' => 1,
+ 'penalty' => 0.1,
+ 'length' => 1,
+ 'single' => 0,
+ 'shuffleanswers' => '1',
+ 'answernumbering' => 'abc',
+ 'correctfeedback' => array(
+ 'text' => '',
+ 'format' => FORMAT_MOODLE,
+ 'files' => array(),
+ ),
+ 'partiallycorrectfeedback' => array(
+ 'text' => '',
+ 'format' => FORMAT_MOODLE,
+ 'files' => array(),
+ ),
+ 'incorrectfeedback' => array(
+ 'text' => '',
+ 'format' => FORMAT_MOODLE,
+ 'files' => array(),
+ ),
+ 'answer' => array(
+ 0 => array(
+ 'text' => 'yellow',
+ 'format' => FORMAT_MOODLE,
+ 'files' => array(),
+ ),
+ 1 => array(
+ 'text' => 'red',
+ 'format' => FORMAT_MOODLE,
+ 'files' => array(),
+ ),
+ 2 => array(
+ 'text' => 'off-beige',
+ 'format' => FORMAT_MOODLE,
+ 'files' => array(),
+ ),
+ 3 => array(
+ 'text' => 'blue',
+ 'format' => FORMAT_PLAIN,
+ 'files' => array(),
+ ),
+ ),
+ 'fraction' => array(0.5, -1, 0.5, -1),
+ 'feedback' => array(
+ 0 => array(
+ 'text' => 'right; good!',
+ 'format' => FORMAT_MOODLE,
+ 'files' => array(),
+ ),
+ 1 => array(
+ 'text' => "wrong",
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ ),
+ 2 => array(
+ 'text' => "right; good!",
+ 'format' => FORMAT_MOODLE,
+ 'files' => array(),
+ ),
+ 3 => array(
+ 'text' => "wrong",
+ 'format' => FORMAT_MOODLE,
+ 'files' => array(),
+ ),
+ ),
+ );
+
+ // Repeated test for better failure messages.
+ $this->assertEqual($expectedq->answer, $q->answer);
+ $this->assertEqual($expectedq->feedback, $q->feedback);
+ $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
+ }
+
public function test_export_multichoice() {
$qdata = (object) array(
'id' => 666 ,
Y.one(form).setAttribute('autocomplete', 'off');
Y.on('key', function (e) {
if (!e.target.test('a') && !e.target.test('input[type=submit]') &&
- !e.target.test('input[type=img]')) {
+ !e.target.test('input[type=img]') && !e.target.test('textarea')) {
e.preventDefault();
}
}, form, 'press:13');
$penaltygrp = array();
$penaltygrp[] =& $mform->createElement('text', 'unitpenalty', get_string('unitpenalty', 'qtype_numerical') ,
array('size' => 6));
- $unitgradingtypes = array('1' => get_string('decfractionofquestiongrade', 'qtype_numerical'), '2' => get_string('decfractionofresponsegrade', 'qtype_numerical'));
+ $unitgradingtypes = array('1' => get_string('decfractionofresponsegrade', 'qtype_numerical'), '2' => get_string('decfractionofquestiongrade', 'qtype_numerical'));
$penaltygrp[] =& $mform->createElement('select', 'unitgradingtypes', '' , $unitgradingtypes );
$mform->addGroup($penaltygrp, 'penaltygrp', get_string('unitpenalty', 'qtype_numerical'),' ' , false);
$multichoicedisplaygrp = array();
break;
case 2 : // NUMERICALQUESTIONUNITTEXTDISPLAY
$default_values['unitrole'] = 1 ;
+ break;
case 3 : // NUMERICALQUESTIONUNITNODISPLAY
$default_values['unitrole'] = 0 ;
// $default_values['showunits1'] = $question->options->showunits ;
$answer = $state->answer;
$result = '';
// randomxx-yy answer format
- if (preg_match('~^random([0-9]+)-(.*)$~', $answer, $matches)) {
+ if (preg_match('~^random([0-9]+)-~', $answer, $matches)) {
$questionid = $matches[1];
- $subanswer = $matches[2];
+ $subanswer = substr($answer, strlen('random' . $questionid . '-'));
$newquestionid = $this->get_mappingid('question', $questionid);
$questionqtype = $DB->get_field('question', 'qtype', array('id' => $newquestionid));
// Delegate subanswer recode to proper qtype, faking one question_states record
/// the other question types in that it now only needs one response
/// record per question.
global $QTYPES, $DB, $OUTPUT;
- if (!preg_match('~^random([0-9]+)-(.*)$~', $state->responses[''], $answerregs)) {
+ if (!preg_match('~^random([0-9]+)-~', $state->responses[''], $matches)) {
if (empty($state->responses[''])) {
// This is the case if there weren't enough questions available in the category.
$question->questiontext = '<span class="notifyproblem">'.
echo $OUTPUT->notification("Wrapped state missing");
}
} else {
- if (!$wrappedquestion = $DB->get_record('question', array('id' => $answerregs[1]))) {
+ $questionid = $matches[1];
+ if (!$wrappedquestion = $DB->get_record('question', array('id' => $questionid))) {
// The teacher must have deleted this question by mistake
// Convert it into a description type question with an explanation to the student
$wrappedquestion = clone($question);
- $wrappedquestion->id = $answerregs[1];
+ $wrappedquestion->id = $questionid;
$wrappedquestion->questiontext = get_string('questiondeleted', 'quiz');
$wrappedquestion->qtype = 'missingtype';
}
- $state->responses[''] = (false === $answerregs[2]) ? '' : $answerregs[2];
+ $state->responses[''] = substr($state->responses[''], strlen('random' . $questionid . '-'));
}
if (!$QTYPES[$wrappedquestion->qtype]
// Output a dash if aggregation method == COUNT as the count is output next to the aggregate anyway
if ($items[0]->rating->settings->aggregationmethod==RATING_AGGREGATE_COUNT or $items[0]->rating->count == 0) {
$aggregatetoreturn = ' - ';
-} else if($rating->scaleid < 0) { //if its non-numeric scale
+} else if($items[0]->rating->settings->scale->id < 0) { //if its non-numeric scale
//dont use the scale item if the aggregation method is sum as adding items from a custom scale makes no sense
if ($items[0]->rating->settings->aggregationmethod!= RATING_AGGREGATE_SUM) {
- $scalerecord = $DB->get_record('scale', array('id' => -$rating->scaleid));
+ $scalerecord = $DB->get_record('scale', array('id' => -$items[0]->rating->settings->scale->id));
if ($scalerecord) {
$scalearray = explode(',', $scalerecord->scale);
$aggregatetoreturn = $scalearray[$aggregatetoreturn-1];
if (empty($saveas_filename)) {
$record->filename = clean_param($_FILES[$elname]['name'], PARAM_FILE);
} else {
- $record->filename = $saveas_filename;
+ $ext = '';
+ $match = array();
+ $filename = clean_param($_FILES[$elname]['name'], PARAM_FILE);
+ if (preg_match('/\.([a-z0-9]+)$/i', $filename, $match)) {
+ if (isset($match[1])) {
+ $ext = $match[1];
+ }
+ }
+ $ext = !empty($ext) ? $ext : '';
+ if (preg_match('#\.(' . $ext . ')$#i', $saveas_filename)) {
+ // saveas filename contains file extension already
+ $record->filename = $saveas_filename;
+ } else {
+ $record->filename = $saveas_filename . '.' . $ext;
+ }
}
if ($this->mimetypes != '*') {
if ($mimetype === 'text/css') {
if ($version == 'moodle') {
- $filecontent = preg_replace('/([a-z_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/'.$frankenstyle.'/'.array_shift($bits).'/$1.$2', $filecontent);
+ $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/'.$frankenstyle.'/'.array_shift($bits).'/$1.$2', $filecontent);
} else if ($version == 'gallery') {
// search for all images in gallery module CSS and serve them through the yui_image.php script
- $filecontent = preg_replace('/([a-z_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/'.$bits[0].'/'.$bits[1].'/$1.$2', $filecontent);
+ $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/'.$bits[0].'/'.$bits[1].'/$1.$2', $filecontent);
} else {
// First we need to remove relative paths to images. These are used by YUI modules to make use of global assets.
// I've added this as a separate regex so it can be easily removed once
// YUI standardise there CSS methods
- $filecontent = preg_replace('#(\.\./\.\./\.\./\.\./assets/skins/sam/)?([a-z_-]+)\.(png|gif)#', '$2.$3', $filecontent);
+ $filecontent = preg_replace('#(\.\./\.\./\.\./\.\./assets/skins/sam/)?([a-z0-9_-]+)\.(png|gif)#', '$2.$3', $filecontent);
// search for all images in yui2 CSS and serve them through the yui_image.php script
- $filecontent = preg_replace('/([a-z_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/$1.$2', $filecontent);
+ $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/$1.$2', $filecontent);
}
}
$mform->addElement('text', 'city', get_string('city'), 'maxlength="120" size="21"');
$mform->setType('city', PARAM_MULTILANG);
$mform->addRule('city', $strrequired, 'required', null, 'client');
-
+ if (!empty($CFG->defaultcity)) {
+ $mform->setDefault('city', $CFG->defaultcity);
+ }
$choices = get_string_manager()->get_list_of_countries();
$choices= array(''=>get_string('selectacountry').'...') + $choices;
$userid = optional_param('id', 0, PARAM_INT);
$edit = optional_param('edit', null, PARAM_BOOL); // Turn editing on and off
+$PAGE->set_url('/user/profile.php', array('id'=>$userid));
+
if (!empty($CFG->forceloginforprofiles)) {
require_login();
if (isguestuser()) {
+ $SESSION->wantsurl = $PAGE->url->out(false);
redirect(get_login_url());
}
} else if (!empty($CFG->forcelogin)) {
// Start setting up the page
$strpublicprofile = get_string('publicprofile');
-$params = array('id'=>$userid);
-$PAGE->set_url('/user/profile.php', $params);
$PAGE->blocks->add_region('content');
$PAGE->set_subpage($currentpage->id);
$PAGE->set_title(fullname($user).": $strpublicprofile");
redirect($CFG->wwwroot.'/user/profile.php?id='.$id); // Immediate redirect
}
-$url = new moodle_url('/user/view.php', array('id'=>$id,'course'=>$courseid));
-$PAGE->set_url($url);
+$PAGE->set_url('/user/view.php', array('id'=>$id,'course'=>$courseid));
$user = $DB->get_record('user', array('id'=>$id), '*', MUST_EXIST);
$course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
print_error('invaliduserid');
}
+if (!empty($CFG->forceloginforprofiles)) {
+ require_login(); // we can not log in to course due to the parent hack bellow
+ if (isguestuser()) {
+ $SESSION->wantsurl = $PAGE->url->out(false);
+ redirect(get_login_url());
+ }
+}
+
$PAGE->set_context($coursecontext);
$PAGE->set_course($course);
$PAGE->set_pagetype('course-view-' . $course->format); // To get the blocks exactly like the course
} else {
// normal course
require_login($course);
- // what to do with users temporary accessing this course? shoudl they see the details?
+ // what to do with users temporary accessing this course? should they see the details?
}
$strpersonalprofile = get_string('personalprofile');
defined('MOODLE_INTERNAL') || die();
-$version = 2011020900; // YYYYMMDD = date of the last version bump
+$version = 2011020900.08; // YYYYMMDD = date of the last version bump
// XX = daily increments
$release = '2.0.1+ (Build: 20110209)'; // Human-friendly version name