--- /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/>.
+
+/**
+ * Allows the admin to enable, disable and uninstall course formats
+ *
+ * @package core_admin
+ * @copyright 2012 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+require_once($CFG->libdir.'/pluginlib.php');
+
+$action = required_param('action', PARAM_ALPHANUMEXT);
+$formatname = required_param('format', PARAM_PLUGIN);
+$confirm = optional_param('confirm', 0, PARAM_BOOL);
+
+$syscontext = context_system::instance();
+$PAGE->set_url('/admin/courseformats.php');
+$PAGE->set_context($syscontext);
+
+require_login();
+require_capability('moodle/site:config', $syscontext);
+require_sesskey();
+
+$return = new moodle_url('/admin/settings.php', array('section' => 'manageformats'));
+
+$allplugins = plugin_manager::instance()->get_plugins();
+$formatplugins = $allplugins['format'];
+$sortorder = array_flip(array_keys($formatplugins));
+
+if (!isset($formatplugins[$formatname])) {
+ print_error('courseformatnotfound', 'error', $return, $formatname);
+}
+
+switch ($action) {
+ case 'disable':
+ if ($formatplugins[$formatname]->is_enabled()) {
+ if (get_config('moodlecourse', 'format') === $formatname) {
+ print_error('cannotdisableformat', 'error', $return);
+ }
+ set_config('disabled', 1, 'format_'. $formatname);
+ }
+ break;
+ case 'enable':
+ if (!$formatplugins[$formatname]->is_enabled()) {
+ unset_config('disabled', 'format_'. $formatname);
+ }
+ break;
+ case 'up':
+ if ($sortorder[$formatname]) {
+ $currentindex = $sortorder[$formatname];
+ $seq = array_keys($formatplugins);
+ $seq[$currentindex] = $seq[$currentindex-1];
+ $seq[$currentindex-1] = $formatname;
+ set_config('format_plugins_sortorder', implode(',', $seq));
+ }
+ break;
+ case 'down':
+ if ($sortorder[$formatname] < count($sortorder)-1) {
+ $currentindex = $sortorder[$formatname];
+ $seq = array_keys($formatplugins);
+ $seq[$currentindex] = $seq[$currentindex+1];
+ $seq[$currentindex+1] = $formatname;
+ set_config('format_plugins_sortorder', implode(',', $seq));
+ }
+ break;
+ case 'uninstall':
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading(get_string('courseformats', 'moodle'));
+
+ $coursecount = $DB->count_records('course', array('format' => $formatname));
+ if ($coursecount) {
+ // Check that default format is set. It will be used to convert courses
+ // using this format
+ $defaultformat = get_config('moodlecourse', 'format');
+ $defaultformat = $formatplugins[get_config('moodlecourse', 'format')];
+ if (!$defaultformat) {
+ echo $OUTPUT->error_text(get_string('defaultformatnotset', 'admin'));
+ echo $OUTPUT->footer();
+ exit;
+ }
+ }
+
+ $format = $formatplugins[$formatname];
+ $deleteurl = $format->get_uninstall_url();
+ if (!$deleteurl) {
+ // somebody was trying to cheat and type non-existing link
+ echo $OUTPUT->error_text(get_string('cannotuninstall', 'admin', $format->displayname));
+ echo $OUTPUT->footer();
+ exit;
+ }
+
+ if (!$confirm) {
+ if ($coursecount) {
+ $message = get_string('formatuninstallwithcourses', 'admin',
+ (object)array('count' => $coursecount, 'format' => $format->displayname,
+ 'defaultformat' => $defaultformat->displayname));
+ } else {
+ $message = get_string('formatuninstallconfirm', 'admin', $format->displayname);
+ }
+ $deleteurl->param('confirm', 1);
+ echo $OUTPUT->confirm($message, $deleteurl, $return);
+ } else {
+ $a = new stdClass();
+ $a->plugin = $format->displayname;
+ $a->directory = $format->rootdir;
+ uninstall_plugin('format', $formatname);
+ echo $OUTPUT->notification(get_string('formatuninstalled', 'admin', $a), 'notifysuccess');
+ echo $OUTPUT->continue_button($return);
+ }
+
+ echo $OUTPUT->footer();
+ exit;
+}
+redirect($return);
/// NOTE: these settings must be applied after all other settings because they depend on them
///main course settings
$temp = new admin_settingpage('coursesettings', new lang_string('coursesettings'));
- $courseformats = get_plugin_list('format');
+ require_once($CFG->dirroot.'/course/lib.php');
+ $courseformats = get_sorted_course_formats(true);
$formcourseformats = array();
- foreach ($courseformats as $courseformat => $courseformatdir) {
+ foreach ($courseformats as $courseformat) {
$formcourseformats[$courseformat] = new lang_string('pluginname', "format_$courseformat");
}
$temp->add(new admin_setting_configselect('moodlecourse/format', new lang_string('format'), new lang_string('coursehelpformat'), 'weeks',$formcourseformats));
// hidden script for converting journals to online assignments (or something like that) linked from elsewhere
$ADMIN->add('modsettings', new admin_externalpage('oacleanup', 'Online Assignment Cleanup', $CFG->wwwroot.'/'.$CFG->admin.'/oacleanup.php', 'moodle/site:config', true));
+ // course formats
+ $ADMIN->add('modules', new admin_category('formatsettings', new lang_string('courseformats')));
+ $temp = new admin_settingpage('manageformats', new lang_string('manageformats', 'core_admin'));
+ $temp->add(new admin_setting_manageformats());
+ $ADMIN->add('formatsettings', $temp);
+ foreach ($allplugins['format'] as $format) {
+ $format->load_settings($ADMIN, 'formatsettings', $hassiteconfig);
+ }
+
// blocks
$ADMIN->add('modules', new admin_category('blocksettings', new lang_string('blocks')));
$ADMIN->add('blocksettings', new admin_page_manageblocks());
// Question type settings
if ($hassiteconfig || has_capability('moodle/question:config', $systemcontext)) {
+ if (!$hassiteconfig) {
+ require_once("$CFG->libdir/pluginlib.php");
+ $allplugins = plugin_manager::instance()->get_plugins();
+ }
// Question behaviour settings.
$ADMIN->add('modules', new admin_category('qbehavioursettings', new lang_string('questionbehaviours', 'admin')));
$ADMIN->add('qbehavioursettings', new admin_page_manageqbehaviours());
$grade_category = new backup_nested_element('grade_category', array('id'), array(
//'courseid',
'parent', 'depth', 'path', 'fullname', 'aggregation', 'keephigh',
- 'dropload', 'aggregateonlygraded', 'aggregateoutcomes', 'aggregatesubcats',
+ 'droplow', 'aggregateonlygraded', 'aggregateoutcomes', 'aggregatesubcats',
'timecreated', 'timemodified', 'hidden'));
$letters = new backup_nested_element('grade_letters');
foreach ($modfullnames as $modname => $modfullname) {
if ($modname === 'resources') {
- $icon = $OUTPUT->pix_icon(file_extension_icon('.htm'), '', 'moodle', array('class' => 'icon')). ' ';
+ $icon = $OUTPUT->pix_icon('icon', '', 'mod_page', array('class' => 'icon'));
$this->content->items[] = '<a href="'.$CFG->wwwroot.'/course/resources.php?id='.$course->id.'">'.$icon.$modfullname.'</a>';
} else {
- $icon = '<img src="'.$OUTPUT->pix_url('icon', $modname) . '" class="icon" alt="" /> ';
+ $icon = '<img src="'.$OUTPUT->pix_url('icon', $modname) . '" class="icon" alt="" />';
$this->content->items[] = '<a href="'.$CFG->wwwroot.'/mod/'.$modname.'/index.php?id='.$course->id.'">'.$icon.$modfullname.'</a>';
}
}
'class' => 'icon', 'alt' => get_string('addcourse', 'block_community')));
$addcourseurl = new moodle_url('/blocks/community/communitycourse.php',
array('add' => true, 'courseid' => $this->page->course->id));
- $searchlink = html_writer::tag('a', $icon . ' ' . get_string('addcourse', 'block_community'),
+ $searchlink = html_writer::tag('a', $icon . get_string('addcourse', 'block_community'),
array('href' => $addcourseurl->out(false)));
$this->content->items[] = $searchlink;
$this->content->icons = array();
$this->content->footer = '';
- $icon = '<img src="' . $OUTPUT->pix_url('i/course') . '" class="icon" alt="" /> ';
+ $icon = '<img src="' . $OUTPUT->pix_url('i/course') . '" class="icon" alt="" />';
$adminseesall = true;
if (isset($CFG->block_course_list_adminview)) {
return;
}
- $icon = '<img src="'.$OUTPUT->pix_url('i/mnethost') . '" class="icon" alt="" /> ';
+ $icon = '<img src="'.$OUTPUT->pix_url('i/mnethost') . '" class="icon" alt="" />';
// shortcut - the rest is only for logged in users!
if (!isloggedin() || isguestuser()) {
'position' : 'relative',
'fontSize' : fontsize,
'width' : width,
- 'top' : width/2,
- 'right' : width/2 - height
+ 'top' : width/2
});
+ // Positioning is different when in RTL mode.
+ if (Y.one(document.body).hasClass('dir-rtl')) {
+ title.setStyle('left', width/2 - height);
+ } else {
+ title.setStyle('right', width/2 - height);
+ }
+
// Rotate the text
title.setStyles({
'transform' : transform,
}, this);
// Add a close icon
// Must set the image src seperatly of we get an error with XML strict headers
- var closeicon = Y.Node.create('<span class="hidepanelicon" tabindex="0"><img alt="" style="width:11px;height:11px;cursor:pointer;" /></span>');
+ var closeicon = Y.Node.create('<span class="hidepanelicon" tabindex="0"><img alt="" /></span>');
closeicon.one('img').setAttribute('src', M.util.image_url('t/dockclose', 'moodle'));
closeicon.on('forceclose|click', this.hide, this);
closeicon.on('dock:actionkey',this.hide, this, {actions:{enter:true,toggle:true}});
/** General display rules **/
.block_navigation .block_tree {margin:5px;padding-left:0px;overflow:visible;}
.block_navigation .block_tree li {margin:3px;list-style: none;padding:0;}
-.block_navigation .block_tree li.item_with_icon > p {position:relative;}
-.block_navigation .block_tree li.item_with_icon > p img {vertical-align:middle;position:absolute;left:0;top:3px}
+.block_navigation .block_tree li.item_with_icon > p {position:relative; padding-left: 21px;}
+.block_navigation .block_tree li.item_with_icon > p img,
+.block_navigation .block_tree .type_activity > p.tree_item.active_tree_node img,
+.block_navigation .block_tree li > p.hasicon img {vertical-align:middle;position:absolute;left:0;top:-1px;width:16px;height:16px;}
.block_navigation .block_tree li.item_with_icon.contains_branch > p img {left:16px;}
-.block_navigation .block_tree li.item_with_icon.contains_branch .tree_item {padding-left:34px;}
+.block_navigation .block_tree .type_activity > p.branch.hasicon,
+.block_navigation .block_tree li.item_with_icon.contains_branch > .tree_item {padding-left:37px;}
.block_navigation .block_tree li ul {padding-left:0;margin:0;}
.block_navigation .block_tree li.depth_2 ul {padding-left:16px;margin:0;}
-.block_navigation .block_tree .tree_item {padding-left: 18px;margin:3px 0px;text-align:left;}
+.block_navigation .block_tree .type_activity > p.tree_item.branch.hasicon.active_tree_node,
+.block_navigation .block_tree .tree_item {padding-left: 21px;margin:3px 0px;text-align:left;}
-.block_navigation .block_tree .tree_item.branch {background-image: url([[pix:t/expanded]]);background-position: 0 10%;background-repeat: no-repeat;}
+.block_navigation .block_tree .tree_item.branch {background-image: url([[pix:t/expanded]]);background-position: 0 0;background-repeat: no-repeat;}
.block_navigation .block_tree .tree_item.branch.navigation_node {background-image:none;padding-left:0;}
.block_navigation .block_tree .type_activity > .tree_item.branch {background-image:none;position:relative;}
-.block_navigation .block_tree .type_activity > .tree_item.branch img {position:absolute;left:0;}
+.block_navigation .block_tree .type_activity > .tree_item.branch img {left: 16px;}
.block_navigation .block_tree .root_node.leaf {padding-left:0px;}
.block_navigation .block_tree .active_tree_node {font-weight:bold;}
.block_navigation .block_tree .depth_1.current_branch ul {font-weight:normal;}
.dock .block_navigation .tree_item {white-space: nowrap;}
.jsenabled .block_navigation .block_tree .tree_item.branch {cursor:pointer;}
-.jsenabled .block_navigation .block_tree .tree_item.emptybranch {background-image: url([[pix:t/collapsed_empty]]);background-position: 0% 5%;background-repeat: no-repeat;}
+.jsenabled .block_navigation .block_tree .tree_item.emptybranch {background-image: url([[pix:t/collapsed_empty]]);background-position: 0 0;background-repeat: no-repeat;}
.jsenabled .block_navigation .block_tree .collapsed ul {display: none;}
+.jsenabled .block_navigation .block_tree .type_activity > .tree_item.branch {background-image: url([[pix:t/expanded]]);}
.jsenabled .block_navigation .block_tree .collapsed .tree_item.branch {background-image: url([[pix:t/collapsed]]);}
.jsenabled .block_navigation .block_tree .tree_item.branch.loadingbranch {background-image:url([[pix:i/loading_small]]);}
.ie6 .block_navigation .block_tree .tree_item {width:100%;}
/** Overide for RTL layout **/
-.dir-rtl .block_navigation .block_tree li.depth_2 ul {padding-left:0;padding-right: 7px;}
-.dir-rtl .block_navigation .block_tree .tree_item {padding-right: 18px;text-align:right;}
+.dir-rtl .block_navigation .block_tree li.depth_2 ul {padding-left:0;padding-right: 16px; padding-left: 0;}
+.dir-rtl .block_navigation .block_tree .type_activity > p.tree_item.branch.hasicon.active_tree_node,
+.dir-rtl .block_navigation .block_tree .tree_item {padding-right: 21px;text-align:right;}
.dir-rtl .block_navigation .block_tree .tree_item.branch {background-position: center right;}
.dir-rtl .block_navigation .block_tree .root_node.leaf {padding-right:0;}
.dir-rtl .block_navigation .block_tree li.item_with_icon > p img,
-.dir-rtl .block_navigation .block_tree .type_activity > .tree_item.branch img {right:0;left:auto;}
+.dir-rtl .block_navigation .block_tree .type_activity > p.tree_item.active_tree_node img,
+.dir-rtl .block_navigation .block_tree li > p.hasicon img {left:auto; right:0;}
+.dir-rtl .block_navigation .block_tree li.item_with_icon.contains_branch > p img {left: auto; right:16px;}
+.dir-rtl .block_navigation .block_tree .type_activity > p.branch.hasicon,
+.dir-rtl .block_navigation .block_tree li.item_with_icon.contains_branch > .tree_item {padding-right:37px; padding-left: 0;}
+.dir-rtl .block_navigation .block_tree .type_activity > .tree_item.branch img {right: 16px; left: auto;}
.jsenabled.dir-rtl .block_navigation .block_tree .tree_item.emptybranch {background-image: url([[pix:t/collapsed_empty_rtl]]);background-position: center right;}
.jsenabled.dir-rtl .block_navigation .block_tree .collapsed .tree_item.branch {background-image: url([[pix:t/collapsed_rtl]]);}
}
}
- $icon = '<img src="'.$OUTPUT->pix_url('i/users') . '" class="icon" alt="" /> ';
+ $icon = '<img src="'.$OUTPUT->pix_url('i/users') . '" class="icon" alt="" />';
$this->content->items[] = '<a title="'.get_string('listofallpeople').'" href="'.
$CFG->wwwroot.'/user/index.php?contextid='.$currentcontext->id.'">'.$icon.get_string('participants').'</a>';
$result = '<ul>';
foreach ($dir['subdirs'] as $subdir) {
$image = $this->output->pix_icon(file_folder_icon(), $subdir['dirname'], 'moodle', array('class'=>'icon'));
- $result .= '<li yuiConfig=\''.json_encode($yuiconfig).'\'><div>'.$image.' '.s($subdir['dirname']).'</div> '.$this->htmllize_tree($tree, $subdir).'</li>';
+ $result .= '<li yuiConfig=\''.json_encode($yuiconfig).'\'><div>'.$image.s($subdir['dirname']).'</div> '.$this->htmllize_tree($tree, $subdir).'</li>';
}
foreach ($dir['files'] as $file) {
$url = file_encode_url("$CFG->wwwroot/pluginfile.php", '/'.$tree->context->id.'/user/private'.$file->get_filepath().$file->get_filename(), true);
$filename = $file->get_filename();
$image = $this->output->pix_icon(file_file_icon($file), $filename, 'moodle', array('class'=>'icon'));
- $result .= '<li yuiConfig=\''.json_encode($yuiconfig).'\'><div>'.html_writer::link($url, $image.' '.$filename).'</div></li>';
+ $result .= '<li yuiConfig=\''.json_encode($yuiconfig).'\'><div>'.html_writer::link($url, $image.$filename).'</div></li>';
}
$result .= '</ul>';
/** General display rules **/
.block_settings .block_tree {margin:5px;padding-left:0px;overflow:visible;}
.block_settings .block_tree li {margin:0;list-style: none;}
-.block_settings .block_tree li ul {padding-left:16px;margin:0;}
+.block_settings .block_tree li ul {padding-left:18px;margin:0;}
.block_settings .block_tree li.item_with_icon > p {position:relative;}
-.block_settings .block_tree li.item_with_icon > p img {vertical-align:middle;position:absolute;left:0;top:-1px}
+.block_settings .block_tree li.item_with_icon > p img {vertical-align:middle;position:absolute;left:0;top:-1px; width: 16px; height: 16px;}
-.block_settings .block_tree .tree_item {padding-left: 18px;margin:3px 0px;text-align:left;}
+.block_settings .block_tree .tree_item {padding-left: 21px;margin:3px 0px;text-align:left;}
.block_settings .block_tree .tree_item.branch {background-image: url([[pix:t/expanded]]);background-position: 0 10%;background-repeat: no-repeat;}
.block_settings .block_tree .root_node.leaf {padding-left:0px;}
/** Overide for RTL layout **/
.dir-rtl .block_settings .block_tree {padding-right:0px;}
-.dir-rtl .block_settings .block_tree li ul {padding-left:0;padding-right: 7px;}
-.dir-rtl .block_settings .block_tree li.item_with_icon > p img,
-.dir-rtl .block_navigation .block_tree .type_activity > .tree_item.branch img {left:auto;right:0;}
-.dir-rtl .block_settings .block_tree .tree_item {padding-right: 18px;text-align:right;}
+.dir-rtl .block_settings .block_tree li ul {padding-left:0;padding-right: 18px;}
+.dir-rtl .block_settings .block_tree .tree_item {padding-right: 21px; padding-left: 0; text-align:right;}
.dir-rtl .block_settings .block_tree .tree_item.branch {background-position: center right;}
.dir-rtl .block_settings .block_tree .root_node.leaf {padding-right:0px;}
+.dir-rtl .block_settings .block_tree li.item_with_icon > p img { right: 0; left: auto;}
.jsenabled.dir-rtl .block_settings .block_tree .tree_item.emptybranch {background-image: url([[pix:t/collapsed_empty_rtl]]);background-position: center right;}
-.jsenabled.dir-rtl .block_settings .block_tree .collapsed .tree_item.branch {background-image: url([[pix:t/collapsed_rtl]]);}
\ No newline at end of file
+.jsenabled.dir-rtl .block_settings .block_tree .collapsed .tree_item.branch {background-image: url([[pix:t/collapsed_rtl]]);}
} else {
$linkcss = $cm->visible ? '' : ' class="dimmed" ';
//Accessibility: incidental image - should be empty Alt text
- $icon = '<img src="' . $cm->get_icon_url() . '" class="icon" alt="" /> ';
+ $icon = '<img src="' . $cm->get_icon_url() . '" class="icon" alt="" />';
$this->content->items[] = '<a title="'.$cm->modplural.'" '.$linkcss.' '.$cm->extra.
' href="' . $url . '">' . $icon . $instancename . '</a>';
}
$this->content->icons[] = '';
} else {
//Accessibility: incidental image - should be empty Alt text
- $icon = '<img src="' . $mod->get_icon_url() . '" class="icon" alt="" /> ';
+ $icon = '<img src="' . $mod->get_icon_url() . '" class="icon" alt="" />';
$this->content->items[] = '<a title="' . $mod->modfullname . '" ' . $linkcss . ' ' . $mod->extra .
' href="' . $url . '">' . $icon . $instancename . '</a>' . $editbuttons;
}
throw new coding_exception('You must provide a mode when creating a cache definition');
}
if (!array_key_exists('component', $definition)) {
- throw new coding_exception('You must provide a mode when creating a cache definition');
+ throw new coding_exception('You must provide a component when creating a cache definition');
}
if (!array_key_exists('area', $definition)) {
- throw new coding_exception('You must provide a mode when creating a cache definition');
+ throw new coding_exception('You must provide an area when creating a cache definition');
}
$mode = (int)$definition['mode'];
$component = (string)$definition['component'];
*/
public function get_data_source() {
if (!$this->has_data_source()) {
- throw new coding_exception('This cache does not use a datasource.');
+ throw new coding_exception('This cache does not use a data source.');
}
return forward_static_call(array($this->datasource, 'get_instance_for_cache'), $this);
}
* Returns true if this store supports multiple identifiers.
* @return bool
*/
- public function supports_multiple_indentifiers() {
+ public function supports_multiple_identifiers() {
return false;
}
public static function initialise_test_instance(cache_definition $definition) {
$cache = new cachestore_dummy('Dummy store test');
$cache->initialise($definition);
- return $cache;;
+ return $cache;
}
/**
public function my_name() {
return $this->name;
}
-}
\ No newline at end of file
+}
*
* Please note that this happens automatically if the cache definition requires locking.
* it is still made a public method so that adhoc caches can use it if they choose.
- * However this doesn't guarantee consistent access. It will become the reponsiblity of the calling code to ensure locks
- * are acquired, checked, and released.
+ * However this doesn't guarantee consistent access. It will become the responsibility of the calling code to ensure
+ * locks are acquired, checked, and released.
*
* @param string|int $key
* @return bool True if the lock could be acquired, false otherwise.
*
* Please note that this happens automatically if the cache definition requires locking.
* it is still made a public method so that adhoc caches can use it if they choose.
- * However this doesn't guarantee consistent access. It will become the reponsiblity of the calling code to ensure locks
- * are acquired, checked, and released.
+ * However this doesn't guarantee consistent access. It will become the responsibility of the calling code to ensure
+ * locks are acquired, checked, and released.
*
* @param string|int $key
* @return bool True if this code has the lock, false if there is a lock but this code doesn't have it,
*
* Please note that this happens automatically if the cache definition requires locking.
* it is still made a public method so that adhoc caches can use it if they choose.
- * However this doesn't guarantee consistent access. It will become the reponsiblity of the calling code to ensure locks
- * are acquired, checked, and released.
+ * However this doesn't guarantee consistent access. It will become the responsibility of the calling code to ensure
+ * locks are acquired, checked, and released.
*
* @param string|int $key
* @return bool True if the lock has been released, false if there was a problem releasing the lock.
*
* @return bool
*/
- public function supports_multiple_indentifiers();
+ public function supports_multiple_identifiers();
/**
* Returns true if this cache store instance promotes data guarantee.
* Cache loaders
*
* This file is part of Moodle's cache API, affectionately called MUC.
- * It contains the components that are requried in order to use caching.
+ * It contains the components that are required in order to use caching.
*
* @package core
* @category cache
*/
protected function parse_key($key) {
// First up if the store supports multiple keys we'll go with that.
- if ($this->store->supports_multiple_indentifiers()) {
+ if ($this->store->supports_multiple_identifiers()) {
$result = $this->definition->generate_multi_key_parts();
$result['key'] = $key;
return $result;
($store->get_supported_modes($return) & cache_store::MODE_REQUEST) == cache_store::MODE_REQUEST,
),
'supports' => array(
- 'multipleidentifiers' => $store->supports_multiple_indentifiers(),
+ 'multipleidentifiers' => $store->supports_multiple_identifiers(),
'dataguarantee' => $store->supports_data_guarantee(),
'nativettl' => $store->supports_native_ttl(),
'nativelocking' => ($store instanceof cache_is_lockable),
*
* @return bool
*/
- public function supports_multiple_indentifiers() {
+ public function supports_multiple_identifiers() {
return false;
}
* Pre-scan the cache to see which keys are present.
*/
protected function prescan_keys() {
- foreach (glob($this->glob_keys_pattern(), GLOB_MARK | GLOB_NOSORT) as $filename) {
- $this->keys[basename($filename)] = filemtime($filename);
+ $files = glob($this->glob_keys_pattern(), GLOB_MARK | GLOB_NOSORT);
+ if (is_array($files)) {
+ foreach ($files as $filename) {
+ $this->keys[basename($filename)] = filemtime($filename);
+ }
}
}
* @return boolean True on success. False otherwise.
*/
public function purge() {
- foreach (glob($this->glob_keys_pattern(), GLOB_MARK | GLOB_NOSORT) as $filename) {
- @unlink($filename);
+ $files = glob($this->glob_keys_pattern(), GLOB_MARK | GLOB_NOSORT);
+ if (is_array($files)) {
+ foreach ($files as $filename) {
+ @unlink($filename);
+ }
}
$this->keys = array();
return true;
*
* @return bool
*/
- public function supports_multiple_indentifiers() {
+ public function supports_multiple_identifiers() {
return false;
}
*
* @return bool
*/
- public function supports_multiple_indentifiers() {
+ public function supports_multiple_identifiers() {
return false;
}
* Returns true if this store is making use of multiple identifiers.
* @return bool
*/
- public function supports_multiple_indentifiers() {
+ public function supports_multiple_identifiers() {
return $this->extendedmode;
}
* @return bool
*/
public function supports_native_ttl() {
- return false;;
+ return false;
}
/**
public function my_name() {
return $this->name;
}
-}
\ No newline at end of file
+}
*
* @return bool
*/
- public function supports_multiple_indentifiers() {
+ public function supports_multiple_identifiers() {
return false;
}
*
* @return bool
*/
- public function supports_multiple_indentifiers() {
+ public function supports_multiple_identifiers() {
return false;
}
// Do something here perhaps.
$cache = new cachestore_static('Static store');
$cache->initialise($definition);
- return $cache;;
+ return $cache;
}
/**
self::$staticstore[$id] = array();
}
}
-}
\ No newline at end of file
+}
// URL.
$mform->addElement('text', 'url', get_string('importfromurl', 'calendar'), array('maxsize' => '255', 'size' => '50'));
- $mform->setType('url', PARAM_URL);
+ // Cannot set as PARAM_URL since we need to allow webcal:// protocol.
+ $mform->setType('url', PARAM_RAW);
// Import file
$mform->addElement('filepicker', 'importfile', get_string('importfromfile', 'calendar'));
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);
- if (empty($data['url']) && empty($data['importfile'])) {
+ $url = $data['url'];
+ if (empty($url) && empty($data['importfile'])) {
if (!empty($data['importfrom']) && $data['importfrom'] == CALENDAR_IMPORT_FROM_FILE) {
$errors['importfile'] = get_string('errorrequiredurlorfile', 'calendar');
} else {
$errors['url'] = get_string('errorrequiredurlorfile', 'calendar');
}
+ } elseif (!empty($url)) {
+ // Url is webcal protocol which is not accepted by PARAM_URL.
+ if (stripos($url, "webcal://") === 0) {
+ $url = substr($url, strlen("webcal://"));
+ }
+ if (clean_param($url, PARAM_URL) !== $url) {
+ $errors['url'] = get_string('invalidurl', 'error');
+ }
}
return $errors;
}
echo $OUTPUT->notification(get_string('removeuserwarning', 'core_cohort'));
// Get the user_selector we will need.
-$potentialuserselector = new cohort_candidate_selector('addselect', array('cohortid'=>$cohort->id));
-$existinguserselector = new cohort_existing_selector('removeselect', array('cohortid'=>$cohort->id));
+$potentialuserselector = new cohort_candidate_selector('addselect', array('cohortid'=>$cohort->id, 'accesscontext'=>$context));
+$existinguserselector = new cohort_existing_selector('removeselect', array('cohortid'=>$cohort->id, 'accesscontext'=>$context));
// Process incoming user assignments to the cohort
preview.li.appendChild(preview.div);
preview.icon.src = M.util.image_url('t/addfile');
+ preview.icon.className = 'icon';
preview.div.appendChild(preview.icon);
preview.div.appendChild(document.createTextNode(' '));
$mform->hardFreeze('summary_editor');
}
- $courseformats = get_plugin_list('format');
+ $courseformats = get_sorted_course_formats(true);
$formcourseformats = array();
- foreach ($courseformats as $courseformat => $formatdir) {
+ foreach ($courseformats as $courseformat) {
$formcourseformats[$courseformat] = get_string('pluginname', "format_$courseformat");
}
+ if (isset($course->format)) {
+ $course->format = course_get_format($course)->get_format(); // replace with default if not found
+ if (!in_array($course->format, $courseformats)) {
+ // this format is disabled. Still display it in the dropdown
+ $formcourseformats[$course->format] = get_string('withdisablednote', 'moodle',
+ get_string('pluginname', 'format_'.$course->format));
+ }
+ }
+
$mform->addElement('select', 'format', get_string('format'), $formcourseformats);
$mform->addHelpButton('format', 'format');
$mform->setDefault('format', $courseconfig->format);
if ($oldcourse !== null) {
$data = (array)$data;
$oldcourse = (array)$oldcourse;
- foreach ($this->course_format_options() as $key => $unused) {
- if (array_key_exists($key, $oldcourse) && !array_key_exists($key, $data)) {
- $data[$key] = $oldcourse[$key];
+ $options = $this->course_format_options();
+ foreach ($options as $key => $unused) {
+ if (!array_key_exists($key, $data)) {
+ if (array_key_exists($key, $oldcourse)) {
+ $data[$key] = $oldcourse[$key];
+ } else if ($key === 'numsections') {
+ // If previous format does not have the field 'numsections' and this one does,
+ // and $data['numsections'] is not set fill it with the maximum section number from the DB
+ $maxsection = $DB->get_field_sql('SELECT max(section) from {course_sections}
+ WHERE course = ?', array($this->courseid));
+ if ($maxsection) {
+ // If there are no sections, or just default 0-section, 'numsections' will be set to default
+ $data['numsections'] = $maxsection;
+ }
+ }
}
}
}
if ($format === 'site') {
return $format;
}
- $plugins = get_plugin_list('format'); // TODO MDL-35260 filter only enabled
- if (isset($plugins[$format])) {
+ $plugins = get_sorted_course_formats();
+ if (in_array($format, $plugins)) {
return $format;
}
// Else return default format
- $defaultformat = reset($plugins); // TODO MDL-35260 get default format from config
- debugging('Format plugin format_'.$format.' is not found or is not enabled. Using default format_'.$defaultformat, DEBUG_DEVELOPER);
+ $defaultformat = get_config('moodlecourse', 'format');
+ if (!in_array($defaultformat, $plugins)) {
+ // when default format is not set correctly, use the first available format
+ $defaultformat = reset($plugins);
+ }
+ static $warningprinted = array();
+ if (empty($warningprinted[$format])) {
+ debugging('Format plugin format_'.$format.' is not found. Using default format_'.$defaultformat, DEBUG_DEVELOPER);
+ $warningprinted[$format] = true;
+ }
return $defaultformat;
}
*/
public function page_set_cm(moodle_page $page) {
}
+
+ /**
+ * Course-specific information to be output on any course page (usually above navigation bar)
+ *
+ * Example of usage:
+ * define
+ * class format_FORMATNAME_XXX implements renderable {}
+ *
+ * create format renderer in course/format/FORMATNAME/renderer.php, define rendering function:
+ * class format_FORMATNAME_renderer extends plugin_renderer_base {
+ * protected function render_format_FORMATNAME_XXX(format_FORMATNAME_XXX $xxx) {
+ * return html_writer::tag('div', 'This is my header/footer');
+ * }
+ * }
+ *
+ * Return instance of format_FORMATNAME_XXX in this function, the appropriate method from
+ * plugin renderer will be called
+ *
+ * @return null|renderable null for no output or object with data for plugin renderer
+ */
+ public function course_header() {
+ return null;
+ }
+
+ /**
+ * Course-specific information to be output on any course page (usually in the beginning of
+ * standard footer)
+ *
+ * See {@link format_base::course_header()} for usage
+ *
+ * @return null|renderable null for no output or object with data for plugin renderer
+ */
+ public function course_footer() {
+ return null;
+ }
+
+ /**
+ * Course-specific information to be output immediately above content on any course page
+ *
+ * See {@link format_base::course_header()} for usage
+ *
+ * @return null|renderable null for no output or object with data for plugin renderer
+ */
+ public function course_content_header() {
+ return null;
+ }
+
+ /**
+ * Course-specific information to be output immediately below content on any course page
+ *
+ * See {@link format_base::course_header()} for usage
+ *
+ * @return null|renderable null for no output or object with data for plugin renderer
+ */
+ public function course_content_footer() {
+ return null;
+ }
+
+ /**
+ * Returns instance of page renderer used by this plugin
+ *
+ * @param moodle_page $page
+ * @return renderer_base
+ */
+ public function get_renderer(moodle_page $page) {
+ return $page->get_renderer('format_'. $this->get_format());
+ }
}
/**
.course-content ul.topics {margin:0;}
.course-content ul.topics li.section {list-style: none;margin:5px 0 0 0;padding:0;}
.course-content ul.topics li.section .content {margin:0 40px;}
-.course-content ul.topics li.section .left {width:40px;float:left;text-align:center;}
+.course-content ul.topics li.section .left {width:40px;float:left;text-align:center;padding-top: 4px;}
.course-content ul.topics li.section .right {width:40px;float:right;text-align:center;padding-top: 4px;}
-.jumpmenu {text-align:center;}
\ No newline at end of file
+.course-content ul.topics li.section .left .section-handle img.icon { padding:0; vertical-align: baseline; }
+.jumpmenu {text-align:center;}
functions callback_XXXX_request_key() are no longer used (where XXXX is the course format name)
* functions get_generic_section_name(), get_all_sections(), add_mod_to_section(), get_all_mods()
are deprecated. See their phpdocs in lib/deprecatedlib.php on how to replace them
+* Course formats may now have their settings.php file as the most of other plugin types
=== 2.3 ===
.course-content ul.weeks {margin:0;}
.course-content ul.weeks li.section {list-style: none;margin:5px 0 0 0;padding:0;}
.course-content ul.weeks li.section .content {margin:0 40px;}
-.course-content ul.weeks li.section .left {width:40px;float:left;text-align:center;}
+.course-content ul.weeks li.section .left {width:40px;float:left;text-align:center;padding-top: 4px;}
.course-content ul.weeks li.section .right {width:40px;float:right;text-align:center;padding-top: 4px;}
+.course-content ul.weeks li.section .left .section-handle img.icon { padding:0; vertical-align: baseline; }
.jumpmenu {text-align:center;}
\ No newline at end of file
$displaylist[0] = get_string('top');
make_categories_list($displaylist, $parentlist);
-echo '<table class="generalbox editcourse boxaligncenter"><tr class="header">';
+echo '<table class="generaltable editcourse boxaligncenter"><tr class="header">';
echo '<th class="header" scope="col">'.$strcategories.'</th>';
echo '<th class="header" scope="col">'.$strcourses.'</th>';
echo '<th class="header" scope="col">'.$stredit.'</th>';
$accesstext = '';
}
if ($linkclasses) {
- $linkcss = 'class="' . trim($linkclasses) . '" ';
+ $linkcss = 'class="activityinstance ' . trim($linkclasses) . '" ';
} else {
- $linkcss = '';
+ $linkcss = 'class="activityinstance"';
}
if ($textclasses) {
$textcss = 'class="' . trim($textclasses) . '" ';
// Display link itself
echo '<a ' . $linkcss . $mod->extra . $onclick .
' href="' . $url . '"><img src="' . $mod->get_icon_url() .
- '" class="activityicon" alt="' . $mod->modfullname . '" /> ' .
+ '" class="iconlarge activityicon" alt="' . $mod->modfullname . '" />' .
$accesstext . '<span class="instancename">' .
$instancename . $altname . '</span></a>';
} else {
$mod->groupmode = false;
}
- echo ' ';
echo make_editing_buttons($mod, $absolute, true, $mod->indent, $sectionreturn);
echo $mod->get_after_edit_icons();
}
if (has_capability('moodle/role:assign', $modcontext)){
$actions[] = new action_link(
new moodle_url('/'.$CFG->admin.'/roles/assign.php', array('contextid' => $modcontext->id)),
- new pix_icon('i/roles', $str->assign, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ new pix_icon('t/assignroles', $str->assign, 'moodle', array('class' => 'iconsmall', 'title' => '')),
null,
array('class' => 'editing_assign', 'title' => $str->assign)
);
return true;
}
+/**
+ * Returns the sorted list of available course formats, filtered by enabled if necessary
+ *
+ * @param bool $enabledonly return only formats that are enabled
+ * @return array array of sorted format names
+ */
+function get_sorted_course_formats($enabledonly = false) {
+ global $CFG;
+ $formats = get_plugin_list('format');
+
+ if (!empty($CFG->format_plugins_sortorder)) {
+ $order = explode(',', $CFG->format_plugins_sortorder);
+ $order = array_merge(array_intersect($order, array_keys($formats)),
+ array_diff(array_keys($formats), $order));
+ } else {
+ $order = array_keys($formats);
+ }
+ if (!$enabledonly) {
+ return $order;
+ }
+ $sortedformats = array();
+ foreach ($order as $formatname) {
+ if (!get_config('format_'.$formatname, 'disabled')) {
+ $sortedformats[] = $formatname;
+ }
+ }
+ return $sortedformats;
+}
+
/**
* The URL to use for the specified course (with section)
*
// Put all options into one tag 'alloptions' to allow us to handle scrolling
$formcontent .= html_writer::start_tag('div', array('class' => 'alloptions'));
- // Activities
- $activities = array_filter($modules,
- create_function('$mod', 'return ($mod->archetype !== MOD_CLASS_RESOURCE);'));
+ // Activities
+ $activities = array_filter($modules, function($mod) {
+ return ($mod->archetype !== MOD_ARCHETYPE_RESOURCE && $mod->archetype !== MOD_ARCHETYPE_SYSTEM);
+ });
if (count($activities)) {
$formcontent .= $this->course_modchooser_title('activities');
$formcontent .= $this->course_modchooser_module_types($activities);
}
// Resources
- $resources = array_filter($modules,
- create_function('$mod', 'return ($mod->archetype === MOD_CLASS_RESOURCE);'));
+ $resources = array_filter($modules, function($mod) {
+ return ($mod->archetype === MOD_ARCHETYPE_RESOURCE);
+ });
if (count($resources)) {
$formcontent .= $this->course_modchooser_title('resources');
$formcontent .= $this->course_modchooser_module_types($resources);
if ((movedown || moveup) && cssleft) {
cssleft.setStyle('cursor', 'move');
- cssleft.appendChild(Y.Node.create('<br />'));
- cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE));
+ cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true));
if (moveup) {
moveup.remove();
});
del.dd.plug(Y.Plugin.DDProxy, {
// Don't move the node at the end of the drag
- moveOnEnd: false
+ moveOnEnd: false,
+ cloneNode: true
});
del.dd.plug(Y.Plugin.DDConstrained, {
// Keep it inside the .course-content
edit_resource_title : function(e) {
// Get the element we're working on
var element = e.target.ancestor(CSS.ACTIVITYLI);
+ var elementdiv = element.one('div');
var instancename = element.one(CSS.INSTANCENAME);
var currenttitle = instancename.get('firstChild');
var oldtitle = currenttitle.get('data');
})
.addClass('titleeditor');
var editform = Y.Node.create('<form />')
- .setStyle('padding', '0')
- .setStyle('display', 'inline')
+ .addClass('activityinstance')
.setAttribute('action', '#');
-
var editinstructions = Y.Node.create('<span />')
.addClass('editinstructions')
.set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle'));
+ var activityicon = element.one('img.activityicon').cloneNode();
// Clear the existing content and put the editor in
currenttitle.set('data', '');
+ editform.appendChild(activityicon);
editform.appendChild(editor);
anchor.replace(editform);
- element.appendChild(editinstructions);
+ elementdiv.appendChild(editinstructions);
e.preventDefault();
// Focus and select the editor text
$this->page = optional_param(self::PAGEVAR, 0, PARAM_INT);
$this->perpage = optional_param(self::PERPAGEVAR, self::DEFAULTPERPAGE, PARAM_INT);
- $this->sort = optional_param(self::SORTVAR, self::DEFAULTSORT, PARAM_ALPHA);
+ $this->sort = optional_param(self::SORTVAR, self::DEFAULTSORT, PARAM_ALPHANUM);
$this->sortdirection = optional_param(self::SORTDIRECTIONVAR, self::DEFAULTSORTDIRECTION, PARAM_ALPHA);
$this->attributes = array('class'=>'userenrolment');
if ($imports = grade_helper::get_plugins_import($courseid)) {
$importnode = $gradenode->add($strings['import'], null, navigation_node::TYPE_CONTAINER);
foreach ($imports as $import) {
- $importnode->add($import->string, $import->link, navigation_node::TYPE_SETTING, null, $import->id, new pix_icon('i/restore', ''));
+ $importnode->add($import->string, $import->link, navigation_node::TYPE_SETTING, null, $import->id, new pix_icon('i/import', ''));
}
}
if ($exports = grade_helper::get_plugins_export($courseid)) {
$exportnode = $gradenode->add($strings['export'], null, navigation_node::TYPE_CONTAINER);
foreach ($exports as $export) {
- $exportnode->add($export->string, $export->link, navigation_node::TYPE_SETTING, null, $export->id, new pix_icon('i/backup', ''));
+ $exportnode->add($export->string, $export->link, navigation_node::TYPE_SETTING, null, $export->id, new pix_icon('i/export', ''));
}
}
$string['calendarsettings'] = 'Calendar';
$string['calendar_weekend'] = 'Weekend days';
$string['cannotdeletemodfilter'] = 'You cannot uninstall the \'{$a->filter}\' because it is part of the \'{$a->module}\' module.';
+$string['cannotuninstall'] = '{$a} can not be uninstalled.';
$string['cfgwwwrootslashwarning'] = 'You have defined $CFG->wwwroot incorrectly in your config.php file. You have included a \'/\' character at the end. Please remove it, or you will experience strange bugs like <a href=\'http://tracker.moodle.org/browse/MDL-11061\'>MDL-11061</a>.';
$string['cfgwwwrootwarning'] = 'You have defined $CFG->wwwroot incorrectly in your config.php file. It does not match the URL you are using to access this page. Please correct it, or you will experience strange bugs like <a href=\'http://tracker.moodle.org/browse/MDL-11061\'>MDL-11061</a>.';
$string['clamfailureonupload'] = 'On clam AV failure';
$string['debugvalidators'] = 'Show validator links';
$string['defaultcity'] = 'Default city';
$string['defaultcity_help'] = 'A city entered here will be the default city when creating new user accounts.';
+$string['defaultformatnotset'] = 'Error determining default course format. Please check site settings.';
$string['defaulthomepage'] = 'Default home page for users';
$string['defaultrequestcategory'] = 'Default category for course requests';
$string['defaultsettinginfo'] = 'Default: {$a}';
$string['forceloginforprofileimage_help'] = 'If enabled, users must login in order to view user profile pictures and the default user picture will be used in all notification emails.';
$string['forceloginforprofiles'] = 'Force users to login for profiles';
$string['forcetimezone'] = 'Force default timezone';
+$string['formatuninstallwithcourses'] = 'There are {$a->count} courses using {$a->format}. Their format will be changed to {$a->defaultformat} (default format for this site). Some format-specific data may be lost. Are you sure you want to proceed?';
+$string['formatuninstallconfirm'] = '{$a} will be uninstalled. No courses currently use it. Continue?';
+$string['formatuninstalled'] = 'All data associated with the format plugin \'{$a->plugin}\' has been deleted from the database. To complete the deletion (and prevent the plugin re-installing itself), you should now delete this directory from your server: {$a->directory}';
$string['frontpage'] = 'Front page';
$string['frontpagebackup'] = 'Front page backup';
$string['frontpagedefaultrole'] = 'Default frontpage role';
$string['maintenancemode'] = 'In maintenance mode';
$string['maintfileopenerror'] = 'Error opening maintenance files!';
$string['maintinprogress'] = 'Maintenance is in progress...';
+$string['manageformats'] = 'Manage course formats';
+$string['manageformatsgotosettings'] = 'Default format can be changed in {$a}';
$string['managelang'] = 'Manage';
$string['managelicenses'] = 'Manage licences';
$string['manageqbehaviours'] = 'Manage question behaviours';
$string['cannotdeleterole'] = 'It cannot be deleted, because {$a}';
$string['cannotdeleterolewithid'] = 'Could not delete role with ID {$a}';
$string['cannotdeletethisrole'] = 'You cannot delete this role because it is used by the system, or because it is the last role with administrator capabilities.';
+$string['cannotdisableformat'] = 'You can not disable the default format';
$string['cannotdownloadcomponents'] = 'Cannot download components';
$string['cannotdownloadlanguageupdatelist'] = 'Cannot download list of language updates from download.moodle.org';
$string['cannotdownloadzipfile'] = 'Cannot download ZIP file';
$string['coursecreators'] = 'Course creator';
$string['coursecreatorsdescription'] = 'Course creators can create new courses.';
$string['coursedisplay'] = 'Course layout';
-$string['coursedisplay_help'] = 'This setting determines whether the whole course is displayed on one page or split over several pages. The setting has no affect on certain course formats, such as SCORM format.';
+$string['coursedisplay_help'] = 'This setting determines whether the whole course is displayed on one page or split over several pages.';
$string['coursedisplay_single'] = 'Show all sections on one page';
$string['coursedisplay_multi'] = 'Show one section per page';
$string['coursedeleted'] = 'Deleted course {$a}';
$string['whattodo'] = 'What to do';
$string['windowclosing'] = 'This window should close automatically. If not, please close it now.';
$string['withchosenfiles'] = 'With chosen files';
+$string['withdisablednote'] = '{$a} (disabled)';
$string['withoutuserdata'] = 'without user data';
$string['withselectedusers'] = 'With selected users...';
$string['withselectedusers_help'] = '* Send message - For sending a message to one or more participants
// Delete block
$DB->delete_records('block', array('id'=>$block->id));
}
+ } else if ($type === 'format') {
+ if (($defaultformat = get_config('moodlecourse', 'format')) && $defaultformat !== $name) {
+ $courses = $DB->get_records('course', array('format' => $name), 'id');
+ $data = (object)array('id' => null, 'format' => $defaultformat);
+ foreach ($courses as $record) {
+ $data->id = $record->id;
+ update_course($data);
+ }
+ }
+ $DB->delete_records('course_format_options', array('format' => $name));
}
// perform clean-up task common for all the plugin/subplugin types
*/
public function __construct() {
global $CFG;
- parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'), "$CFG->wwwroot/$CFG->admin/qtypes.php");
+ parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
+ new moodle_url('/admin/qtypes.php'));
}
/**
}
}
+/**
+ * Course formats manager. Allows to enable/disable formats and jump to settings
+ */
+class admin_setting_manageformats extends admin_setting {
+
+ /**
+ * Calls parent::__construct with specific arguments
+ */
+ public function __construct() {
+ $this->nosave = true;
+ parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
+ }
+
+ /**
+ * Always returns true
+ *
+ * @return true
+ */
+ public function get_setting() {
+ return true;
+ }
+
+ /**
+ * Always returns true
+ *
+ * @return true
+ */
+ public function get_defaultsetting() {
+ return true;
+ }
+
+ /**
+ * Always returns '' and doesn't write anything
+ *
+ * @param mixed $data string or array, must not be NULL
+ * @return string Always returns ''
+ */
+ public function write_setting($data) {
+ // do not write any setting
+ return '';
+ }
+
+ /**
+ * Search to find if Query is related to format plugin
+ *
+ * @param string $query The string to search for
+ * @return bool true for related false for not
+ */
+ public function is_related($query) {
+ if (parent::is_related($query)) {
+ return true;
+ }
+ $allplugins = plugin_manager::instance()->get_plugins();
+ $formats = $allplugins['format'];
+ foreach ($formats as $format) {
+ if (strpos($format->component, $query) !== false ||
+ strpos(textlib::strtolower($format->displayname), $query) !== false) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return XHTML to display control
+ *
+ * @param mixed $data Unused
+ * @param string $query
+ * @return string highlight
+ */
+ public function output_html($data, $query='') {
+ global $CFG, $OUTPUT;
+ $return = '';
+ $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
+ $return .= $OUTPUT->box_start('generalbox formatsui');
+
+ $allplugins = plugin_manager::instance()->get_plugins();
+ $formats = $allplugins['format'];
+
+ // display strings
+ $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default', 'delete'));
+ $txt->updown = "$txt->up/$txt->down";
+
+ $table = new html_table();
+ $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->delete, $txt->settings);
+ $table->align = array('left', 'center', 'center', 'center', 'center');
+ $table->width = '90%';
+ $table->attributes['class'] = 'manageformattable generaltable';
+ $table->data = array();
+
+ $cnt = 0;
+ $defaultformat = get_config('moodlecourse', 'format');
+ $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon'));
+ foreach ($formats as $format) {
+ $url = new moodle_url('/admin/courseformats.php',
+ array('sesskey' => sesskey(), 'format' => $format->name));
+ $isdefault = '';
+ if ($format->is_enabled()) {
+ $strformatname = html_writer::tag('span', $format->displayname);
+ if ($defaultformat === $format->name) {
+ $hideshow = $txt->default;
+ } else {
+ $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
+ $OUTPUT->pix_icon('i/hide', $txt->disable, 'moodle', array('class' => 'icon')));
+ }
+ } else {
+ $strformatname = html_writer::tag('span', $format->displayname, array('class' => 'dimmed_text'));
+ $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
+ $OUTPUT->pix_icon('i/show', $txt->enable, 'moodle', array('class' => 'icon')));
+ }
+ $updown = '';
+ if ($cnt) {
+ $updown .= html_writer::link($url->out(false, array('action' => 'up')),
+ $OUTPUT->pix_icon('t/up', $txt->up, 'moodle')). ' ';
+ } else {
+ $updown .= $spacer;
+ }
+ if ($cnt < count($formats) - 1) {
+ $updown .= ' '.html_writer::link($url->out(false, array('action' => 'down')),
+ $OUTPUT->pix_icon('t/down', $txt->down, 'moodle'));
+ } else {
+ $updown .= $spacer;
+ }
+ $cnt++;
+ $settings = '';
+ if ($format->get_settings_url()) {
+ $settings = html_writer::link($format->get_settings_url(), $txt->settings);
+ }
+ $uninstall = '';
+ if ($defaultformat !== $format->name) {
+ $uninstall = html_writer::link($format->get_uninstall_url(), $txt->delete);
+ }
+ $table->data[] =array($strformatname, $hideshow, $updown, $uninstall, $settings);
+ }
+ $return .= html_writer::table($table);
+ $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
+ $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
+ $return .= $OUTPUT->box_end();
+ return highlight($query, $return);
+ }
+}
/**
* Special class for filter administration.
$controls[] = array('url' => $CFG->wwwroot . '/' . $CFG->admin .
'/roles/assign.php?contextid=' . $block->context->id . '&returnurl=' . urlencode($return),
- 'icon' => 'i/roles', 'caption' => get_string('assignroles', 'role'), 'class' => 'editing_roles');
+ 'icon' => 't/assignroles', 'caption' => get_string('assignroles', 'role'), 'class' => 'editing_roles');
}
return $controls;
Minify::setCache(null, false);
$options = array(
+ // JSMin is not GNU GPL compatible, use the plus version instead.
+ 'minifiers' => array(Minify::TYPE_JS => array('JSMinPlus', 'minify')),
'bubbleCssImports' => false,
// Don't gzip content we just want text for storage
'encodeOutput' => false,
<?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20121102" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20121112" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
<KEY NAME="fk_raterid" TYPE="foreign" FIELDS="raterid" REFTABLE="user" REFFIELDS="id" PREVIOUS="fk_definitionid"/>
</KEYS>
</TABLE>
- <TABLE NAME="event_subscriptions" COMMENT="Tracks subscriptions to remote calendars." PREVIOUS="grading_instances">
+ <TABLE NAME="event_subscriptions" COMMENT="Tracks subscriptions to remote calendars." PREVIOUS="grading_instances" NEXT="temp_enroled_template">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="url"/>
<FIELD NAME="url" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="courseid"/>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
</TABLE>
+ <TABLE NAME="temp_enroled_template" COMMENT="Temporary storage for course enrolments" PREVIOUS="event_subscriptions" NEXT="temp_log_template">
+ <FIELDS>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="userid"/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="courseid"/>
+ <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="userid" NEXT="roleid"/>
+ <FIELD NAME="roleid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" PREVIOUS="courseid"/>
+ </FIELDS>
+ <KEYS>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ </KEYS>
+ <INDEXES>
+ <INDEX NAME="userid" UNIQUE="false" FIELDS="userid" NEXT="courseid"/>
+ <INDEX NAME="courseid" UNIQUE="false" FIELDS="courseid" PREVIOUS="userid" NEXT="roleid"/>
+ <INDEX NAME="roleid" UNIQUE="false" FIELDS="roleid" PREVIOUS="courseid"/>
+ </INDEXES>
+ </TABLE>
+ <TABLE NAME="temp_log_template" COMMENT="Temporary storage for daily logs" PREVIOUS="temp_enroled_template">
+ <FIELDS>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="userid"/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="course"/>
+ <FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="userid" NEXT="action"/>
+ <FIELD NAME="action" TYPE="char" LENGTH="40" NOTNULL="true" SEQUENCE="false" PREVIOUS="course"/>
+ </FIELDS>
+ <KEYS>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ </KEYS>
+ <INDEXES>
+ <INDEX NAME="action" UNIQUE="false" FIELDS="action" NEXT="course"/>
+ <INDEX NAME="course" UNIQUE="false" FIELDS="course" PREVIOUS="action" NEXT="user"/>
+ <INDEX NAME="user" UNIQUE="false" FIELDS="userid" PREVIOUS="course" NEXT="usercourseaction"/>
+ <INDEX NAME="usercourseaction" UNIQUE="false" FIELDS="userid, course, action" PREVIOUS="user"/>
+ </INDEXES>
+ </TABLE>
</TABLES>
-</XMLDB>
+</XMLDB>
\ No newline at end of file
upgrade_main_savepoint(true, 2012110700.01);
}
+ if ($oldversion < 2012111200.00) {
+
+ // Define table temp_enroled_template to be created
+ $table = new xmldb_table('temp_enroled_template');
+
+ // Adding fields to table temp_enroled_template
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+ $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+ $table->add_field('courseid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+ $table->add_field('roleid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+
+ // Adding keys to table temp_enroled_template
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+
+ // Adding indexes to table temp_enroled_template
+ $table->add_index('userid', XMLDB_INDEX_NOTUNIQUE, array('userid'));
+ $table->add_index('courseid', XMLDB_INDEX_NOTUNIQUE, array('courseid'));
+ $table->add_index('roleid', XMLDB_INDEX_NOTUNIQUE, array('roleid'));
+
+ // Conditionally launch create table for temp_enroled_template
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+ // Define table temp_log_template to be created
+ $table = new xmldb_table('temp_log_template');
+
+ // Adding fields to table temp_log_template
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+ $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+ $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+ $table->add_field('action', XMLDB_TYPE_CHAR, '40', null, XMLDB_NOTNULL, null, null);
+
+ // Adding keys to table temp_log_template
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+
+ // Adding indexes to table temp_log_template
+ $table->add_index('action', XMLDB_INDEX_NOTUNIQUE, array('action'));
+ $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
+ $table->add_index('user', XMLDB_INDEX_NOTUNIQUE, array('userid'));
+ $table->add_index('usercourseaction', XMLDB_INDEX_NOTUNIQUE, array('userid', 'course', 'action'));
+
+ // Conditionally launch create table for temp_log_template
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+ // Main savepoint reached
+ upgrade_main_savepoint(true, 2012111200.00);
+ }
+
+
return true;
}
$htmllang = get_html_lang();
header('Content-Type: text/html; charset=utf-8');
+header('X-UA-Compatible: IE=edge');
?>
<!DOCTYPE html>
<html <?php echo $htmllang ?>
}
$file = $cachefile;
+} else if ($mimetype === 'text/html') {
+ header('X-UA-Compatible: IE=edge');
}
// Serve file.
$htmllang = get_html_lang();
header('Content-Type: text/html; charset=utf-8');
+header('X-UA-Compatible: IE=edge');
?>
<!DOCTYPE html>
<html <?php echo $htmllang ?>
$matches = $this->_getMatches($lang, $word);\r
\r
if (count($matches) > 0)\r
- $sug = explode("\t", utf8_encode($this->_unhtmlentities($matches[0][4])));\r
+ $sug = explode("\t", $this->_unhtmlentities($matches[0][4]));\r
\r
// Remove empty\r
foreach ($sug as $item) {\r
Changes:
-1/ zIndex 300000 and 200000 changed to 3000 and 2000 - this prevents collision with YUI,
+ * zIndex 300000 and 200000 changed to 3000 and 2000 - this prevents collision with YUI,
see MDL-35771
+ * MDL-25736 - French spellchecker fixes.
TODO:
* create some new automated script that sends other languages from upstream into AMOS
if ($course->id == SITEID or (!empty($CFG->adminsassignrolesincourse) and is_siteadmin())) {
if (has_capability('moodle/role:assign', $coursecontext)) {
$url = new moodle_url('/admin/roles/assign.php', array('contextid'=>$coursecontext->id));
- $permissionsnode->add(get_string('assignedroles', 'role'), $url, navigation_node::TYPE_SETTING, null, 'roles', new pix_icon('i/roles', ''));
+ $permissionsnode->add(get_string('assignedroles', 'role'), $url, navigation_node::TYPE_SETTING, null, 'roles', new pix_icon('i/assignroles', ''));
}
}
// Check role permissions
//TODO, create some new UI for role assignments at course level
if (has_capability('moodle/role:assign', $coursecontext)) {
$url = new moodle_url('/enrol/otherusers.php', array('id'=>$course->id));
- $usersnode->add(get_string('notenrolledusers', 'enrol'), $url, navigation_node::TYPE_SETTING, null, 'otherusers', new pix_icon('i/roles', ''));
+ $usersnode->add(get_string('notenrolledusers', 'enrol'), $url, navigation_node::TYPE_SETTING, null, 'otherusers', new pix_icon('i/assignroles', ''));
}
}
* @return mixes $files Array in the format get_listing uses for folders
*/
public function get_albums() {
+ $files = array();
$content = $this->googleoauth->get(self::LIST_ALBUMS_URL);
- $xml = new SimpleXMLElement($content);
- $files = array();
+ if (empty($content)) {
+ return $files;
+ }
+ $xml = new SimpleXMLElement($content);
foreach ($xml->entry as $album) {
$gphoto = $album->children('http://schemas.google.com/photos/2007');
'thumbnail_height' => 160,
'children' => array(),
);
-
}
return $files;
* @return mixed $files A list of files for the file picker
*/
public function get_photo_details($rawxml) {
+ $files = array();
+ if (empty($rawxml)) {
+ return $files;
+ }
$xml = new SimpleXMLElement($rawxml);
$this->lastalbumname = (string)$xml->title;
- $files = array();
-
foreach ($xml->entry as $photo) {
$gphoto = $photo->children('http://schemas.google.com/photos/2007');
global $CFG, $OUTPUT; //TODO: MUST NOT USE $OUTPUT HERE!!!
@header('Content-Type: text/html; charset=UTF-8');
+ @header('X-UA-Compatible: IE=edge');
@header('Cache-Control: no-store, no-cache, must-revalidate');
@header('Cache-Control: post-check=0, pre-check=0', false);
@header('Pragma: no-cache');
global $CFG;
@header('Content-Type: text/html; charset=UTF-8');
+ @header('X-UA-Compatible: IE=edge');
@header('Cache-Control: no-store, no-cache, must-revalidate');
@header('Cache-Control: post-check=0, pre-check=0', false);
@header('Pragma: no-cache');
'top' : 0,
'left' : 0,
'backgroundColor' : 'white',
- 'text-align' : 'center'
+ 'textAlign' : 'center'
})
.setAttribute('class', 'lightbox')
.hide();
Minify::setCache(null, false);
$options = array(
+ // JSMin is not GNU GPL compatible, use the plus version instead.
+ 'minifiers' => array(Minify::TYPE_JS => array('JSMinPlus', 'minify')),
'bubbleCssImports' => false,
// Don't gzip content we just want text for storage
'encodeOutput' => false,
function message_get_providers_for_user($userid) {
global $DB, $CFG;
- $systemcontext = context_system::instance();
-
$providers = get_message_providers();
- // Remove all the providers we aren't allowed to see now
- foreach ($providers as $providerid => $provider) {
- if (!empty($provider->capability)) {
- if (!has_capability($provider->capability, $systemcontext, $userid)) {
- unset($providers[$providerid]); // Not allowed to see this
- continue;
+ // Ensure user is not allowed to configure instantmessage if it is globally disabled.
+ if (!$CFG->messaging) {
+ foreach ($providers as $providerid => $provider) {
+ if ($provider->name == 'instantmessage') {
+ unset($providers[$providerid]);
+ break;
}
}
+ }
- // Ensure user is not allowed to configure instantmessage if it is globally disabled.
- if (!$CFG->messaging && $provider->name == 'instantmessage') {
+ // If the component is an enrolment plugin, check it is enabled
+ foreach ($providers as $providerid => $provider) {
+ list($type, $name) = normalize_component($provider->component);
+ if ($type == 'enrol' && !enrol_is_enabled($name)) {
unset($providers[$providerid]);
+ }
+ }
+
+ // Now we need to check capabilities. We need to eliminate the providers
+ // where the user does not have the corresponding capability anywhere.
+ // Here we deal with the common simple case of the user having the
+ // capability in the system context. That handles $CFG->defaultuserroleid.
+ // For the remaining providers/capabilities, we need to do a more complex
+ // query involving all overrides everywhere.
+ $unsureproviders = array();
+ $unsurecapabilities = array();
+ $systemcontext = context_system::instance();
+ foreach ($providers as $providerid => $provider) {
+ if (empty($provider->capability) || has_capability($provider->capability, $systemcontext, $userid)) {
+ // The provider is relevant to this user.
continue;
}
- // If the component is an enrolment plugin, check it is enabled
- list($type, $name) = normalize_component($provider->component);
- if ($type == 'enrol') {
- if (!enrol_is_enabled($name)) {
- unset($providers[$providerid]);
- continue;
- }
+ $unsureproviders[$providerid] = $provider;
+ $unsurecapabilities[$provider->capability] = 1;
+ unset($providers[$providerid]);
+ }
+
+ if (empty($unsureproviders)) {
+ // More complex checks are not required.
+ return $providers;
+ }
+
+ // Now check the unsure capabilities.
+ list($capcondition, $params) = $DB->get_in_or_equal(
+ array_keys($unsurecapabilities), SQL_PARAMS_NAMED);
+ $params['userid'] = $userid;
+
+ $sql = "SELECT DISTINCT rc.capability, 1
+
+ FROM {role_assignments} ra
+ JOIN {context} actx ON actx.id = ra.contextid
+ JOIN {role_capabilities} rc ON rc.roleid = ra.roleid
+ JOIN {context} cctx ON cctx.id = rc.contextid
+
+ WHERE ra.userid = :userid
+ AND rc.capability $capcondition
+ AND rc.permission > 0
+ AND (".$DB->sql_concat('actx.path', "'/'")." LIKE ".$DB->sql_concat('cctx.path', "'/%'").
+ " OR ".$DB->sql_concat('cctx.path', "'/'")." LIKE ".$DB->sql_concat('actx.path', "'/%'").")";
+
+ if (!empty($CFG->defaultfrontpageroleid)) {
+ $frontpagecontext = context_course::instance(SITEID);
+
+ list($capcondition2, $params2) = $DB->get_in_or_equal(
+ array_keys($unsurecapabilities), SQL_PARAMS_NAMED);
+ $params = array_merge($params, $params2);
+ $params['frontpageroleid'] = $CFG->defaultfrontpageroleid;
+ $params['frontpagepathpattern'] = $frontpagecontext->path . '/';
+
+ $sql .= "
+ UNION
+
+ SELECT DISTINCT rc.capability, 1
+
+ FROM {role_capabilities} rc
+ JOIN {context} cctx ON cctx.id = rc.contextid
+
+ WHERE rc.roleid = :frontpageroleid
+ AND rc.capability $capcondition2
+ AND rc.permission > 0
+ AND ".$DB->sql_concat('cctx.path', "'/'")." LIKE :frontpagepathpattern";
+ }
+
+ $relevantcapabilities = $DB->get_records_sql_menu($sql, $params);
+
+ // Add back any providers based on the detailed capability check.
+ foreach ($unsureproviders as $providerid => $provider) {
+ if (array_key_exists($provider->capability, $relevantcapabilities)) {
+ $providers[$providerid] = $provider;
}
}
+++ /dev/null
-<?php
-/**
- * JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
- *
- * <code>
- * $minifiedJs = JSMin::minify($js);
- * </code>
- *
- * This is a modified port of jsmin.c. Improvements:
- *
- * Does not choke on some regexp literals containing quote characters. E.g. /'/
- *
- * Spaces are preserved after some add/sub operators, so they are not mistakenly
- * converted to post-inc/dec. E.g. a + ++b -> a+ ++b
- *
- * Preserves multi-line comments that begin with /*!
- *
- * PHP 5 or higher is required.
- *
- * Permission is hereby granted to use this version of the library under the
- * same terms as jsmin.c, which has the following license:
- *
- * --
- * Copyright (c) 2002 Douglas Crockford (www.crockford.com)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is furnished to do
- * so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * The Software shall be used for Good, not Evil.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- * --
- *
- * @package JSMin
- * @author Ryan Grove <ryan@wonko.com> (PHP port)
- * @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
- * @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
- * @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
- * @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
- * @license http://opensource.org/licenses/mit-license.php MIT License
- * @link http://code.google.com/p/jsmin-php/
- */
-
-class JSMin {
- const ORD_LF = 10;
- const ORD_SPACE = 32;
- const ACTION_KEEP_A = 1;
- const ACTION_DELETE_A = 2;
- const ACTION_DELETE_A_B = 3;
-
- protected $a = "\n";
- protected $b = '';
- protected $input = '';
- protected $inputIndex = 0;
- protected $inputLength = 0;
- protected $lookAhead = null;
- protected $output = '';
- protected $lastByteOut = '';
-
- /**
- * Minify Javascript.
- *
- * @param string $js Javascript to be minified
- *
- * @return string
- */
- public static function minify($js)
- {
- $jsmin = new JSMin($js);
- return $jsmin->min();
- }
-
- /**
- * @param string $input
- */
- public function __construct($input)
- {
- $this->input = $input;
- }
-
- /**
- * Perform minification, return result
- *
- * @return string
- */
- public function min()
- {
- if ($this->output !== '') { // min already run
- return $this->output;
- }
-
- $mbIntEnc = null;
- if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
- $mbIntEnc = mb_internal_encoding();
- mb_internal_encoding('8bit');
- }
- $this->input = str_replace("\r\n", "\n", $this->input);
- $this->inputLength = strlen($this->input);
-
- $this->action(self::ACTION_DELETE_A_B);
-
- while ($this->a !== null) {
- // determine next command
- $command = self::ACTION_KEEP_A; // default
- if ($this->a === ' ') {
- if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
- && ($this->b === $this->lastByteOut)) {
- // Don't delete this space. If we do, the addition/subtraction
- // could be parsed as a post-increment
- } elseif (! $this->isAlphaNum($this->b)) {
- $command = self::ACTION_DELETE_A;
- }
- } elseif ($this->a === "\n") {
- if ($this->b === ' ') {
- $command = self::ACTION_DELETE_A_B;
- // in case of mbstring.func_overload & 2, must check for null b,
- // otherwise mb_strpos will give WARNING
- } elseif ($this->b === null
- || (false === strpos('{[(+-', $this->b)
- && ! $this->isAlphaNum($this->b))) {
- $command = self::ACTION_DELETE_A;
- }
- } elseif (! $this->isAlphaNum($this->a)) {
- if ($this->b === ' '
- || ($this->b === "\n"
- && (false === strpos('}])+-"\'', $this->a)))) {
- $command = self::ACTION_DELETE_A_B;
- }
- }
- $this->action($command);
- }
- $this->output = trim($this->output);
-
- if ($mbIntEnc !== null) {
- mb_internal_encoding($mbIntEnc);
- }
- return $this->output;
- }
-
- /**
- * ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
- * ACTION_DELETE_A = Copy B to A. Get the next B.
- * ACTION_DELETE_A_B = Get the next B.
- *
- * @param int $command
- * @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException
- */
- protected function action($command)
- {
- if ($command === self::ACTION_DELETE_A_B
- && $this->b === ' '
- && ($this->a === '+' || $this->a === '-')) {
- // Note: we're at an addition/substraction operator; the inputIndex
- // will certainly be a valid index
- if ($this->input[$this->inputIndex] === $this->a) {
- // This is "+ +" or "- -". Don't delete the space.
- $command = self::ACTION_KEEP_A;
- }
- }
- switch ($command) {
- case self::ACTION_KEEP_A:
- $this->output .= $this->a;
- $this->lastByteOut = $this->a;
-
- // fallthrough
- case self::ACTION_DELETE_A:
- $this->a = $this->b;
- if ($this->a === "'" || $this->a === '"') { // string literal
- $str = $this->a; // in case needed for exception
- while (true) {
- $this->output .= $this->a;
- $this->lastByteOut = $this->a;
-
- $this->a = $this->get();
- if ($this->a === $this->b) { // end quote
- break;
- }
- if (ord($this->a) <= self::ORD_LF) {
- throw new JSMin_UnterminatedStringException(
- "JSMin: Unterminated String at byte "
- . $this->inputIndex . ": {$str}");
- }
- $str .= $this->a;
- if ($this->a === '\\') {
- $this->output .= $this->a;
- $this->lastByteOut = $this->a;
-
- $this->a = $this->get();
- $str .= $this->a;
- }
- }
- }
- // fallthrough
- case self::ACTION_DELETE_A_B:
- $this->b = $this->next();
- if ($this->b === '/' && $this->isRegexpLiteral()) { // RegExp literal
- $this->output .= $this->a . $this->b;
- $pattern = '/'; // in case needed for exception
- while (true) {
- $this->a = $this->get();
- $pattern .= $this->a;
- if ($this->a === '/') { // end pattern
- break; // while (true)
- } elseif ($this->a === '\\') {
- $this->output .= $this->a;
- $this->a = $this->get();
- $pattern .= $this->a;
- } elseif (ord($this->a) <= self::ORD_LF) {
- throw new JSMin_UnterminatedRegExpException(
- "JSMin: Unterminated RegExp at byte "
- . $this->inputIndex .": {$pattern}");
- }
- $this->output .= $this->a;
- $this->lastByteOut = $this->a;
- }
- $this->b = $this->next();
- }
- // end case ACTION_DELETE_A_B
- }
- }
-
- /**
- * @return bool
- */
- protected function isRegexpLiteral()
- {
- if (false !== strpos("\n{;(,=:[!&|?", $this->a)) { // we aren't dividing
- return true;
- }
- if (' ' === $this->a) {
- $length = strlen($this->output);
- if ($length < 2) { // weird edge case
- return true;
- }
- // you can't divide a keyword
- if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) {
- if ($this->output === $m[0]) { // odd but could happen
- return true;
- }
- // make sure it's a keyword, not end of an identifier
- $charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
- if (! $this->isAlphaNum($charBeforeKeyword)) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Get next char. Convert ctrl char to space.
- *
- * @return string
- */
- protected function get()
- {
- $c = $this->lookAhead;
- $this->lookAhead = null;
- if ($c === null) {
- if ($this->inputIndex < $this->inputLength) {
- $c = $this->input[$this->inputIndex];
- $this->inputIndex += 1;
- } else {
- return null;
- }
- }
- if ($c === "\r" || $c === "\n") {
- return "\n";
- }
- if (ord($c) < self::ORD_SPACE) { // control char
- return ' ';
- }
- return $c;
- }
-
- /**
- * Get next char. If is ctrl character, translate to a space or newline.
- *
- * @return string
- */
- protected function peek()
- {
- $this->lookAhead = $this->get();
- return $this->lookAhead;
- }
-
- /**
- * Is $c a letter, digit, underscore, dollar sign, escape, or non-ASCII?
- *
- * @param string $c
- *
- * @return bool
- */
- protected function isAlphaNum($c)
- {
- return (preg_match('/^[0-9a-zA-Z_\\$\\\\]$/', $c) || ord($c) > 126);
- }
-
- /**
- * @return string
- */
- protected function singleLineComment()
- {
- $comment = '';
- while (true) {
- $get = $this->get();
- $comment .= $get;
- if (ord($get) <= self::ORD_LF) { // EOL reached
- // if IE conditional comment
- if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
- return "/{$comment}";
- }
- return $get;
- }
- }
- }
-
- /**
- * @return string
- * @throws JSMin_UnterminatedCommentException
- */
- protected function multipleLineComment()
- {
- $this->get();
- $comment = '';
- while (true) {
- $get = $this->get();
- if ($get === '*') {
- if ($this->peek() === '/') { // end of comment reached
- $this->get();
- // if comment preserved by YUI Compressor
- if (0 === strpos($comment, '!')) {
- return "\n/*!" . substr($comment, 1) . "*/\n";
- }
- // if IE conditional comment
- if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
- return "/*{$comment}*/";
- }
- return ' ';
- }
- } elseif ($get === null) {
- throw new JSMin_UnterminatedCommentException(
- "JSMin: Unterminated comment at byte "
- . $this->inputIndex . ": /*{$comment}");
- }
- $comment .= $get;
- }
- }
-
- /**
- * Get the next character, skipping over comments.
- * Some comments may be preserved.
- *
- * @return string
- */
- protected function next()
- {
- $get = $this->get();
- if ($get !== '/') {
- return $get;
- }
- switch ($this->peek()) {
- case '/': return $this->singleLineComment();
- case '*': return $this->multipleLineComment();
- default: return $get;
- }
- }
-}
-
-class JSMin_UnterminatedStringException extends Exception {}
-class JSMin_UnterminatedCommentException extends Exception {}
-class JSMin_UnterminatedRegExpException extends Exception {}
* Removed /builder/* - Not needed
* Removed .htaccess - Not needed
* Changed config.php - added moodle specific settings
+ * Removed lib/JSMin.php which is not GNU GPL compatible.
* Returns reference to full info about modules in course (including visibility).
* Cached and as fast as possible (0 or 1 db query).
*
+ * use get_fast_modinfo($courseid, 0, true) to reset the static cache for particular course
+ * use get_fast_modinfo(0, 0, true) to reset the static cache for all courses
+ *
* @uses MAX_MODINFO_CACHE_SIZE
- * @param int|stdClass $courseorid object or 'reset' string to reset caches, modinfo may be updated in db
- * @param int $userid Set 0 (default) for current user
+ * @param int|stdClass $courseorid object from DB table 'course' or just a course id
+ * @param int $userid User id to populate 'uservisible' attributes of modules and sections.
+ * Set to 0 for current user (default)
* @param bool $resetonly whether we want to get modinfo or just reset the cache
* @return course_modinfo|null Module information for course, or null if resetting
*/
// which should know about this updated property, as this event is meant to pass the full course record
$course->timemodified = time();
- $DB->delete_records("course", array("id"=>$courseid));
+ $DB->delete_records("course", array("id" => $courseid));
+ $DB->delete_records("course_format_options", array("courseid" => $courseid));
//trigger events
$course->context = $context; // you can not fetch context in the event because it was already deleted
if (strpos($agent, 'Opera') !== false) { // Reject Opera
return false;
}
- // in case of IE we have to deal with BC of the version parameter
+ // In case of IE we have to deal with BC of the version parameter.
if (is_null($version)) {
- $version = 5.5; // anything older is not considered a browser at all!
+ $version = 5.5; // Anything older is not considered a browser at all!
}
-
- //see: http://www.useragentstring.com/pages/Internet%20Explorer/
+ // IE uses simple versions, let's cast it to float to simplify the logic here.
+ $version = round($version, 1);
+ // See: http://www.useragentstring.com/pages/Internet%20Explorer/
if (preg_match("/MSIE ([0-9\.]+)/", $agent, $match)) {
- if (version_compare($match[1], $version) >= 0) {
- return true;
- }
+ $browser = $match[1];
+ } else {
+ return false;
+ }
+ // IE8 and later versions may pretend to be IE7 for intranet sites, use Trident version instead,
+ // the Trident should always describe the capabilities of IE in any emulation mode.
+ if ($browser === '7.0' and preg_match("/Trident\/([0-9\.]+)/", $agent, $match)) {
+ $browser = $match[1] + 4; // NOTE: Hopefully this will work also for future IE versions.
}
+ $browser = round($browser, 1);
+ return ($browser >= $version);
break;
if (check_browser_version("MSIE", "0")) {
$classes[] = 'ie';
- if (check_browser_version("MSIE", 9)) {
- $classes[] = 'ie9';
- } else if (check_browser_version("MSIE", 8)) {
- $classes[] = 'ie8';
- } elseif (check_browser_version("MSIE", 7)) {
- $classes[] = 'ie7';
- } elseif (check_browser_version("MSIE", 6)) {
- $classes[] = 'ie6';
+ for($i=12; $i>=6; $i--) {
+ if (check_browser_version("MSIE", $i)) {
+ $classes[] = 'ie'.$i;
+ break;
+ }
}
} else if (check_browser_version("Firefox") || check_browser_version("Gecko") || check_browser_version("Camino")) {
// Import data from other courses
if (has_capability('moodle/restore:restoretargetimport', $coursecontext)) {
$url = new moodle_url('/backup/import.php', array('id'=>$course->id));
- $coursenode->add(get_string('import'), $url, self::TYPE_SETTING, null, 'import', new pix_icon('i/restore', ''));
+ $coursenode->add(get_string('import'), $url, self::TYPE_SETTING, null, 'import', new pix_icon('i/import', ''));
}
// Publish course on a hub
$returnurl->param('sesskey', sesskey());
foreach ($roles as $key => $name) {
$url = new moodle_url('/course/switchrole.php', array('id'=>$course->id,'sesskey'=>sesskey(), 'switchrole'=>$key, 'returnurl'=>$returnurl->out(false)));
- $switchroles->add($name, $url, self::TYPE_SETTING, null, $key, new pix_icon('i/roles', ''));
+ $switchroles->add($name, $url, self::TYPE_SETTING, null, $key, new pix_icon('i/switchrole', ''));
}
}
// Return we are done
// Assign local roles
if (has_capability('moodle/role:assign', $this->context)) {
$assignurl = new moodle_url('/'.$CFG->admin.'/roles/assign.php', array('contextid'=>$this->context->id));
- $categorynode->add(get_string('assignroles', 'role'), $assignurl, self::TYPE_SETTING, null, 'roles', new pix_icon('i/roles', ''));
+ $categorynode->add(get_string('assignroles', 'role'), $assignurl, self::TYPE_SETTING, null, 'roles', new pix_icon('i/assignroles', ''));
}
// Override roles
if (empty($_SERVER['HTTP_USER_AGENT'])) {
// Can't be sure, just say no.
$this->usesvg = false;
- } else if (preg_match('#MSIE +[5-8]\.#', $_SERVER['HTTP_USER_AGENT'])) {
+ } else if (check_browser_version('MSIE', 0) and !check_browser_version('MSIE', 9)) {
// IE < 9 doesn't support SVG. Say no.
$this->usesvg = false;
} else if (preg_match('#Android +[0-2]\.#', $_SERVER['HTTP_USER_AGENT'])) {
$header = $this->doctype() . $header;
}
+ // If this theme version is below 2.4 release and this is a course view page
+ if ((!isset($this->page->theme->settings->version) || $this->page->theme->settings->version < 2012101500) &&
+ $this->page->pagelayout === 'course' && $this->page->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
+ // check if course content header/footer have not been output during render of theme layout
+ $coursecontentheader = $this->course_content_header(true);
+ $coursecontentfooter = $this->course_content_footer(true);
+ if (!empty($coursecontentheader)) {
+ // display debug message and add header and footer right above and below main content
+ // Please note that course header and footer (to be displayed above and below the whole page)
+ // are not displayed in this case at all.
+ // Besides the content header and footer are not displayed on any other course page
+ debugging('The current theme is not optimised for 2.4, the course-specific header and footer defined in course format will not be output', DEBUG_DEVELOPER);
+ $header .= $coursecontentheader;
+ $footer = $coursecontentfooter. $footer;
+ }
+ }
+
send_headers($this->contenttype, $this->page->cacheable);
$this->opencontainers->push('header/footer', $footer);
return $this->opencontainers->pop_all_but_last($shouldbenone);
}
+ /**
+ * Returns course-specific information to be output immediately above content on any course page
+ * (for the current course)
+ *
+ * @param bool $onlyifnotcalledbefore output content only if it has not been output before
+ * @return string
+ */
+ public function course_content_header($onlyifnotcalledbefore = false) {
+ global $CFG;
+ if ($this->page->course->id == SITEID) {
+ // return immediately and do not include /course/lib.php if not necessary
+ return '';
+ }
+ static $functioncalled = false;
+ if ($functioncalled && $onlyifnotcalledbefore) {
+ // we have already output the content header
+ return '';
+ }
+ require_once($CFG->dirroot.'/course/lib.php');
+ $functioncalled = true;
+ $courseformat = course_get_format($this->page->course);
+ if (($obj = $courseformat->course_content_header()) !== null) {
+ return $courseformat->get_renderer($this->page)->render($obj);
+ }
+ return '';
+ }
+
+ /**
+ * Returns course-specific information to be output immediately below content on any course page
+ * (for the current course)
+ *
+ * @param bool $onlyifnotcalledbefore output content only if it has not been output before
+ * @return string
+ */
+ public function course_content_footer($onlyifnotcalledbefore = false) {
+ global $CFG;
+ if ($this->page->course->id == SITEID) {
+ // return immediately and do not include /course/lib.php if not necessary
+ return '';
+ }
+ static $functioncalled = false;
+ if ($functioncalled && $onlyifnotcalledbefore) {
+ // we have already output the content footer
+ return '';
+ }
+ $functioncalled = true;
+ require_once($CFG->dirroot.'/course/lib.php');
+ $courseformat = course_get_format($this->page->course);
+ if (($obj = $courseformat->course_content_footer()) !== null) {
+ return $courseformat->get_renderer($this->page)->render($obj);
+ }
+ return '';
+ }
+
+ /**
+ * Returns course-specific information to be output on any course page in the header area
+ * (for the current course)
+ *
+ * @return string
+ */
+ public function course_header() {
+ global $CFG;
+ if ($this->page->course->id == SITEID) {
+ // return immediately and do not include /course/lib.php if not necessary
+ return '';
+ }
+ require_once($CFG->dirroot.'/course/lib.php');
+ $courseformat = course_get_format($this->page->course);
+ if (($obj = $courseformat->course_header()) !== null) {
+ return $courseformat->get_renderer($this->page)->render($obj);
+ }
+ return '';
+ }
+
+ /**
+ * Returns course-specific information to be output on any course page in the footer area
+ * (for the current course)
+ *
+ * @return string
+ */
+ public function course_footer() {
+ global $CFG;
+ if ($this->page->course->id == SITEID) {
+ // return immediately and do not include /course/lib.php if not necessary
+ return '';
+ }
+ require_once($CFG->dirroot.'/course/lib.php');
+ $courseformat = course_get_format($this->page->course);
+ if (($obj = $courseformat->course_footer()) !== null) {
+ return $courseformat->get_renderer($this->page)->render($obj);
+ }
+ return '';
+ }
+
/**
* Returns lang menu or '', this method also checks forcing of languages in courses.
*
$this->recentfetch = $config->recentfetch;
$this->recentresponse = $this->decode_response($config->recentresponse);
} catch (available_update_checker_exception $e) {
- debugging('Invalid info about available updates detected and will be ignored: '.$e->getMessage(), DEBUG_ALL);
+ // The server response is not valid. Behave as if no data were fetched yet.
+ // This may happen when the most recent update info (cached locally) has been
+ // fetched with the previous branch of Moodle (like during an upgrade from 2.x
+ // to 2.y) or when the API of the response has changed.
$this->recentresponse = array();
}
$section = $this->get_settings_section_name();
$settings = null;
- if ($hassiteconfig && file_exists($this->full_path('settings.php'))) {
+ $systemcontext = context_system::instance();
+ if (($hassiteconfig || has_capability('moodle/question:config', $systemcontext)) &&
+ file_exists($this->full_path('settings.php'))) {
$settings = new admin_settingpage($section, $this->displayname,
- 'moodle/site:config', $this->is_enabled() === false);
+ 'moodle/question:config', $this->is_enabled() === false);
include($this->full_path('settings.php')); // this may also set $settings to null
}
if ($settings) {
array('sesskey' => sesskey(), 'action' => 'uninstall', 'webservice' => $this->name));
}
}
+
+/**
+ * Class for course formats
+ */
+class plugininfo_format extends plugininfo_base {
+
+ /**
+ * Gathers and returns the information about all plugins of the given type
+ *
+ * @param string $type the name of the plugintype, eg. mod, auth or workshopform
+ * @param string $typerootdir full path to the location of the plugin dir
+ * @param string $typeclass the name of the actually called class
+ * @return array of plugintype classes, indexed by the plugin name
+ */
+ public static function get_plugins($type, $typerootdir, $typeclass) {
+ global $CFG;
+ $formats = parent::get_plugins($type, $typerootdir, $typeclass);
+ require_once($CFG->dirroot.'/course/lib.php');
+ $order = get_sorted_course_formats();
+ $sortedformats = array();
+ foreach ($order as $formatname) {
+ $sortedformats[$formatname] = $formats[$formatname];
+ }
+ return $sortedformats;
+ }
+
+ public function get_settings_section_name() {
+ return 'formatsetting' . $this->name;
+ }
+
+ public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+ global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them
+ $ADMIN = $adminroot; // also may be used in settings.php
+ $section = $this->get_settings_section_name();
+
+ $settings = null;
+ if ($hassiteconfig && file_exists($this->full_path('settings.php'))) {
+ $settings = new admin_settingpage($section, $this->displayname,
+ 'moodle/site:config', $this->is_enabled() === false);
+ include($this->full_path('settings.php')); // this may also set $settings to null
+ }
+ if ($settings) {
+ $ADMIN->add($parentnodename, $settings);
+ }
+ }
+
+ public function is_enabled() {
+ return !get_config($this->component, 'disabled');
+ }
+
+ public function get_uninstall_url() {
+ if ($this->name !== get_config('moodlecourse', 'format') && $this->name !== 'site') {
+ return new moodle_url('/admin/courseformats.php',
+ array('sesskey' => sesskey(), 'action' => 'uninstall', 'format' => $this->name));
+ }
+ return parent::get_uninstall_url();
+ }
+}
if (file_exists("$CFG->dataroot/climaintenance.html")) {
if (!CLI_SCRIPT) {
header('Content-type: text/html; charset=utf-8');
+ header('X-UA-Compatible: IE=edge');
/// Headers to make it not cacheable and json
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
// better disable any caching
@header('Content-Type: text/html; charset=utf-8');
+ @header('X-UA-Compatible: IE=edge');
@header('Cache-Control: no-store, no-cache, must-revalidate');
@header('Cache-Control: post-check=0, pre-check=0', false);
@header('Pragma: no-cache');
define('STATS_MODE_DETAILED',2);
define('STATS_MODE_RANKED',3); // admins only - ranks courses
+// Output string when nodebug is on
+define('STATS_PLACEHOLDER_OUTPUT', '.');
+
/**
* Print daily cron progress
* @param string $ident
*/
-function stats_daily_progress($ident) {
+function stats_progress($ident) {
static $start = 0;
static $init = 0;
if ($ident == 'init') {
- $init = $start = time();
+ $init = $start = microtime(true);
return;
}
- $elapsed = time() - $start;
- $start = time();
+ $elapsed = round(microtime(true) - $start);
+ $start = microtime(true);
if (debugging('', DEBUG_ALL)) {
mtrace("$ident:$elapsed ", '');
} else {
- mtrace('.', '');
+ mtrace(STATS_PLACEHOLDER_OUTPUT, '');
+ }
+}
+
+/**
+ * Execute individual daily statistics queries
+ *
+ * @param string $sql The query to run
+ * @return boolean success
+ */
+function stats_run_query($sql, $parameters = array()) {
+ global $DB;
+
+ try {
+ $DB->execute($sql, $parameters);
+ } catch (dml_exception $e) {
+
+ if (debugging('', DEBUG_ALL)) {
+ mtrace($e->getMessage());
+ }
+ return false;
}
+ return true;
}
/**
* Execute daily statistics gathering
+ *
* @param int $maxdays maximum number of days to be processed
* @return boolean success
*/
// Note: This will work fine for sites running cron each 4 hours or less (hopefully, 99.99% of sites). MDL-16709
// check to make sure we're due to run, at least 20 hours after last run
- if (isset($CFG->statslastexecution) and ((time() - 20*60*60) < $CFG->statslastexecution)) {
+ if (isset($CFG->statslastexecution) && ((time() - 20*60*60) < $CFG->statslastexecution)) {
mtrace("...preventing stats to run, last execution was less than 20 hours ago.");
return false;
// also check that we are a max of 4 hours after scheduled time, stats won't run after that
mtrace("Running daily statistics gathering, starting at $timestart:");
- $days = 0;
- $failed = false; // failed stats flag
+ $days = 0;
+ $total = 0;
+ $failed = false; // failed stats flag
+ $timeout = false;
+
+ if (!stats_temp_table_create()) {
+ $days = 1;
+ $failed = true;
+ }
+ mtrace('Temporary tables created');
+
+ if(!stats_temp_table_setup()) {
+ $days = 1;
+ $failed = true;
+ }
+ mtrace('Enrolments calculated');
+
+ $totalactiveusers = $DB->count_records('user', array('deleted' => '0'));
- while ($now > $nextmidnight) {
+ while (!$failed && ($now > $nextmidnight)) {
if ($days >= $maxdays) {
- mtrace("...stopping early, reached maximum number of $maxdays days - will continue next time.");
- set_cron_lock('statsrunning', null);
- return false;
+ $timeout = true;
+ break;
}
$days++;
$daystart = time();
- $timesql = "l.time >= $timestart AND l.time < $nextmidnight";
- $timesql1 = "l1.time >= $timestart AND l1.time < $nextmidnight";
- $timesql2 = "l2.time >= $timestart AND l2.time < $nextmidnight";
-
- stats_daily_progress('init');
+ stats_progress('init');
+ if (!stats_temp_table_fill($timestart, $nextmidnight)) {
+ $failed = true;
+ break;
+ }
- /// find out if any logs available for this day
- $sql = "SELECT 'x'
- FROM {log} l
- WHERE $timesql";
+ // Find out if any logs available for this day
+ $sql = "SELECT 'x' FROM {temp_log1} l";
$logspresent = $DB->get_records_sql($sql, null, 0, 1);
- /// process login info first
- $sql = "INSERT INTO {stats_user_daily} (stattype, timeend, courseid, userid, statsreads)
+ if ($logspresent) {
+ // Insert blank record to force Query 10 to generate additional row when no logs for
+ // the site with userid 0 exist. Added for backwards compatibility.
+ $DB->insert_record('temp_log1', array('userid' => 0, 'course' => SITEID, 'action' => ''));
+ }
- SELECT 'logins', timeend, courseid, userid, count(statsreads)
- FROM (
- SELECT $nextmidnight AS timeend, ".SITEID." AS courseid, l.userid, l.id AS statsreads
- FROM {log} l
- WHERE action = 'login' AND $timesql
- ) inline_view
+ // Calculate the number of active users today
+ $sql = 'SELECT COUNT(DISTINCT u.id)
+ FROM {user} u
+ JOIN {temp_log1} l ON l.userid = u.id
+ WHERE u.deleted = 0';
+ $dailyactiveusers = $DB->count_records_sql($sql);
+
+ stats_progress('0');
+
+ // Process login info first
+ // Note: PostgreSQL doesn't like aliases in HAVING clauses
+ $sql = "INSERT INTO {temp_stats_user_daily}
+ (stattype, timeend, courseid, userid, statsreads)
+
+ SELECT 'logins', $nextmidnight AS timeend, ".SITEID." AS courseid,
+ userid, COUNT(id) AS statsreads
+ FROM {temp_log1} l
+ WHERE action = 'login'
GROUP BY timeend, courseid, userid
- HAVING count(statsreads) > 0";
+ HAVING COUNT(id) > 0";
- if ($logspresent and !$DB->execute($sql)) {
+ if ($logspresent && !stats_run_query($sql)) {
$failed = true;
break;
}
- stats_daily_progress('1');
- $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
+ stats_progress('1');
- SELECT 'logins' AS stattype, $nextmidnight AS timeend, ".SITEID." as courseid, 0,
- COALESCE((SELECT SUM(statsreads)
- FROM {stats_user_daily} s1
- WHERE s1.stattype = 'logins' AND timeend = $nextmidnight), 0) AS stat1,
- (SELECT COUNT('x')
- FROM {stats_user_daily} s2
- WHERE s2.stattype = 'logins' AND timeend = $nextmidnight) AS stat2" .
- $DB->sql_null_from_clause();
+ $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
+
+ SELECT 'logins' AS stattype, $nextmidnight AS timeend, ".SITEID." AS courseid, 0,
+ COALESCE(SUM(statsreads), 0) as stat1, COUNT('x') as stat2
+ FROM {temp_stats_user_daily}
+ WHERE stattype = 'logins' AND timeend = $nextmidnight";
- if ($logspresent and !$DB->execute($sql)) {
+ if ($logspresent && !stats_run_query($sql)) {
$failed = true;
break;
}
- stats_daily_progress('2');
+ stats_progress('2');
// Enrolments and active enrolled users
// in that case, we'll count non-deleted users.
//
- $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
+ $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
- SELECT 'enrolments', timeend, courseid, roleid, COUNT(DISTINCT userid), 0
- FROM (
- SELECT $nextmidnight AS timeend, e.courseid, ra.roleid, ue.userid
- FROM {role_assignments} ra
- JOIN {context} c ON (c.id = ra.contextid AND c.contextlevel = :courselevel)
- JOIN {enrol} e ON e.courseid = c.instanceid
- JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid)
- ) inline_view
- GROUP BY timeend, courseid, roleid";
+ SELECT 'enrolments' as stattype, $nextmidnight as timeend, courseid, roleid,
+ COUNT(DISTINCT userid) as stat1, 0 as stat2
+ FROM {temp_enroled}
+ GROUP BY courseid, roleid";
- if (!$DB->execute($sql, array('courselevel'=>CONTEXT_COURSE))) {
+ if (!stats_run_query($sql)) {
$failed = true;
break;
}
- stats_daily_progress('3');
+ stats_progress('3');
+ // Set stat2 to the number distinct users with role assignments in the course that were active
// using table alias in UPDATE does not work in pg < 8.2
- $sql = "UPDATE {stats_daily}
- SET stat2 = (SELECT COUNT(DISTINCT ra.userid)
- FROM {role_assignments} ra
- JOIN {context} c ON (c.id = ra.contextid AND c.contextlevel = :courselevel)
- JOIN {enrol} e ON e.courseid = c.instanceid
- JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid)
- WHERE ra.roleid = {stats_daily}.roleid AND
- e.courseid = {stats_daily}.courseid AND
- EXISTS (SELECT 'x'
- FROM {log} l
- WHERE l.course = {stats_daily}.courseid AND
- l.userid = ra.userid AND $timesql))
- WHERE {stats_daily}.stattype = 'enrolments' AND
- {stats_daily}.timeend = $nextmidnight AND
- {stats_daily}.courseid IN
- (SELECT DISTINCT l.course
- FROM {log} l
- WHERE $timesql)";
-
- if (!$DB->execute($sql, array('courselevel'=>CONTEXT_COURSE))) {
+ $sql = "UPDATE {temp_stats_daily}
+ SET stat2 = (
+
+ SELECT COUNT(DISTINCT userid)
+ FROM {temp_enroled} te
+ WHERE roleid = {temp_stats_daily}.roleid
+ AND courseid = {temp_stats_daily}.courseid
+ AND EXISTS (
+
+ SELECT 'x'
+ FROM {temp_log1} l
+ WHERE l.course = {temp_stats_daily}.courseid
+ AND l.userid = te.userid
+ )
+ )
+ WHERE {temp_stats_daily}.stattype = 'enrolments'
+ AND {temp_stats_daily}.timeend = $nextmidnight
+ AND {temp_stats_daily}.courseid IN (
+
+ SELECT DISTINCT course FROM {temp_log2})";
+
+ if ($logspresent && !stats_run_query($sql, array('courselevel'=>CONTEXT_COURSE))) {
$failed = true;
break;
}
- stats_daily_progress('4');
+ stats_progress('4');
- /// now get course total enrolments (roleid==0) - except frontpage
- $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
+ // Now get course total enrolments (roleid==0) - except frontpage
+ $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
- SELECT 'enrolments', timeend, id, nroleid, COUNT(DISTINCT userid), 0
- FROM (
- SELECT $nextmidnight AS timeend, e.courseid AS id, 0 AS nroleid, ue.userid
- FROM {enrol} e
- JOIN {user_enrolments} ue ON ue.enrolid = e.id
- ) inline_view
- GROUP BY timeend, id, nroleid
+ SELECT 'enrolments', $nextmidnight AS timeend, te.courseid AS courseid, 0 AS roleid,
+ COUNT(DISTINCT userid) AS stat1, 0 AS stat2
+ FROM {temp_enroled} te
+ GROUP BY courseid
HAVING COUNT(DISTINCT userid) > 0";
- if ($logspresent and !$DB->execute($sql)) {
+ if ($logspresent && !stats_run_query($sql)) {
$failed = true;
break;
}
- stats_daily_progress('5');
-
- $sql = "UPDATE {stats_daily}
- SET stat2 = (SELECT COUNT(DISTINCT ue.userid)
- FROM {enrol} e
- JOIN {user_enrolments} ue ON ue.enrolid = e.id
- WHERE e.courseid = {stats_daily}.courseid AND
- EXISTS (SELECT 'x'
- FROM {log} l
- WHERE l.course = {stats_daily}.courseid AND
- l.userid = ue.userid AND $timesql))
- WHERE {stats_daily}.stattype = 'enrolments' AND
- {stats_daily}.timeend = $nextmidnight AND
- {stats_daily}.roleid = 0 AND
- {stats_daily}.courseid IN
- (SELECT l.course
- FROM {log} l
- WHERE $timesql AND l.course <> ".SITEID.")";
-
- if ($logspresent and !$DB->execute($sql, array())) {
+ stats_progress('5');
+
+ // Set stat 2 to the number of enrolled users who were active in the course
+ $sql = "UPDATE {temp_stats_daily}
+ SET stat2 = (
+
+ SELECT COUNT(DISTINCT te.userid)
+ FROM {temp_enroled} te
+ WHERE te.courseid = {temp_stats_daily}.courseid
+ AND EXISTS (
+
+ SELECT 'x'
+ FROM {temp_log1} l
+ WHERE l.course = {temp_stats_daily}.courseid
+ AND l.userid = te.userid
+ )
+ )
+
+ WHERE {temp_stats_daily}.stattype = 'enrolments'
+ AND {temp_stats_daily}.timeend = $nextmidnight
+ AND {temp_stats_daily}.roleid = 0
+ AND {temp_stats_daily}.courseid IN (
+
+ SELECT l.course
+ FROM {temp_log2} l
+ WHERE l.course <> ".SITEID.")";
+
+ if ($logspresent && !stats_run_query($sql, array())) {
$failed = true;
break;
}
- stats_daily_progress('6');
+ stats_progress('6');
- /// frontapge(==site) enrolments total
- $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
+ // Frontpage(==site) enrolments total
+ $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
- SELECT 'enrolments', $nextmidnight, ".SITEID.", 0,
- (SELECT COUNT('x')
- FROM {user} u
- WHERE u.deleted = 0) AS stat1,
- (SELECT COUNT(DISTINCT u.id)
- FROM {user} u
- JOIN {log} l ON l.userid = u.id
- WHERE u.deleted = 0 AND $timesql) AS stat2" .
+ SELECT 'enrolments', $nextmidnight, ".SITEID.", 0, $totalactiveusers AS stat1,
+ $dailyactiveusers AS stat2" .
$DB->sql_null_from_clause();
- if ($logspresent and !$DB->execute($sql)) {
+ if ($logspresent && !stats_run_query($sql)) {
$failed = true;
break;
}
- stats_daily_progress('7');
+ stats_progress('7');
- /// Default frontpage role enrolments are all site users (not deleted)
+ // Default frontpage role enrolments are all site users (not deleted)
if ($defaultfproleid) {
// first remove default frontpage role counts if created by previous query
$sql = "DELETE
- FROM {stats_daily}
- WHERE stattype = 'enrolments' AND courseid = ".SITEID." AND
- roleid = $defaultfproleid AND timeend = $nextmidnight";
- if ($logspresent and !$DB->execute($sql)) {
+ FROM {temp_stats_daily}
+ WHERE stattype = 'enrolments'
+ AND courseid = ".SITEID."
+ AND roleid = $defaultfproleid
+ AND timeend = $nextmidnight";
+
+ if ($logspresent && !stats_run_query($sql)) {
$failed = true;
break;
}
- stats_daily_progress('8');
+ stats_progress('8');
- $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
+ $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
SELECT 'enrolments', $nextmidnight, ".SITEID.", $defaultfproleid,
- (SELECT COUNT('x')
- FROM {user} u
- WHERE u.deleted = 0) AS stat1,
- (SELECT COUNT(DISTINCT u.id)
- FROM {user} u
- JOIN {log} l ON l.userid = u.id
- WHERE u.deleted = 0 AND $timesql) AS stat2" .
+ $totalactiveusers AS stat1, $dailyactiveusers AS stat2" .
$DB->sql_null_from_clause();;
- if ($logspresent and !$DB->execute($sql)) {
+ if ($logspresent && !stats_run_query($sql)) {
$failed = true;
break;
}
- stats_daily_progress('9');
+ stats_progress('9');
} else {
- stats_daily_progress('x');
- stats_daily_progress('x');
+ stats_progress('x');
+ stats_progress('x');
}
-
- /// individual user stats (including not-logged-in) in each course, this is slow - reuse this data if possible
+ /// individual user stats (including not-logged-in) in each course, this is slow - reuse this data if possible
list($viewactionssql, $params1) = $DB->get_in_or_equal($viewactions, SQL_PARAMS_NAMED, 'view');
list($postactionssql, $params2) = $DB->get_in_or_equal($postactions, SQL_PARAMS_NAMED, 'post');
- $sql = "INSERT INTO {stats_user_daily} (stattype, timeend, courseid, userid, statsreads, statswrites)
+ $sql = "INSERT INTO {temp_stats_user_daily} (stattype, timeend, courseid, userid, statsreads, statswrites)
- SELECT 'activity' AS stattype, $nextmidnight AS timeend, d.courseid, d.userid,
- (SELECT COUNT('x')
- FROM {log} l
- WHERE l.userid = d.userid AND
- l.course = d.courseid AND $timesql AND
- l.action $viewactionssql) AS statsreads,
- (SELECT COUNT('x')
- FROM {log} l
- WHERE l.userid = d.userid AND
- l.course = d.courseid AND $timesql AND
- l.action $postactionssql) AS statswrites
- FROM (SELECT DISTINCT u.id AS userid, l.course AS courseid
- FROM {user} u, {log} l
- WHERE u.id = l.userid AND $timesql
- UNION
- SELECT 0 AS userid, ".SITEID." AS courseid" . $DB->sql_null_from_clause() . ") d";
- // can not use group by here because pg can not handle it :-(
-
- if ($logspresent and !$DB->execute($sql, array_merge($params1, $params2))) {
+ SELECT 'activity' AS stattype, $nextmidnight AS timeend, course AS courseid, userid,
+ SUM(CASE WHEN action $viewactionssql THEN 1 ELSE 0 END) AS statsreads,
+ SUM(CASE WHEN action $postactionssql THEN 1 ELSE 0 END) AS statswrites
+ FROM {temp_log1} l
+ GROUP BY userid, courseid";
+
+ if ($logspresent && !stats_run_query($sql, array_merge($params1, $params2))) {
$failed = true;
break;
}
- stats_daily_progress('10');
+ stats_progress('10');
- /// how many view/post actions in each course total
- $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
+ /// How many view/post actions in each course total
+ $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
SELECT 'activity' AS stattype, $nextmidnight AS timeend, c.id AS courseid, 0,
- (SELECT COUNT('x')
- FROM {log} l1
- WHERE l1.course = c.id AND l1.action $viewactionssql AND
- $timesql1) AS stat1,
- (SELECT COUNT('x')
- FROM {log} l2
- WHERE l2.course = c.id AND l2.action $postactionssql AND
- $timesql2) AS stat2
- FROM {course} c
- WHERE EXISTS (SELECT 'x'
- FROM {log} l
- WHERE l.course = c.id and $timesql)";
-
- if ($logspresent and !$DB->execute($sql, array_merge($params1, $params2))) {
+ SUM(CASE WHEN l.action $viewactionssql THEN 1 ELSE 0 END) AS stat1,
+ SUM(CASE WHEN l.action $postactionssql THEN 1 ELSE 0 END) AS stat2
+ FROM {course} c, {temp_log1} l
+ WHERE l.course = c.id
+ GROUP BY courseid";
+
+ if ($logspresent && !stats_run_query($sql, array_merge($params1, $params2))) {
$failed = true;
break;
}
- stats_daily_progress('11');
+ stats_progress('11');
- /// how many view actions for each course+role - excluding guests and frontpage
+ /// how many view actions for each course+role - excluding guests and frontpage
- $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
+ $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
- SELECT 'activity', timeend, courseid, roleid, SUM(statsreads), SUM(statswrites)
+ SELECT 'activity', $nextmidnight AS timeend, courseid, roleid, SUM(statsreads), SUM(statswrites)
FROM (
- SELECT $nextmidnight AS timeend, pl.courseid, pl.roleid, sud.statsreads, sud.statswrites
- FROM {stats_user_daily} sud,
- (SELECT DISTINCT ra.userid, ra.roleid, e.courseid
- FROM {role_assignments} ra
- JOIN {context} c ON (c.id = ra.contextid AND c.contextlevel = :courselevel)
- JOIN {enrol} e ON e.courseid = c.instanceid
- JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid)
- WHERE ra.roleid <> $guestrole AND
- ra.userid <> $guest
- ) pl
- WHERE sud.userid = pl.userid AND
- sud.courseid = pl.courseid AND
- sud.timeend = $nextmidnight AND
- sud.stattype='activity'
+
+ SELECT pl.courseid, pl.roleid, sud.statsreads, sud.statswrites
+ FROM {temp_stats_user_daily} sud, (
+
+ SELECT DISTINCT te.userid, te.roleid, te.courseid
+ FROM {temp_enroled} te
+ WHERE te.roleid <> $guestrole
+ AND te.userid <> $guest
+ ) pl
+
+ WHERE sud.userid = pl.userid
+ AND sud.courseid = pl.courseid
+ AND sud.timeend = $nextmidnight
+ AND sud.stattype='activity'
) inline_view
+
GROUP BY timeend, courseid, roleid
HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
- if ($logspresent and !$DB->execute($sql, array('courselevel'=>CONTEXT_COURSE))) {
+ if ($logspresent && !stats_run_query($sql, array('courselevel'=>CONTEXT_COURSE))) {
$failed = true;
break;
}
- stats_daily_progress('12');
+ stats_progress('12');
- /// how many view actions from guests only in each course - excluding frontpage
- /// normal users may enter course with temporary guest access too
+ /// how many view actions from guests only in each course - excluding frontpage
+ /// normal users may enter course with temporary guest access too
- $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
+ $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
- SELECT 'activity', timeend, courseid, nroleid, SUM(statsreads), SUM(statswrites)
+ SELECT 'activity', $nextmidnight AS timeend, courseid, $guestrole AS roleid,
+ SUM(statsreads), SUM(statswrites)
FROM (
- SELECT $nextmidnight AS timeend, sud.courseid, $guestrole AS nroleid, sud.statsreads, sud.statswrites
- FROM {stats_user_daily} sud
- WHERE sud.timeend = $nextmidnight AND sud.courseid <> ".SITEID." AND
- sud.stattype='activity' AND
- (sud.userid = $guest OR sud.userid
- NOT IN (SELECT ue.userid
- FROM {user_enrolments} ue
- JOIN {enrol} e ON ue.enrolid = e.id
- WHERE e.courseid = sud.courseid))
+
+ SELECT sud.courseid, sud.statsreads, sud.statswrites
+ FROM {temp_stats_user_daily} sud
+ WHERE sud.timeend = $nextmidnight
+ AND sud.courseid <> ".SITEID."
+ AND sud.stattype='activity'
+ AND (sud.userid = $guest OR sud.userid NOT IN (
+
+ SELECT userid
+ FROM {temp_enroled} te
+ WHERE te.courseid = sud.courseid
+ ))
) inline_view
- GROUP BY timeend, courseid, nroleid
+
+ GROUP BY timeend, courseid, roleid
HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
- if ($logspresent and !$DB->execute($sql, array())) {
+ if ($logspresent && !stats_run_query($sql, array())) {
$failed = true;
break;
}
- stats_daily_progress('13');
+ stats_progress('13');
- /// how many view actions for each role on frontpage - excluding guests, not-logged-in and default frontpage role
- $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
+ /// How many view actions for each role on frontpage - excluding guests, not-logged-in and default frontpage role
+ $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
- SELECT 'activity', timeend, courseid, roleid, SUM(statsreads), SUM(statswrites)
+ SELECT 'activity', $nextmidnight AS timeend, courseid, roleid,
+ SUM(statsreads), SUM(statswrites)
FROM (
- SELECT $nextmidnight AS timeend, pl.courseid, pl.roleid, sud.statsreads, sud.statswrites
- FROM {stats_user_daily} sud,
- (SELECT DISTINCT ra.userid, ra.roleid, c.instanceid AS courseid
- FROM {role_assignments} ra
- JOIN {context} c ON c.id = ra.contextid
- WHERE ra.contextid = :fpcontext AND
- ra.roleid <> $defaultfproleid AND
- ra.roleid <> $guestrole AND
- ra.userid <> $guest
- ) pl
- WHERE sud.userid = pl.userid AND
- sud.courseid = pl.courseid AND
- sud.timeend = $nextmidnight AND
- sud.stattype='activity'
+ SELECT pl.courseid, pl.roleid, sud.statsreads, sud.statswrites
+ FROM {temp_stats_user_daily} sud, (
+
+ SELECT DISTINCT ra.userid, ra.roleid, c.instanceid AS courseid
+ FROM {role_assignments} ra
+ JOIN {context} c ON c.id = ra.contextid
+ WHERE ra.contextid = :fpcontext
+ AND ra.roleid <> $defaultfproleid
+ AND ra.roleid <> $guestrole
+ AND ra.userid <> $guest
+ ) pl
+ WHERE sud.userid = pl.userid
+ AND sud.courseid = pl.courseid
+ AND sud.timeend = $nextmidnight
+ AND sud.stattype='activity'
) inline_view
+
GROUP BY timeend, courseid, roleid
HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
- if ($logspresent and !$DB->execute($sql, array('fpcontext'=>$fpcontext->id))) {
+ if ($logspresent && !stats_run_query($sql, array('fpcontext'=>$fpcontext->id))) {
$failed = true;
break;
}
- stats_daily_progress('14');
+ stats_progress('14');
- /// how many view actions for default frontpage role on frontpage only
- $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
+ // How many view actions for default frontpage role on frontpage only
+ $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
- SELECT 'activity', timeend, courseid, nroleid, SUM(statsreads), SUM(statswrites)
+ SELECT 'activity', timeend, courseid, $defaultfproleid AS roleid,
+ SUM(statsreads), SUM(statswrites)
FROM (
- SELECT sud.timeend AS timeend, sud.courseid, $defaultfproleid AS nroleid, sud.statsreads, sud.statswrites
- FROM {stats_user_daily} sud
- WHERE sud.timeend = :nextm AND sud.courseid = :siteid AND
- sud.stattype='activity' AND
- sud.userid <> $guest AND sud.userid <> 0 AND sud.userid
- NOT IN (SELECT ra.userid
- FROM {role_assignments} ra
- WHERE ra.roleid <> $guestrole AND
- ra.roleid <> $defaultfproleid AND ra.contextid = :fpcontext)
+ SELECT sud.timeend AS timeend, sud.courseid, sud.statsreads, sud.statswrites
+ FROM {temp_stats_user_daily} sud
+ WHERE sud.timeend = :nextm
+ AND sud.courseid = :siteid
+ AND sud.stattype='activity'
+ AND sud.userid <> $guest
+ AND sud.userid <> 0
+ AND sud.userid NOT IN (
+
+ SELECT ra.userid
+ FROM {role_assignments} ra
+ WHERE ra.roleid <> $guestrole
+ AND ra.roleid <> $defaultfproleid
+ AND ra.contextid = :fpcontext)
) inline_view
- GROUP BY timeend, courseid, nroleid
+
+ GROUP BY timeend, courseid, roleid
HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
- if ($logspresent and !$DB->execute($sql, array('fpcontext'=>$fpcontext->id, 'siteid'=>SITEID, 'nextm'=>$nextmidnight))) {
+ if ($logspresent && !stats_run_query($sql, array('fpcontext'=>$fpcontext->id, 'siteid'=>SITEID, 'nextm'=>$nextmidnight))) {
$failed = true;
break;
}
- stats_daily_progress('15');
+ stats_progress('15');
- /// how many view actions for guests or not-logged-in on frontpage
- $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
+ // How many view actions for guests or not-logged-in on frontpage
+ $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
- SELECT 'activity', timeend, courseid, nroleid, SUM(statsreads), SUM(statswrites)
+ SELECT 'activity', $nextmidnight AS timeend, ".SITEID." AS courseid, $guestrole AS roleid,
+ SUM(statsreads), SUM(statswrites)
FROM (
- SELECT $nextmidnight AS timeend, ".SITEID." AS courseid, $guestrole AS nroleid, pl.statsreads, pl.statswrites
- FROM (
- SELECT sud.statsreads, sud.statswrites
- FROM {stats_user_daily} sud
- WHERE (sud.userid = $guest OR sud.userid = 0) AND
- sud.timeend = $nextmidnight AND sud.courseid = ".SITEID." AND
- sud.stattype='activity'
- ) pl
- ) inline_view
- GROUP BY timeend, courseid, nroleid
+
+ SELECT sud.statsreads, sud.statswrites
+ FROM {temp_stats_user_daily} sud
+ WHERE (sud.userid = $guest OR sud.userid = 0)
+ AND sud.timeend = $nextmidnight
+ AND sud.courseid = ".SITEID."
+ AND sud.stattype='activity'
+ ) inline_view
+
+ GROUP BY timeend, courseid, roleid
HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
- if ($logspresent and !$DB->execute($sql)) {
+ if ($logspresent && !stats_run_query($sql)) {
$failed = true;
break;
}
- stats_daily_progress('16');
+ stats_progress('16');
+
+ stats_temp_table_clean();
+
+ stats_progress('out');
// remember processed days
set_config('statslastdaily', $nextmidnight);
- mtrace(" finished until $nextmidnight: ".userdate($nextmidnight)." (in ".(time()-$daystart)." s)");
+ $elapsed = time()-$daystart;
+ mtrace(" finished until $nextmidnight: ".userdate($nextmidnight)." (in $elapsed s)");
+ $total += $elapsed;
$timestart = $nextmidnight;
$nextmidnight = stats_get_next_day_start($nextmidnight);
}
+ stats_temp_table_drop();
+
set_cron_lock('statsrunning', null);
if ($failed) {
$days--;
- mtrace("...error occurred, completed $days days of statistics.");
+ mtrace("...error occurred, completed $days days of statistics in {$total} s.");
+ return false;
+
+ } else if ($timeout) {
+ mtrace("...stopping early, reached maximum number of $maxdays days ({$total} s) - will continue next time.");
return false;
} else {
- mtrace("...completed $days days of statistics.");
+ mtrace("...completed $days days of statistics in {$total} s.");
return true;
}
}
$logtimesql = "l.time >= $timestart AND l.time < $nextstartweek";
$stattimesql = "timeend > $timestart AND timeend <= $nextstartweek";
+ $weekstart = time();
+ stats_progress('init');
+
/// process login info first
$sql = "INSERT INTO {stats_user_weekly} (stattype, timeend, courseid, userid, statsreads)
WHERE action = 'login' AND $logtimesql
) inline_view
GROUP BY timeend, courseid, userid
- HAVING count(statsreads) > 0";
+ HAVING COUNT(statsreads) > 0";
$DB->execute($sql);
+ stats_progress('1');
+
$sql = "INSERT INTO {stats_weekly} (stattype, timeend, courseid, roleid, stat1, stat2)
SELECT 'logins' AS stattype, $nextstartweek AS timeend, ".SITEID." as courseid, 0,
$DB->execute($sql);
+ stats_progress('2');
/// now enrolments averages
$sql = "INSERT INTO {stats_weekly} (stattype, timeend, courseid, roleid, stat1, stat2)
$DB->execute($sql);
+ stats_progress('3');
/// activity read/write averages
$sql = "INSERT INTO {stats_weekly} (stattype, timeend, courseid, roleid, stat1, stat2)
$DB->execute($sql);
+ stats_progress('4');
/// user read/write averages
$sql = "INSERT INTO {stats_user_weekly} (stattype, timeend, courseid, userid, statsreads, statswrites)
$DB->execute($sql);
+ stats_progress('5');
+
set_config('statslastweekly', $nextstartweek);
- mtrace(" finished until $nextstartweek: ".userdate($nextstartweek));
+ $elapsed = time()-$weekstart;
+ mtrace(" finished until $nextstartweek: ".userdate($nextstartweek) ." (in $elapsed s)");
$timestart = $nextstartweek;
$nextstartweek = stats_get_next_week_start($nextstartweek);
$logtimesql = "l.time >= $timestart AND l.time < $nextstartmonth";
$stattimesql = "timeend > $timestart AND timeend <= $nextstartmonth";
+ $monthstart = time();
+ stats_progress('init');
+
/// process login info first
$sql = "INSERT INTO {stats_user_monthly} (stattype, timeend, courseid, userid, statsreads)
$DB->execute($sql);
+ stats_progress('1');
+
$sql = "INSERT INTO {stats_monthly} (stattype, timeend, courseid, roleid, stat1, stat2)
SELECT 'logins' AS stattype, $nextstartmonth AS timeend, ".SITEID." as courseid, 0,
$DB->execute($sql);
+ stats_progress('2');
/// now enrolments averages
$sql = "INSERT INTO {stats_monthly} (stattype, timeend, courseid, roleid, stat1, stat2)
$DB->execute($sql);
+ stats_progress('3');
/// activity read/write averages
$sql = "INSERT INTO {stats_monthly} (stattype, timeend, courseid, roleid, stat1, stat2)
$DB->execute($sql);
+ stats_progress('4');
/// user read/write averages
$sql = "INSERT INTO {stats_user_monthly} (stattype, timeend, courseid, userid, statsreads, statswrites)
$DB->execute($sql);
+ stats_progress('5');
+
set_config('statslastmonthly', $nextstartmonth);
- mtrace(" finished until $nextstartmonth: ".userdate($nextstartmonth));
+ $elapsed = time() - $monthstart;
+ mtrace(" finished until $nextstartmonth: ".userdate($nextstartmonth) ." (in $elapsed s)");
$timestart = $nextstartmonth;
$nextstartmonth = stats_get_next_month_start($nextstartmonth);
$reportoptions[STATS_REPORT_PARTICIPATORY_COURSES] = get_string('statsreport'.STATS_REPORT_PARTICIPATORY_COURSES);
$reportoptions[STATS_REPORT_PARTICIPATORY_COURSES_RW] = get_string('statsreport'.STATS_REPORT_PARTICIPATORY_COURSES_RW);
}
- break;
+ break;
}
return $reportoptions;
//return error as string
return get_string('statscatchupmode','error',$a);
}
+
+/**
+ * Create temporary tables to speed up log generation
+ */
+function stats_temp_table_create() {
+ global $CFG, $DB;
+
+ $dbman = $DB->get_manager(); // We are going to use database_manager services
+
+ stats_temp_table_drop();
+
+ $xmlfile = $CFG->dirroot . '/lib/db/install.xml';
+ $tempfile = $CFG->dirroot . '/lib/db/temp_stats_log_template.xml';
+ $tables = array();
+
+ // Allows for the additional xml files to be used (if necessary)
+ $files = array(
+ $xmlfile => array(
+ 'stats_daily' => array('temp_stats_daily'),
+ 'stats_user_daily' => array('temp_stats_user_daily'),
+ 'temp_enroled_template' => array('temp_enroled'),
+ 'temp_log_template' => array('temp_log1', 'temp_log2'),
+ ),
+ );
+
+ foreach ($files as $file => $contents) {
+
+ $xmldb_file = new xmldb_file($file);
+ if (!$xmldb_file->fileExists()) {
+ throw new ddl_exception('ddlxmlfileerror', null, 'File does not exist');
+ }
+ $loaded = $xmldb_file->loadXMLStructure();
+ if (!$loaded || !$xmldb_file->isLoaded()) {
+ throw new ddl_exception('ddlxmlfileerror', null, 'not loaded??');
+ }
+ $xmldb_structure = $xmldb_file->getStructure();
+
+ foreach ($contents as $template => $names) {
+ $table = $xmldb_structure->getTable($template);
+
+ if (is_null($table)) {
+ throw new ddl_exception('ddlunknowntable', null, 'The table '. $name .' is not defined in the file '. $xmlfile);
+ }
+ $table->setNext(null);\r
+ $table->setPrevious(null);
+
+ foreach ($names as $name) {
+ $named = clone $table;
+ $named->setName($name);
+ $tables[$name] = $named;
+ }
+ }
+ }
+
+ try {
+
+ foreach ($tables as $table) {
+ $dbman->create_temp_table($table);
+ }
+
+ } catch (Exception $e) {
+ mtrace('Temporary table creation failed: '. $e->getMessage());
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Deletes summary logs table for stats calculation
+ */
+function stats_temp_table_drop() {
+ global $DB;
+
+ $dbman = $DB->get_manager();
+
+ $tables = array('temp_log1', 'temp_log2', 'temp_stats_daily', 'temp_stats_user_daily', 'temp_enroled');
+
+ foreach ($tables as $name) {
+
+ if ($dbman->table_exists($name)) {
+ $table = new xmldb_table($name);
+
+ try {
+ $dbman->drop_table($table);
+ } catch (Exception $e) {
+ mtrace("Error occured while dropping temporary tables!");
+ }
+ }
+ }
+}
+
+/**
+ * Fills the temporary stats tables with new data
+ *
+ * This function is meant to be called once at the start of stats generation
+ *
+ * @param timestart timestamp of the start time of logs view
+ * @param timeend timestamp of the end time of logs view
+ * @returns boolen success (true) or failure(false)
+ */
+function stats_temp_table_setup() {
+ global $DB;
+
+ $sql = "INSERT INTO {temp_enroled} (userid, courseid, roleid)
+
+ SELECT ue.userid, e.courseid, ra.roleid
+ FROM {role_assignments} ra
+ JOIN {context} c ON (c.id = ra.contextid AND c.contextlevel = :courselevel)
+ JOIN {enrol} e ON e.courseid = c.instanceid
+ JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid)";
+
+ return stats_run_query($sql, array('courselevel' => CONTEXT_COURSE));
+}
+
+/**
+ * Fills the temporary stats tables with new data
+ *
+ * This function is meant to be called to get a new day of data
+ *
+ * @param timestart timestamp of the start time of logs view
+ * @param timeend timestamp of the end time of logs view
+ * @returns boolen success (true) or failure(false)
+ */
+function stats_temp_table_fill($timestart, $timeend) {
+ global $DB;
+
+ $sql = 'INSERT INTO {temp_log1} (userid, course, action)
+
+ SELECT userid, course, action FROM {log}
+ WHERE time >= ? AND time < ?';
+
+ $DB->execute($sql, array($timestart, $timeend));
+
+ $sql = 'INSERT INTO {temp_log2} (userid, course, action)
+
+ SELECT userid, course, action FROM {temp_log1}';
+
+ $DB->execute($sql);
+
+ return true;
+}
+
+
+/**
+ * Deletes summary logs table for stats calculation
+ *
+ * @returns boolen success (true) or failure(false)
+ */
+function stats_temp_table_clean() {
+ global $DB;
+
+ $sql = array();
+
+ $sql['up1'] = 'INSERT INTO {stats_daily} (courseid, roleid, stattype, timeend, stat1, stat2)
+
+ SELECT courseid, roleid, stattype, timeend, stat1, stat2 FROM {temp_stats_daily}';
+
+ $sql['up2'] = 'INSERT INTO {stats_user_daily}
+ (courseid, userid, roleid, timeend, statsreads, statswrites, stattype)
+
+ SELECT courseid, userid, roleid, timeend, statsreads, statswrites, stattype
+ FROM {temp_stats_user_daily}';
+
+ foreach ($sql as $id => $query) {
+ if (! stats_run_query($query)) {
+ mtrace("Error during table cleanup!");
+ return false;
+ }
+ }
+
+ $tables = array('temp_log1', 'temp_log2', 'temp_stats_daily', 'temp_stats_user_daily');
+
+ foreach ($tables as $name) {
+ $DB->delete_records($name);
+ }
+
+ return true;
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- Tests no logs - Only query 3 should be processed -->
+<dataset>
+ <table name="log">
+ <column>time</column>
+ <column>userid</column>
+ <column>course</column>
+ <column>action</column>
+ </table>
+ <table name="stats_daily">
+ <column>courseid</column>
+ <column>timeend</column>
+ <column>roleid</column>
+ <column>stattype</column>
+ <column>stat1</column>
+ <column>stat2</column>
+ <row>
+ <value>[course1_id]</value>
+ <value>[end_no_logs]</value>
+ <value>5</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ </table>
+ <table name="stats_user_daily">
+ <column>courseid</column>
+ <column>userid</column>
+ <column>roleid</column>
+ <column>timeend</column>
+ <column>statsreads</column>
+ <column>statswrites</column>
+ <column>stattype</column>
+ </table>
+</dataset>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- No login test - Tests queries 2, 3, 5, 7, 9 (and 8), 10 (read) -->
+<dataset>
+ <table name="log">
+ <column>time</column>
+ <column>userid</column>
+ <column>course</column>
+ <column>action</column>
+ <row>
+ <value>[start_1]</value>
+ <value>[guest_id]</value>
+ <value>[site_id]</value>
+ <value>view</value>
+ </row>
+ </table>
+ <table name="stats_daily">
+ <column>courseid</column>
+ <column>timeend</column>
+ <column>roleid</column>
+ <column>stattype</column>
+ <column>stat1</column>
+ <column>stat2</column>
+ <!-- Query 2 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>logins</value>
+ <value>0</value>
+ <value>0</value>
+ </row>
+ <!-- Query 3 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[student_roleid]</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 5 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 7 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 9 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[frontpage_roleid]</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 16 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[guest_roleid]</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ </table>
+ <table name="stats_user_daily">
+ <column>courseid</column>
+ <column>userid</column>
+ <column>roleid</column>
+ <column>timeend</column>
+ <column>statsreads</column>
+ <column>statswrites</column>
+ <column>stattype</column>
+ <!-- Query 10 - read -->
+ <row>
+ <value>[site_id]</value>
+ <value>[guest_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 - default record -->
+ <row>
+ <value>[site_id]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ </table>
+</dataset>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- Single login - Tests queries 1, 2 (with logins), 4 -->
+<dataset>
+ <table name="log">
+ <column>time</column>
+ <column>userid</column>
+ <column>course</column>
+ <column>action</column>
+ <row>
+ <value>[start_1]</value>
+ <value>[user1_id]</value>
+ <value>[site_id]</value>
+ <value>login</value>
+ </row>
+ </table>
+ <table name="stats_daily">
+ <column>courseid</column>
+ <column>timeend</column>
+ <column>roleid</column>
+ <column>stattype</column>
+ <column>stat1</column>
+ <column>stat2</column>
+ <!-- Query 2 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>logins</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Query 3 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[student_roleid]</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 5 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 7 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 9 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[frontpage_roleid]</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>0</value>
+ <value>0</value>
+ </row>
+ </table>
+ <table name="stats_user_daily">
+ <column>courseid</column>
+ <column>userid</column>
+ <column>roleid</column>
+ <column>timeend</column>
+ <column>statsreads</column>
+ <column>statswrites</column>
+ <column>stattype</column>
+ <!-- Query 1 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[user1_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>logins</value>
+ </row>
+ <!-- Query 10 - read -->
+ <row>
+ <value>[site_id]</value>
+ <value>[user1_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 - default record -->
+ <row>
+ <value>[site_id]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ </table>
+</dataset>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- Guest login and view course - Tests queries 11 (read), 13 (guest) -->
+<dataset>
+ <table name="log">
+ <column>time</column>
+ <column>userid</column>
+ <column>course</column>
+ <column>action</column>
+ <row>
+ <value>[start_1]</value>
+ <value>[guest_id]</value>
+ <value>[site_id]</value>
+ <value>login</value>
+ </row>
+ <row>
+ <value>[start_2]</value>
+ <value>[guest_id]</value>
+ <value>[course1_id]</value>
+ <value>view</value>
+ </row>
+ </table>
+ <table name="stats_daily">
+ <column>courseid</column>
+ <column>timeend</column>
+ <column>roleid</column>
+ <column>stattype</column>
+ <column>stat1</column>
+ <column>stat2</column>
+ <!-- Query 2 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>logins</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Query 3 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[student_roleid]</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 5 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 7 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 9 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[frontpage_roleid]</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>0</value>
+ <value>0</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 13 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[guest_roleid]</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ </table>
+ <table name="stats_user_daily">
+ <column>courseid</column>
+ <column>userid</column>
+ <column>roleid</column>
+ <column>timeend</column>
+ <column>statsreads</column>
+ <column>statswrites</column>
+ <column>stattype</column>
+ <!-- Query 1 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[guest_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>logins</value>
+ </row>
+ <!-- Query 10 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[guest_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[guest_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 - default record -->
+ <row>
+ <value>[site_id]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ </table>
+</dataset>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- Guest login, view course, and upload assignment - Tests queries 10+11 (write) -->
+<dataset>
+ <table name="log">
+ <column>time</column>
+ <column>userid</column>
+ <column>course</column>
+ <column>action</column>
+ <row>
+ <value>[start_1]</value>
+ <value>[guest_id]</value>
+ <value>[site_id]</value>
+ <value>login</value>
+ </row>
+ <row>
+ <value>[start_2]</value>
+ <value>[guest_id]</value>
+ <value>[course1_id]</value>
+ <value>view</value>
+ </row>
+ <row>
+ <value>[start_3]</value>
+ <value>[guest_id]</value>
+ <value>[course1_id]</value>
+ <value>add post</value>
+ </row>
+ </table>
+ <table name="stats_daily">
+ <column>courseid</column>
+ <column>timeend</column>
+ <column>roleid</column>
+ <column>stattype</column>
+ <column>stat1</column>
+ <column>stat2</column>
+ <!-- Query 2 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>logins</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Query 3 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[student_roleid]</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 5 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 7 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 9 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[frontpage_roleid]</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>0</value>
+ <value>0</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Query 13 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[guest_roleid]</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ </table>
+ <table name="stats_user_daily">
+ <column>courseid</column>
+ <column>userid</column>
+ <column>roleid</column>
+ <column>timeend</column>
+ <column>statsreads</column>
+ <column>statswrites</column>
+ <column>stattype</column>
+ <!-- Query 1 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[guest_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>logins</value>
+ </row>
+ <!-- Query 10 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[guest_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>1</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[guest_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 - default record -->
+ <row>
+ <value>[site_id]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ </table>
+</dataset>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- User login and view course - Tests queries 4, 6, 10, 12, 14 (read) -->
+<dataset>
+ <table name="log">
+ <column>time</column>
+ <column>userid</column>
+ <column>course</column>
+ <column>action</column>
+ <row>
+ <value>[start_1]</value>
+ <value>[user1_id]</value>
+ <value>[site_id]</value>
+ <value>login</value>
+ </row>
+ <row>
+ <value>[start_2]</value>
+ <value>[user1_id]</value>
+ <value>[course1_id]</value>
+ <value>view</value>
+ </row>
+ </table>
+ <table name="stats_daily">
+ <column>courseid</column>
+ <column>timeend</column>
+ <column>roleid</column>
+ <column>stattype</column>
+ <column>stat1</column>
+ <column>stat2</column>
+ <!-- Query 2 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>logins</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Queris 3 (stat1) and 4 (stat2) -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[student_roleid]</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Queries 5 (stat1) and 6 (stat2) -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Query 7 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 9 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[frontpage_roleid]</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>0</value>
+ <value>0</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 12 (read) -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[student_roleid]</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ </table>
+ <table name="stats_user_daily">
+ <column>courseid</column>
+ <column>userid</column>
+ <column>roleid</column>
+ <column>timeend</column>
+ <column>statsreads</column>
+ <column>statswrites</column>
+ <column>stattype</column>
+ <!-- Query 1 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[user1_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>logins</value>
+ </row>
+ <!-- Query 10 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[user1_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[user1_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 - default record -->
+ <row>
+ <value>[site_id]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ </table>
+</dataset>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- User login, view course and post - Tests queries 10, 12, 14 (write) -->
+<dataset>
+ <table name="log">
+ <column>time</column>
+ <column>userid</column>
+ <column>course</column>
+ <column>action</column>
+ <row>
+ <value>[start_1]</value>
+ <value>[user1_id]</value>
+ <value>[site_id]</value>
+ <value>login</value>
+ </row>
+ <row>
+ <value>[start_2]</value>
+ <value>[user1_id]</value>
+ <value>[course1_id]</value>
+ <value>view</value>
+ </row>
+ <row>
+ <value>[start_3]</value>
+ <value>[user1_id]</value>
+ <value>[course1_id]</value>
+ <value>add post</value>
+ </row>
+ </table>
+ <table name="stats_daily">
+ <column>courseid</column>
+ <column>timeend</column>
+ <column>roleid</column>
+ <column>stattype</column>
+ <column>stat1</column>
+ <column>stat2</column>
+ <!-- Query 2 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>logins</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Queris 3 (stat1) and 4 (stat2) -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[student_roleid]</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Queries 5 (stat1) and 6 (stat2) -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Query 7 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 9 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[frontpage_roleid]</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>0</value>
+ <value>0</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Query 12 (read) -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[student_roleid]</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ </table>
+ <table name="stats_user_daily">
+ <column>courseid</column>
+ <column>userid</column>
+ <column>roleid</column>
+ <column>timeend</column>
+ <column>statsreads</column>
+ <column>statswrites</column>
+ <column>stattype</column>
+ <!-- Query 1 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[user1_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>logins</value>
+ </row>
+ <!-- Query 10 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[user1_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>1</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[user1_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 - default record -->
+ <row>
+ <value>[site_id]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ </table>
+</dataset>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- User login and view course (not enrolled) - Tests queries 13 (not enroled), 14 (not default) -->
+<dataset>
+ <table name="log">
+ <column>time</column>
+ <column>userid</column>
+ <column>course</column>
+ <column>action</column>
+ <row>
+ <value>[start_1]</value>
+ <value>[user2_id]</value>
+ <value>[site_id]</value>
+ <value>login</value>
+ </row>
+ <row>
+ <value>[start_2]</value>
+ <value>[user2_id]</value>
+ <value>[site_id]</value>
+ <value>view</value>
+ </row>
+ <row>
+ <value>[start_3]</value>
+ <value>[user2_id]</value>
+ <value>[course1_id]</value>
+ <value>view</value>
+ </row>
+ </table>
+ <table name="stats_daily">
+ <column>courseid</column>
+ <column>timeend</column>
+ <column>roleid</column>
+ <column>stattype</column>
+ <column>stat1</column>
+ <column>stat2</column>
+ <!-- Query 2 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>logins</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Query 3 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[student_roleid]</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 5 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 7 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 9 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[frontpage_roleid]</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 13 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[guest_roleid]</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 14 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[frontpage_roleid]</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ </table>
+ <table name="stats_user_daily">
+ <column>courseid</column>
+ <column>userid</column>
+ <column>roleid</column>
+ <column>timeend</column>
+ <column>statsreads</column>
+ <column>statswrites</column>
+ <column>stattype</column>
+ <!-- Query 1 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[user2_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>logins</value>
+ </row>
+ <!-- Query 10 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[user2_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[user2_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 - default record -->
+ <row>
+ <value>[site_id]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ </table>
+</dataset>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- User login and view site - Tests query 15 (front page views) -->
+<dataset>
+ <table name="log">
+ <column>time</column>
+ <column>userid</column>
+ <column>course</column>
+ <column>action</column>
+ <row>
+ <value>[start_1]</value>
+ <value>[user1_id]</value>
+ <value>[site_id]</value>
+ <value>login</value>
+ </row>
+ <row>
+ <value>[start_2]</value>
+ <value>[user1_id]</value>
+ <value>[site_id]</value>
+ <value>view</value>
+ </row>
+ </table>
+ <table name="stats_daily">
+ <column>courseid</column>
+ <column>timeend</column>
+ <column>roleid</column>
+ <column>stattype</column>
+ <column>stat1</column>
+ <column>stat2</column>
+ <!-- Query 2 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>logins</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Query 3 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[student_roleid]</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 5 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 7 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 9 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[frontpage_roleid]</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 15 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[frontpage_roleid]</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ </table>
+ <table name="stats_user_daily">
+ <column>courseid</column>
+ <column>userid</column>
+ <column>roleid</column>
+ <column>timeend</column>
+ <column>statsreads</column>
+ <column>statswrites</column>
+ <column>stattype</column>
+ <!-- Query 1 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[user1_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>logins</value>
+ </row>
+ <!-- Query 10 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[user1_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 - default record -->
+ <row>
+ <value>[site_id]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ </table>
+</dataset>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- Multiple logins on different days -->
+<dataset>
+ <table name="log">
+ <column>time</column>
+ <column>userid</column>
+ <column>course</column>
+ <column>action</column>
+ <row>
+ <value>[start_0]</value>
+ <value>[user1_id]</value>
+ <value>[site_id]</value>
+ <value>login</value>
+ </row>
+ <row>
+ <value>[start_1]</value>
+ <value>[user1_id]</value>
+ <value>[site_id]</value>
+ <value>login</value>
+ </row>
+ <row>
+ <value>[start_4]</value>
+ <value>[user1_id]</value>
+ <value>[site_id]</value>
+ <value>login</value>
+ </row>
+ </table>
+ <table name="stats_daily">
+ <column>courseid</column>
+ <column>timeend</column>
+ <column>roleid</column>
+ <column>stattype</column>
+ <column>stat1</column>
+ <column>stat2</column>
+ <!-- Query 2 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>logins</value>
+ <value>1</value>
+ <value>1</value>
+ </row>
+ <!-- Query 3 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[student_roleid]</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 5 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 7 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 9 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[frontpage_roleid]</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ </table>
+ <table name="stats_user_daily">
+ <column>courseid</column>
+ <column>userid</column>
+ <column>roleid</column>
+ <column>timeend</column>
+ <column>statsreads</column>
+ <column>statswrites</column>
+ <column>stattype</column>
+ <!-- Query 1 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[user1_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>logins</value>
+ </row>
+ <!-- Query 10 - read -->
+ <row>
+ <value>[site_id]</value>
+ <value>[user1_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 - default record -->
+ <row>
+ <value>[site_id]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ </table>
+</dataset>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- No default profile id test -->
+<dataset>
+ <table name="log">
+ <column>time</column>
+ <column>userid</column>
+ <column>course</column>
+ <column>action</column>
+ <row>
+ <value>[start_1]</value>
+ <value>[guest_id]</value>
+ <value>[site_id]</value>
+ <value>view</value>
+ </row>
+ </table>
+ <table name="stats_daily">
+ <column>courseid</column>
+ <column>timeend</column>
+ <column>roleid</column>
+ <column>stattype</column>
+ <column>stat1</column>
+ <column>stat2</column>
+ <!-- Query 2 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>logins</value>
+ <value>0</value>
+ <value>0</value>
+ </row>
+ <!-- Query 3 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>[student_roleid]</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 5 -->
+ <row>
+ <value>[course1_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 7 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>enrolments</value>
+ <value>4</value>
+ <value>1</value>
+ </row>
+ <!-- Query 11 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ <!-- Query 16 -->
+ <row>
+ <value>[site_id]</value>
+ <value>[end]</value>
+ <value>[guest_roleid]</value>
+ <value>activity</value>
+ <value>1</value>
+ <value>0</value>
+ </row>
+ </table>
+ <table name="stats_user_daily">
+ <column>courseid</column>
+ <column>userid</column>
+ <column>roleid</column>
+ <column>timeend</column>
+ <column>statsreads</column>
+ <column>statswrites</column>
+ <column>stattype</column>
+ <!-- Query 10 - read -->
+ <row>
+ <value>[site_id]</value>
+ <value>[guest_id]</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>1</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ <!-- Query 10 - default record -->
+ <row>
+ <value>[site_id]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>[end]</value>
+ <value>0</value>
+ <value>0</value>
+ <value>activity</value>
+ </row>
+ </table>
+</dataset>
--- /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/>.
+
+/**
+ * Tests for messagelib.php.
+ *
+ * @package core_message
+ * @copyright 2012 The Open Universtiy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+class messagelib_testcase extends advanced_testcase {
+
+ public function test_message_get_providers_for_user() {
+ global $CFG, $DB;
+
+ $this->resetAfterTest(true);
+
+ $generator = $this->getDataGenerator();
+
+ // Create a course category and course
+ $cat = $generator->create_category(array('parent' => 0));
+ $course = $generator->create_course(array('category' => $cat->id));
+ $quiz = $generator->create_module('quiz', array('course' => $course->id));
+ $user = $generator->create_user();
+
+ $coursecontext = context_course::instance($course->id);
+ $quizcontext = context_module::instance($quiz->cmid);
+ $frontpagecontext = context_course::instance(SITEID);
+
+ $studentrole = $DB->get_record('role', array('shortname' => 'student'));
+
+ // The user is a student in a course, and has the capability for quiz
+ // confirmation emails in one quiz in that course.
+ role_assign($studentrole->id, $user->id, $coursecontext->id);
+ assign_capability('mod/quiz:emailconfirmsubmission', CAP_ALLOW, $studentrole->id, $quizcontext->id);
+
+ // Give this message type to the front page role.
+ assign_capability('mod/quiz:emailwarnoverdue', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext->id);
+
+ $providers = message_get_providers_for_user($user->id);
+ $this->assertTrue($this->message_type_present('mod_forum', 'posts', $providers));
+ $this->assertTrue($this->message_type_present('mod_quiz', 'confirmation', $providers));
+ $this->assertTrue($this->message_type_present('mod_quiz', 'attempt_overdue', $providers));
+ $this->assertFalse($this->message_type_present('mod_quiz', 'submission', $providers));
+
+ // A user is a student in a different course, they should not get confirmation.
+ $course2 = $generator->create_course(array('category' => $cat->id));
+ $user2 = $generator->create_user();
+ $coursecontext2 = context_course::instance($course2->id);
+ role_assign($studentrole->id, $user2->id, $coursecontext2->id);
+ accesslib_clear_all_caches_for_unit_testing();
+ $providers = message_get_providers_for_user($user2->id);
+ $this->assertTrue($this->message_type_present('mod_forum', 'posts', $providers));
+ $this->assertFalse($this->message_type_present('mod_quiz', 'confirmation', $providers));
+
+ // Now remove the frontpage role id, and attempt_overdue message should go away.
+ unset_config('defaultfrontpageroleid');
+ accesslib_clear_all_caches_for_unit_testing();
+
+ $providers = message_get_providers_for_user($user->id);
+ $this->assertTrue($this->message_type_present('mod_quiz', 'confirmation', $providers));
+ $this->assertFalse($this->message_type_present('mod_quiz', 'attempt_overdue', $providers));
+ $this->assertFalse($this->message_type_present('mod_quiz', 'submission', $providers));
+ }
+
+ public function test_message_get_providers_for_user_more() {
+ global $DB;
+
+ $this->resetAfterTest(true);
+
+ // Create a course
+ $course = $this->getDataGenerator()->create_course();
+ $coursecontext = context_course::instance($course->id);
+
+ // It would probably be better to use a quiz instance as it has capability controlled messages
+ // however mod_quiz doesn't have a data generator
+ // Instead we're going to use backup notifications and give and take away the capability at various levels
+ $assign = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id));
+ $modulecontext = context_module::instance($assign->id);
+
+ // Create and enrol a teacher
+ $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
+ $teacher = $this->getDataGenerator()->create_user();
+ role_assign($teacherrole->id, $teacher->id, $coursecontext);
+ $enrolplugin = enrol_get_plugin('manual');
+ $enrolplugin->add_instance($course);
+ $enrolinstances = enrol_get_instances($course->id, false);
+ foreach ($enrolinstances as $enrolinstance) {
+ if ($enrolinstance->enrol === 'manual') {
+ break;
+ }
+ }
+ $enrolplugin->enrol_user($enrolinstance, $teacher->id);
+
+ // Make the teacher the current user
+ $this->setUser($teacher);
+
+ // Teacher shouldn't have the required capability so they shouldn't be able to see the backup message
+ $this->assertFalse(has_capability('moodle/site:config', $modulecontext));
+ $providers = message_get_providers_for_user($teacher->id);
+ $this->assertFalse($this->message_type_present('moodle', 'backup', $providers));
+
+ // Give the user the required capability in an activity module
+ // They should now be able to see the backup message
+ assign_capability('moodle/site:config', CAP_ALLOW, $teacherrole->id, $modulecontext->id, true);
+ accesslib_clear_all_caches_for_unit_testing();
+ $modulecontext = context_module::instance($assign->id);
+ $this->assertTrue(has_capability('moodle/site:config', $modulecontext));
+
+ $providers = message_get_providers_for_user($teacher->id);
+ $this->assertTrue($this->message_type_present('moodle', 'backup', $providers));
+
+ // Prohibit the capability for the user at the course level
+ // This overrules the CAP_ALLOW at the module level
+ // They should not be able to see the backup message
+ assign_capability('moodle/site:config', CAP_PROHIBIT, $teacherrole->id, $coursecontext->id, true);
+ accesslib_clear_all_caches_for_unit_testing();
+ $modulecontext = context_module::instance($assign->id);
+ $this->assertFalse(has_capability('moodle/site:config', $modulecontext));
+
+ $providers = message_get_providers_for_user($teacher->id);
+ // Actually, handling PROHIBITs would be too expensive. We do not
+ // care if users with PROHIBITs see a few more preferences than they should.
+ // $this->assertFalse($this->message_type_present('moodle', 'backup', $providers));
+ }
+
+ /**
+ * Is a particular message type in the list of message types.
+ * @param string $name a message name.
+ * @param array $providers as returned by message_get_providers_for_user.
+ * @return bool whether the message type is present.
+ */
+ protected function message_type_present($component, $name, $providers) {
+ foreach ($providers as $provider) {
+ if ($provider->component == $component && $provider->name == $name) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
'6.0' => array('Windows XP SP2' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)'),
'7.0' => array('Windows XP SP2' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; YPC 3.0.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)'),
'8.0' => array('Windows Vista' => 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648)'),
- '9.0' => array('Windows 7' => 'Mozilla/5.0 (Windows; U; MSIE 9.0; WIndows NT 9.0; en-US))'),
-
+ '9.0' => array('Windows 7' => 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)'),
+ '9.0i' => array('Windows 7' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)'),
+ '10.0' => array('Windows 8' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; Touch)'),
+ '10.0i' => array('Windows 8' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; Trident/6.0; Touch; .NET4.0E; .NET4.0C; Tablet PC 2.0)'),
),
'Firefox' => array(
'1.0.6' => array('Windows XP' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.10) Gecko/20050716 Firefox/1.0.6'),
$this->assertTrue(check_browser_version('MSIE', '9.0'));
$this->assertFalse(check_browser_version('MSIE', '10'));
+ $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['9.0i']['Windows 7'];
+ $this->assertTrue(check_browser_version('MSIE'));
+ $this->assertTrue(check_browser_version('MSIE', 0));
+ $this->assertTrue(check_browser_version('MSIE', '5.0'));
+ $this->assertTrue(check_browser_version('MSIE', '9.0'));
+ $this->assertFalse(check_browser_version('MSIE', '10'));
+
+ $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['10.0']['Windows 8'];
+ $this->assertTrue(check_browser_version('MSIE'));
+ $this->assertTrue(check_browser_version('MSIE', 0));
+ $this->assertTrue(check_browser_version('MSIE', '5.0'));
+ $this->assertTrue(check_browser_version('MSIE', '9.0'));
+ $this->assertTrue(check_browser_version('MSIE', '10'));
+ $this->assertFalse(check_browser_version('MSIE', '11'));
+
+ $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['10.0i']['Windows 8'];
+ $this->assertTrue(check_browser_version('MSIE'));
+ $this->assertTrue(check_browser_version('MSIE', 0));
+ $this->assertTrue(check_browser_version('MSIE', '5.0'));
+ $this->assertTrue(check_browser_version('MSIE', '9.0'));
+ $this->assertTrue(check_browser_version('MSIE', '10'));
+ $this->assertFalse(check_browser_version('MSIE', '11'));
+
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['2.0']['Windows XP'];
$this->assertTrue(check_browser_version('Firefox'));
$this->assertTrue(check_browser_version('Firefox', '1.5'));
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['8.0']['Windows Vista'];
$this->assertEquals(array('ie', 'ie8'), get_browser_version_classes());
+ $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['9.0']['Windows 7'];
+ $this->assertEquals(array('ie', 'ie9'), get_browser_version_classes());
+
+ $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['9.0i']['Windows 7'];
+ $this->assertEquals(array('ie', 'ie9'), get_browser_version_classes());
+
+ $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['10.0']['Windows 8'];
+ $this->assertEquals(array('ie', 'ie10'), get_browser_version_classes());
+
+ $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['10.0i']['Windows 8'];
+ $this->assertEquals(array('ie', 'ie10'), get_browser_version_classes());
+
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['2.0']['Windows XP'];
$this->assertEquals(array('gecko', 'gecko18'), get_browser_version_classes());
'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)' => false,
// IE9 on Windows 7.
'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)' => true,
- // IE9 on Windows 7 in compatability mode.
- 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/5.0)' => false,
+ // IE9 on Windows 7 in intranet mode.
+ 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/5.0)' => true,
// Chrome 11 on Windows.
'Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/11.0.652.0 Safari/534.17' => true,
// Chrome 22 on Windows.
--- /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/>.
+
+/**
+ * Tests for ../statslib.php
+ *
+ * @package core
+ * @subpackage stats
+ * @copyright 2012 Tyler Bannister
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir . '/adminlib.php');
+require_once($CFG->libdir . '/statslib.php');
+
+/**
+ * Test functions that affect daily stats
+ */
+class statslib_daily_testcase extends advanced_testcase {
+ /** The student role ID **/
+ const STID = 5;
+
+ /** The day to use for testing **/
+ const DAY = 1272672000;
+
+ /** The timezone to use for testing **/
+ const TIMEZONE = 0;
+
+ /** @var array The list of temporary tables created for the statistic calculations **/
+ protected $tables = array('temp_log1', 'temp_log2', 'temp_stats_daily', 'temp_stats_user_daily');
+
+ /** @var array The replacements to be used when loading XML files **/
+ protected $replacements = null;
+
+ /**
+ * Set up the database for tests
+ *
+ * This function is needed so that daily_log_provider has the before-test set up from setUp()
+ */
+ public function setUpDB() {
+ global $DB;
+
+ if ($DB->record_exists('user', array('username' => 'user1'))) {
+ return;
+ }
+
+ $datagen = self::getDataGenerator();
+
+ $user1 = $datagen->create_user(array('username'=>'user1'));
+ $user2 = $datagen->create_user(array('username'=>'user2'));
+
+ $course1 = $datagen->create_course(array('shortname'=>'course1'));
+
+ $success = enrol_try_internal_enrol($course1->id, $user1->id, 5);
+
+ if (! $success) {
+ trigger_error('User enrollment failed', E_USER_ERROR);
+ }
+
+ $context = context_system::instance();
+ role_assign(self::STID, $user2->id, $context->id);
+
+ $this->generate_replacement_list();
+ }
+
+ /**
+ * Setup function
+ * - Allow changes to CFG->debug for testing purposes.
+ */
+ protected function setUp() {
+ global $CFG;
+ parent::setUp();
+
+ // Settings to force statistic to run during testing
+ $CFG->timezone = self::TIMEZONE;
+ $CFG->statsfirstrun = 'all';
+ $CFG->statslastdaily = 0;
+ $CFG->statslastexecution = 0;
+
+ // Figure out the broken day start so I can figure out when to the start time should be
+ $time = time();
+ $offset = get_timezone_offset($CFG->timezone);
+ $stime = $time + $offset;
+ $stime = intval($stime / (60*60*24)) * 60*60*24;
+ $stime -= $offset;
+
+ $shour = intval(($time - $stime) / (60*60));
+
+ $CFG->statsruntimestarthour = $shour;
+ $CFG->statsruntimestartminute = 0;
+
+ $this->setUpDB();
+
+ $this->resetAfterTest(true);
+ }
+
+ /**
+ * Function to setup database.
+ *
+ * @param array $dataset An array of tables including the log table.
+ */
+ protected function prepare_db($dataset, $tables) {
+ global $DB;
+
+ foreach ($tables as $tablename) {
+ $DB->delete_records($tablename);
+
+ foreach ($dataset as $name => $table) {
+
+ if ($tablename == $name) {
+
+ $rows = $table->getRowCount();
+
+ for ($i = 0; $i < $rows; $i++) {
+ $row = $table->getRow($i);
+
+ $DB->insert_record($tablename, $row, false, true);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Load dataset from XML file
+ *
+ * @param string $file The name of the file to load
+ */
+ protected function generate_replacement_list() {
+ global $CFG, $DB;
+
+ if ($this->replacements !== null) {
+ return;
+ }
+
+ $CFG->timezone = self::TIMEZONE;
+
+ $guest = $DB->get_record('user', array('id' => $CFG->siteguest));
+ $user1 = $DB->get_record('user', array('username' => 'user1'));
+ $user2 = $DB->get_record('user', array('username' => 'user2'));
+
+ if (($guest === false) || ($user1 === false) || ($user2 === false)) {
+ trigger_error('User setup incomplete', E_USER_ERROR);
+ }
+
+ $site = $DB->get_record('course', array('id' => SITEID));
+ $course1 = $DB->get_record('course', array('shortname' => 'course1'));
+
+ if (($site === false) || ($course1 === false)) {
+ trigger_error('Course setup incomplete', E_USER_ERROR);
+ }
+
+ $offset = get_timezone_offset($CFG->timezone);
+
+ $start = stats_get_base_daily(self::DAY + 3600);
+ $startnolog = stats_get_base_daily(stats_get_start_from('daily'));
+ $gr = get_guest_role();
+
+ $this->replacements = array(
+ // Start and end times
+ '[start_0]' => $start - 14410, // 4 hours before
+ '[start_1]' => $start + 14410, // 4 hours after
+ '[start_2]' => $start + 14420,
+ '[start_3]' => $start + 14430,
+ '[start_4]' => $start + 100800, // 28 hours after
+ '[end]' => stats_get_next_day_start($start),
+ '[end_no_logs]' => stats_get_next_day_start($startnolog),
+
+ // User ids
+ '[guest_id]' => $guest->id,
+ '[user1_id]' => $user1->id,
+ '[user2_id]' => $user2->id,
+
+ // Course ids
+ '[course1_id]' => $course1->id,
+ '[site_id]' => SITEID,
+
+ // Role ids
+ '[frontpage_roleid]' => (int) $CFG->defaultfrontpageroleid,
+ '[guest_roleid]' => $gr->id,
+ '[student_roleid]' => self::STID,
+ );
+ }
+
+ /**
+ * Load dataset from XML file
+ *
+ * @param string $file The name of the file to load
+ */
+ protected function load_xml_data_file($file) {
+ static $replacements = null;
+
+ $raw = $this->createXMLDataSet($file);
+ $clean = new PHPUnit_Extensions_Database_DataSet_ReplacementDataSet($raw);
+
+ foreach ($this->replacements as $placeholder => $value) {
+ $clean->addFullReplacement($placeholder, $value);
+ }
+
+ $logs = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($clean);
+ $logs->addIncludeTables(array('log'));
+
+ $stats = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($clean);
+ $stats->addIncludeTables(array('stats_daily', 'stats_user_daily'));
+
+ return array($logs, $stats);
+ }
+
+ /**
+ * Provides the log data for test_statslib_cron_daily
+ */
+ public function daily_log_provider() {
+ global $CFG, $DB;
+
+ $this->setUpDB();
+
+ $tests = array('00', '01', '02', '03', '04', '05', '06', '07', '08');
+
+ $dataset = array();
+
+ foreach ($tests as $test) {
+ $dataset[] = $this->load_xml_data_file(__DIR__."/fixtures/statslib-test{$test}.xml");
+ }
+
+ return $dataset;
+ }
+
+ /**
+ * Compare the expected stats to those in the database.
+ *
+ * @param array $stats An array of arrays of arrays of both types of stats
+ */
+ protected function verify_stats($expected) {
+ global $DB;
+
+ // Note: We can not use $this->assertDataSetEqual($expected, $actual) because there's no
+ // $this->getConnection() in advanced_testcase.
+
+ foreach ($expected as $type => $table) {
+ $records = $DB->get_records($type);
+
+ $rows = $table->getRowCount();
+
+ $this->assertEquals($rows, sizeof($records),
+ 'Incorrect number of results returned for '. $type);
+
+ for ($i = 0; $i < $rows; $i++) {
+ $row = $table->getRow($i);
+ $found = 0;
+
+ foreach ($records as $key => $record) {
+ $record = (array) $record;
+ unset($record['id']);
+ $diff = array_merge(array_diff_assoc($row, $record),
+ array_diff_assoc($record, $row));
+
+ if (empty($diff)) {
+ $found = $key;
+ break;
+ }
+ }
+ $this->assertGreaterThan(0, $found, 'Expected log '. var_export($row, true)
+ ." was not found in $type ". var_export($records, true));
+ unset($records[$found]);
+ }
+ }
+ }
+
+ /**
+ * Test progress output when debug is on
+ */
+ public function test_statslib_progress_debug() {
+ global $CFG;
+
+ $CFG->debug = DEBUG_ALL;
+ $this->expectOutputString('1:0 ');
+ stats_progress('init');
+ stats_progress('1');
+ }
+
+ /**
+ * Test progress output when debug is off
+ */
+ public function test_statslib_progress_no_debug() {
+ global $CFG;
+
+ $CFG->debug = DEBUG_NONE;
+ $this->expectOutputString('.');
+ stats_progress('init');
+ stats_progress('1');
+ }
+
+ /**
+ * Test the function that gets the start date from the config
+ */
+ public function test_statslib_get_start_from() {
+ global $CFG, $DB;
+
+ $dataset = $this->load_xml_data_file(__DIR__."/fixtures/statslib-test01.xml");
+ $time = time();
+ $DB->delete_records('log');
+
+ // Don't ask. I don't think get_timezone_offset works correctly.
+ $day = self::DAY - get_timezone_offset($CFG->timezone);
+
+ $CFG->statsfirstrun = 'all';
+ // Allow 1 second difference in case we cross a second boundary.
+ $this->assertLessThanOrEqual(1, stats_get_start_from('daily') - ($time - (3 * 24 * 3600)), 'All start time');
+
+ $this->prepare_db($dataset[0], array('log'));
+ $records = $DB->get_records('log');
+
+ $this->assertEquals($day + 14410, stats_get_start_from('daily'), 'Log entry start');
+
+ $CFG->statsfirstrun = 'none';
+ $this->assertLessThanOrEqual(1, stats_get_start_from('daily') - ($time - (3 * 24 * 3600)), 'None start time');
+
+ $CFG->statsfirstrun = 14515200;
+ $this->assertLessThanOrEqual(1, stats_get_start_from('daily') - ($time - (14515200)), 'Specified start time');
+
+ $this->prepare_db($dataset[1], array('stats_daily'));
+ $this->assertEquals($day + (24 * 3600), stats_get_start_from('daily'), 'Daily stats start time');
+ }
+
+ /**
+ * Test the function that calculates the start of the day
+ *
+ * NOTE: I don't think this is the way this function should work.
+ * This test documents the current functionality.
+ */
+ public function test_statslib_get_base_daily() {
+ global $CFG;
+
+ for ($x = 0; $x < 24; $x += 1) {
+ $CFG->timezone = $x;
+
+ $start = 1272672000 - ($x * 3600);
+ if ($x >= 20) {
+ $start += (24 * 3600);
+ }
+
+ $this->assertEquals($start, stats_get_base_daily(1272686410), "Timezone $x check");
+ }
+ }
+
+ /**
+ * Test the function that gets the start of the next day
+ */
+ public function test_statslib_get_next_day_start() {
+ global $CFG;
+
+ $CFG->timezone = 0;
+ $this->assertEquals(1272758400, stats_get_next_day_start(1272686410));
+ }
+
+ /**
+ * Test the function that gets the action names
+ *
+ * Note: The function results depend on installed modules. The hard coded lists are the
+ * defaults for a new Moodle 2.3 install.
+ */
+ public function test_statslib_get_action_names() {
+ $basepostactions = array (
+ 0 => 'add',
+ 1 => 'delete',
+ 2 => 'edit',
+ 3 => 'add mod',
+ 4 => 'delete mod',
+ 5 => 'edit sectionenrol',
+ 6 => 'loginas',
+ 7 => 'new',
+ 8 => 'unenrol',
+ 9 => 'update',
+ 10 => 'update mod',
+ 11 => 'upload',
+ 12 => 'submit',
+ 13 => 'submit for grading',
+ 14 => 'talk',
+ 15 => 'choose',
+ 16 =>&