This commit is contained in:
Paul Charsley
2012-08-28 10:24:05 +12:00
141 changed files with 3618 additions and 904 deletions
+4 -6
View File
@@ -1039,12 +1039,10 @@ class potential_assignees_below_course extends role_assign_user_selector_base {
$countfields = 'SELECT COUNT(u.id)';
$sql = " FROM {user} u
WHERE u.id IN ($enrolsql) $wherecondition
AND u.id NOT IN (
SELECT r.userid
FROM {role_assignments} r
WHERE r.contextid = :contextid
AND r.roleid = :roleid)";
LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.roleid = :roleid AND ra.contextid = :contextid)
WHERE u.id IN ($enrolsql)
$wherecondition
AND ra.id IS NULL";
$order = ' ORDER BY lastname ASC, firstname ASC';
$params['contextid'] = $this->context->id;
+5
View File
@@ -47,6 +47,11 @@ if ($hassiteconfig
$temp->add(new admin_setting_configselect('moodlecourse/legacyfiles', new lang_string('courselegacyfiles'), new lang_string('courselegacyfiles_help'), key($choices), $choices));
}
$choices = array();
$choices[COURSE_DISPLAY_SINGLEPAGE] = new lang_string('coursedisplay_single');
$choices[COURSE_DISPLAY_MULTIPAGE] = new lang_string('coursedisplay_multi');
$temp->add(new admin_setting_configselect('moodlecourse/coursedisplay', new lang_string('coursedisplay'), new lang_string('coursedisplay_help'), COURSE_DISPLAY_SINGLEPAGE, $choices));
$temp->add(new admin_setting_heading('groups', new lang_string('groups', 'group'), ''));
$choices = array();
$choices[NOGROUPS] = new lang_string('groupsnone', 'group');
+1 -1
View File
@@ -14,7 +14,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
$temp->add(new admin_setting_heading('iplookup', new lang_string('iplookup', 'admin'), new lang_string('iplookupinfo', 'admin')));
$temp->add(new admin_setting_configfile('geoipfile', new lang_string('geoipfile', 'admin'), new lang_string('configgeoipfile', 'admin', $CFG->dataroot.'/geoip/'), $CFG->dataroot.'/geoip/GeoLiteCity.dat'));
$temp->add(new admin_setting_configtext('googlemapkey', new lang_string('googlemapkey', 'admin'), new lang_string('configgooglemapkey', 'admin', $CFG->wwwroot), ''));
$temp->add(new admin_setting_configtext('googlemapkey3', new lang_string('googlemapkey3', 'admin'), new lang_string('googlemapkey3_help', 'admin'), '', PARAM_RAW, 60));
$temp->add(new admin_setting_configtext('allcountrycodes', new lang_string('allcountrycodes', 'admin'), new lang_string('configallcountrycodes', 'admin'), '', '/^(?:\w+(?:,\w+)*)?$/'));
+1 -1
View File
@@ -150,7 +150,7 @@ if ($diag) {
} else if ($drop) {
// make sure tests do not run in parallel
phpunit_util::acquire_test_lock();
phpunit_util::drop_site();
phpunit_util::drop_site(true);
// note: we must stop here because $CFG is messed up and we can not reinstall, sorry
exit(0);
+3 -1
View File
@@ -132,7 +132,9 @@ class auth_plugin_email extends auth_plugin_base {
} else if ($user->secret == $confirmsecret) { // They have provided the secret key to get in
$DB->set_field("user", "confirmed", 1, array("id"=>$user->id));
$DB->set_field("user", "firstaccess", time(), array("id"=>$user->id));
if ($user->firstaccess == 0) {
$DB->set_field("user", "firstaccess", time(), array("id"=>$user->id));
}
return AUTH_CONFIRM_OK;
}
} else {
+3 -1
View File
@@ -546,7 +546,9 @@ class auth_plugin_ldap extends auth_plugin_base {
return AUTH_CONFIRM_FAIL;
}
$DB->set_field('user', 'confirmed', 1, array('id'=>$user->id));
$DB->set_field('user', 'firstaccess', time(), array('id'=>$user->id));
if ($user->firstaccess == 0) {
$DB->set_field('user', 'firstaccess', time(), array('id'=>$user->id));
}
return AUTH_CONFIRM_OK;
}
} else {
+3 -1
View File
@@ -170,7 +170,9 @@ class auth_plugin_manual extends auth_plugin_base {
return AUTH_CONFIRM_ALREADY;
} else {
$DB->set_field("user", "confirmed", 1, array("id"=>$user->id));
$DB->set_field("user", "firstaccess", time(), array("id"=>$user->id));
if ($user->firstaccess == 0) {
$DB->set_field("user", "firstaccess", time(), array("id"=>$user->id));
}
return AUTH_CONFIRM_OK;
}
} else {
+18 -9
View File
@@ -182,8 +182,17 @@ abstract class backup_questions_activity_structure_step extends backup_activity_
/**
* Attach to $element (usually attempts) the needed backup structures
* for question_usages and all the associated data.
*
* @param backup_nested_element $element the element that will contain all the question_usages data.
* @param string $usageidname the name of the element that holds the usageid.
* This must be child of $element, and must be a final element.
* @param string $nameprefix this prefix is added to all the element names we create.
* Element names in the XML must be unique, so if you are using usages in
* two different ways, you must give a prefix to at least one of them. If
* you only use one sort of usage, then you can just use the default empty prefix.
* This should include a trailing underscore. For example "myprefix_"
*/
protected function add_question_usages($element, $usageidname) {
protected function add_question_usages($element, $usageidname, $nameprefix = '') {
global $CFG;
require_once($CFG->dirroot . '/question/engine/lib.php');
@@ -195,21 +204,21 @@ abstract class backup_questions_activity_structure_step extends backup_activity_
throw new backup_step_exception('question_states_bad_question_attempt_element', $usageidname);
}
$quba = new backup_nested_element('question_usage', array('id'),
$quba = new backup_nested_element($nameprefix . 'question_usage', array('id'),
array('component', 'preferredbehaviour'));
$qas = new backup_nested_element('question_attempts');
$qa = new backup_nested_element('question_attempt', array('id'), array(
$qas = new backup_nested_element($nameprefix . 'question_attempts');
$qa = new backup_nested_element($nameprefix . 'question_attempt', array('id'), array(
'slot', 'behaviour', 'questionid', 'maxmark', 'minfraction',
'flagged', 'questionsummary', 'rightanswer', 'responsesummary',
'timemodified'));
$steps = new backup_nested_element('steps');
$step = new backup_nested_element('step', array('id'), array(
$steps = new backup_nested_element($nameprefix . 'steps');
$step = new backup_nested_element($nameprefix . 'step', array('id'), array(
'sequencenumber', 'state', 'fraction', 'timecreated', 'userid'));
$response = new backup_nested_element('response');
$variable = new backup_nested_element('variable', null, array('name', 'value'));
$response = new backup_nested_element($nameprefix . 'response');
$variable = new backup_nested_element($nameprefix . 'variable', null, array('name', 'value'));
// Build the tree
$element->add_child($quba);
@@ -1835,7 +1844,7 @@ class backup_annotate_all_user_files extends backup_execution_step {
'backupid' => $this->get_backupid(), 'itemname' => 'userfinal'));
foreach ($rs as $record) {
$userid = $record->itemid;
$userctx = context_user::instance($userid);
$userctx = context_user::instance($userid, IGNORE_MISSING);
if (!$userctx) {
continue; // User has not context, sure it's a deleted user, so cannot have files
}
+69 -19
View File
@@ -3478,31 +3478,74 @@ abstract class restore_questions_activity_structure_step extends restore_activit
/**
* Attach below $element (usually attempts) the needed restore_path_elements
* to restore question_usages and all they contain.
*
* If you use the $nameprefix parameter, then you will need to implement some
* extra methods in your class, like
*
* protected function process_{nameprefix}question_attempt($data) {
* $this->restore_question_usage_worker($data, '{nameprefix}');
* }
* protected function process_{nameprefix}question_attempt($data) {
* $this->restore_question_attempt_worker($data, '{nameprefix}');
* }
* protected function process_{nameprefix}question_attempt_step($data) {
* $this->restore_question_attempt_step_worker($data, '{nameprefix}');
* }
*
* @param restore_path_element $element the parent element that the usages are stored inside.
* @param array $paths the paths array that is being built.
* @param string $nameprefix should match the prefix passed to the corresponding
* backup_questions_activity_structure_step::add_question_usages call.
*/
protected function add_question_usages($element, &$paths) {
protected function add_question_usages($element, &$paths, $nameprefix = '') {
// Check $element is restore_path_element
if (! $element instanceof restore_path_element) {
throw new restore_step_exception('element_must_be_restore_path_element', $element);
}
// Check $paths is one array
if (!is_array($paths)) {
throw new restore_step_exception('paths_must_be_array', $paths);
}
$paths[] = new restore_path_element('question_usage',
$element->get_path() . '/question_usage');
$paths[] = new restore_path_element('question_attempt',
$element->get_path() . '/question_usage/question_attempts/question_attempt');
$paths[] = new restore_path_element('question_attempt_step',
$element->get_path() . '/question_usage/question_attempts/question_attempt/steps/step',
$paths[] = new restore_path_element($nameprefix . 'question_usage',
$element->get_path() . "/{$nameprefix}question_usage");
$paths[] = new restore_path_element($nameprefix . 'question_attempt',
$element->get_path() . "/{$nameprefix}question_usage/{$nameprefix}question_attempts/{$nameprefix}question_attempt");
$paths[] = new restore_path_element($nameprefix . 'question_attempt_step',
$element->get_path() . "/{$nameprefix}question_usage/{$nameprefix}question_attempts/{$nameprefix}question_attempt/{$nameprefix}steps/{$nameprefix}step",
true);
$paths[] = new restore_path_element('question_attempt_step_data',
$element->get_path() . '/question_usage/question_attempts/question_attempt/steps/step/response/variable');
$paths[] = new restore_path_element($nameprefix . 'question_attempt_step_data',
$element->get_path() . "/{$nameprefix}question_usage/{$nameprefix}question_attempts/{$nameprefix}question_attempt/{$nameprefix}steps/{$nameprefix}step/{$nameprefix}response/{$nameprefix}variable");
}
/**
* Process question_usages
*/
protected function process_question_usage($data) {
$this->restore_question_usage_worker($data, '');
}
/**
* Process question_attempts
*/
protected function process_question_attempt($data) {
$this->restore_question_attempt_worker($data, '');
}
/**
* Process question_attempt_steps
*/
protected function process_question_attempt_step($data) {
$this->restore_question_attempt_step_worker($data, '');
}
/**
* This method does the acutal work for process_question_usage or
* process_{nameprefix}_question_usage.
* @param array $data the data from the XML file.
* @param string $nameprefix the element name prefix.
*/
protected function restore_question_usage_worker($data, $nameprefix) {
global $DB;
// Clear our caches.
@@ -3520,7 +3563,7 @@ abstract class restore_questions_activity_structure_step extends restore_activit
$this->inform_new_usage_id($newitemid);
$this->set_mapping('question_usage', $oldid, $newitemid, false);
$this->set_mapping($nameprefix . 'question_usage', $oldid, $newitemid, false);
}
/**
@@ -3532,30 +3575,36 @@ abstract class restore_questions_activity_structure_step extends restore_activit
abstract protected function inform_new_usage_id($newusageid);
/**
* Process question_attempts
* This method does the acutal work for process_question_attempt or
* process_{nameprefix}_question_attempt.
* @param array $data the data from the XML file.
* @param string $nameprefix the element name prefix.
*/
protected function process_question_attempt($data) {
protected function restore_question_attempt_worker($data, $nameprefix) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$question = $this->get_mapping('question', $data->questionid);
$data->questionusageid = $this->get_new_parentid('question_usage');
$data->questionusageid = $this->get_new_parentid($nameprefix . 'question_usage');
$data->questionid = $question->newitemid;
$data->timemodified = $this->apply_date_offset($data->timemodified);
$newitemid = $DB->insert_record('question_attempts', $data);
$this->set_mapping('question_attempt', $oldid, $newitemid);
$this->set_mapping($nameprefix . 'question_attempt', $oldid, $newitemid);
$this->qtypes[$newitemid] = $question->info->qtype;
$this->newquestionids[$newitemid] = $data->questionid;
}
/**
* Process question_attempt_steps
* This method does the acutal work for process_question_attempt_step or
* process_{nameprefix}_question_attempt_step.
* @param array $data the data from the XML file.
* @param string $nameprefix the element name prefix.
*/
protected function process_question_attempt_step($data) {
protected function restore_question_attempt_step_worker($data, $nameprefix) {
global $DB;
$data = (object)$data;
@@ -3563,14 +3612,14 @@ abstract class restore_questions_activity_structure_step extends restore_activit
// Pull out the response data.
$response = array();
if (!empty($data->response['variable'])) {
foreach ($data->response['variable'] as $variable) {
if (!empty($data->{$nameprefix . 'response'}[$nameprefix . 'variable'])) {
foreach ($data->{$nameprefix . 'response'}[$nameprefix . 'variable'] as $variable) {
$response[$variable['name']] = $variable['value'];
}
}
unset($data->response);
$data->questionattemptid = $this->get_new_parentid('question_attempt');
$data->questionattemptid = $this->get_new_parentid($nameprefix . 'question_attempt');
$data->timecreated = $this->apply_date_offset($data->timecreated);
$data->userid = $this->get_mappingid('user', $data->userid);
@@ -3583,6 +3632,7 @@ abstract class restore_questions_activity_structure_step extends restore_activit
$this->qtypes[$data->questionattemptid],
$this->newquestionids[$data->questionattemptid],
$data->sequencenumber, $response);
foreach ($response as $name => $value) {
$row = new stdClass();
$row->attemptstepid = $newitemid;
@@ -112,7 +112,7 @@ abstract class backup_plan_dbops extends backup_dbops {
// Get all sections belonging to requested course
$sectionsarr = array();
$sections = $DB->get_records('course_sections', array('course' => $courseid));
$sections = $DB->get_records('course_sections', array('course' => $courseid), 'section');
foreach ($sections as $section) {
$sectionsarr[] = $section->id;
}
+47 -25
View File
@@ -110,7 +110,7 @@ abstract class backup_cron_automated_helper {
$nextstarttime = backup_cron_automated_helper::calculate_next_automated_backup($admin->timezone, $now);
$showtime = "undefined";
if ($nextstarttime > 0) {
$showtime = userdate($nextstarttime,"",$admin->timezone);
$showtime = date('r', $nextstarttime);
}
$rs = $DB->get_recordset('course');
@@ -124,7 +124,14 @@ abstract class backup_cron_automated_helper {
}
// Skip courses that do not yet need backup
$skipped = !(($backupcourse->nextstarttime >= 0 && $backupcourse->nextstarttime < $now) || $rundirective == self::RUN_IMMEDIATELY);
$skipped = !(($backupcourse->nextstarttime > 0 && $backupcourse->nextstarttime < $now) || $rundirective == self::RUN_IMMEDIATELY);
if ($skipped && $backupcourse->nextstarttime != $nextstarttime) {
$backupcourse->nextstarttime = $nextstarttime;
$backupcourse->laststatus = backup_cron_automated_helper::BACKUP_STATUS_SKIPPED;
$DB->update_record('backup_courses', $backupcourse);
mtrace('Backup of \'' . $course->fullname . '\' is scheduled on ' . $showtime);
}
// Skip backup of unavailable courses that have remained unmodified in a month
if (!$skipped && empty($course->visible) && ($now - $course->timemodified) > 31*24*60*60) { //Hidden + settings were unmodified last month
//Check log if there were any modifications to the course content
@@ -139,9 +146,10 @@ abstract class backup_cron_automated_helper {
$skipped = true;
}
}
//Now we backup every non-skipped course
if (!$skipped) {
mtrace('Backing up '.$course->fullname, '...');
mtrace('Backing up '.$course->fullname.'...');
//We have to send a email because we have included at least one backup
$emailpending = true;
@@ -255,7 +263,7 @@ abstract class backup_cron_automated_helper {
self::BACKUP_STATUS_SKIPPED => 0,
);
$statuses = $DB->get_records_sql('SELECT DISTINCT bc.laststatus, COUNT(bc.courseid) statuscount FROM {backup_courses} bc GROUP BY bc.laststatus');
$statuses = $DB->get_records_sql('SELECT DISTINCT bc.laststatus, COUNT(bc.courseid) AS statuscount FROM {backup_courses} bc GROUP BY bc.laststatus');
foreach ($statuses as $status) {
if (empty($status->statuscount)) {
@@ -270,38 +278,47 @@ abstract class backup_cron_automated_helper {
/**
* Works out the next time the automated backup should be run.
*
* @param mixed $timezone
* @param int $now
* @return int
* @param mixed $timezone user timezone
* @param int $now timestamp, should not be in the past, most likely time()
* @return int timestamp of the next execution at server time
*/
public static function calculate_next_automated_backup($timezone, $now) {
$result = -1;
$result = 0;
$config = get_config('backup');
$midnight = usergetmidnight($now, $timezone);
$autohour = $config->backup_auto_hour;
$automin = $config->backup_auto_minute;
// Gets the user time relatively to the server time.
$date = usergetdate($now, $timezone);
$usertime = mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year']);
$diff = $now - $usertime;
// Get number of days (from today) to execute backups
// Get number of days (from user's today) to execute backups.
$automateddays = substr($config->backup_auto_weekdays, $date['wday']) . $config->backup_auto_weekdays;
$daysfromtoday = strpos($automateddays, "1", 1);
$daysfromnow = strpos($automateddays, "1");
// If we can't find the next day, we set it to tomorrow
if (empty($daysfromtoday)) {
$daysfromtoday = 1;
// Error, there are no days to schedule the backup for.
if ($daysfromnow === false) {
return 0;
}
// If some day has been found
if ($daysfromtoday !== false) {
// Calculate distance
$dist = ($daysfromtoday * 86400) + // Days distance
($config->backup_auto_hour * 3600) + // Hours distance
($config->backup_auto_minute * 60); // Minutes distance
$result = $midnight + $dist;
// Checks if the date would happen in the future (of the user).
$userresult = mktime($autohour, $automin, 0, $date['mon'], $date['mday'] + $daysfromnow, $date['year']);
if ($userresult <= $usertime) {
// If not, we skip the first scheduled day, that should fix it.
$daysfromnow = strpos($automateddays, "1", 1);
$userresult = mktime($autohour, $automin, 0, $date['mon'], $date['mday'] + $daysfromnow, $date['year']);
}
// If that time is past, call the function recursively to obtain the next valid day
if ($result > 0 && $result < time()) {
$result = self::calculate_next_automated_backup($timezone, $result);
// Now we generate the time relative to the server.
$result = $userresult + $diff;
// If that time is past, call the function recursively to obtain the next valid day.
if ($result <= $now) {
// Checking time() in here works, but makes PHPUnit Tests extremely hard to predict.
// $now should never be earlier than time() anyway...
$result = self::calculate_next_automated_backup($timezone, $now + DAYSECS);
}
return $result;
@@ -411,7 +428,12 @@ abstract class backup_cron_automated_helper {
$config = get_config('backup');
$active = (int)$config->backup_auto_active;
if ($active === self::AUTO_BACKUP_DISABLED || ($rundirective == self::RUN_ON_SCHEDULE && $active === self::AUTO_BACKUP_MANUAL)) {
$weekdays = (string)$config->backup_auto_weekdays;
// In case of automated backup also check that it is scheduled for at least one weekday.
if ($active === self::AUTO_BACKUP_DISABLED ||
($rundirective == self::RUN_ON_SCHEDULE && $active === self::AUTO_BACKUP_MANUAL) ||
($rundirective == self::RUN_ON_SCHEDULE && strpos($weekdays, '1') === false)) {
return self::STATE_DISABLED;
} else if (!empty($config->backup_auto_running)) {
// Detect if the backup_auto_running semaphore is a valid one
@@ -0,0 +1,442 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for backups cron helper.
*
* @package core_backup
* @category phpunit
* @copyright 2012 Frédéric Massart <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/backup/util/helper/backup_cron_helper.class.php');
/**
* Unit tests for backup cron helper
*/
class backup_cron_helper_testcase extends advanced_testcase {
/**
* Test {@link backup_cron_automated_helper::calculate_next_automated_backup}.
*/
public function test_next_automated_backup() {
$this->resetAfterTest();
set_config('backup_auto_active', '1', 'backup');
// Notes
// - backup_auto_weekdays starts on Sunday
// - Tests cannot be done in the past
// - Only the DST on the server side is handled.
// Every Tue and Fri at 11pm.
set_config('backup_auto_weekdays', '0010010', 'backup');
set_config('backup_auto_hour', '23', 'backup');
set_config('backup_auto_minute', '0', 'backup');
$timezone = 99;
$now = strtotime('next Monday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('2-23:00', date('w-H:i', $next));
$now = strtotime('next Tuesday 18:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('2-23:00', date('w-H:i', $next));
$now = strtotime('next Wednesday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('5-23:00', date('w-H:i', $next));
$now = strtotime('next Thursday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('5-23:00', date('w-H:i', $next));
$now = strtotime('next Friday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('5-23:00', date('w-H:i', $next));
$now = strtotime('next Saturday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('2-23:00', date('w-H:i', $next));
$now = strtotime('next Sunday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('2-23:00', date('w-H:i', $next));
// Every Sun and Sat at 12pm.
set_config('backup_auto_weekdays', '1000001', 'backup');
set_config('backup_auto_hour', '0', 'backup');
set_config('backup_auto_minute', '0', 'backup');
$timezone = 99;
$now = strtotime('next Monday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
$now = strtotime('next Tuesday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
$now = strtotime('next Wednesday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
$now = strtotime('next Thursday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
$now = strtotime('next Friday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
$now = strtotime('next Saturday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-00:00', date('w-H:i', $next));
$now = strtotime('next Sunday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
// Every Sun at 4am.
set_config('backup_auto_weekdays', '1000000', 'backup');
set_config('backup_auto_hour', '4', 'backup');
set_config('backup_auto_minute', '0', 'backup');
$timezone = 99;
$now = strtotime('next Monday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
$now = strtotime('next Tuesday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
$now = strtotime('next Wednesday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
$now = strtotime('next Thursday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
$now = strtotime('next Friday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
$now = strtotime('next Saturday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
$now = strtotime('next Sunday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
// Every day but Wed at 8:30pm.
set_config('backup_auto_weekdays', '1110111', 'backup');
set_config('backup_auto_hour', '20', 'backup');
set_config('backup_auto_minute', '30', 'backup');
$timezone = 99;
$now = strtotime('next Monday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('1-20:30', date('w-H:i', $next));
$now = strtotime('next Tuesday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('2-20:30', date('w-H:i', $next));
$now = strtotime('next Wednesday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('4-20:30', date('w-H:i', $next));
$now = strtotime('next Thursday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('4-20:30', date('w-H:i', $next));
$now = strtotime('next Friday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('5-20:30', date('w-H:i', $next));
$now = strtotime('next Saturday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-20:30', date('w-H:i', $next));
$now = strtotime('next Sunday 17:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-20:30', date('w-H:i', $next));
// Sun, Tue, Thu, Sat at 12pm.
set_config('backup_auto_weekdays', '1010101', 'backup');
set_config('backup_auto_hour', '0', 'backup');
set_config('backup_auto_minute', '0', 'backup');
$timezone = 99;
$now = strtotime('next Monday 13:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('2-00:00', date('w-H:i', $next));
$now = strtotime('next Tuesday 13:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('4-00:00', date('w-H:i', $next));
$now = strtotime('next Wednesday 13:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('4-00:00', date('w-H:i', $next));
$now = strtotime('next Thursday 13:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
$now = strtotime('next Friday 13:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
$now = strtotime('next Saturday 13:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-00:00', date('w-H:i', $next));
$now = strtotime('next Sunday 13:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('2-00:00', date('w-H:i', $next));
// None.
set_config('backup_auto_weekdays', '0000000', 'backup');
set_config('backup_auto_hour', '15', 'backup');
set_config('backup_auto_minute', '30', 'backup');
$timezone = 99;
$now = strtotime('next Sunday 13:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0', $next);
// Playing with timezones.
set_config('backup_auto_weekdays', '1111111', 'backup');
set_config('backup_auto_hour', '20', 'backup');
set_config('backup_auto_minute', '00', 'backup');
$timezone = 99;
date_default_timezone_set('Australia/Perth');
$now = strtotime('18:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals(date('w-20:00'), date('w-H:i', $next));
$timezone = 99;
date_default_timezone_set('Europe/Brussels');
$now = strtotime('18:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals(date('w-20:00'), date('w-H:i', $next));
$timezone = 99;
date_default_timezone_set('America/New_York');
$now = strtotime('18:00:00');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals(date('w-20:00'), date('w-H:i', $next));
// Viva Australia! (UTC+8).
date_default_timezone_set('Australia/Perth');
$now = strtotime('18:00:00');
$timezone = -10.0; // 12am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals(date('w-14:00', strtotime('tomorrow')), date('w-H:i', $next));
$timezone = -5.0; // 5am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals(date('w-09:00', strtotime('tomorrow')), date('w-H:i', $next));
$timezone = 0.0; // 10am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals(date('w-04:00', strtotime('tomorrow')), date('w-H:i', $next));
$timezone = 3.0; // 1pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals(date('w-01:00', strtotime('tomorrow')), date('w-H:i', $next));
$timezone = 8.0; // 6pm for the user (same than the server).
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals(date('w-20:00'), date('w-H:i', $next));
$timezone = 9.0; // 7pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals(date('w-19:00'), date('w-H:i', $next));
$timezone = 13.0; // 12am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals(date('w-15:00', strtotime('tomorrow')), date('w-H:i', $next));
// Let's have a Belgian beer! (UTC+1 / UTC+2 DST).
date_default_timezone_set('Europe/Brussels');
$now = strtotime('18:00:00');
$dst = date('I');
$timezone = -10.0; // 7am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-07:00', strtotime('tomorrow')) : date('w-08:00', strtotime('tomorrow'));
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = -5.0; // 12pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-02:00', strtotime('tomorrow')) : date('w-03:00', strtotime('tomorrow'));
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 0.0; // 5pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-21:00') : date('w-22:00');
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 3.0; // 8pm for the user (note the expected time is today while in DST).
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-18:00', strtotime('tomorrow')) : date('w-19:00');
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 8.0; // 1am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-13:00', strtotime('tomorrow')) : date('w-14:00', strtotime('tomorrow'));
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 9.0; // 2am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-12:00', strtotime('tomorrow')) : date('w-13:00', strtotime('tomorrow'));
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 13.0; // 6am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-08:00', strtotime('tomorrow')) : date('w-09:00', strtotime('tomorrow'));
$this->assertEquals($expected, date('w-H:i', $next));
// The big apple! (UTC-5 / UTC-4 DST).
date_default_timezone_set('America/New_York');
$now = strtotime('18:00:00');
$dst = date('I');
$timezone = -10.0; // 1pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-01:00', strtotime('tomorrow')) : date('w-02:00', strtotime('tomorrow'));
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = -5.0; // 6pm for the user (server time).
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-20:00') : date('w-21:00');
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 0.0; // 11pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-15:00', strtotime('tomorrow')) : date('w-16:00', strtotime('tomorrow'));
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 3.0; // 2am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-12:00', strtotime('tomorrow')) : date('w-13:00', strtotime('tomorrow'));
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 8.0; // 7am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-07:00', strtotime('tomorrow')) : date('w-08:00', strtotime('tomorrow'));
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 9.0; // 8am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-06:00', strtotime('tomorrow')) : date('w-07:00', strtotime('tomorrow'));
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 13.0; // 6am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? date('w-02:00', strtotime('tomorrow')) : date('w-03:00', strtotime('tomorrow'));
$this->assertEquals($expected, date('w-H:i', $next));
// Some more timezone tests
set_config('backup_auto_weekdays', '0100001', 'backup');
set_config('backup_auto_hour', '20', 'backup');
set_config('backup_auto_minute', '00', 'backup');
date_default_timezone_set('Europe/Brussels');
$now = strtotime('next Monday 18:00:00');
$dst = date('I');
$timezone = -12.0; // 1pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? '2-09:00' : '2-10:00';
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = -4.0; // 1pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? '2-01:00' : '2-02:00';
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 0.0; // 5pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? '1-21:00' : '1-22:00';
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 2.0; // 7pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? '1-19:00' : '1-20:00';
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 4.0; // 9pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? '6-17:00' : '6-18:00';
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 12.0; // 6am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? '6-09:00' : '6-10:00';
$this->assertEquals($expected, date('w-H:i', $next));
// Some more timezone tests
set_config('backup_auto_weekdays', '0100001', 'backup');
set_config('backup_auto_hour', '02', 'backup');
set_config('backup_auto_minute', '00', 'backup');
date_default_timezone_set('America/New_York');
$now = strtotime('next Monday 04:00:00');
$dst = date('I');
$timezone = -12.0; // 8pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? '1-09:00' : '1-10:00';
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = -4.0; // 4am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? '6-01:00' : '6-02:00';
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 0.0; // 8am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? '5-21:00' : '5-22:00';
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 2.0; // 10am for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? '5-19:00' : '5-20:00';
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 4.0; // 12pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? '5-17:00' : '5-18:00';
$this->assertEquals($expected, date('w-H:i', $next));
$timezone = 12.0; // 8pm for the user.
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$expected = !$dst ? '5-09:00' : '5-10:00';
$this->assertEquals($expected, date('w-H:i', $next));
}
}
@@ -47,9 +47,9 @@ class restore_path_element {
/**
* Constructor - instantiates one restore_path_element, specifying its basic info.
*
* @param string $name name of the element
* @param string $path path of the element
* @param bool $grouped to gather information in grouped mode or no
* @param string $name name of the thing being restored. This determines the name of the process_... method called.
* @param string $path path of the element.
* @param bool $grouped to gather information in grouped mode or no.
*/
public function __construct($name, $path, $grouped = false) {
+3
View File
@@ -324,6 +324,9 @@ abstract class base_moodleform extends moodleform {
$config->noLabel = get_string('confirmcancelno', 'backup');
$PAGE->requires->yui_module('moodle-backup-confirmcancel', 'M.core_backup.watch_cancel_buttons', array($config));
$PAGE->requires->yui_module('moodle-backup-backupselectall', 'M.core_backup.select_all_init',
array(array('select' => get_string('select'), 'all' => get_string('all'), 'none' => get_string('none'))));
parent::display();
}
+80
View File
@@ -0,0 +1,80 @@
YUI.add('moodle-backup-backupselectall', function(Y) {
// Namespace for the backup
M.core_backup = M.core_backup || {};
/**
* Adds select all/none links to the top of the backup/restore/import schema page.
*/
M.core_backup.select_all_init = function(str) {
var formid = null;
var helper = function(e, check, type) {
e.preventDefault();
var len = type.length;
Y.all('input[type="checkbox"]').each(function(checkbox) {
var name = checkbox.get('name');
if (name.substring(name.length - len) == type) {
checkbox.set('checked', check);
}
});
// At this point, we really need to persuade the form we are part of to
// update all of its disabledIf rules. However, as far as I can see,
// given the way that lib/form/form.js is written, that is impossible.
if (formid && M.form) {
M.form.updateFormState(formid);
}
};
var html_generator = function(classname, idtype) {
return '<div class="' + classname + '">' +
'<div class="fitem fitem_fcheckbox">' +
'<div class="fitemtitle">' + str.select + '</div>' +
'<div class="felement">' +
'<a id="backup-all-' + idtype + '" href="#">' + str.all + '</a> / ' +
'<a id="backup-none-' + idtype + '" href="#">' + str.none + '</a>' +
'</div>' +
'</div>' +
'</div>';
};
var firstsection = Y.one('fieldset#coursesettings .fcontainer.clearfix .grouped_settings.section_level');
if (!firstsection) {
// This is not a relevant page.
return;
}
if (!firstsection.one('.felement.fcheckbox')) {
// No checkboxes.
return;
}
formid = firstsection.ancestor('form').getAttribute('id');
var withuserdata = false;
Y.all('input[type="checkbox"]').each(function(checkbox) {
var name = checkbox.get('name');
if (name.substring(name.length - 9) == '_userdata') {
withuserdata = '_userdata';
} else if (name.substring(name.length - 9) == '_userinfo') {
withuserdata = '_userinfo';
}
});
var html = html_generator('include_setting section_level', 'included');
if (withuserdata) {
html += html_generator('normal_setting', 'userdata');
}
var links = Y.Node.create('<div class="grouped_settings section_level">' + html + '</div>');
firstsection.insert(links, 'before');
Y.one('#backup-all-included').on('click', function(e) { helper(e, true, '_included'); });
Y.one('#backup-none-included').on('click', function(e) { helper(e, false, '_included'); });
if (withuserdata) {
Y.one('#backup-all-userdata').on('click', function(e) { helper(e, true, withuserdata); });
Y.one('#backup-none-userdata').on('click', function(e) { helper(e, false, withuserdata); });
}
}
}, '@VERSION@', {'requires':['base','node','event', 'node-event-simulate']});
+15
View File
@@ -131,4 +131,19 @@ class block_html extends block_base {
public function instance_can_be_docked() {
return (!empty($this->config->title) && parent::instance_can_be_docked());
}
/*
* Add custom html attributes to aid with theming and styling
*
* @return array
*/
function html_attributes() {
$attributes = parent::html_attributes();
if (!empty($this->config->classes)) {
$attributes['class'] .= ' '.$this->config->classes;
}
return $attributes;
}
}
+4
View File
@@ -41,6 +41,10 @@ class block_html_edit_form extends block_edit_form {
$mform->addElement('editor', 'config_text', get_string('configcontent', 'block_html'), null, $editoroptions);
$mform->addRule('config_text', null, 'required', null, 'client');
$mform->setType('config_text', PARAM_RAW); // XSS is prevented when printing the block contents and serving files
$mform->addElement('text', 'config_classes', get_string('configclasses', 'block_html'));
$mform->setType('config_classes', PARAM_TEXT);
$mform->addHelpButton('config_classes', 'configclasses', 'block_html');
}
function set_data($defaults) {
+2
View File
@@ -23,6 +23,8 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['configclasses'] = 'Additional HTML classes';
$string['configclasses_help'] = 'The purpose of this configuration is to aid with theming by helping distinguish HTML blocks from each other. Any CSS classes entered here (space delimited) will be appended to the block\'s default classes.';
$string['configcontent'] = 'Content';
$string['configtitle'] = 'Block title';
$string['leaveblanktohide'] = 'leave blank to hide the title';
+1
View File
@@ -40,6 +40,7 @@ class block_tags extends block_base {
global $CFG, $COURSE, $SITE, $USER, $SCRIPT, $OUTPUT;
if (empty($CFG->usetags)) {
$this->content = new stdClass();
$this->content->text = '';
if ($this->page->user_is_editing()) {
$this->content->text = get_string('disabledtags', 'block_tags');
+5 -1
View File
@@ -158,7 +158,11 @@ class completion_completion extends data_object {
$this->timecompleted = $timecomplete;
// Save record
return $this->_save();
if ($result = $this->_save()) {
events_trigger('course_completed', $this->get_record_data());
}
return $result;
}
/**
+3 -1
View File
@@ -211,10 +211,12 @@ $CFG->admin = 'admin';
// You can specify a different class to be created for the $PAGE global, and to
// compute which blocks appear on each page. However, I cannot think of any good
// reason why you would need to change that. It just felt wrong to hard-code the
// the class name. You are stronly advised not to use these to settings unless
// the class name. You are strongly advised not to use these to settings unless
// you are absolutely sure you know what you are doing.
// $CFG->moodlepageclass = 'moodle_page';
// $CFG->moodlepageclassfile = "$CFG->dirroot/local/myplugin/mypageclass.php";
// $CFG->blockmanagerclass = 'block_manager';
// $CFG->blockmanagerclassfile = "$CFG->dirroot/local/myplugin/myblockamanagerclass.php";
//
// Seconds for files to remain in caches. Decrease this if you are worried
// about students being served outdated versions of uploaded files.
+1 -1
View File
@@ -124,7 +124,7 @@ class course_edit_form extends moodleform {
array(COURSE_DISPLAY_SINGLEPAGE => get_string('coursedisplay_single'),
COURSE_DISPLAY_MULTIPAGE => get_string('coursedisplay_multi')));
$mform->addHelpButton('coursedisplay', 'coursedisplay');
$mform->setDefault('coursedisplay', COURSE_DISPLAY_SINGLEPAGE);
$mform->setDefault('coursedisplay', $courseconfig->coursedisplay);
for ($i = 0; $i <= $courseconfig->maxsections; $i++) {
$sectionmenu[$i] = "$i";
+8 -5
View File
@@ -235,7 +235,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
}
}
if (!$onsectionpage && has_capability('moodle/course:update', $coursecontext)) {
if (!$onsectionpage && has_capability('moodle/course:movesections', $coursecontext)) {
$url = clone($baseurl);
if ($section->section > 1) { // Add a arrow to move section up.
$url->param('section', $section->section);
@@ -291,8 +291,11 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
$o .= html_writer::tag('div', '', array('class' => 'right side'));
$o .= html_writer::start_tag('div', array('class' => 'content'));
$title = html_writer::tag('a', get_section_name($course, $section),
array('href' => course_get_url($course, $section->section), 'class' => $linkclasses));
$title = get_section_name($course, $section);
if ($section->uservisible) {
$title = html_writer::tag('a', $title,
array('href' => course_get_url($course, $section->section), 'class' => $linkclasses));
}
$o .= $this->output->heading($title, 3, 'section-title');
$o.= html_writer::start_tag('div', array('class' => 'summarytext'));
@@ -448,7 +451,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
$links = array('previous' => '', 'next' => '');
$back = $sectionno - 1;
while ($back > 0 and empty($links['previous'])) {
if ($canviewhidden || $sections[$back]->visible) {
if ($canviewhidden || $sections[$back]->uservisible) {
$params = array();
if (!$sections[$back]->visible) {
$params = array('class' => 'dimmed_text');
@@ -462,7 +465,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
$forward = $sectionno + 1;
while ($forward <= $course->numsections and empty($links['next'])) {
if ($canviewhidden || $sections[$forward]->visible) {
if ($canviewhidden || $sections[$forward]->uservisible) {
$params = array();
if (!$sections[$forward]->visible) {
$params = array('class' => 'dimmed_text');
-3
View File
@@ -47,9 +47,6 @@ define('FIRSTUSEDEXCELROW', 3);
define('MOD_CLASS_ACTIVITY', 0);
define('MOD_CLASS_RESOURCE', 1);
define('COURSE_DISPLAY_SINGLEPAGE', 0); // display all sections on one page
define('COURSE_DISPLAY_MULTIPAGE', 1); // split pages into a page per section
function make_log_url($module, $url) {
switch ($module) {
case 'course':
-2
View File
@@ -101,8 +101,6 @@ class recent_form extends moodleform {
$mform->setAdvanced('user');
}
$sectiontitle = get_string('sectionname', 'format_'.$COURSE->format);
$options = array(''=>get_string('allactivities'));
$modsused = array();
+1 -1
View File
@@ -19,7 +19,7 @@ class course_reset_form extends moodleform {
$mform->addElement('checkbox', 'reset_logs', get_string('deletelogs'));
$mform->addElement('checkbox', 'reset_notes', get_string('deletenotes', 'notes'));
$mform->addElement('checkbox', 'reset_comments', get_string('deleteallcomments', 'moodle'));
$mform->addElement('checkbox', 'reset_course_completion', get_string('deletecoursecompletiondata', 'completion'));
$mform->addElement('checkbox', 'reset_completion', get_string('deletecompletiondata', 'completion'));
$mform->addElement('checkbox', 'delete_blog_associations', get_string('deleteblogassociations', 'blog'));
$mform->addHelpButton('delete_blog_associations', 'deleteblogassociations', 'blog');
+1 -1
View File
@@ -88,7 +88,7 @@ switch($requestmethod) {
break;
case 'move':
require_capability('moodle/course:update', $coursecontext);
require_capability('moodle/course:movesections', $coursecontext);
move_section_to($course, $id, $value);
// See if format wants to do something about it
$libfile = $CFG->dirroot.'/course/format/'.$course->format.'/lib.php';
+1 -1
View File
@@ -315,7 +315,7 @@ class core_course_external_testcase extends externallib_advanced_testcase {
$course2['format'] = 'weeks';
$course2['showgrades'] = 1;
$course2['newsitems'] = 3;
$course2['startdate'] = 32882306400; // 01/01/3012
$course2['startdate'] = 1420092000; // 01/01/2015
$course2['numsections'] = 4;
$course2['maxbytes'] = 100000;
$course2['showreports'] = 1;
+1 -1
View File
@@ -176,7 +176,7 @@
if (has_capability('moodle/course:update', $context)) {
if (!empty($section)) {
if (!empty($move) and confirm_sesskey()) {
if (!empty($move) and has_capability('moodle/course:movesections', $context) and confirm_sesskey()) {
$destsection = $section + $move;
if (move_section_to($course, $section, $destsection)) {
// Rebuild course cache, after moving section
+31 -7
View File
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -19,15 +18,14 @@
* CLI sync for full category enrol synchronisation.
*
* Sample execution:
* $sudo -u www-data /usr/bin/php /var/www/moodle/enrol/category/cli/sync.php
* $ sudo -u www-data /usr/bin/php /var/www/moodle/enrol/category/cli/sync.php
*
* Notes:
* - it is required to use the web server account when executing PHP CLI scripts
* - you need to change the "www-data" to match the apache user account
* - use "su" if "sudo" not available
*
* @package enrol
* @subpackage category
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
@@ -36,9 +34,35 @@ define('CLI_SCRIPT', true);
require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php');
require_once("$CFG->dirroot/enrol/category/locallib.php");
require_once("$CFG->libdir/clilib.php");
if (!enrol_is_enabled('category')) {
die('enrol_category plugin is disabled, sync is disabled');
// Now get cli options.
list($options, $unrecognized) = cli_get_params(array('verbose'=>false, 'help'=>false), array('v'=>'verbose', 'h'=>'help'));
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
enrol_category_sync_full();
if ($options['help']) {
$help =
"Execute course category enrolment sync.
Options:
-v, --verbose Print verbose progess information
-h, --help Print out this help
Example:
\$ sudo -u www-data /usr/bin/php enrol/category/cli/sync.php
";
echo $help;
die;
}
if (!enrol_is_enabled('category')) {
cli_error('enrol_category plugin is disabled, synchronisation stopped', 2);
}
$verbose = !empty($options['verbose']);
return enrol_category_sync_full($verbose);
+2 -2
View File
@@ -25,9 +25,9 @@
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
// marks roles that have category role assignments synchronised to course enrolments
// Marks roles that have category role assignments synchronised to course enrolments
// overrides below system context are ignored (for performance reasons).
// by default his is not allowed in new installs, admins have to explicitly allow category enrolments
// By default his is not allowed in new installs, admins have to explicitly allow category enrolments.
'enrol/category:synchronised' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_SYSTEM,
+3 -3
View File
@@ -17,10 +17,10 @@
/**
* Category enrolment plugin event handler definition.
*
* @package enrol_category
* @category event
* @package enrol_category
* @category event
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+1 -3
View File
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -18,8 +17,7 @@
/**
* category enrolment plugin installation.
*
* @package enrol
* @subpackage category
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+2 -4
View File
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -16,10 +15,9 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'enrol_category', language 'en', branch 'MOODLE_20_STABLE'
* Strings for component 'enrol_category', language 'en'.
*
* @package enrol
* @subpackage category
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+11 -14
View File
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -18,25 +17,25 @@
/**
* Category enrolment plugin.
*
* @package enrol
* @subpackage category
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* category enrolment plugin implementation.
* @author Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol_category_plugin extends enrol_plugin {
/**
* Is it possible to delete enrol instance via standard UI?
*
* @param object $instance
* @param stdClass $instance
* @return bool
*/
public function instance_deleteable($instance) {
@@ -45,7 +44,7 @@ class enrol_category_plugin extends enrol_plugin {
if (!enrol_is_enabled('category')) {
return true;
}
// allow delete only when no synced users here
// Allow delete only when no synced users here.
return !$DB->record_exists('user_enrolments', array('enrolid'=>$instance->id));
}
@@ -55,8 +54,8 @@ class enrol_category_plugin extends enrol_plugin {
* @return moodle_url page url
*/
public function get_newinstance_link($courseid) {
// instances are added automatically as necessary
return NULL;
// Instances are added automatically as necessary.
return null;
}
/**
@@ -78,8 +77,8 @@ class enrol_category_plugin extends enrol_plugin {
* Called after updating/inserting course.
*
* @param bool $inserted true if course just inserted
* @param object $course
* @param object $data form data
* @param stdClass $course
* @param stdClass $data form data
* @return void
*/
public function course_updated($inserted, $course, $data) {
@@ -89,10 +88,8 @@ class enrol_category_plugin extends enrol_plugin {
return;
}
// sync category enrols
// Sync category enrols.
require_once("$CFG->dirroot/enrol/category/locallib.php");
enrol_category_sync_course($course);
}
}
+83 -40
View File
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -18,14 +17,14 @@
/**
* Local stuff for category enrolment plugin.
*
* @package enrol
* @subpackage category
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Event handler for category enrolment plugin.
*
@@ -33,6 +32,12 @@ defined('MOODLE_INTERNAL') || die();
* it may fail sometimes, so we always do a full sync in cron too.
*/
class enrol_category_handler {
/**
* Triggered when user is assigned a new role.
* @static
* @param stdClass $ra
* @return bool
*/
public static function role_assigned($ra) {
global $DB;
@@ -40,20 +45,20 @@ class enrol_category_handler {
return true;
}
//only category level roles are interesting
// Only category level roles are interesting.
$parentcontext = get_context_instance_by_id($ra->contextid);
if ($parentcontext->contextlevel != CONTEXT_COURSECAT) {
return true;
}
// make sure the role is to be actually synchronised
// please note we are ignoring overrides of the synchronised capability (for performance reasons in full sync)
// Make sure the role is to be actually synchronised,
// please note we are ignoring overrides of the synchronised capability (for performance reasons in full sync).
$syscontext = context_system::instance();
if (!$DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$ra->roleid, 'capability'=>'enrol/category:synchronised', 'permission'=>CAP_ALLOW))) {
return true;
}
// add necessary enrol instances
// Add necessary enrol instances.
$plugin = enrol_get_plugin('category');
$sql = "SELECT c.*
FROM {course} c
@@ -67,7 +72,7 @@ class enrol_category_handler {
}
$rs->close();
// now look for missing enrols
// Now look for missing enrolments.
$sql = "SELECT e.*
FROM {course} c
JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel AND ctx.path LIKE :match)
@@ -84,6 +89,12 @@ class enrol_category_handler {
return true;
}
/**
* Triggered when user role is unassigned.
* @static
* @param stdClass $ra
* @return bool
*/
public static function role_unassigned($ra) {
global $DB;
@@ -91,13 +102,13 @@ class enrol_category_handler {
return true;
}
// only category level roles are interesting
// Only category level roles are interesting.
$parentcontext = get_context_instance_by_id($ra->contextid);
if ($parentcontext->contextlevel != CONTEXT_COURSECAT) {
return true;
}
// now this is going to be a bit slow, take all enrolments in child courses and verify each separately
// Now this is going to be a bit slow, take all enrolments in child courses and verify each separately.
$syscontext = context_system::instance();
if (!$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext)) {
return true;
@@ -119,7 +130,7 @@ class enrol_category_handler {
foreach ($rs as $instance) {
$coursecontext = context_course::instance($instance->courseid);
$contextids = get_parent_contexts($coursecontext);
array_pop($contextids); // remove system context, we are interested in categories only
array_pop($contextids); // Remove system context, we are interested in categories only.
list($contextids, $contextparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'c');
$params = array_merge($params, $contextparams);
@@ -128,7 +139,7 @@ class enrol_category_handler {
FROM {role_assignments} ra
WHERE ra.userid = :userid AND ra.contextid $contextids AND ra.roleid $roleids";
if (!$DB->record_exists_sql($sql, $params)) {
// user does not have any interesting role in any parent context, let's unenrol
// User does not have any interesting role in any parent context, let's unenrol.
$plugin->unenrol_user($instance, $ra->userid);
}
}
@@ -140,7 +151,7 @@ class enrol_category_handler {
/**
* Sync all category enrolments in one course
* @param int $courseid course id
* @param stdClass $course
* @return void
*/
function enrol_category_sync_course($course) {
@@ -156,7 +167,7 @@ function enrol_category_sync_course($course) {
$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext);
if (!$roles) {
//nothing to sync, so remove the instance completely if exists
// Nothing to sync, so remove the instance completely if exists.
if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) {
foreach ($instances as $instance) {
$plugin->delete_instance($instance);
@@ -165,10 +176,10 @@ function enrol_category_sync_course($course) {
return;
}
// first find out if any parent category context contains interesting role assignments
// First find out if any parent category context contains interesting role assignments.
$coursecontext = context_course::instance($course->id);
$contextids = get_parent_contexts($coursecontext);
array_pop($contextids); // remove system context, we are interested in categories only
array_pop($contextids); // Remove system context, we are interested in categories only.
list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r');
list($contextids, $contextparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'c');
@@ -180,7 +191,7 @@ function enrol_category_sync_course($course) {
WHERE roleid $roleids AND contextid $contextids";
if (!$DB->record_exists_sql($sql, $params)) {
if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) {
// should be max one instance, but anyway
// Should be max one instance, but anyway.
foreach ($instances as $instance) {
$plugin->delete_instance($instance);
}
@@ -188,7 +199,7 @@ function enrol_category_sync_course($course) {
return;
}
// make sure the enrol instance exists - there should be always only one instance
// Make sure the enrol instance exists - there should be always only one instance.
$delinstances = array();
if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) {
$instance = array_shift($instances);
@@ -198,7 +209,7 @@ function enrol_category_sync_course($course) {
$instance = $DB->get_record('enrol', array('id'=>$i));
}
// add new enrolments
// Add new enrolments.
$sql = "SELECT ra.userid, ra.estart
FROM (SELECT xra.userid, MIN(xra.timemodified) AS estart
FROM {role_assignments} xra
@@ -214,7 +225,7 @@ function enrol_category_sync_course($course) {
}
$rs->close();
// remove unwanted enrolments
// Remove unwanted enrolments.
$sql = "SELECT DISTINCT ue.userid
FROM {user_enrolments} ue
LEFT JOIN {role_assignments} ra ON (ra.roleid $roleids AND ra.contextid $contextids AND ra.userid = ue.userid)
@@ -226,46 +237,68 @@ function enrol_category_sync_course($course) {
$rs->close();
if ($delinstances) {
// we have to do this as the last step in order to prevent temporary unenrolment
// We have to do this as the last step in order to prevent temporary unenrolment.
foreach ($delinstances as $delinstance) {
$plugin->delete_instance($delinstance);
}
}
}
function enrol_category_sync_full() {
/**
* Synchronise courses in all categories.
*
* It gets out-of-sync if:
* - you move course to different category
* - reorder categories
* - disable enrol_category and enable it again
*
* @param bool $verbose
* @return int exit code - 0 is ok, 1 means error, 2 if plugin disabled
*/
function enrol_category_sync_full($verbose = false) {
global $DB;
if (!enrol_is_enabled('category')) {
return;
return 2;
}
// we may need a lot of time here
// We may need a lot of time here.
@set_time_limit(0);
$plugin = enrol_get_plugin('category');
$syscontext = context_system::instance();
// any interesting roles worth synchronising?
// Any interesting roles worth synchronising?
if (!$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext)) {
// yay, nothing to do, so let's remove all leftovers
if ($verbose) {
mtrace("No roles with 'enrol/category:synchronised' capability found.");
}
if ($instances = $DB->get_records('enrol', array('enrol'=>'category'))) {
foreach ($instances as $instance) {
if ($verbose) {
mtrace(" deleting category enrol instance from course {$instance->courseid}");
}
$plugin->delete_instance($instance);
}
}
return;
return 0;
}
$rolenames = role_fix_names($roles, null, ROLENAME_SHORT, true);
if ($verbose) {
mtrace('Synchronising category enrolments for roles: '.implode(', ', $rolenames).'...');
}
list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r');
$params['courselevel'] = CONTEXT_COURSE;
$params['catlevel'] = CONTEXT_COURSECAT;
// first of all add necessary enrol instances to all courses
// First of all add necessary enrol instances to all courses.
$parentcat = $DB->sql_concat("cat.path", "'/%'");
// need whole course records to be used by add_instance(), use inner view (ci) to
$parentcctx = $DB->sql_concat("cctx.path", "'/%'");
// Need whole course records to be used by add_instance(), use inner view (ci) to
// get distinct records only.
// TODO: Moodle 2.1. Improve enrol API to accept courseid / courserec
$sql = "SELECT c.*
@@ -288,17 +321,16 @@ function enrol_category_sync_full() {
}
$rs->close();
// now look for courses that do not have any interesting roles in parent contexts,
// but still have the instance and delete them
// Now look for courses that do not have any interesting roles in parent contexts,
// but still have the instance and delete them.
$sql = "SELECT e.*
FROM {enrol} e
JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)
LEFT JOIN (SELECT DISTINCT cctx.path
FROM {course_categories} cc
LEFT JOIN ({course_categories} cc
JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
) cat ON (ctx.path LIKE $parentcat)
WHERE e.enrol = 'category' AND cat.path IS NULL";
) ON (ctx.path LIKE $parentcctx)
WHERE e.enrol = 'category' AND cc.id IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $instance) {
@@ -306,7 +338,7 @@ function enrol_category_sync_full() {
}
$rs->close();
// add missing enrolments
// Add missing enrolments.
$sql = "SELECT e.*, cat.userid, cat.estart
FROM {enrol} e
JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)
@@ -325,25 +357,36 @@ function enrol_category_sync_full() {
unset($instance->userid);
unset($instance->estart);
$plugin->enrol_user($instance, $userid, null, $estart);
if ($verbose) {
mtrace(" enrolling: user $userid ==> course $instance->courseid");
}
}
$rs->close();
// remove stale enrolments
// Remove stale enrolments.
$sql = "SELECT e.*, ue.userid
FROM {enrol} e
JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)
JOIN {user_enrolments} ue ON (ue.enrolid = e.id)
LEFT JOIN (SELECT DISTINCT cctx.path, ra.userid
FROM {course_categories} cc
LEFT JOIN ({course_categories} cc
JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
) cat ON (ctx.path LIKE $parentcat AND cat.userid = ue.userid)
WHERE e.enrol = 'category' AND cat.userid IS NULL";
) ON (ctx.path LIKE $parentcctx AND ra.userid = ue.userid)
WHERE e.enrol = 'category' AND cc.id IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $instance) {
$userid = $instance->userid;
unset($instance->userid);
$plugin->unenrol_user($instance, $userid);
if ($verbose) {
mtrace(" unenrolling: user $userid ==> course $instance->courseid");
}
}
$rs->close();
if ($verbose) {
mtrace('...user enrolment synchronisation finished.');
}
return 0;
}
+2 -5
View File
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -16,10 +15,9 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* category enrolment plugin settings and presets.
* Category enrolment plugin settings and presets.
*
* @package enrol
* @subpackage category
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
@@ -35,4 +33,3 @@ if ($ADMIN->fulltree) {
//--- enrol instance defaults ----------------------------------------------------------------------------
}
+365
View File
@@ -0,0 +1,365 @@
<?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/>.
/**
* Category enrolment sync functional test.
*
* @package enrol_category
* @category phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot.'/enrol/category/locallib.php');
class enrol_category_testcase extends advanced_testcase {
protected function enable_plugin() {
$enabled = enrol_get_plugins(true);
$enabled['category'] = true;
$enabled = array_keys($enabled);
set_config('enrol_plugins_enabled', implode(',', $enabled));
}
protected function disable_plugin() {
$enabled = enrol_get_plugins(true);
unset($enabled['category']);
$enabled = array_keys($enabled);
set_config('enrol_plugins_enabled', implode(',', $enabled));
}
protected function enable_role_sync($roleid) {
global $DB;
$syscontext = context_system::instance();
if ($rc = $DB->record_exists('role_capabilities', array('capability'=>'enrol/category:synchronised', 'roleid'=>$roleid, 'contextid'=>$syscontext->id))) {
if ($rc->permission != CAP_ALLOW) {
$rc->permission = CAP_ALLOW;
$DB->update_record('role_capabilities', $rc);
}
} else {
$rc = new stdClass();
$rc->capability = 'enrol/category:synchronised';
$rc->roleid = $roleid;
$rc->contextid = $syscontext->id;
$rc->permission = CAP_ALLOW;
$rc->timemodified = time();
$rc->modifierid = 0;
$DB->insert_record('role_capabilities', $rc);
}
}
protected function disable_role_sync($roleid) {
global $DB;
$syscontext = context_system::instance();
$DB->delete_records('role_capabilities', array('capability'=>'enrol/category:synchronised', 'roleid'=>$roleid, 'contextid'=>$syscontext->id));
}
/**
* Test utility methods used in syn test, fail here means something
* in core accesslib was changed, but it is possible that only this test
* is affected, nto the plugin itself...
*/
public function test_utils() {
global $DB;
$this->resetAfterTest();
$syscontext = context_system::instance();
$this->assertFalse(enrol_is_enabled('category'));
$this->enable_plugin();
$this->assertTrue(enrol_is_enabled('category'));
$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext);
$this->assertEmpty($roles);
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$this->enable_role_sync($studentrole->id);
$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext);
$this->assertEquals(1, count($roles));
$this->assertEquals($studentrole, reset($roles));
$this->disable_role_sync($studentrole->id);
$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext);
$this->assertEmpty($roles);
}
public function test_handler_sync() {
global $DB;
$this->resetAfterTest();
// Setup a few courses and categories.
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
$this->assertNotEmpty($teacherrole);
$managerrole = $DB->get_record('role', array('shortname'=>'manager'));
$this->assertNotEmpty($managerrole);
$cat1 = $this->getDataGenerator()->create_category();
$cat2 = $this->getDataGenerator()->create_category();
$cat3 = $this->getDataGenerator()->create_category(array('parent'=>$cat2->id));
$course1 = $this->getDataGenerator()->create_course(array('category'=>$cat1->id));
$course2 = $this->getDataGenerator()->create_course(array('category'=>$cat2->id));
$course3 = $this->getDataGenerator()->create_course(array('category'=>$cat3->id));
$course4 = $this->getDataGenerator()->create_course(array('category'=>$cat3->id));
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
$this->enable_role_sync($studentrole->id);
$this->enable_role_sync($teacherrole->id);
$this->enable_plugin();
$this->assertEquals(0, $DB->count_records('role_assignments', array()));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
// Test assign event.
role_assign($managerrole->id, $user1->id, context_coursecat::instance($cat1->id));
role_assign($managerrole->id, $user3->id, context_course::instance($course1->id));
role_assign($managerrole->id, $user3->id, context_course::instance($course2->id));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
role_assign($studentrole->id, $user1->id, context_coursecat::instance($cat2->id));
$this->assertTrue(is_enrolled(context_course::instance($course2->id), $user1->id));
$this->assertTrue(is_enrolled(context_course::instance($course3->id), $user1->id));
$this->assertTrue(is_enrolled(context_course::instance($course4->id), $user1->id));
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
role_assign($managerrole->id, $user2->id, context_coursecat::instance($cat3->id));
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
role_assign($teacherrole->id, $user4->id, context_coursecat::instance($cat1->id));
$this->assertTrue(is_enrolled(context_course::instance($course1->id), $user4->id));
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
// Test role unassigned event.
role_unassign($teacherrole->id, $user4->id, context_coursecat::instance($cat1->id)->id);
$this->assertFalse(is_enrolled(context_course::instance($course1->id), $user4->id));
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
// Make sure handlers are disabled when plugin disabled.
$this->disable_plugin();
role_unassign($studentrole->id, $user1->id, context_coursecat::instance($cat2->id)->id);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
role_assign($studentrole->id, $user3->id, context_coursecat::instance($cat1->id));
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
}
public function test_sync_course() {
global $DB;
$this->resetAfterTest();
// Setup a few courses and categories.
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
$this->assertNotEmpty($teacherrole);
$managerrole = $DB->get_record('role', array('shortname'=>'manager'));
$this->assertNotEmpty($managerrole);
$cat1 = $this->getDataGenerator()->create_category();
$cat2 = $this->getDataGenerator()->create_category();
$cat3 = $this->getDataGenerator()->create_category(array('parent'=>$cat2->id));
$course1 = $this->getDataGenerator()->create_course(array('category'=>$cat1->id));
$course2 = $this->getDataGenerator()->create_course(array('category'=>$cat2->id));
$course3 = $this->getDataGenerator()->create_course(array('category'=>$cat3->id));
$course4 = $this->getDataGenerator()->create_course(array('category'=>$cat3->id));
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
$this->enable_role_sync($studentrole->id);
$this->enable_role_sync($teacherrole->id);
$this->enable_plugin();
$this->assertEquals(0, $DB->count_records('role_assignments', array()));
role_assign($managerrole->id, $user1->id, context_coursecat::instance($cat1->id));
role_assign($managerrole->id, $user3->id, context_course::instance($course1->id));
role_assign($managerrole->id, $user3->id, context_course::instance($course2->id));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->disable_plugin(); // Stops the event handlers.
role_assign($studentrole->id, $user1->id, context_coursecat::instance($cat2->id));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->enable_plugin();
enrol_category_sync_course($course2);
$this->assertTrue(is_enrolled(context_course::instance($course2->id), $user1->id));
$this->assertFalse(is_enrolled(context_course::instance($course3->id), $user1->id));
$this->assertFalse(is_enrolled(context_course::instance($course4->id), $user1->id));
$this->assertEquals(1, $DB->count_records('user_enrolments', array()));
enrol_category_sync_course($course2);
enrol_category_sync_course($course3);
enrol_category_sync_course($course4);
$this->assertFalse(is_enrolled(context_course::instance($course1->id), $user1->id));
$this->assertTrue(is_enrolled(context_course::instance($course2->id), $user1->id));
$this->assertTrue(is_enrolled(context_course::instance($course3->id), $user1->id));
$this->assertTrue(is_enrolled(context_course::instance($course4->id), $user1->id));
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->disable_plugin(); // Stops the event handlers.
role_assign($studentrole->id, $user2->id, context_coursecat::instance($cat1->id));
role_assign($teacherrole->id, $user4->id, context_coursecat::instance($cat1->id));
role_unassign($studentrole->id, $user1->id, context_coursecat::instance($cat2->id)->id);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->enable_plugin();
enrol_category_sync_course($course2);
$this->assertFalse(is_enrolled(context_course::instance($course2->id), $user1->id));
$this->assertFalse(is_enrolled(context_course::instance($course2->id), $user2->id));
$this->assertFalse(is_enrolled(context_course::instance($course2->id), $user4->id));
enrol_category_sync_course($course1);
enrol_category_sync_course($course3);
enrol_category_sync_course($course4);
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$this->assertTrue(is_enrolled(context_course::instance($course1->id), $user2->id));
$this->assertTrue(is_enrolled(context_course::instance($course1->id), $user4->id));
$this->disable_role_sync($studentrole->id);
enrol_category_sync_course($course1);
enrol_category_sync_course($course2);
enrol_category_sync_course($course3);
enrol_category_sync_course($course4);
$this->assertEquals(1, $DB->count_records('user_enrolments', array()));
$this->assertTrue(is_enrolled(context_course::instance($course1->id), $user4->id));
$this->assertEquals(1, $DB->count_records('enrol', array('enrol'=>'category')));
$this->disable_role_sync($teacherrole->id);
enrol_category_sync_course($course1);
enrol_category_sync_course($course2);
enrol_category_sync_course($course3);
enrol_category_sync_course($course4);
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->assertEquals(0, $DB->count_records('enrol', array('enrol'=>'category')));
}
public function test_sync_full() {
global $DB;
$this->resetAfterTest();
// Setup a few courses and categories.
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
$this->assertNotEmpty($teacherrole);
$managerrole = $DB->get_record('role', array('shortname'=>'manager'));
$this->assertNotEmpty($managerrole);
$cat1 = $this->getDataGenerator()->create_category();
$cat2 = $this->getDataGenerator()->create_category();
$cat3 = $this->getDataGenerator()->create_category(array('parent'=>$cat2->id));
$course1 = $this->getDataGenerator()->create_course(array('category'=>$cat1->id));
$course2 = $this->getDataGenerator()->create_course(array('category'=>$cat2->id));
$course3 = $this->getDataGenerator()->create_course(array('category'=>$cat3->id));
$course4 = $this->getDataGenerator()->create_course(array('category'=>$cat3->id));
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
$this->enable_role_sync($studentrole->id);
$this->enable_role_sync($teacherrole->id);
$this->enable_plugin();
$this->assertEquals(0, $DB->count_records('role_assignments', array()));
role_assign($managerrole->id, $user1->id, context_coursecat::instance($cat1->id));
role_assign($managerrole->id, $user3->id, context_course::instance($course1->id));
role_assign($managerrole->id, $user3->id, context_course::instance($course2->id));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$result = enrol_category_sync_full();
$this->assertSame(0, $result);
$this->disable_plugin();
role_assign($studentrole->id, $user1->id, context_coursecat::instance($cat2->id));
$this->enable_plugin();
$result = enrol_category_sync_full();
$this->assertSame(0, $result);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertTrue(is_enrolled(context_course::instance($course2->id), $user1->id));
$this->assertTrue(is_enrolled(context_course::instance($course3->id), $user1->id));
$this->assertTrue(is_enrolled(context_course::instance($course4->id), $user1->id));
$this->disable_plugin();
role_unassign($studentrole->id, $user1->id, context_coursecat::instance($cat2->id)->id);
role_assign($studentrole->id, $user2->id, context_coursecat::instance($cat1->id));
role_assign($teacherrole->id, $user4->id, context_coursecat::instance($cat1->id));
role_assign($teacherrole->id, $user3->id, context_coursecat::instance($cat2->id));
role_assign($managerrole->id, $user3->id, context_course::instance($course3->id));
$this->enable_plugin();
$result = enrol_category_sync_full();
$this->assertSame(0, $result);
$this->assertEquals(5, $DB->count_records('user_enrolments', array()));
$this->assertTrue(is_enrolled(context_course::instance($course1->id), $user2->id));
$this->assertTrue(is_enrolled(context_course::instance($course1->id), $user4->id));
$this->assertTrue(is_enrolled(context_course::instance($course2->id), $user3->id));
$this->assertTrue(is_enrolled(context_course::instance($course3->id), $user3->id));
$this->assertTrue(is_enrolled(context_course::instance($course4->id), $user3->id));
// Cleanup everything.
$this->assertNotEmpty($DB->count_records('role_assignments', array()));
$this->assertNotEmpty($DB->count_records('user_enrolments', array()));
$this->disable_plugin();
role_unassign_all(array('roleid'=>$studentrole->id));
role_unassign_all(array('roleid'=>$managerrole->id));
role_unassign_all(array('roleid'=>$teacherrole->id));
$result = enrol_category_sync_full();
$this->assertSame(2, $result);
$this->assertEquals(0, $DB->count_records('role_assignments', array()));
$this->assertNotEmpty($DB->count_records('user_enrolments', array()));
$this->disable_role_sync($studentrole->id);
$this->disable_role_sync($teacherrole->id);
$this->enable_plugin();
$result = enrol_category_sync_full();
$this->assertSame(0, $result);
$this->assertEquals(0, $DB->count_records('role_assignments', array()));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->assertEquals(0, $DB->count_records('enrol', array('enrol'=>'category')));
}
}
+4 -5
View File
@@ -17,15 +17,14 @@
/**
* Category enrolment plugin version specification.
*
* @package enrol
* @subpackage category
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2012061700; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2012061700; // Requires this Moodle version
$plugin->version = 2012081800; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2012062501; // Requires this Moodle version
$plugin->component = 'enrol_category'; // Full name of the plugin (used for diagnostics)
$plugin->cron = 60;
$plugin->cron = 60;
+1 -10
View File
@@ -121,16 +121,7 @@ class enrol_cohort_plugin extends enrol_plugin {
* @return void
*/
public function course_updated($inserted, $course, $data) {
global $CFG;
if (!$inserted) {
// sync cohort enrols
require_once("$CFG->dirroot/enrol/cohort/locallib.php");
enrol_cohort_sync($course->id);
} else {
// cohorts are never inserted automatically
}
// It turns out there is no need for cohorts to deal with this hook, see MDL-34870.
}
/**
+5 -9
View File
@@ -278,7 +278,7 @@ class course_enrolment_manager {
global $DB, $CFG;
// Add some additional sensible conditions
$tests = array("id <> :guestid", 'u.deleted = 0', 'u.confirmed = 1');
$tests = array("u.id <> :guestid", 'u.deleted = 0', 'u.confirmed = 1');
$params = array('guestid' => $CFG->siteguest);
if (!empty($search)) {
$conditions = get_extra_user_fields($this->get_context());
@@ -306,10 +306,9 @@ class course_enrolment_manager {
$fields = 'SELECT '.$ufields;
$countfields = 'SELECT COUNT(1)';
$sql = " FROM {user} u
LEFT JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid)
WHERE $wherecondition
AND u.id NOT IN (SELECT ue.userid
FROM {user_enrolments} ue
JOIN {enrol} e ON (e.id = ue.enrolid AND e.id = :enrolid))";
AND ue.id IS NULL";
$order = ' ORDER BY u.lastname ASC, u.firstname ASC';
$params['enrolid'] = $enrolid;
$totalusers = $DB->count_records_sql($countfields . $sql, $params);
@@ -353,12 +352,9 @@ class course_enrolment_manager {
$fields = 'SELECT '.user_picture::fields('u', array('username','lastaccess'));
$countfields = 'SELECT COUNT(u.id)';
$sql = " FROM {user} u
LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.contextid = :contextid)
WHERE $wherecondition
AND u.id NOT IN (
SELECT u.id
FROM {role_assignments} r, {user} u
WHERE r.contextid = :contextid AND
u.id = r.userid)";
AND ra.id IS NULL";
$order = ' ORDER BY lastname ASC, firstname ASC';
$params['contextid'] = $this->context->id;
+1 -1
View File
@@ -30,7 +30,7 @@ $string['altertimestart'] = 'Alter start time';
$string['assignrole'] = 'Assign role';
$string['confirmbulkdeleteenrolment'] = 'Are you sure you want to delete these users enrolments?';
$string['defaultperiod'] = 'Default enrolment duration';
$string['defaultperiod_desc'] = 'Default length of time that the enrolment is valid (in seconds). If set to zero, the enrolment duration will be unlimited by default.';
$string['defaultperiod_desc'] = 'Default length of time that the enrolment is valid. If set to zero, the enrolment duration will be unlimited by default.';
$string['defaultperiod_help'] = 'Default length of time that the enrolment is valid, starting with the moment the user is enrolled. If disabled, the enrolment duration will be unlimited by default.';
$string['deleteselectedusers'] = 'Delete selected user enrolments';
$string['editenrolment'] = 'Edit enrolment';
+3 -5
View File
@@ -55,11 +55,9 @@ class enrol_manual_potential_participant extends user_selector_base {
$countfields = 'SELECT COUNT(1)';
$sql = " FROM {user} u
WHERE $wherecondition AND
u.id NOT IN (
SELECT ue.userid
FROM {user_enrolments} ue
JOIN {enrol} e ON (e.id = ue.enrolid AND e.id = :enrolid))";
LEFT JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid)
WHERE $wherecondition
AND ue.id IS NULL";
$order = ' ORDER BY u.lastname ASC, u.firstname ASC';
if (!$this->is_validating()) {
+2 -2
View File
@@ -44,8 +44,8 @@ if ($ADMIN->fulltree) {
$settings->add(new admin_setting_configselect('enrol_manual/status',
get_string('status', 'enrol_manual'), get_string('status_desc', 'enrol_manual'), ENROL_INSTANCE_ENABLED, $options));
$settings->add(new admin_setting_configtext('enrol_manual/enrolperiod',
get_string('defaultperiod', 'enrol_manual'), get_string('defaultperiod_desc', 'enrol_manual'), 0, PARAM_INT));
$settings->add(new admin_setting_configduration('enrol_manual/enrolperiod',
get_string('defaultperiod', 'enrol_manual'), get_string('defaultperiod_desc', 'enrol_manual'), 0));
if (!during_initial_install()) {
$options = get_default_enrol_roles(context_system::instance());
+1 -1
View File
@@ -37,7 +37,7 @@ $string['enrolenddate'] = 'End date';
$string['enrolenddate_help'] = 'If enabled, users can be enrolled until this date only.';
$string['enrolenddaterror'] = 'Enrolment end date cannot be earlier than start date';
$string['enrolperiod'] = 'Enrolment duration';
$string['enrolperiod_desc'] = 'Default length of time that the enrolment is valid (in seconds). If set to zero, the enrolment duration will be unlimited by default.';
$string['enrolperiod_desc'] = 'Default length of time that the enrolment is valid. If set to zero, the enrolment duration will be unlimited by default.';
$string['enrolperiod_help'] = 'Length of time that the enrolment is valid, starting with the moment the user is enrolled. If disabled, the enrolment duration will be unlimited.';
$string['enrolstartdate'] = 'Start date';
$string['enrolstartdate_help'] = 'If enabled, users can be enrolled from this date onward only.';
+2 -2
View File
@@ -68,6 +68,6 @@ if ($ADMIN->fulltree) {
get_string('defaultrole', 'enrol_paypal'), get_string('defaultrole_desc', 'enrol_paypal'), $student->id, $options));
}
$settings->add(new admin_setting_configtext('enrol_paypal/enrolperiod',
get_string('enrolperiod', 'enrol_paypal'), get_string('enrolperiod_desc', 'enrol_paypal'), 0, PARAM_INT));
$settings->add(new admin_setting_configduration('enrol_paypal/enrolperiod',
get_string('enrolperiod', 'enrol_paypal'), get_string('enrolperiod_desc', 'enrol_paypal'), 0));
}
+1 -1
View File
@@ -39,7 +39,7 @@ $string['enrolenddate_help'] = 'If enabled, users can enrol themselves until thi
$string['enrolenddaterror'] = 'Enrolment end date cannot be earlier than start date';
$string['enrolme'] = 'Enrol me';
$string['enrolperiod'] = 'Enrolment duration';
$string['enrolperiod_desc'] = 'Default length of time that the enrolment is valid (in seconds). If set to zero, the enrolment duration will be unlimited by default.';
$string['enrolperiod_desc'] = 'Default length of time that the enrolment is valid. If set to zero, the enrolment duration will be unlimited by default.';
$string['enrolperiod_help'] = 'Length of time that the enrolment is valid, starting with the moment the user enrols themselves. If disabled, the enrolment duration will be unlimited.';
$string['enrolstartdate'] = 'Start date';
$string['enrolstartdate_help'] = 'If enabled, users can enrol themselves from this date onward only.';
+2 -2
View File
@@ -65,8 +65,8 @@ if ($ADMIN->fulltree) {
get_string('defaultrole', 'enrol_self'), get_string('defaultrole_desc', 'enrol_self'), $student->id, $options));
}
$settings->add(new admin_setting_configtext('enrol_self/enrolperiod',
get_string('enrolperiod', 'enrol_self'), get_string('enrolperiod_desc', 'enrol_self'), 0, PARAM_INT));
$settings->add(new admin_setting_configduration('enrol_self/enrolperiod',
get_string('enrolperiod', 'enrol_self'), get_string('enrolperiod_desc', 'enrol_self'), 0));
$options = array(0 => get_string('never'),
1800 * 3600 * 24 => get_string('numdays', '', 1800),
+1 -1
View File
@@ -30,6 +30,6 @@
defined('MOODLE_INTERNAL') || die();
$string['parentlanguage'] = 'es';
$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Español - Mexico';
+1 -1
View File
@@ -81,7 +81,7 @@ $string['phpversionhelp'] = '<p>Moodle需要PHP 4.3.0或5.1.05.0.x有若干
(如果正使用5.0.x,您也可以降级到4.4.x版)</p>
';
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
$string['welcomep20'] = '您看到这个页面表明您已经成功地在您的计算机上安装了<strong>{$a->packname} {$a->packversion}</strong>。恭喜您!';
$string['welcomep20'] = '您看到这个页面表明您已经成功地在您的计算机上安装并启用了<strong>{$a->packname} {$a->packversion}</strong>软件包。恭喜您!';
$string['welcomep30'] = '<strong>{$a->installername}</strong>的此发行版包含了可以创建<strong>Moodle</strong>运行环境的应用程序:';
$string['welcomep40'] = '这个软件包还包含了<strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>。';
$string['welcomep50'] = '使用本软件包中包含的应用程序时应遵循它们各自的授权协议。整个<strong>{$a->installername}</strong>软件包都是<a href="http://www.opensource.org/docs/definition_plain.html">开源</a>的,并且遵循<a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>授权协议发布。';
+10 -8
View File
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -20,8 +19,7 @@
*
* This script is not compatible with IPv6.
*
* @package core
* @subpackage iplookup
* @package core_iplookup
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
@@ -35,7 +33,7 @@ $ip = optional_param('ip', getremoteaddr(), PARAM_HOST);
$user = optional_param('user', 0, PARAM_INT);
if (isset($CFG->iplookup)) {
//clean up of old settings
// Clean up of old settings.
set_config('iplookup', NULL);
}
@@ -61,7 +59,7 @@ if ($match[1] == '127' or $match[1] == '10' or ($match[1] == '172' and $match[2]
$info = iplookup_find_location($ip);
if ($info['error']) {
// can not display
// Can not display.
notice($info['error']);
}
@@ -80,7 +78,7 @@ $PAGE->set_title(get_string('iplookup', 'admin').': '.$title);
$PAGE->set_heading($title);
echo $OUTPUT->header();
if (empty($CFG->googlemapkey)) {
if (empty($CFG->googlemapkey3)) {
$imgwidth = 620;
$imgheight = 310;
$dotwidth = 18;
@@ -96,9 +94,13 @@ if (empty($CFG->googlemapkey)) {
echo '<div id="note">'.$info['note'].'</div>';
} else {
$PAGE->requires->js(new moodle_url("http://maps.google.com/maps?file=api&v=2&key=$CFG->googlemapkey"));
if (strpos($CFG->wwwroot, 'https:') === 0) {
$PAGE->requires->js(new moodle_url('https://maps.googleapis.com/maps/api/js', array('key'=>$CFG->googlemapkey3, 'sensor'=>'false')));
} else {
$PAGE->requires->js(new moodle_url('http://maps.googleapis.com/maps/api/js', array('key'=>$CFG->googlemapkey3, 'sensor'=>'false')));
}
$module = array('name'=>'core_iplookup', 'fullpath'=>'/iplookup/module.js');
$PAGE->requires->js_init_call('M.core_iplookup.init', array($info['latitude'], $info['longitude']), true, $module);
$PAGE->requires->js_init_call('M.core_iplookup.init3', array($info['latitude'], $info['longitude'], $ip), true, $module);
echo '<div id="map" style="width: 650px; height: 360px"></div>';
echo '<div id="note">'.$info['note'].'</div>';
+16 -17
View File
@@ -16,28 +16,27 @@
/**
* Iplookup utility functions
*
* @package core
* @subpackage iplookup
* @package core_iplookup
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.core_iplookup = {};
M.core_iplookup.init = function(Y, latitude, longitude) {
if (GBrowserIsCompatible()) {
var map = new GMap2(document.getElementById("map"));
map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl());
var point = new GLatLng(latitude, longitude);
map.setCenter(point, 4);
map.addOverlay(new GMarker(point));
map.setMapType(G_HYBRID_MAP);
M.core_iplookup.init3 = function(Y, latitude, longitude, ip) {
var ipLatlng = new google.maps.LatLng(latitude, longitude);
Y.on('unload', function() {
if (GBrowserIsCompatible()) {
GUnload();
}
}, document.body);
}
var mapOptions = {
center: ipLatlng,
zoom: 6,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map"), mapOptions);
var marker = new google.maps.Marker({
position: ipLatlng,
map: map,
title: ip
});
};
+3 -3
View File
@@ -215,11 +215,10 @@ $string['configforcelogin'] = 'Normally, the front page of the site and the cour
$string['configforceloginforprofiles'] = 'This setting forces people to login as a real (non-guest) account before viewing any user\'s profile. If you disabled this setting, you may find that some users post advertising (spam) or other inappropriate content in their profiles, which is then visible to the whole world.';
$string['configfrontpage'] = 'The items selected above will be displayed on the site\'s front page.';
$string['configfrontpageloggedin'] = 'The items selected above will be displayed on the site\'s front page when a user is logged in.';
$string['configfullnamedisplay'] = 'This defines how names are shown when they are displayed in full. For most mono-lingual sites the most efficient setting is the default "First name + Surname", but you may choose to hide surnames altogether, or to leave it up to the current language pack to decide (some languages have different conventions).';
$string['configfullnamedisplay'] = 'This defines how names are shown when they are displayed in full. For most mono-lingual sites the most efficient setting is "First name + Surname", but you may choose to hide surnames altogether, or to leave it up to the current language pack to decide (some languages have different conventions).';
$string['configgdversion'] = 'Indicate the version of GD that is installed. The version shown by default is the one that has been auto-detected. Don\'t change this unless you really know what you\'re doing.';
$string['configgeoipfile'] = 'Location of GeoIP City binary data file. This file is not part of Moodle distribution and must be obtained separately from <a href="http://www.maxmind.com/">MaxMind</a>. You can either buy a commercial version or use the free version.<br />Simply download <a href="http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz" >http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz</a> and extract it into "{$a}" directory on your server.';
$string['configgetremoteaddrconf'] = 'If your server is behind a reverse proxy, you can use this setting to specify which HTTP headers can be trusted to contain the remote IP address. The headers are read in order, using the first one that is available.';
$string['configgooglemapkey'] = 'You need to enter a special key to use Google Maps for IP address lookup visualization. You can obtain the key free of charge at <a href="http://code.google.com/apis/maps/signup.html" >http://code.google.com/apis/maps/signup.html</a>.<br />Your web site URL is: {$a}';
$string['configgradebookroles'] = 'This setting allows you to control who appears on the gradebook. Users need to have at least one of these roles in a course to be shown in the gradebook for that course.';
$string['configgradeexport'] = 'Choose which gradebook export formats are your primary methods for exporting grades. Chosen plugins will then set and use a "last exported" field for every grade. For example, this might result in exported records being identified as being "new" or "updated". If you are not sure about this then leave everything unchecked.';
$string['confighiddenuserfields'] = 'Select which user information fields you wish to hide from other users other than course teachers/admins. This will increase student privacy. Hold CTRL key to select multiple fields.';
@@ -546,7 +545,8 @@ $string['globalsquoteswarning'] = '<p><strong>Security Warning</strong>: to oper
$string['globalswarning'] = '<p><strong>SECURITY WARNING!</strong></p><p> To operate properly, Moodle requires <br />that you make certain changes to your current PHP settings.</p><p>You <em>must</em> set <code>register_globals=off</code>.</p><p>This setting is controlled by editing your <code>php.ini</code>, Apache/IIS <br />configuration or <code>.htaccess</code> file.</p>';
$string['groupenrolmentkeypolicy'] = 'Group enrolment key policy';
$string['groupenrolmentkeypolicy_desc'] = 'Turning this on will make Moodle check group enrolment keys against a valid password policy.';
$string['googlemapkey'] = 'Google Maps API key';
$string['googlemapkey3'] = 'Google Maps API V3 key';
$string['googlemapkey3_help'] = 'You need to enter a special key to use Google Maps for IP address lookup visualization. You can obtain the key free of charge at <a href="https://developers.google.com/maps/documentation/javascript/tutorial#api_key" target="_blank">https://developers.google.com/maps/documentation/javascript/tutorial#api_key</a>';
$string['gotofirst'] = 'Go to first missing string';
$string['gradebook'] = 'Gradebook';
$string['gradebookroles'] = 'Graded roles';
+1 -1
View File
@@ -70,7 +70,7 @@ $string['completionview'] = 'Require view';
$string['completionview_desc'] = 'Student must view this activity to complete it';
$string['configenablecompletion'] = 'When enabled, this lets you turn on completion tracking (progress) features at course level.';
$string['csvdownload'] = 'Download in spreadsheet format (UTF-8 .csv)';
$string['deletecoursecompletiondata'] = 'Delete course completion data';
$string['deletecompletiondata'] = 'Delete completion data';
$string['enablecompletion'] = 'Enable completion tracking';
$string['err_noactivities'] = 'Completion information is not enabled for any activity, so none can be displayed. You can enable completion information by editing the settings for an activity.';
$string['err_nousers'] = 'There are no students on this course or group for whom completion information is displayed. (By default, completion information is displayed only for students, so if there are no students, you will see this error. Administrators can alter this option via the admin screens.)';
+1
View File
@@ -1805,6 +1805,7 @@ $string['visibletostudents'] = 'Visible to {$a}';
$string['warningdeleteresource'] = 'Warning: {$a} is referred in a resource. Would you like to update the resource?';
$string['webpage'] = 'Web page';
$string['week'] = 'Week';
$string['weeks'] = 'weeks';
$string['weekhide'] = 'Hide this week from {$a}';
$string['weeklyoutline'] = 'Weekly outline';
$string['weekshow'] = 'Show this week to {$a}';
+1
View File
@@ -127,6 +127,7 @@ $string['course:managegrades'] = 'Manage grades';
$string['course:managegroups'] = 'Manage groups';
$string['course:managescales'] = 'Manage scales';
$string['course:markcomplete'] = 'Mark users as complete in course completion';
$string['course:movesections'] = 'Move sections';
$string['course:publish'] = 'Publish a course into hub';
$string['course:request'] = 'Request new courses';
$string['course:reset'] = 'Reset course';
+162
View File
@@ -2768,6 +2768,168 @@ class admin_setting_configtime extends admin_setting {
}
/**
* Seconds duration setting.
*
* @copyright 2012 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class admin_setting_configduration extends admin_setting {
/** @var int default duration unit */
protected $defaultunit;
/**
* Constructor
* @param string $name unique ascii name, either 'mysetting' for settings that in config,
* or 'myplugin/mysetting' for ones in config_plugins.
* @param string $visiblename localised name
* @param string $description localised long description
* @param mixed $defaultsetting string or array depending on implementation
* @param int $defaultunit - day, week, etc. (in seconds)
*/
public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
if (is_number($defaultsetting)) {
$defaultsetting = self::parse_seconds($defaultsetting);
}
$units = self::get_units();
if (isset($units[$defaultunit])) {
$this->defaultunit = $defaultunit;
} else {
$this->defaultunit = 86400;
}
parent::__construct($name, $visiblename, $description, $defaultsetting);
}
/**
* Returns selectable units.
* @static
* @return array
*/
protected static function get_units() {
return array(
604800 => get_string('weeks'),
86400 => get_string('days'),
3600 => get_string('hours'),
60 => get_string('minutes'),
1 => get_string('seconds'),
);
}
/**
* Converts seconds to some more user friendly string.
* @static
* @param int $seconds
* @return string
*/
protected static function get_duration_text($seconds) {
if (empty($seconds)) {
return get_string('none');
}
$data = self::parse_seconds($seconds);
switch ($data['u']) {
case (60*60*24*7):
return get_string('numweeks', '', $data['v']);
case (60*60*24):
return get_string('numdays', '', $data['v']);
case (60*60):
return get_string('numhours', '', $data['v']);
case (60):
return get_string('numminutes', '', $data['v']);
default:
return get_string('numseconds', '', $data['v']*$data['u']);
}
}
/**
* Finds suitable units for given duration.
* @static
* @param int $seconds
* @return array
*/
protected static function parse_seconds($seconds) {
foreach (self::get_units() as $unit => $unused) {
if ($seconds % $unit === 0) {
return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
}
}
return array('v'=>(int)$seconds, 'u'=>1);
}
/**
* Get the selected duration as array.
*
* @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
*/
public function get_setting() {
$seconds = $this->config_read($this->name);
if (is_null($seconds)) {
return null;
}
return self::parse_seconds($seconds);
}
/**
* Store the duration as seconds.
*
* @param array $data Must be form 'h'=>xx, 'm'=>xx
* @return bool true if success, false if not
*/
public function write_setting($data) {
if (!is_array($data)) {
return '';
}
$seconds = (int)($data['v']*$data['u']);
if ($seconds < 0) {
return get_string('errorsetting', 'admin');
}
$result = $this->config_write($this->name, $seconds);
return ($result ? '' : get_string('errorsetting', 'admin'));
}
/**
* Returns duration text+select fields.
*
* @param array $data Must be form 'v'=>xx, 'u'=>xx
* @param string $query
* @return string duration text+select fields and wrapping div(s)
*/
public function output_html($data, $query='') {
$default = $this->get_defaultsetting();
if (is_number($default)) {
$defaultinfo = self::get_duration_text($default);
} else if (is_array($default)) {
$defaultinfo = self::get_duration_text($default['v']*$default['u']);
} else {
$defaultinfo = null;
}
$units = self::get_units();
$return = '<div class="form-duration defaultsnext">';
$return .= '<input type="text" size="5" id="'.$this->get_id().'v" name="'.$this->get_full_name().'[v]" value="'.s($data['v']).'" />';
$return .= '<select id="'.$this->get_id().'u" name="'.$this->get_full_name().'[u]">';
foreach ($units as $val => $text) {
$selected = '';
if ($data['v'] == 0) {
if ($val == $this->defaultunit) {
$selected = ' selected="selected"';
}
} else if ($val == $data['u']) {
$selected = ' selected="selected"';
}
$return .= '<option value="'.$val.'"'.$selected.'>'.$text.'</option>';
}
$return .= '</select></div>';
return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
}
}
/**
* Used to validate a textarea used for ip addresses
*
+6
View File
@@ -457,6 +457,12 @@ class block_manager {
if (!$this->page->theme->enable_dock) {
return false;
}
// Do not dock the region when the user attemps to move a block.
if ($this->movingblock) {
return false;
}
$this->check_is_loaded();
$this->ensure_content_created($region, $output);
foreach($this->visibleblockcontent[$region] as $instance) {
+25
View File
@@ -711,6 +711,31 @@ class completion_info {
$DB->delete_records('course_completion_crit_compl', array('course' => $this->course_id));
}
/**
* Deletes all activity and course completion data for an entire course
* (the below delete_all_state function does this for a single activity).
*
* Used by course reset page.
*/
public function delete_all_completion_data() {
global $DB, $SESSION;
// Delete from database.
$DB->delete_records_select('course_modules_completion',
'coursemoduleid IN (SELECT id FROM {course_modules} WHERE course=?)',
array($this->course_id));
// Reset cache for current user.
if (isset($SESSION->completioncache) &&
array_key_exists($this->course_id, $SESSION->completioncache)) {
unset($SESSION->completioncache[$this->course_id]);
}
// Wipe course completion data too.
$this->delete_course_completion_data();
}
/**
* Deletes completion state related to an activity for all users.
*
+7
View File
@@ -373,6 +373,13 @@ function cron_run() {
}
// Run question bank clean-up.
mtrace("Starting the question bank cron...", '');
require_once($CFG->libdir . '/questionlib.php');
question_bank::cron();
mtrace('done.');
//Run registration updated cron
mtrace(get_string('siteupdatesstart', 'hub'));
require_once($CFG->dirroot . '/' . $CFG->admin . '/registration/lib.php');
+11
View File
@@ -1427,6 +1427,17 @@ $capabilities = array(
)
),
'moodle/course:movesections' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/course:update'
),
'moodle/site:mnetlogintoremote' => array(
'captype' => 'read',
+7
View File
@@ -1113,5 +1113,12 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2012081400.01);
}
if ($oldversion < 2012081600.01) {
// Delete removed setting - Google Maps API V2 will not work in 2013.
unset_config('googlemapkey');
upgrade_main_savepoint(true, 2012081600.01);
}
return true;
}
+37
View File
@@ -0,0 +1,37 @@
<?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/>.
/**
* TinyMCE admin setting stuff.
*
* @package editor_tinymce
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once("$CFG->libdir/pluginlib.php");
/**
* Editor subplugin info class.
*/
class plugininfo_tinymce extends plugininfo_base {
public function get_uninstall_url() {
return new moodle_url('/lib/editor/tinymce/subplugins.php', array('delete' => $this->name, 'sesskey' => sesskey()));
}
}
@@ -1 +0,0 @@
temp
@@ -29,6 +29,7 @@ $string['common:browsemedia'] = 'Find or upload a sound, video or applet...';
$string['fontselectlist'] = 'Available fonts list';
$string['media_dlg:filename'] = 'Filename';
$string['pluginname'] = 'TinyMCE HTML editor';
$string['subplugindeleteconfirm'] = 'You are about to completely delete TinyMCE subplugin \'{$a}\'. This will completely delete everything in the database associated with this subplugin. Are you SURE you want to continue?';
// == TinyMCE upstream lang strings from all standard upstream plugins ==
+74
View File
@@ -0,0 +1,74 @@
<?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/>.
/**
* TinyMCE subplugin management.
*
* @package editor_tinymce
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require(__DIR__ . '/../../../config.php');
require_once($CFG->libdir.'/adminlib.php');
$delete = optional_param('delete', '', PARAM_PLUGIN);
$confirm = optional_param('confirm', '', PARAM_BOOL);
$return = optional_param('return', 'overview', PARAM_ALPHA);
$PAGE->set_context(context_system::instance());
$PAGE->set_url('/lib/editor/tinymce/subplugins.php', array('delete'=>$delete));
require_login();
require_capability('moodle/site:config', context_system::instance());
require_sesskey();
if ($return === 'settings') {
$returnurl = new moodle_url('/admin/settings.php', array('section'=>'editorsettingstinymce'));
} else {
$returnurl = new moodle_url('/admin/plugins.php');
}
if ($delete) {
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('pluginname', 'editor_tinymce'));
if (!$confirm) {
if (get_string_manager()->string_exists('pluginname', 'tinymce_' . $delete)) {
$strpluginname = get_string('pluginname', 'tinymce_' . $delete);
} else {
$strpluginname = $delete;
}
echo $OUTPUT->confirm(get_string('subplugindeleteconfirm', 'editor_tinymce', $strpluginname),
new moodle_url($PAGE->url, array('delete' => $delete, 'confirm' => 1, 'return'=>$return)),
$returnurl);
echo $OUTPUT->footer();
die();
} else {
uninstall_plugin('tinymce', $delete);
$a = new stdclass();
$a->name = $delete;
$pluginlocation = get_plugin_types();
$a->directory = $pluginlocation['tinymce'] . '/' . $delete;
echo $OUTPUT->notification(get_string('plugindeletefiles', '', $a), 'notifysuccess');
echo $OUTPUT->continue_button($returnurl);
echo $OUTPUT->footer();
die();
}
}
redirect($returnurl);
+3 -2
View File
@@ -58,8 +58,8 @@ class MoodleQuickForm_duration extends MoodleQuickForm_group {
* @param string $elementName Element's name
* @param mixed $elementLabel Label(s) for an element
* @param array $options Options to control the element's display. Recognised values are
'optional' => true/false - whether to display an 'enabled' checkbox next to the element.
'defaultunit' => 1|60|3600|86400 - the default unit to display when the time is blank.
* 'optional' => true/false - whether to display an 'enabled' checkbox next to the element.
* 'defaultunit' => 1|60|3600|86400|604800 - the default unit to display when the time is blank.
* If not specified, minutes is used.
* @param mixed $attributes Either a typical HTML attribute string or an associative array
*/
@@ -91,6 +91,7 @@ class MoodleQuickForm_duration extends MoodleQuickForm_group {
public function get_units() {
if (is_null($this->_units)) {
$this->_units = array(
604800 => get_string('weeks'),
86400 => get_string('days'),
3600 => get_string('hours'),
60 => get_string('minutes'),
+2 -2
View File
@@ -128,7 +128,7 @@ class dateselector_form_element_testcase extends basic_testcase {
'year' => 2011,
'usertimezone' => 0.0,
'timezone' => 0.0,
'timestamp' => 1309449600
'timestamp' => 1309478400 // 6am at UTC+0
),
array (
'day' => 1,
@@ -136,7 +136,7 @@ class dateselector_form_element_testcase extends basic_testcase {
'year' => 2011,
'usertimezone' => 0.0,
'timezone' => 99,
'timestamp' => 1309449600
'timestamp' => 1309478400 // 6am at UTC+0
)
);
}
+2 -2
View File
@@ -138,7 +138,7 @@ class datetimeselector_form_element_testcase extends basic_testcase {
'year' => 2011,
'usertimezone' => 0.0,
'timezone' => 0.0,
'timestamp' => 1309449600
'timestamp' => 1309478400 // 6am at UTC+0
),
array (
'minute' => 0,
@@ -148,7 +148,7 @@ class datetimeselector_form_element_testcase extends basic_testcase {
'year' => 2011,
'usertimezone' => 0.0,
'timezone' => 99,
'timestamp' => 1309449600
'timestamp' => 1309478400 // 6am at UTC+0
)
);
}
+1 -1
View File
@@ -77,7 +77,7 @@ class duration_form_element_testcase extends basic_testcase {
$units = $this->element->get_units();
ksort($units);
$this->assertEquals($units, array(1 => get_string('seconds'), 60 => get_string('minutes'),
3600 => get_string('hours'), 86400 => get_string('days')));
3600 => get_string('hours'), 86400 => get_string('days'), 604800 => get_string('weeks')));
}
/**
+2 -1
View File
@@ -742,7 +742,8 @@ class grade_item extends grade_object {
// Standardise score to the new grade range
// NOTE: this is not compatible with current assignment grading
if ($this->itemmodule != 'assignment' and ($rawmin != $this->grademin or $rawmax != $this->grademax)) {
$isassignmentmodule = ($this->itemmodule == 'assignment') || ($this->itemmodule == 'assign');
if (!$isassignmentmodule && ($rawmin != $this->grademin or $rawmax != $this->grademax)) {
$rawgrade = grade_grade::standardise_score($rawgrade, $rawmin, $rawmax, $this->grademin, $this->grademax);
}
+1 -1
View File
@@ -60,7 +60,7 @@ function message_send($eventdata) {
//TODO: we need to solve problems with database transactions here somehow, for now we just prevent transactions - sorry
$DB->transactions_forbidden();
if (is_int($eventdata->userto)) {
if (is_number($eventdata->userto)) {
$eventdata->userto = $DB->get_record('user', array('id' => $eventdata->userto));
}
if (is_int($eventdata->userfrom)) {
+29 -13
View File
@@ -487,6 +487,12 @@ define('MOODLE_OFFICIAL_MOBILE_SERVICE', 'moodle_mobile_app');
*/
define('USER_CAN_IGNORE_FILE_SIZE_LIMITS', -1);
/**
* Course display settings
*/
define('COURSE_DISPLAY_SINGLEPAGE', 0); // display all sections on one page
define('COURSE_DISPLAY_MULTIPAGE', 1); // split pages into a page per section
/// PARAMETER HANDLING ////////////////////////////////////////////////////
/**
@@ -2330,10 +2336,10 @@ function get_user_timezone($tz = 99) {
$tz = 99;
while(($tz == '' || $tz == 99 || $tz == NULL) && $next = each($timezones)) {
// Loop while $tz is, empty but not zero, or 99, and there is another timezone is the array
while(((empty($tz) && !is_numeric($tz)) || $tz == 99) && $next = each($timezones)) {
$tz = $next['value'];
}
return is_numeric($tz) ? (float) $tz : $tz;
}
@@ -3289,12 +3295,25 @@ function get_user_key($script, $userid, $instance=null, $iprestriction=null, $va
function update_user_login_times() {
global $USER, $DB;
$user = new stdClass();
$USER->lastlogin = $user->lastlogin = $USER->currentlogin;
$USER->currentlogin = $user->lastaccess = $user->currentlogin = time();
$now = time();
$user = new stdClass();
$user->id = $USER->id;
// Make sure all users that logged in have some firstaccess.
if ($USER->firstaccess == 0) {
$USER->firstaccess = $user->firstaccess = $now;
}
// Store the previous current as lastlogin.
$USER->lastlogin = $user->lastlogin = $USER->currentlogin;
$USER->currentlogin = $user->currentlogin = $now;
// Function user_accesstime_log() may not update immediately, better do it here.
$USER->lastaccess = $user->lastaccess = $now;
$USER->lastip = $user->lastip = getremoteaddr();
$DB->update_record('user', $user);
return true;
}
@@ -4093,10 +4112,6 @@ function authenticate_user_login($username, $password) {
$DB->set_field('user', 'auth', $auth, array('username'=>$username));
$user->auth = $auth;
}
if (empty($user->firstaccess)) { //prevent firstaccess from remaining 0 for manual account that never required confirmation
$DB->set_field('user','firstaccess', $user->timemodified, array('id' => $user->id));
$user->firstaccess = $user->timemodified;
}
update_internal_user_password($user, $password); // just in case salt or encoding were changed (magic quotes too one day)
@@ -4850,12 +4865,13 @@ function reset_course_userdata($data) {
$status[] = array('component'=>$componentstr, 'item'=>get_string('deleteblogassociations', 'blog'), 'error'=>false);
}
if (!empty($data->reset_course_completion)) {
// Delete course completion information
if (!empty($data->reset_completion)) {
// Delete course and activity completion information.
$course = $DB->get_record('course', array('id'=>$data->courseid));
$cc = new completion_info($course);
$cc->delete_course_completion_data();
$status[] = array('component'=>$componentstr, 'item'=>get_string('deletecoursecompletiondata', 'completion'), 'error'=>false);
$cc->delete_all_completion_data();
$status[] = array('component' => $componentstr,
'item' => get_string('deletecompletiondata', 'completion'), 'error' => false);
}
$componentstr = get_string('roles');
+3
View File
@@ -578,6 +578,9 @@ class moodle_page {
global $CFG;
if (is_null($this->_blocks)) {
if (!empty($CFG->blockmanagerclass)) {
if (!empty($CFG->blockmanagerclassfile)) {
require_once($CFG->blockmanagerclassfile);
}
$classname = $CFG->blockmanagerclass;
} else {
$classname = 'block_manager';
+6 -6
View File
@@ -240,11 +240,11 @@ EOD;
}
if (!isset($record['descriptionformat'])) {
$record['description'] = FORMAT_MOODLE;
$record['descriptionformat'] = FORMAT_MOODLE;
}
if (!isset($record['parent'])) {
$record['descriptionformat'] = 0;
$record['parent'] = 0;
}
if (empty($record['parent'])) {
@@ -310,12 +310,12 @@ EOD;
$record['numsections'] = 5;
}
if (!isset($record['description'])) {
$record['description'] = "Test course $i\n$this->loremipsum";
if (!isset($record['summary'])) {
$record['summary'] = "Test course $i\n$this->loremipsum";
}
if (!isset($record['descriptionformat'])) {
$record['description'] = FORMAT_MOODLE;
if (!isset($record['summaryformat'])) {
$record['summaryformat'] = FORMAT_MOODLE;
}
if (!isset($record['category'])) {
@@ -34,6 +34,20 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class Hint_ResultPrinter extends PHPUnit_TextUI_ResultPrinter {
public function __construct() {
// ARRGH - PHPUnit does not give us commandline arguments or xml config, so let's hack hard!
if (defined('DEBUG_BACKTRACE_PROVIDE_OBJECT')) {
$backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);
if (isset($backtrace[2]['object']) and ($backtrace[2]['object'] instanceof PHPUnit_TextUI_Command)) {
list($verbose, $colors, $debug) = Hacky_TextUI_Command_reader::get_settings_hackery($backtrace[2]['object']);
parent::__construct(null, $verbose, $colors, $debug);
return;
}
}
// Fallback if something goes wrong.
parent::__construct(null, false, false, false);
}
protected function printDefectTrace(PHPUnit_Framework_TestFailure $defect) {
global $CFG;
@@ -83,3 +97,30 @@ class Hint_ResultPrinter extends PHPUnit_TextUI_ResultPrinter {
$this->write("\nTo re-run:\n $executable $testName $file\n");
}
}
/**
* Class used in bloody hack that works around result printer constructor troubles.
*
* @package core
* @category phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class Hacky_TextUI_Command_reader extends PHPUnit_TextUI_Command {
public static function get_settings_hackery(PHPUnit_TextUI_Command $toread) {
$arguments = $toread->arguments;
$config = PHPUnit_Util_Configuration::getInstance($arguments['configuration'])->getPHPUnitConfiguration();
$verbose = isset($config['verbose']) ? $config['verbose'] : false;
$verbose = isset($arguments['verbose']) ? $arguments['verbose'] : $verbose;
$colors = isset($config['colors']) ? $config['colors'] : false;
$colors = isset($arguments['colors']) ? $arguments['colors'] : $colors;
$debug = isset($config['debug']) ? $config['debug'] : false;
$debug = isset($arguments['debug']) ? $arguments['debug'] : $debug;
return array($verbose, $colors, $debug);
}
}
+25 -2
View File
@@ -762,16 +762,20 @@ class phpunit_util {
* Note: To be used from CLI scripts only.
*
* @static
* @param bool $displayprogress if true, this method will echo progress information.
* @return void may terminate execution with exit code
*/
public static function drop_site() {
public static function drop_site($displayprogress = false) {
global $DB, $CFG;
if (!self::is_test_site()) {
phpunit_bootstrap_error(PHPUNIT_EXITCODE_CONFIGERROR, 'Can not drop non-test site!!');
}
// purge dataroot
// Purge dataroot
if ($displayprogress) {
echo "Purging dataroot:\n";
}
self::reset_dataroot();
phpunit_bootstrap_initdataroot($CFG->dataroot);
$keep = array('.', '..', 'lock', 'webrunner.xml');
@@ -795,9 +799,28 @@ class phpunit_util {
unset($tables['config']);
$tables['config'] = 'config';
}
if ($displayprogress) {
echo "Dropping tables:\n";
}
$dotsonline = 0;
foreach ($tables as $tablename) {
$table = new xmldb_table($tablename);
$DB->get_manager()->drop_table($table);
if ($dotsonline == 60) {
if ($displayprogress) {
echo "\n";
}
$dotsonline = 0;
}
if ($displayprogress) {
echo '.';
}
$dotsonline += 1;
}
if ($displayprogress) {
echo "\n";
}
}
+12
View File
@@ -48,10 +48,22 @@ class core_phpunit_generator_testcase extends advanced_testcase {
$count = $DB->count_records('course_categories');
$category = $generator->create_category();
$this->assertEquals($count+1, $DB->count_records('course_categories'));
$this->assertRegExp('/^Course category \d/', $category->name);
$this->assertSame('', $category->idnumber);
$this->assertRegExp('/^Test course category \d/', $category->description);
$this->assertSame(FORMAT_MOODLE, $category->descriptionformat);
$count = $DB->count_records('course');
$course = $generator->create_course();
$this->assertEquals($count+1, $DB->count_records('course'));
$this->assertRegExp('/^Test course \d/', $course->fullname);
$this->assertRegExp('/^tc_\d/', $course->shortname);
$this->assertSame('', $course->idnumber);
$this->assertSame('topics', $course->format);
$this->assertEquals(0, $course->newsitems);
$this->assertEquals(5, $course->numsections);
$this->assertRegExp('/^Test course \d/', $course->summary);
$this->assertSame(FORMAT_MOODLE, $course->summaryformat);
$section = $generator->create_course_section(array('course'=>$course->id, 'section'=>3));
$this->assertEquals($course->id, $section->course);
+18 -3
View File
@@ -30,8 +30,6 @@
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/filelib.php'); // curl class needed here
/**
* Singleton class providing general plugins management functionality
*/
@@ -100,6 +98,16 @@ class plugin_manager {
global $CFG;
if ($disablecache or is_null($this->pluginsinfo)) {
// Hack: include mod and editor subplugin management classes first,
// the adminlib.php is supposed to contain extra admin settings too.
require_once($CFG->libdir.'/adminlib.php');
foreach(array('mod', 'editor') as $type) {
foreach (get_plugin_list($type) as $dir) {
if (file_exists("$dir/adminlib.php")) {
include_once("$dir/adminlib.php");
}
}
}
$this->pluginsinfo = array();
$plugintypes = get_plugin_types();
$plugintypes = $this->reorder_plugin_types($plugintypes);
@@ -148,10 +156,11 @@ class plugin_manager {
if ($disablecache or is_null($this->subpluginsinfo)) {
$this->subpluginsinfo = array();
foreach (array('mod', 'editor') as $type) {
$owners = get_plugin_list('type');
$owners = get_plugin_list($type);
foreach ($owners as $component => $ownerdir) {
$componentsubplugins = array();
if (file_exists($ownerdir . '/db/subplugins.php')) {
$subplugins = array();
include($ownerdir . '/db/subplugins.php');
foreach ($subplugins as $subplugintype => $subplugintyperootdir) {
$subplugin = new stdClass();
@@ -785,6 +794,9 @@ class available_update_checker {
* @throws available_update_checker_exception
*/
protected function get_response() {
global $CFG;
require_once($CFG->libdir.'/filelib.php');
$curl = new curl(array('proxy' => true));
$response = $curl->post($this->prepare_request_url(), $this->prepare_request_params());
$curlinfo = $curl->get_info();
@@ -961,6 +973,9 @@ class available_update_checker {
return;
}
$version = null;
$release = null;
require($CFG->dirroot.'/version.php');
$this->currentversion = $version;
$this->currentrelease = $release;
+3
View File
@@ -791,6 +791,9 @@ moodle_setlocale();
// Create the $PAGE global - this marks the PAGE and OUTPUT fully initialised, this MUST be done at the end of setup!
if (!empty($CFG->moodlepageclass)) {
if (!empty($CFG->moodlepageclassfile)) {
require_once($CFG->moodlepageclassfile);
}
$classname = $CFG->moodlepageclass;
} else {
$classname = 'moodle_page';
+5
View File
@@ -941,6 +941,7 @@ class flexible_table {
} else {
$this->start_html();
$this->print_headers();
echo html_writer::start_tag('tbody');
}
}
@@ -1004,6 +1005,7 @@ class flexible_table {
$this->print_nothing_to_display();
} else {
echo html_writer::end_tag('tbody');
echo html_writer::end_tag('table');
echo html_writer::end_tag('div');
$this->wrap_html_finish();
@@ -1051,6 +1053,7 @@ class flexible_table {
function print_headers() {
global $CFG, $OUTPUT;
echo html_writer::start_tag('thead');
echo html_writer::start_tag('tr');
foreach ($this->columns as $column => $index) {
@@ -1121,6 +1124,7 @@ class flexible_table {
}
echo html_writer::end_tag('tr');
echo html_writer::end_tag('thead');
}
/**
@@ -1717,6 +1721,7 @@ EOF;
function output_headers($headers) {
$this->table->print_headers();
echo html_writer::start_tag('tbody');
}
function add_data($row) {
-184
View File
@@ -1,184 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for backups.
*
* @package core
* @category phpunit
* @copyright 2012 Frédéric Massart <fred@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/backup/util/helper/backup_cron_helper.class.php');
/**
* Unit tests for backup system
*/
class backup_testcase extends advanced_testcase {
public function test_next_automated_backup() {
$this->resetAfterTest();
$admin = get_admin();
$timezone = $admin->timezone;
// Notes
// - The next automated backup will never be on the same date than $now
// - backup_auto_weekdays starts on Sunday
// - Tests cannot be done in the past.
// Every Wed and Sat at 11pm.
set_config('backup_auto_active', '1', 'backup');
set_config('backup_auto_weekdays', '0010010', 'backup');
set_config('backup_auto_hour', '23', 'backup');
set_config('backup_auto_minute', '0', 'backup');
$now = strtotime('next Monday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('2-23:00', date('w-H:i', $next));
$now = strtotime('next Tuesday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('5-23:00', date('w-H:i', $next));
$now = strtotime('next Wednesday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('5-23:00', date('w-H:i', $next));
$now = strtotime('next Thursday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('5-23:00', date('w-H:i', $next));
$now = strtotime('next Friday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('2-23:00', date('w-H:i', $next));
$now = strtotime('next Saturday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('2-23:00', date('w-H:i', $next));
$now = strtotime('next Sunday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('2-23:00', date('w-H:i', $next));
// Every Sun and Sat at 12pm.
set_config('backup_auto_active', '1', 'backup');
set_config('backup_auto_weekdays', '1000001', 'backup');
set_config('backup_auto_hour', '0', 'backup');
set_config('backup_auto_minute', '0', 'backup');
$now = strtotime('next Monday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
$now = strtotime('next Tuesday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
$now = strtotime('next Wednesday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
$now = strtotime('next Thursday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
$now = strtotime('next Friday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
$now = strtotime('next Saturday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-00:00', date('w-H:i', $next));
$now = strtotime('next Sunday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-00:00', date('w-H:i', $next));
// Every Sun at 4am.
set_config('backup_auto_active', '1', 'backup');
set_config('backup_auto_weekdays', '1000000', 'backup');
set_config('backup_auto_hour', '4', 'backup');
set_config('backup_auto_minute', '0', 'backup');
$now = strtotime('next Monday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
$now = strtotime('next Tuesday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
$now = strtotime('next Wednesday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
$now = strtotime('next Thursday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
$now = strtotime('next Friday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
$now = strtotime('next Saturday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
$now = strtotime('next Sunday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-04:00', date('w-H:i', $next));
// Every day but Wed at 8:30pm.
set_config('backup_auto_active', '1', 'backup');
set_config('backup_auto_weekdays', '1110111', 'backup');
set_config('backup_auto_hour', '20', 'backup');
set_config('backup_auto_minute', '30', 'backup');
$now = strtotime('next Monday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('2-20:30', date('w-H:i', $next));
$now = strtotime('next Tuesday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('4-20:30', date('w-H:i', $next));
$now = strtotime('next Wednesday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('4-20:30', date('w-H:i', $next));
$now = strtotime('next Thursday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('5-20:30', date('w-H:i', $next));
$now = strtotime('next Friday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('6-20:30', date('w-H:i', $next));
$now = strtotime('next Saturday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('0-20:30', date('w-H:i', $next));
$now = strtotime('next Sunday');
$next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
$this->assertEquals('1-20:30', date('w-H:i', $next));
}
}
+3 -3
View File
@@ -1549,9 +1549,9 @@ class moodlelib_testcase extends advanced_testcase {
'hour' => '10',
'minutes' => '00',
'seconds' => '00',
'timezone' => '0.0', //no dst offset
'applydst' => false,
'expectedoutput' => '1309528800'
'timezone' => '0.0',
'applydst' => false, //no dst offset
'expectedoutput' => '1309514400' // 6pm at UTC+0
),
array(
'usertimezone' => 'America/Moncton',
+1
View File
@@ -5,6 +5,7 @@ information provided here is intended especially for developers.
* Pagelib: Numerous deprecated functions were removed as classes page_base, page_course
and page_generic_activity.
* use $CFG->googlemapkey3 instead of removed $CFG->googlemapkey and migrate to Google Maps API V3
YUI changes:
* moodle-enrol-notification has been renamed to moodle-core-notification
+25 -10
View File
@@ -2551,23 +2551,38 @@ function obfuscate_text($plaintext) {
* @param string $email The email address to display
* @param string $label The text to displayed as hyperlink to $email
* @param boolean $dimmed If true then use css class 'dimmed' for hyperlink
* @param string $subject The subject of the email in the mailto link
* @param string $body The content of the email in the mailto link
* @return string The obfuscated mailto link
*/
function obfuscate_mailto($email, $label='', $dimmed=false) {
function obfuscate_mailto($email, $label='', $dimmed=false, $subject = '', $body = '') {
if (empty($label)) {
$label = $email;
}
if ($dimmed) {
$title = get_string('emaildisable');
$dimmed = ' class="dimmed"';
} else {
$title = '';
$dimmed = '';
$label = obfuscate_text($label);
$email = obfuscate_email($email);
$mailto = obfuscate_text('mailto');
$url = new moodle_url("mailto:$email");
$attrs = array();
if (!empty($subject)) {
$url->param('subject', format_string($subject));
}
return sprintf("<a href=\"%s:%s\" $dimmed title=\"$title\">%s</a>",
obfuscate_text('mailto'), obfuscate_email($email),
obfuscate_text($label));
if (!empty($body)) {
$url->param('body', format_string($body));
}
// Use the obfuscated mailto
$url = preg_replace('/^mailto/', $mailto, $url->out());
if ($dimmed) {
$attrs['title'] = get_string('emaildisable');
$attrs['class'] = 'dimmed';
}
return html_writer::link($url, $label, $attrs);
}
/**
@@ -55,7 +55,8 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st
'duedate',
'allowsubmissionsfromdate',
'grade',
'timemodified'));
'timemodified',
'completionsubmit'));
$submissions = new backup_nested_element('submissions');
+2 -1
View File
@@ -21,7 +21,8 @@
<FIELD NAME="allowsubmissionsfromdate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="If set, submissions will only be accepted after this date." PREVIOUS="duedate" NEXT="grade"/>
<FIELD NAME="grade" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The maximum grade for this assignment. Can be negative to indicate the use of a scale." PREVIOUS="allowsubmissionsfromdate" NEXT="timemodified"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The time the settings for this assign module instance were last modified." PREVIOUS="grade" NEXT="requiresubmissionstatement"/>
<FIELD NAME="requiresubmissionstatement" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Forces the student to accept a submission statement when submitting an assignment" PREVIOUS="timemodified"/>
<FIELD NAME="requiresubmissionstatement" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Forces the student to accept a submission statement when submitting an assignment" PREVIOUS="timemodified" NEXT="completionsubmit"/>
<FIELD NAME="completionsubmit" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="If this field is set to 1, then the activity will be automatically marked as 'complete' once the user submits their assignment." PREVIOUS="requiresubmissionstatement"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="The unique id for this assignment instance."/>
+15
View File
@@ -65,6 +65,21 @@ function xmldb_assign_upgrade($oldversion) {
upgrade_mod_savepoint(true, 2012071800, 'assign');
}
if ($oldversion < 2012081600) {
// Define field sendlatenotifications to be added to assign.
$table = new xmldb_table('assign');
$field = new xmldb_field('completionsubmit', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'timemodified');
// Conditionally launch add field sendlatenotifications.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Assign savepoint reached.
upgrade_mod_savepoint(true, 2012081600, 'assign');
}
return true;
}
+4 -2
View File
@@ -287,13 +287,15 @@ class assign_grading_table extends table_sql implements renderable {
}
/**
* Format a user record for display (don't link to profile)
* Format a user record for display (link to profile)
*
* @param stdClass $row
* @return string
*/
function col_fullname($row) {
return fullname($row);
$courseid = $this->assignment->get_course()->id;
$link= new moodle_url('/user/view.php', array('id' =>$row->id, 'course'=>$courseid));
return $this->output->action_link($link, fullname($row));
}
/**
+7 -2
View File
@@ -56,8 +56,13 @@ foreach ($assignments as $assignment) {
$cm = get_coursemodule_from_instance('assign', $assignment->id, 0, false, MUST_EXIST);
$link = html_writer::link(new moodle_url('/mod/assign/view.php', array('id' => $cm->id)), $assignment->name);
$date = userdate($assignment->duedate);
$submissions = $DB->count_records('assign_submission', array('assignment'=>$cm->instance));
$date = '-';
if (!empty($assignment->duedate)) {
$date = userdate($assignment->duedate);
}
$params = array('assignment'=>$cm->instance, 'status'=>ASSIGN_SUBMISSION_STATUS_SUBMITTED);
$submissions = $DB->count_records('assign_submission', $params);
$row = array($link, $date, $submissions);
$table->data[] = $row;
+2
View File
@@ -65,7 +65,9 @@ $string['batchoperationconfirmreverttodraft'] = 'Revert selected submissions to
$string['batchoperationlock'] = 'lock submissions';
$string['batchoperationunlock'] = 'unlock submissions';
$string['batchoperationreverttodraft'] = 'revert submissions to draft';
$string['changegradewarning'] = 'This assignment has graded submissions and changing the grade will not automatically re-calculate existing submission grades. You must re-grade all existing submissions, if you wish to change the grade.';
$string['comment'] = 'Comment';
$string['completionsubmit'] = 'Student must submit to this activity to complete it';
$string['conversionexception'] = 'Could not convert assignment. Exception was: {$a}.';
$string['configshowrecentsubmissions'] = 'Everyone can see notifications of submissions in recent activity reports.';
$string['confirmsubmission'] = 'Are you sure you want to submit your work for grading? You will not be able to make any more changes';
+27
View File
@@ -83,6 +83,7 @@ function assign_supports($feature) {
case FEATURE_GROUPMEMBERSONLY: return true;
case FEATURE_MOD_INTRO: return true;
case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
case FEATURE_COMPLETION_HAS_RULES: return true;
case FEATURE_GRADE_HAS_GRADE: return true;
case FEATURE_GRADE_OUTCOMES: return true;
case FEATURE_BACKUP_MOODLE2: return true;
@@ -937,3 +938,29 @@ function assign_user_outline($course, $user, $coursemodule, $assignment) {
return $result;
}
/**
* Obtains the automatic completion state for this module based on any conditions
* in assign settings.
*
* @param object $course Course
* @param object $cm Course-module
* @param int $userid User ID
* @param bool $type Type of comparison (or/and; can be used as return value if no conditions)
* @return bool True if completed, false if not, $type if conditions not set.
*/
function assign_get_completion_state($course, $cm, $userid, $type) {
global $CFG,$DB;
require_once($CFG->dirroot . '/mod/assign/locallib.php');
$assign = new assign(null, $cm, $course);
// If completion option is enabled, evaluate it and return true/false.
if ($assign->get_instance()->completionsubmit) {
$submission = $DB->get_record('assign_submission', array('assignment'=>$assign->get_instance()->id, 'userid'=>$userid), '*', IGNORE_MISSING);
return $submission && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED;
} else {
// Completion option is not enabled so just return $type.
return $type;
}
}
+21 -1
View File
@@ -417,6 +417,7 @@ class assign {
$update->duedate = $formdata->duedate;
$update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
$update->grade = $formdata->grade;
$update->completionsubmit = $formdata->completionsubmit;
$returnid = $DB->insert_record('assign', $update);
$this->instance = $DB->get_record('assign', array('id'=>$returnid), '*', MUST_EXIST);
// cache the course record
@@ -636,6 +637,7 @@ class assign {
$update->duedate = $formdata->duedate;
$update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
$update->grade = $formdata->grade;
$update->completionsubmit = $formdata->completionsubmit;
$result = $DB->update_record('assign', $update);
$this->instance = $DB->get_record('assign', array('id'=>$update->id), '*', MUST_EXIST);
@@ -2561,6 +2563,11 @@ class assign {
$submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
$this->update_submission($submission);
$completion = new completion_info($this->get_course());
if ($completion->is_enabled($this->get_course_module()) && $this->get_instance()->completionsubmit) {
$completion->update_state($this->get_course_module(), COMPLETION_COMPLETE, $USER->id);
}
if (isset($data->submissionstatement)) {
$this->add_to_log('submission statement accepted', get_string('submissionstatementacceptedlog', 'mod_assign', fullname($USER)));
}
@@ -2838,6 +2845,15 @@ class assign {
}
$this->add_to_log('submit', $this->format_submission_for_log($submission));
$complete = COMPLETION_INCOMPLETE;
if ($submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
$complete = COMPLETION_COMPLETE;
}
$completion = new completion_info($this->get_course());
if ($completion->is_enabled($this->get_course_module()) && $this->get_instance()->completionsubmit) {
$completion->update_state($this->get_course_module(), $complete, $USER->id);
}
if (!$this->get_instance()->submissiondrafts) {
$this->notify_student_submission_receipt($submission);
$this->notify_graders($submission);
@@ -3140,7 +3156,7 @@ class assign {
* @return void
*/
private function process_revert_to_draft($userid = 0) {
global $USER, $DB;
global $DB;
// Need grade permission
require_capability('mod/assign:grade', $this->context);
@@ -3163,6 +3179,10 @@ class assign {
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
$completion = new completion_info($this->get_course());
if ($completion->is_enabled($this->get_course_module()) && $this->get_instance()->completionsubmit) {
$completion->update_state($this->get_course_module(), COMPLETION_INCOMPLETE, $userid);
}
$this->add_to_log('revert submission to draft', get_string('reverttodraftforstudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user))));
}
+26 -1
View File
@@ -45,7 +45,7 @@ class mod_assign_mod_form extends moodleform_mod {
* @return void
*/
function definition() {
global $CFG, $DB;
global $CFG, $DB, $PAGE;
$mform = $this->_form;
$mform->addElement('header', 'general', get_string('general', 'form'));
@@ -120,6 +120,21 @@ class mod_assign_mod_form extends moodleform_mod {
$this->standard_coursemodule_elements();
$this->add_action_buttons();
// Add warning popup/noscript tag, if grades are changed by user.
if ($mform->elementExists('grade') && !empty($this->_instance) && $DB->record_exists_select('assign_grades', 'assignment = ? AND grade <> -1', array($this->_instance))) {
$module = array(
'name' => 'mod_assign',
'fullpath' => '/mod/assign/module.js',
'requires' => array('node', 'event'),
'strings' => array(array('changegradewarning', 'mod_assign'))
);
$PAGE->requires->js_init_call('M.mod_assign.init_grade_change', null, false, $module);
// Add noscript tag in case
$noscriptwarning = $mform->createElement('static', 'warning', null, html_writer::tag('noscript', get_string('changegradewarning', 'mod_assign')));
$mform->insertElementBefore($noscriptwarning, 'grade');
}
}
/**
@@ -161,5 +176,15 @@ class mod_assign_mod_form extends moodleform_mod {
$assignment->plugin_data_preprocessing($defaultvalues);
}
function add_completion_rules() {
$mform =& $this->_form;
$mform->addElement('checkbox', 'completionsubmit', '', get_string('completionsubmit', 'assign'));
return array('completionsubmit');
}
function completion_rule_enabled($data) {
return !empty($data['completionsubmit']);
}
}
+12
View File
@@ -127,4 +127,16 @@ M.mod_assign.init_grading_options = function(Y) {
});
}
});
};
M.mod_assign.init_grade_change = function(Y) {
var gradenode = Y.one('#id_grade');
if (gradenode) {
var originalvalue = gradenode.get('value');
gradenode.on('change', function() {
if (gradenode.get('value') != originalvalue) {
alert(M.str.mod_assign.changegradewarning);
}
});
}
};
+1 -1
View File
@@ -417,7 +417,7 @@ class mod_assign_renderer extends plugin_renderer_base {
$row = new html_table_row();
$cell1 = new html_table_cell(get_string('timeremaining', 'assign'));
if ($duedate - $time <= 0) {
if (!$status->submission || $status->submission != ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
if (!$status->submission || $status->submission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
if ($status->submissionsenabled) {
$cell2 = new html_table_cell(get_string('overdue', 'assign', format_time($time - $duedate)));
$cell2->attributes = array('class'=>'overdue');
+1 -1
View File
@@ -25,7 +25,7 @@
defined('MOODLE_INTERNAL') || die();
$module->component = 'mod_assign'; // Full name of the plugin (used for diagnostics)
$module->version = 2012071800; // The current module version (Date: YYYYMMDDXX)
$module->version = 2012081600; // The current module version (Date: YYYYMMDDXX)
$module->requires = 2012061700; // Requires this Moodle version
$module->cron = 60;
+12
View File
@@ -35,3 +35,15 @@ M.mod_assignment.init_tree = function(Y, expand_all, htmlid) {
tree.render();
});
};
M.mod_assignment.init_grade_change = function(Y) {
var gradenode = Y.one('#id_grade');
if (gradenode) {
var originalvalue = gradenode.get('value');
gradenode.on('change', function() {
if (gradenode.get('value') != originalvalue) {
alert(M.str.mod_assignment.changegradewarning);
}
});
}
};
+1
View File
@@ -57,6 +57,7 @@ $string['assignment:view'] = 'View assignment';
$string['availabledate'] = 'Available from';
$string['cannotdeletefiles'] = 'An error occurred and files could not be deleted';
$string['cannotviewassignment'] = 'You can not view this assignment';
$string['changegradewarning'] = 'This assignment has graded submissions and changing the grade will not automatically re-calculate existing submission grades. You must re-grade all existing submissions, if you wish to change the grade.';
$string['comment'] = 'Comment';
$string['commentinline'] = 'Comment inline';
$string['commentinline_help'] = 'If enabled, the submission text will be copied into the feedback comment field during grading, making it easier to comment inline (using a different colour, perhaps) or to edit the original text.';
+16 -1
View File
@@ -9,7 +9,7 @@ class mod_assignment_mod_form extends moodleform_mod {
protected $_assignmentinstance = null;
function definition() {
global $CFG, $DB;
global $CFG, $DB, $PAGE;
$mform =& $this->_form;
// this hack is needed for different settings of each subtype
@@ -76,6 +76,21 @@ class mod_assignment_mod_form extends moodleform_mod {
$this->standard_coursemodule_elements();
$this->add_action_buttons();
// Add warning popup/noscript tag, if grades are changed by user.
if ($mform->elementExists('grade') && !empty($this->_instance) && $DB->record_exists_select('assignment_submissions', 'assignment = ? AND grade <> -1', array($this->_instance))) {
$module = array(
'name' => 'mod_assignment',
'fullpath' => '/mod/assignment/assignment.js',
'requires' => array('node', 'event'),
'strings' => array(array('changegradewarning', 'mod_assignment'))
);
$PAGE->requires->js_init_call('M.mod_assignment.init_grade_change', null, false, $module);
// Add noscript tag in case
$noscriptwarning = $mform->createElement('static', 'warning', null, html_writer::tag('noscript', get_string('changegradewarning', 'mod_assignment')));
$mform->insertElementBefore($noscriptwarning, 'grade');
}
}
// Needed by plugin assignment types if they include a filemanager element in the settings form

Some files were not shown because too many files have changed in this diff Show More