Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'MOODLE_22_STABLE' into install_22_STABLE

  • Loading branch information...
commit eca810808c72f9ac62bd919168b4c047eba0c0fa 2 parents 986343b + 555112a
AMOS bot authored
Showing with 837 additions and 302 deletions.
  1. +111 −0 admin/environment.xml
  2. +7 −2 backup/moodle2/backup_custom_fields.php
  3. +17 −0 backup/upgrade.txt
  4. +9 −5 backup/util/dbops/backup_plan_dbops.class.php
  5. +21 −1 backup/util/dbops/restore_dbops.class.php
  6. +44 −23 backup/util/helper/backup_cron_helper.class.php
  7. +11 −0 backup/util/plan/backup_structure_step.class.php
  8. +28 −0 backup/util/plan/base_task.class.php
  9. +8 −2 backup/util/plan/restore_structure_step.class.php
  10. +73 −0 backup/util/structure/backup_nested_element.class.php
  11. +3 −0  backup/util/ui/backup_ui_stage.class.php
  12. +3 −0  backup/util/ui/restore_ui_stage.class.php
  13. +14 −12 blocks/completionstatus/block_completionstatus.php
  14. +127 −112 blocks/completionstatus/details.php
  15. +1 −0  blocks/completionstatus/lang/en/block_completionstatus.php
  16. +9 −0 blog/external_blogs.php
  17. +2 −3 blog/locallib.php
  18. +1 −1  enrol/manual/yui/quickenrolment/quickenrolment.js
  19. +11 −11 enrol/paypal/ipn.php
  20. +15 −0 filter/mediaplugin/filter.php
  21. +3 −0  filter/mediaplugin/simpletest/testfiltermediaplugin.php
  22. +2 −1  grade/edit/tree/category_form.php
  23. +1 −1  lang/en/admin.php
  24. +2 −0  lang/en/backup.php
  25. +1 −0  lang/en/moodle.php
  26. +1 −1  lib/accesslib.php
  27. +10 −0 lib/db/upgrade.php
  28. +11 −9 lib/googleapi.php
  29. +33 −3 lib/moodlelib.php
  30. +7 −5 lib/questionlib.php
  31. +1 −1  mod/data/field/checkbox/mod.html
  32. +5 −4 mod/data/field/latlong/field.class.php
  33. +1 −1  mod/data/field/menu/mod.html
  34. +1 −1  mod/data/field/multimenu/mod.html
  35. +2 −2 mod/data/field/picture/field.class.php
  36. +4 −4 mod/data/field/picture/mod.html
  37. +1 −1  mod/data/field/radiobutton/mod.html
  38. +2 −2 mod/data/field/textarea/mod.html
  39. +11 −17 mod/data/lib.php
  40. +32 −0 mod/data/styles.css
  41. +7 −7 mod/data/templates.php
  42. +17 −1 mod/quiz/lib.php
  43. +1 −0  mod/quiz/styles.css
  44. +5 −2 question/category_class.php
  45. +3 −0  question/type/multichoice/styles.css
  46. +10 −3 report/backups/index.php
  47. +2 −2 report/stats/lib.php
  48. +1 −1  report/stats/settings.php
  49. +38 −10 repository/flickr/lib.php
  50. +48 −26 repository/flickr_public/lib.php
  51. +3 −1 repository/googledocs/lib.php
  52. +6 −0 repository/lib.php
  53. +10 −6 repository/manage_instances.php
  54. +4 −0 theme/afterburner/style/afterburner_styles.css
  55. +1 −2  theme/anomaly/style/general.css
  56. +1 −0  theme/base/style/admin.css
  57. +30 −13 user/profile.php
  58. +2 −1  user/selector/module.js
  59. +2 −2 version.php
111 admin/environment.xml
View
@@ -655,4 +655,115 @@
</PHP_SETTING>
</PHP_SETTINGS>
</MOODLE>
+ <MOODLE version="2.4" requires="2.2">
+ <UNICODE level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unicoderequired" />
+ </FEEDBACK>
+ </UNICODE>
+ <DATABASE level="required">
+ <VENDOR name="mysql" version="5.1.33" />
+ <VENDOR name="postgres" version="8.3" />
+ <VENDOR name="mssql" version="9.0" />
+ <VENDOR name="odbc_mssql" version="9.0" />
+ <VENDOR name="mssql_n" version="9.0" />
+ <VENDOR name="oracle" version="10.2" />
+ <VENDOR name="sqlite" version="2.0" />
+ </DATABASE>
+ <PHP version="5.3.2" level="required">
+ </PHP>
+ <PHP_EXTENSIONS>
+ <PHP_EXTENSION name="iconv" level="required">
+ <FEEDBACK>
+ <ON_CHECK message="iconvrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="mbstring" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="mbstringrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="curl" level="required">
+ <FEEDBACK>
+ <ON_CHECK message="curlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="openssl" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="opensslrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="tokenizer" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="tokenizerrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="xmlrpc" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="xmlrpcrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="soap" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="soaprecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="ctype" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="ctyperequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="zip" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="ziprequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="gd" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="gdrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="simplexml" level="required">
+ <FEEDBACK>
+ <ON_CHECK message="simplexmlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="spl" level="required">
+ <FEEDBACK>
+ <ON_CHECK message="splrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="pcre" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="dom" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="xml" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="intl" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="intlrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="json" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="hash" level="required"/>
+ </PHP_EXTENSIONS>
+ <PHP_SETTINGS>
+ <PHP_SETTING name="memory_limit" value="40M" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="settingmemorylimit" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ <PHP_SETTING name="safe_mode" value="0" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="settingsafemode" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ <PHP_SETTING name="file_uploads" value="1" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="settingfileuploads" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ </PHP_SETTINGS>
+ </MOODLE>
</COMPATIBILITY_MATRIX>
9 backup/moodle2/backup_custom_fields.php
View
@@ -91,14 +91,19 @@ public function process($processor) {
if (is_null($this->backupid)) {
$this->backupid = $processor->get_var(backup::VAR_BACKUPID);
}
- parent::process($processor);
+ return parent::process($processor);
}
public function fill_values($values) {
// Fill values
parent::fill_values($values);
// Do our own tasks (copy file from moodle to backup)
- backup_file_manager::copy_file_moodle2backup($this->backupid, $values);
+ try {
+ backup_file_manager::copy_file_moodle2backup($this->backupid, $values);
+ } catch (file_exception $e) {
+ $this->add_result(array('missing_files_in_pool' => true));
+ $this->add_log('missing file in pool: ' . $e->debuginfo, backup::LOG_WARNING);
+ }
}
}
17 backup/upgrade.txt
View
@@ -0,0 +1,17 @@
+This files describes API changes in /backup/*,
+information provided here is intended especially for developers.
+
+=== 2.4 ===
+
+* Since 2.3.1+ the backup file name schema has changed. The ID of the course will always be part of
+ the filename regardless of the setting 'backup_shortname'. See MDL-33812.
+
+=== 2.3 ===
+
+* Since 2.3.1+ the backup file name schema has changed. The ID of the course will always be part of
+ the filename regardless of the setting 'backup_shortname'. See MDL-33812.
+
+=== 2.2 ===
+
+* Since 2.2.4+ the backup file name schema has changed. The ID of the course will always be part of
+ the filename regardless of the setting 'backup_shortname'. See MDL-33812.
14 backup/util/dbops/backup_plan_dbops.class.php
View
@@ -197,19 +197,19 @@ public static function get_mnet_localhost_wwwroot() {
* @param int $courseid/$sectionid/$cmid
* @param bool $users Should be true is users were included in the backup
* @param bool $anonymised Should be true is user information was anonymized.
- * @param bool $useidasname true to use id, false to use strings (default)
+ * @param bool $useidonly only use the ID in the file name
* @return string The filename to use
*/
- public static function get_default_backup_filename($format, $type, $id, $users, $anonymised, $useidasname = false) {
+ public static function get_default_backup_filename($format, $type, $id, $users, $anonymised, $useidonly = false) {
global $DB;
// Calculate backup word
$backupword = str_replace(' ', '_', moodle_strtolower(get_string('backupfilename')));
$backupword = trim(clean_filename($backupword), '_');
+ // Not $useidonly, lets fetch the name
$shortname = '';
- // Not $useidasname, lets calculate it, else $id will be used
- if (!$useidasname) {
+ if (!$useidonly) {
// Calculate proper name element (based on type)
switch ($type) {
case backup::TYPE_1COURSE:
@@ -231,7 +231,11 @@ public static function get_default_backup_filename($format, $type, $id, $users,
$shortname = moodle_strtolower(trim(clean_filename($shortname), '_'));
}
- $name = empty($shortname) ? $id : $shortname;
+ // The name will always contain the ID, but we append the course short name if requested.
+ $name = $id;
+ if (!$useidonly && $shortname != '') {
+ $name .= '-' . $shortname;
+ }
// Calculate date
$backupdateformat = str_replace(' ', '_', get_string('backupnameformat', 'langconfig'));
22 backup/util/dbops/restore_dbops.class.php
View
@@ -622,10 +622,24 @@ public static function restore_get_questions($restoreid, $qcatid) {
* Given one component/filearea/context and
* optionally one source itemname to match itemids
* put the corresponding files in the pool
+ *
+ * @param string $basepath the full path to the root of unzipped backup file
+ * @param string $restoreid the restore job's identification
+ * @param string $component
+ * @param string $filearea
+ * @param int $oldcontextid
+ * @param int $dfltuserid default $file->user if the old one can't be mapped
+ * @param string|null $itemname
+ * @param int|null $olditemid
+ * @param int|null $forcenewcontextid explicit value for the new contextid (skip mapping)
+ * @param bool $skipparentitemidctxmatch
+ * @return array of result object
*/
public static function send_files_to_pool($basepath, $restoreid, $component, $filearea, $oldcontextid, $dfltuserid, $itemname = null, $olditemid = null, $forcenewcontextid = null, $skipparentitemidctxmatch = false) {
global $DB;
+ $results = array();
+
if ($forcenewcontextid) {
// Some components can have "forced" new contexts (example: questions can end belonging to non-standard context mappings,
// with questions originally at system/coursecat context in source being restored to course context in target). So we need
@@ -701,7 +715,12 @@ public static function send_files_to_pool($basepath, $restoreid, $component, $fi
// Find file in backup pool
$backuppath = $basepath . backup_file_manager::get_backup_content_file_location($file->contenthash);
if (!file_exists($backuppath)) {
- throw new restore_dbops_exception('file_not_found_in_pool', $file);
+ $result = new stdClass();
+ $result->code = 'file_missing_in_backup';
+ $result->message = sprintf('missing file %s%s in backup', $file->filepath, $file->filename);
+ $result->level = backup::LOG_WARNING;
+ $results[] = $result;
+ continue;
}
if (!$fs->file_exists($newcontextid, $component, $filearea, $rec->newitemid, $file->filepath, $file->filename)) {
$file_record = array(
@@ -721,6 +740,7 @@ public static function send_files_to_pool($basepath, $restoreid, $component, $fi
}
}
$rs->close();
+ return $results;
}
/**
67 backup/util/helper/backup_cron_helper.class.php
View
@@ -46,6 +46,8 @@
const BACKUP_STATUS_UNFINISHED = 2;
/** Course automated backup was skipped */
const BACKUP_STATUS_SKIPPED = 3;
+ /** Course automated backup had warnings */
+ const BACKUP_STATUS_WARNING = 4;
/** Run if required by the schedule set in config. Default. **/
const RUN_ON_SCHEDULE = 0;
@@ -139,7 +141,7 @@ public static function run_automated_backup($rundirective = self::RUN_ON_SCHEDUL
$params = array('courseid' => $course->id, 'time' => $now-31*24*60*60, 'action' => '%view%');
$logexists = $DB->record_exists_select('log', $sqlwhere, $params);
if (!$logexists) {
- $backupcourse->laststatus = backup_cron_automated_helper::BACKUP_STATUS_SKIPPED;
+ $backupcourse->laststatus = self::BACKUP_STATUS_SKIPPED;
$backupcourse->nextstarttime = $nextstarttime;
$DB->update_record('backup_courses', $backupcourse);
mtrace('Skipping unchanged course '.$course->fullname);
@@ -160,7 +162,7 @@ public static function run_automated_backup($rundirective = self::RUN_ON_SCHEDUL
$starttime = time();
$backupcourse->laststarttime = time();
- $backupcourse->laststatus = backup_cron_automated_helper::BACKUP_STATUS_UNFINISHED;
+ $backupcourse->laststatus = self::BACKUP_STATUS_UNFINISHED;
$DB->update_record('backup_courses', $backupcourse);
$backupcourse->laststatus = backup_cron_automated_helper::launch_automated_backup($course, $backupcourse->laststarttime, $admin->id);
@@ -169,7 +171,7 @@ public static function run_automated_backup($rundirective = self::RUN_ON_SCHEDUL
$DB->update_record('backup_courses', $backupcourse);
- if ($backupcourse->laststatus) {
+ if ($backupcourse->laststatus === self::BACKUP_STATUS_OK) {
// Clean up any excess course backups now that we have
// taken a successful backup.
$removedcount = backup_cron_automated_helper::remove_excess_backups($course);
@@ -188,17 +190,18 @@ public static function run_automated_backup($rundirective = self::RUN_ON_SCHEDUL
$message = "";
$count = backup_cron_automated_helper::get_backup_status_array();
- $haserrors = ($count[backup_cron_automated_helper::BACKUP_STATUS_ERROR] != 0 || $count[backup_cron_automated_helper::BACKUP_STATUS_UNFINISHED] != 0);
+ $haserrors = ($count[self::BACKUP_STATUS_ERROR] != 0 || $count[self::BACKUP_STATUS_UNFINISHED] != 0);
//Build the message text
//Summary
$message .= get_string('summary')."\n";
$message .= "==================================================\n";
$message .= " ".get_string('courses').": ".array_sum($count)."\n";
- $message .= " ".get_string('ok').": ".$count[backup_cron_automated_helper::BACKUP_STATUS_OK]."\n";
- $message .= " ".get_string('skipped').": ".$count[backup_cron_automated_helper::BACKUP_STATUS_SKIPPED]."\n";
- $message .= " ".get_string('error').": ".$count[backup_cron_automated_helper::BACKUP_STATUS_ERROR]."\n";
- $message .= " ".get_string('unfinished').": ".$count[backup_cron_automated_helper::BACKUP_STATUS_UNFINISHED]."\n\n";
+ $message .= " ".get_string('ok').": ".$count[self::BACKUP_STATUS_OK]."\n";
+ $message .= " ".get_string('skipped').": ".$count[self::BACKUP_STATUS_SKIPPED]."\n";
+ $message .= " ".get_string('error').": ".$count[self::BACKUP_STATUS_ERROR]."\n";
+ $message .= " ".get_string('unfinished').": ".$count[self::BACKUP_STATUS_UNFINISHED]."\n";
+ $message .= " ".get_string('warning').": ".$count[self::BACKUP_STATUS_WARNING]."\n\n";
//Reference
if ($haserrors) {
@@ -261,6 +264,7 @@ public static function get_backup_status_array() {
self::BACKUP_STATUS_OK => 0,
self::BACKUP_STATUS_UNFINISHED => 0,
self::BACKUP_STATUS_SKIPPED => 0,
+ self::BACKUP_STATUS_WARNING => 0
);
$statuses = $DB->get_records_sql('SELECT DISTINCT bc.laststatus, COUNT(bc.courseid) AS statuscount FROM {backup_courses} bc GROUP BY bc.laststatus');
@@ -334,7 +338,7 @@ public static function calculate_next_automated_backup($timezone, $now) {
*/
public static function launch_automated_backup($course, $starttime, $userid) {
- $outcome = true;
+ $outcome = self::BACKUP_STATUS_OK;
$config = get_config('backup');
$bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_AUTOMATED, $userid);
@@ -369,6 +373,7 @@ public static function launch_automated_backup($course, $starttime, $userid) {
$bc->execute_plan();
$results = $bc->get_results();
+ $outcome = self::outcome_from_results($results);
$file = $results['backup_destination']; // may be empty if file already moved to target location
$dir = $config->backup_auto_destination;
$storage = (int)$config->backup_auto_storage;
@@ -377,8 +382,10 @@ public static function launch_automated_backup($course, $starttime, $userid) {
}
if ($file && !empty($dir) && $storage !== 0) {
$filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised, !$config->backup_shortname);
- $outcome = $file->copy_content_to($dir.'/'.$filename);
- if ($outcome && $storage === 1) {
+ if (!$file->copy_content_to($dir.'/'.$filename)) {
+ $outcome = self::BACKUP_STATUS_ERROR;
+ }
+ if ($outcome != self::BACKUP_STATUS_ERROR && $storage === 1) {
$file->delete();
}
}
@@ -387,7 +394,7 @@ public static function launch_automated_backup($course, $starttime, $userid) {
$bc->log('backup_auto_failed_on_course', backup::LOG_ERROR, $course->shortname); // Log error header.
$bc->log('Exception: ' . $e->errorcode, backup::LOG_ERROR, $e->a, 1); // Log original exception problem.
$bc->log('Debug: ' . $e->debuginfo, backup::LOG_DEBUG, null, 1); // Log original debug information.
- $outcome = false;
+ $outcome = self::BACKUP_STATUS_ERROR;
}
$bc->destroy();
@@ -397,6 +404,30 @@ public static function launch_automated_backup($course, $starttime, $userid) {
}
/**
+ * Returns the backup outcome by analysing its results.
+ *
+ * @param array $results returned by a backup
+ * @return int {@link self::BACKUP_STATUS_OK} and other constants
+ */
+ public static function outcome_from_results($results) {
+ $outcome = self::BACKUP_STATUS_OK;
+ foreach ($results as $code => $value) {
+ // Each possible error and warning code has to be specified in this switch
+ // which basically analyses the results to return the correct backup status.
+ switch ($code) {
+ case 'missing_files_in_pool':
+ $outcome = self::BACKUP_STATUS_WARNING;
+ break;
+ }
+ // If we found the highest error level, we exit the loop.
+ if ($outcome == self::BACKUP_STATUS_ERROR) {
+ break;
+ }
+ }
+ return $outcome;
+ }
+
+ /**
* Removes deleted courses fromn the backup_courses table so that we don't
* waste time backing them up.
*
@@ -530,17 +561,7 @@ public static function remove_excess_backups($course) {
if (!empty($dir) && ($storage == 1 || $storage == 2)) {
// Calculate backup filename regex, ignoring the date/time/info parts that can be
// variable, depending of languages, formats and automated backup settings
-
- // MDL-33531: use different filenames depending on backup_shortname option
- if ( !empty($config->backup_shortname) ) {
- $context = get_context_instance(CONTEXT_COURSE, $course->id);
- $courseref = format_string($course->shortname, true, array('context' => $context));
- $courseref = str_replace(' ', '_', $courseref);
- $courseref = moodle_strtolower(trim(clean_filename($courseref), '_'));
- } else {
- $courseref = $course->id;
- }
- $filename = $backupword . '-' . backup::FORMAT_MOODLE . '-' . backup::TYPE_1COURSE . '-' .$courseref . '-';
+ $filename = $backupword . '-' . backup::FORMAT_MOODLE . '-' . backup::TYPE_1COURSE . '-' .$course->id . '-';
$regex = '#^'.preg_quote($filename, '#').'.*\.mbz$#';
// Store all the matching files into fullpath => timemodified array
11 backup/util/plan/backup_structure_step.class.php
View
@@ -94,11 +94,22 @@ public function execute() {
// Process structure definition
$structure->process($pr);
+ // Get the results from the nested elements
+ $results = $structure->get_results();
+
+ // Get the log messages to append to the log
+ $logs = $structure->get_logs();
+ foreach ($logs as $log) {
+ $this->log($log->message, $log->level, $log->a, $log->depth, $log->display);
+ }
+
// Close everything
$xw->stop();
// Destroy the structure. It helps PHP 5.2 memory a lot!
$structure->destroy();
+
+ return $results;
}
/**
28 backup/util/plan/base_task.class.php
View
@@ -191,6 +191,34 @@ public function calculate_checksum() {
backup_general_helper::array_checksum_recursive($this->steps));
}
+ /**
+ * Add the given info to the current plan's results.
+ *
+ * @see base_plan::add_result()
+ * @param array $result associative array describing a result of a task/step
+ */
+ public function add_result($result) {
+ if (!is_null($this->plan)) {
+ $this->plan->add_result($result);
+ } else {
+ debugging('Attempting to add a result of a task not binded with a plan', DEBUG_DEVELOPER);
+ }
+ }
+
+ /**
+ * Return the current plan's results
+ *
+ * @return array|null
+ */
+ public function get_results() {
+ if (!is_null($this->plan)) {
+ return $this->plan->get_results();
+ } else {
+ debugging('Attempting to get results of a task not binded with a plan', DEBUG_DEVELOPER);
+ return null;
+ }
+ }
+
// Protected API starts here
/**
10 backup/util/plan/restore_structure_step.class.php
View
@@ -218,8 +218,14 @@ public function get_mapping($itemname, $oldid) {
*/
public function add_related_files($component, $filearea, $mappingitemname, $filesctxid = null, $olditemid = null) {
$filesctxid = is_null($filesctxid) ? $this->task->get_old_contextid() : $filesctxid;
- restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component,
- $filearea, $filesctxid, $this->task->get_userid(), $mappingitemname, $olditemid);
+ $results = restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component,
+ $filearea, $filesctxid, $this->task->get_userid(), $mappingitemname, $olditemid);
+ $resultstoadd = array();
+ foreach ($results as $result) {
+ $this->log($result->message, $result->level);
+ $resultstoadd[$result->code] = true;
+ }
+ $this->task->add_result($resultstoadd);
}
/**
73 backup/util/structure/backup_nested_element.class.php
View
@@ -37,6 +37,8 @@ class backup_nested_element extends base_nested_element implements processable {
protected $aliases; // Define DB->final element aliases
protected $fileannotations; // array of file areas to be searched by file annotations
protected $counter; // Number of instances of this element that have been processed
+ protected $results; // Logs the results we encounter during the process.
+ protected $logs; // Some log messages that could be retrieved later.
/**
* Constructor - instantiates one backup_nested_element, specifying its basic info.
@@ -55,8 +57,16 @@ public function __construct($name, $attributes = null, $final_elements = null) {
$this->aliases = array();
$this->fileannotations = array();
$this->counter = 0;
+ $this->results = array();
+ $this->logs = array();
}
+ /**
+ * Process the nested element
+ *
+ * @param object $processor the processor
+ * @return void
+ */
public function process($processor) {
if (!$processor instanceof base_processor) { // No correct processor, throw exception
throw new base_element_struct_exception('incorrect_processor');
@@ -113,6 +123,69 @@ public function process($processor) {
$iterator->close();
}
+ /**
+ * Saves a log message to an array
+ *
+ * @see backup_helper::log()
+ * @param string $message to add to the logs
+ * @param int $level level of importance {@link backup::LOG_DEBUG} and other constants
+ * @param mixed $a to be included in $message
+ * @param int $depth of the message
+ * @param display $bool supporting translation via get_string() if true
+ * @return void
+ */
+ protected function add_log($message, $level, $a = null, $depth = null, $display = false) {
+ // Adding the result to the oldest parent.
+ if ($this->get_parent()) {
+ $parent = $this->get_grandparent();
+ $parent->add_log($message, $level, $a, $depth, $display);
+ } else {
+ $log = new stdClass();
+ $log->message = $message;
+ $log->level = $level;
+ $log->a = $a;
+ $log->depth = $depth;
+ $log->display = $display;
+ $this->logs[] = $log;
+ }
+ }
+
+ /**
+ * Saves the results to an array
+ *
+ * @param array $result associative array
+ * @return void
+ */
+ protected function add_result($result) {
+ if (is_array($result)) {
+ // Adding the result to the oldest parent.
+ if ($this->get_parent()) {
+ $parent = $this->get_grandparent();
+ $parent->add_result($result);
+ } else {
+ $this->results = array_merge($this->results, $result);
+ }
+ }
+ }
+
+ /**
+ * Returns the logs
+ *
+ * @return array of log objects
+ */
+ public function get_logs() {
+ return $this->logs;
+ }
+
+ /**
+ * Returns the results
+ *
+ * @return associative array of results
+ */
+ public function get_results() {
+ return $this->results;
+ }
+
public function set_source_array($arr) {
// TODO: Only elements having final elements can set source
$this->var_array = $arr;
3  backup/util/ui/backup_ui_stage.class.php
View
@@ -469,6 +469,9 @@ public function display() {
}
echo $OUTPUT->box_start();
+ if (!empty($this->results['missing_files_in_pool'])) {
+ echo $OUTPUT->notification(get_string('missingfilesinpool', 'backup'), 'notifyproblem');
+ }
echo $OUTPUT->notification(get_string('executionsuccess', 'backup'), 'notifysuccess');
echo $OUTPUT->continue_button($restorerul);
echo $OUTPUT->box_end();
3  backup/util/ui/restore_ui_stage.class.php
View
@@ -757,6 +757,9 @@ public function display(core_backup_renderer $renderer) {
$html = '';
$html .= $renderer->box_start();
+ if (array_key_exists('file_missing_in_backup', $this->results)) {
+ $html .= $renderer->notification(get_string('restorefileweremissing', 'backup'), 'notifyproblem');
+ }
$html .= $renderer->notification(get_string('restoreexecutionsuccess', 'backup'), 'notifysuccess');
$html .= $renderer->continue_button(new moodle_url('/course/view.php', array(
'id' => $this->get_ui()->get_controller()->get_courseid())), 'get');
26 blocks/completionstatus/block_completionstatus.php
View
@@ -19,15 +19,14 @@
*
* @package block
* @subpackage completion
- * @copyright 2009 Catalyst IT Ltd
+ * @copyright 2009-2012 Catalyst IT Ltd
* @author Aaron Barnes <aaronb@catalyst.net.nz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
-
-require_once($CFG->libdir.'/completionlib.php');
+require_once("{$CFG->libdir}/completionlib.php");
/**
* Course completion status
@@ -40,21 +39,23 @@ public function init() {
}
public function get_content() {
- global $USER, $CFG, $DB, $COURSE;
+ global $USER;
// If content is cached
if ($this->content !== NULL) {
return $this->content;
}
+ $course = $this->page->course;
+
// Create empty content
- $this->content = new stdClass;
+ $this->content = new stdClass();
// Can edit settings?
- $can_edit = has_capability('moodle/course:update', get_context_instance(CONTEXT_COURSE, $this->page->course->id));
+ $can_edit = has_capability('moodle/course:update', get_context_instance(CONTEXT_COURSE, $course->id));
// Get course completion data
- $info = new completion_info($this->page->course);
+ $info = new completion_info($course);
// Don't display if completion isn't enabled!
if (!completion_info::is_enabled_for_site()) {
@@ -84,9 +85,9 @@ public function get_content() {
// Check this user is enroled
if (!$info->is_tracked_user($USER->id)) {
// If not enrolled, but are can view the report:
- if (has_capability('report/completion:view', get_context_instance(CONTEXT_COURSE, $COURSE->id))) {
- $this->content->text = '<a href="'.$CFG->wwwroot.'/report/completion/index.php?course='.$COURSE->id.
- '">'.get_string('viewcoursereport', 'completion').'</a>';
+ if (has_capability('report/completion:view', get_context_instance(CONTEXT_COURSE, $course->id))) {
+ $report = new moodle_url('/report/completion/index.php', array('course' => $course->id));
+ $this->content->text = '<a href="'.$report->out().'">'.get_string('viewcoursereport', 'completion').'</a>';
return $this->content;
}
@@ -187,7 +188,7 @@ public function get_content() {
// Load course completion
$params = array(
'userid' => $USER->id,
- 'course' => $COURSE->id
+ 'course' => $course->id
);
$ccompletion = new completion_completion($params);
@@ -221,7 +222,8 @@ public function get_content() {
$this->content->text .= $shtml.'</tbody></table>';
// Display link to detailed view
- $this->content->footer = '<br><a href="'.$CFG->wwwroot.'/blocks/completionstatus/details.php?course='.$COURSE->id.'">'.get_string('moredetails', 'completion').'</a>';
+ $details = new moodle_url('/blocks/completionstatus/details.php', array('course' => $course->id));
+ $this->content->footer = '<br><a href="'.$details->out().'">'.get_string('moredetails', 'completion').'</a>';
return $this->content;
}
239 blocks/completionstatus/details.php
View
@@ -19,27 +19,23 @@
*
* @package block
* @subpackage completion
- * @copyright 2009 Catalyst IT Ltd
+ * @copyright 2009-2012 Catalyst IT Ltd
* @author Aaron Barnes <aaronb@catalyst.net.nz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-require_once('../../config.php');
-require_once($CFG->libdir.'/completionlib.php');
-
-
-// TODO: Make this page Moodle 2.0 compliant
+require_once(dirname(__FILE__).'/../../config.php');
+require_once("{$CFG->libdir}/completionlib.php");
///
/// Load data
///
$id = required_param('course', PARAM_INT);
-// User id
$userid = optional_param('user', 0, PARAM_INT);
// Load course
-$course = $DB->get_record('course', array('id' => $id));
+$course = $DB->get_record('course', array('id' => $id), '*', MUST_EXIST);
// Load user
if ($userid) {
@@ -76,21 +72,13 @@
// Load completion data
$info = new completion_info($course);
-$returnurl = "{$CFG->wwwroot}/course/view.php?id={$id}";
+$returnurl = new moodle_url('/course/view.php', array('id' => $id));
// Don't display if completion isn't enabled!
if (!$info->is_enabled()) {
print_error('completionnotenabled', 'completion', $returnurl);
}
-// Load criteria to display
-$completions = $info->get_completions($user->id);
-
-// Check if this course has any criteria
-if (empty($completions)) {
- print_error('nocriteriaset', 'completion', $returnurl);
-}
-
// Check this user is enroled
if (!$info->is_tracked_user($user->id)) {
if ($USER->id == $user->id) {
@@ -104,6 +92,7 @@
///
/// Display page
///
+$PAGE->set_context(context_course::instance($course->id));
// Print header
$page = get_string('completionprogressdetails', 'block_completionstatus');
@@ -111,7 +100,7 @@
$PAGE->navbar->add($page);
$PAGE->set_pagelayout('standard');
-$PAGE->set_url('/blocks/completionstatus/details.php', array('course' => $course->id));
+$PAGE->set_url('/blocks/completionstatus/details.php', array('course' => $course->id, 'user' => $user->id));
$PAGE->set_title(get_string('course') . ': ' . $course->fullname);
$PAGE->set_heading($title);
echo $OUTPUT->header();
@@ -135,122 +124,148 @@
// Has this user completed any criteria?
$criteriacomplete = $info->count_course_user_data($user->id);
+// Load course completion
+$params = array(
+ 'userid' => $user->id,
+ 'course' => $course->id,
+);
+$ccompletion = new completion_completion($params);
+
if ($coursecomplete) {
echo get_string('complete');
-} else if (!$criteriacomplete) {
+} else if (!$criteriacomplete && !$ccompletion->timestarted) {
echo '<i>'.get_string('notyetstarted', 'completion').'</i>';
} else {
echo '<i>'.get_string('inprogress','completion').'</i>';
}
echo '</td></tr>';
-echo '<tr><td colspan="2"><b>'.get_string('required').':</b> ';
-// Get overall aggregation method
-$overall = $info->get_aggregation_method();
+// Load criteria to display
+$completions = $info->get_completions($user->id);
-if ($overall == COMPLETION_AGGREGATION_ALL) {
- echo get_string('criteriarequiredall', 'completion');
+// Check if this course has any criteria
+if (empty($completions)) {
+ echo '<tr><td colspan="2"><br />';
+ echo $OUTPUT->box(get_string('err_nocriteria', 'completion'), 'noticebox');
+ echo '</td></tr></tbody></table>';
} else {
- echo get_string('criteriarequiredany', 'completion');
-}
+ echo '<tr><td colspan="2"><b>'.get_string('required').':</b> ';
-echo '</td></tr></tbody></table>';
-
-// Generate markup for criteria statuses
-echo '<table class="generalbox boxaligncenter" cellpadding="3"><tbody>';
-echo '<tr class="ccheader">';
-echo '<th class="c0 header" scope="col">'.get_string('criteriagroup', 'block_completionstatus').'</th>';
-echo '<th class="c1 header" scope="col">'.get_string('criteria', 'completion').'</th>';
-echo '<th class="c2 header" scope="col">'.get_string('requirement', 'block_completionstatus').'</th>';
-echo '<th class="c3 header" scope="col">'.get_string('status').'</th>';
-echo '<th class="c4 header" scope="col">'.get_string('complete').'</th>';
-echo '<th class="c5 header" scope="col">'.get_string('completiondate', 'report_completion').'</th>';
-echo '</tr>';
-
-// Save row data
-$rows = array();
-
-global $COMPLETION_CRITERIA_TYPES;
-
-// Loop through course criteria
-foreach ($completions as $completion) {
- $criteria = $completion->get_criteria();
- $complete = $completion->is_complete();
-
- $row = array();
- $row['type'] = $criteria->criteriatype;
- $row['title'] = $criteria->get_title();
- $row['status'] = $completion->get_status();
- $row['timecompleted'] = $completion->timecompleted;
- $row['details'] = $criteria->get_details($completion);
- $rows[] = $row;
-}
+ // Get overall aggregation method
+ $overall = $info->get_aggregation_method();
-// Print table
-$last_type = '';
-$agg_type = false;
+ if ($overall == COMPLETION_AGGREGATION_ALL) {
+ echo get_string('criteriarequiredall', 'completion');
+ } else {
+ echo get_string('criteriarequiredany', 'completion');
+ }
+
+ echo '</td></tr></tbody></table>';
+
+ // Generate markup for criteria statuses
+ echo '<table class="generalbox logtable boxaligncenter" id="criteriastatus" width="100%"><tbody>';
+ echo '<tr class="ccheader">';
+ echo '<th class="c0 header" scope="col">'.get_string('criteriagroup', 'block_completionstatus').'</th>';
+ echo '<th class="c1 header" scope="col">'.get_string('criteria', 'completion').'</th>';
+ echo '<th class="c2 header" scope="col">'.get_string('requirement', 'block_completionstatus').'</th>';
+ echo '<th class="c3 header" scope="col">'.get_string('status').'</th>';
+ echo '<th class="c4 header" scope="col">'.get_string('complete').'</th>';
+ echo '<th class="c5 header" scope="col">'.get_string('completiondate', 'report_completion').'</th>';
+ echo '</tr>';
-foreach ($rows as $row) {
+ // Save row data
+ $rows = array();
+
+ // Loop through course criteria
+ foreach ($completions as $completion) {
+ $criteria = $completion->get_criteria();
+
+ $row = array();
+ $row['type'] = $criteria->criteriatype;
+ $row['title'] = $criteria->get_title();
+ $row['status'] = $completion->get_status();
+ $row['complete'] = $completion->is_complete();
+ $row['timecompleted'] = $completion->timecompleted;
+ $row['details'] = $criteria->get_details($completion);
+ $rows[] = $row;
+ }
- // Criteria group
- echo '<td class="c0">';
- if ($last_type !== $row['details']['type']) {
- $last_type = $row['details']['type'];
- echo $last_type;
+ // Print table
+ $last_type = '';
+ $agg_type = false;
+ $oddeven = 0;
- // Reset agg type
- $agg_type = true;
- } else {
- // Display aggregation type
- if ($agg_type) {
- $agg = $info->get_aggregation_method($row['type']);
+ foreach ($rows as $row) {
- echo '(<i>';
+ echo '<tr class="r' . $oddeven . '">';
- if ($agg == COMPLETION_AGGREGATION_ALL) {
- echo strtolower(get_string('all', 'completion'));
- } else {
- echo strtolower(get_string('any', 'completion'));
- }
+ // Criteria group
+ echo '<td class="cell c0">';
+ if ($last_type !== $row['details']['type']) {
+ $last_type = $row['details']['type'];
+ echo $last_type;
+
+ // Reset agg type
+ $agg_type = true;
+ } else {
+ // Display aggregation type
+ if ($agg_type) {
+ $agg = $info->get_aggregation_method($row['type']);
- echo '</i> '.strtolower(get_string('required')).')';
- $agg_type = false;
+ echo '(<i>';
+
+ if ($agg == COMPLETION_AGGREGATION_ALL) {
+ echo strtolower(get_string('aggregateall', 'completion'));
+ } else {
+ echo strtolower(get_string('aggregateany', 'completion'));
+ }
+
+ echo '</i> '.strtolower(get_string('required')).')';
+ $agg_type = false;
+ }
}
+ echo '</td>';
+
+ // Criteria title
+ echo '<td class="cell c1">';
+ echo $row['details']['criteria'];
+ echo '</td>';
+
+ // Requirement
+ echo '<td class="cell c2">';
+ echo $row['details']['requirement'];
+ echo '</td>';
+
+ // Status
+ echo '<td class="cell c3">';
+ echo $row['details']['status'];
+ echo '</td>';
+
+ // Is complete
+ echo '<td class="cell c4">';
+ echo $row['complete'] ? get_string('yes') : get_string('no');
+ echo '</td>';
+
+ // Completion data
+ echo '<td class="cell c5">';
+ if ($row['timecompleted']) {
+ echo userdate($row['timecompleted'], get_string('strftimedate', 'langconfig'));
+ } else {
+ echo '-';
+ }
+ echo '</td>';
+ echo '</tr>';
+ // for row striping
+ $oddeven = $oddeven ? 0 : 1;
}
- echo '</td>';
-
- // Criteria title
- echo '<td class="c1">';
- echo $row['details']['criteria'];
- echo '</td>';
-
- // Requirement
- echo '<td class="c2">';
- echo $row['details']['requirement'];
- echo '</td>';
-
- // Status
- echo '<td class="c3">';
- echo $row['details']['status'];
- echo '</td>';
-
- // Is complete
- echo '<td class="c4">';
- echo ($row['status'] === get_string('yes')) ? get_string('yes') : get_string('no');
- echo '</td>';
-
- // Completion data
- echo '<td class="c5">';
- if ($row['timecompleted']) {
- echo userdate($row['timecompleted'], '%e %B %G');
- } else {
- echo '-';
- }
- echo '</td>';
- echo '</tr>';
+
+ echo '</tbody></table>';
}
-echo '</tbody></table>';
+echo '<div class="buttons">';
+$courseurl = new moodle_url("/course/view.php", array('id' => $course->id));
+echo $OUTPUT->single_button($courseurl, get_string('returntocourse', 'block_completionstatus'), 'get');
+echo '</div>';
echo $OUTPUT->footer();
1  blocks/completionstatus/lang/en/block_completionstatus.php
View
@@ -6,3 +6,4 @@
$string['firstofsecond'] = '{$a->first} of {$a->second}';
$string['pluginname'] = 'Course completion status';
$string['requirement'] = 'Requirement';
+$string['returntocourse'] = 'Return to course';
9 blog/external_blogs.php
View
@@ -44,7 +44,16 @@
if ($delete && confirm_sesskey()) {
$externalbloguserid = $DB->get_field('blog_external', 'userid', array('id' => $delete));
if ($externalbloguserid == $USER->id) {
+ // Delete the external blog
$DB->delete_records('blog_external', array('id' => $delete));
+
+ // Delete the external blog's posts
+ $deletewhere = 'module = :module
+ AND userid = :userid
+ AND ' . $DB->sql_isnotempty('post', 'uniquehash', false, false) . '
+ AND ' . $DB->sql_compare_text('content') . ' = ' . $DB->sql_compare_text(':delete');
+ $DB->delete_records_select('post', $deletewhere, array('module' => 'blog_external', 'userid' => $USER->id, 'delete' => $delete));
+
$message = get_string('externalblogdeleted', 'blog');
}
}
5 blog/locallib.php
View
@@ -405,11 +405,10 @@ public function edit($params=array(), $form=null, $summaryoptions=array(), $atta
* @return void
*/
public function delete() {
- global $DB, $USER;
-
- $returnurl = '';
+ global $DB;
$this->delete_attachments();
+ $this->remove_associations();
$DB->delete_records('post', array('id' => $this->id));
tag_set('post', $this->id, array());
2  enrol/manual/yui/quickenrolment/quickenrolment.js
View
@@ -338,7 +338,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
count++;
var user = result.response.users[i];
users.append(create('<div class="'+CSS.USER+' clearfix" rel="'+user.id+'"></div>')
- .addClass((i%2)?CSS.ODD:CSS.EVEN)
+ .addClass((count%2)?CSS.ODD:CSS.EVEN)
.append(create('<div class="'+CSS.COUNT+'">'+count+'</div>'))
.append(create('<div class="'+CSS.PICTURE+'"></div>')
.append(create(user.picture)))
22 enrol/paypal/ipn.php
View
@@ -34,6 +34,7 @@
require_once("lib.php");
require_once($CFG->libdir.'/eventslib.php');
require_once($CFG->libdir.'/enrollib.php');
+require_once($CFG->libdir . '/filelib.php');
/// Keep out casual intruders
@@ -89,14 +90,17 @@
$plugin = enrol_get_plugin('paypal');
/// Open a connection back to PayPal to validate the data
-$header = '';
-$header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
-$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
-$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
+$c = new curl();
+$options = array(
+ 'returntransfer' => true,
+ 'httpheader' => array('application/x-www-form-urlencoded'),
+ 'timeout' => 30,
+);
$paypaladdr = empty($CFG->usepaypalsandbox) ? 'www.paypal.com' : 'www.sandbox.paypal.com';
-$fp = fsockopen ($paypaladdr, 80, $errno, $errstr, 30);
+$location = "https://$paypaladdr/cgi-bin/webscr";
+$result = $c->post($location, $req, $options);
-if (!$fp) { /// Could not open a socket to PayPal - FAIL
+if (!$result) { /// Could not connect to PayPal - FAIL
echo "<p>Error: could not access paypal.com</p>";
message_paypal_error_to_admin("Could not access paypal.com to verify payment", $data);
die;
@@ -104,12 +108,9 @@
/// Connection is OK, so now we post the data to validate it
-fputs ($fp, $header.$req);
-
/// Now read the response and check if everything is OK.
-while (!feof($fp)) {
- $result = fgets($fp, 1024);
+if (strlen($result) > 0) {
if (strcmp($result, "VERIFIED") == 0) { // VALID PAYMENT!
@@ -296,7 +297,6 @@
}
}
-fclose($fp);
exit;
15 filter/mediaplugin/filter.php
View
@@ -90,6 +90,9 @@ function filter($text, array $options = array()) {
$search = '/<a\s[^>]*href="(https?:\/\/www\.youtube(-nocookie)?\.com)\/v\/([a-z0-9\-_]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?[^>]*>([^>]*)<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
+ $search = '/<a\s[^>]*href="(https?:\/\/(www\.)?(youtu|y2u)\.be)\/([a-z0-9\-_]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?"[^>]*>([^>]*)<\/a>/is';
+ $newtext = preg_replace_callback($search, 'filter_mediaplugin_shortened_youtube_callback', $newtext);
+
$search = '/<a\s[^>]*href="(https?:\/\/www\.youtube(-nocookie)?\.com)\/view_play_list\?p=([a-z0-9\-_]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?[^>]*>([^>]*)<\/a>/is';
$newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_playlist_callback', $newtext);
@@ -759,6 +762,18 @@ function filter_mediaplugin_youtube_playlist_callback($link) {
}
/**
+ * Change shortened links to YouTube into embedded YouTube videos
+ *
+ * @param $link
+ * @return string
+ */
+function filter_mediaplugin_shortened_youtube_callback($link) {
+ $newlink = array($link[0], 'https://www.youtube.com','',$link[4],'',$link[6],$link[7],$link[8]);
+ return filter_mediaplugin_youtube_callback($newlink);
+}
+
+
+/**
* Change links to Vimeo into embedded Vimeo videos
*
* @param $link
3  filter/mediaplugin/simpletest/testfiltermediaplugin.php
View
@@ -58,7 +58,10 @@ function test_filter_mediaplugin_link() {
'<a id="movie player" class="center" href="http://moodle.org/testfile/test.mpg">test mpg</a>',
'<a href="http://moodle.org/testfile/test.ram">test</a>',
'<a href="http://www.youtube.com/watch?v=JghQgA2HMX8" class="href=css">test file</a>',
+ '<a href="http://www.youtube-nocookie.com/watch?v=JghQgA2HMX8" class="href=css">test file</a>',
'<a class="youtube" href="http://www.youtube.com/watch?v=JghQgA2HMX8">test file</a>',
+ '<a href="http://youtu.be/JghQgA2HMX8" class="href=css">test file</a>',
+ '<a href="http://y2u.be/JghQgA2HMX8" class="href=css">test file</a>',
'<a class="_blanktarget" href="http://moodle.org/testfile/test.flv?d=100x100">test flv</a>',
'<a class="hrefcss" href="http://www.youtube.com/watch?v=JghQgA2HMX8">test file</a>',
'<a class="content" href="http://moodle.org/testfile/test.avi">test mp3</a>',
3  grade/edit/tree/category_form.php
View
@@ -218,7 +218,7 @@ function definition() {
$mform->addElement('header', 'headerparent', get_string('parentcategory', 'grades'));
$options = array();
- $default = '';
+ $default = -1;
$categories = grade_category::fetch_all(array('courseid'=>$COURSE->id));
foreach ($categories as $cat) {
@@ -231,6 +231,7 @@ function definition() {
if (count($categories) > 1) {
$mform->addElement('select', 'parentcategory', get_string('parentcategory', 'grades'), $options);
+ $mform->setDefault('parentcategory', $default);
$mform->addElement('static', 'currentparentaggregation', get_string('currentparentaggregation', 'grades'));
}
2  lang/en/admin.php
View
@@ -67,7 +67,7 @@
$string['backgroundcolour'] = 'Transparent colour';
$string['backups'] = 'Backups';
$string['backup_shortname'] = 'Use course name in backup filename';
-$string['backup_shortnamehelp'] = 'Use the course name as part of the backup filename instead of the course id number.';
+$string['backup_shortnamehelp'] = 'Use the course name as part of the backup filename.';
$string['badwordsconfig'] = 'Enter your list of bad words separated by commas.';
$string['badwordsdefault'] = 'If the custom list is empty, a default list from the language pack will be used.';
$string['badwordslist'] = 'Custom bad words list';
2  lang/en/backup.php
View
@@ -153,6 +153,7 @@
$string['lockedbyconfig'] = 'This setting has been locked by the default backup settings';
$string['lockedbyhierarchy'] = 'Locked by dependencies';
$string['managefiles'] = 'Manage backup files';
+$string['missingfilesinpool'] = 'Some files could not be saved during the backup, it won\'t be possible to restore them.';
$string['moodleversion'] = 'Moodle version';
$string['moreresults'] = 'There are too many results, enter a more specific search.';
$string['nomatchingcourses'] = 'There are no courses to display';
@@ -167,6 +168,7 @@
$string['restorecourse'] = 'Restore course';
$string['restorecoursesettings'] = 'Course settings';
$string['restoreexecutionsuccess'] = 'The course was restored successfully, clicking the continue button below will take you to view the course you restored.';
+$string['restorefileweremissing'] = 'Some files could not be restored because they were missing in the backup.';
$string['restorenewcoursefullname'] = 'New course name';
$string['restorenewcourseshortname'] = 'New course short name';
$string['restorenewcoursestartdate'] = 'New start date';
1  lang/en/moodle.php
View
@@ -1764,6 +1764,7 @@
$string['virusplaceholder'] = 'This file that has been uploaded was found to contain a virus and has been moved or deleted and the user notified.';
$string['visible'] = 'Visible';
$string['visibletostudents'] = 'Visible to {$a}';
+$string['warning'] = 'Warning';
$string['warningdeleteresource'] = 'Warning: {$a} is referred in a resource. Would you like to update the resource?';
$string['webpage'] = 'Web page';
$string['week'] = 'Week';
2  lib/accesslib.php
View
@@ -6419,7 +6419,7 @@ public function get_context_name($withprefix = true, $short = false) {
if ($withprefix){
$name = get_string('modulename', $cm->modname).': ';
}
- $name .= $mod->name;
+ $name .= format_string($mod->name, true, array('context' => $this));
}
}
return $name;
10 lib/db/upgrade.php
View
@@ -7149,5 +7149,15 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2011120504.05);
}
+ if ($oldversion < 2011120504.12) {
+ $subquery = 'SELECT b.id FROM {blog_external} b where b.id = ' . $DB->sql_cast_char2int('{post}.content', true);
+ $sql = 'DELETE FROM {post}
+ WHERE {post}.module = \'blog_external\'
+ AND NOT EXISTS (' . $subquery . ')
+ AND ' . $DB->sql_isnotempty('post', 'uniquehash', false, false);
+ $DB->execute($sql);
+ upgrade_main_savepoint(true, 2011120504.12);
+ }
+
return true;
}
20 lib/googleapi.php
View
@@ -316,19 +316,21 @@ public function get_file_list($search = ''){
$source = 'https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key='.$docid.'&exportFormat=xls';
break;
case 'pdf':
+ case 'file':
$title = (string)$gdoc->title;
- $source = (string)$gdoc->content[0]->attributes()->src;
+ // Some files don't have a content probably because the download has been restricted.
+ if (isset($gdoc->content)) {
+ $source = (string)$gdoc->content[0]->attributes()->src;
+ }
break;
}
- if(!empty($source)){
- $files[] = array( 'title' => $title,
- 'url' => "{$gdoc->link[0]->attributes()->href}",
- 'source' => $source,
- 'date' => usertime(strtotime($gdoc->updated)),
- 'thumbnail' => (string) $OUTPUT->pix_url(file_extension_icon($title, 32))
- );
- }
+ $files[] = array( 'title' => $title,
+ 'url' => "{$gdoc->link[0]->attributes()->href}",
+ 'source' => $source,
+ 'date' => usertime(strtotime($gdoc->updated)),
+ 'thumbnail' => (string) $OUTPUT->pix_url(file_extension_icon($title, 32))
+ );
}
return $files;
36 lib/moodlelib.php
View
@@ -3828,15 +3828,45 @@ function truncate_userinfo($info) {
* Any plugin that needs to purge user data should register the 'user_deleted' event.
*
* @param stdClass $user full user object before delete
- * @return boolean always true
+ * @return boolean success
+ * @throws coding_exception if invalid $user parameter detected
*/
-function delete_user($user) {
+function delete_user(stdClass $user) {
global $CFG, $DB;
require_once($CFG->libdir.'/grouplib.php');
require_once($CFG->libdir.'/gradelib.php');
require_once($CFG->dirroot.'/message/lib.php');
require_once($CFG->dirroot.'/tag/lib.php');
+ // Make sure nobody sends bogus record type as parameter.
+ if (!property_exists($user, 'id') or !property_exists($user, 'username')) {
+ throw new coding_exception('Invalid $user parameter in delete_user() detected');
+ }
+
+ // Better not trust the parameter and fetch the latest info,
+ // this will be very expensive anyway.
+ if (!$user = $DB->get_record('user', array('id'=>$user->id))) {
+ debugging('Attempt to delete unknown user account.');
+ return false;
+ }
+
+ // There must be always exactly one guest record,
+ // originally the guest account was identified by username only,
+ // now we use $CFG->siteguest for performance reasons.
+ if ($user->username === 'guest' or isguestuser($user)) {
+ debugging('Guest user account can not be deleted.');
+ return false;
+ }
+
+ // Admin can be theoretically from different auth plugin,
+ // but we want to prevent deletion of internal accoutns only,
+ // if anything goes wrong ppl may force somebody to be admin via
+ // config.php setting $CFG->siteadmins.
+ if ($user->auth === 'manual' and is_siteadmin($user)) {
+ debugging('Local administrator accounts can not be deleted.');
+ return false;
+ }
+
// delete all grades - backup is kept in grade_grades_history table
grade_user_delete($user->id);
@@ -4678,7 +4708,7 @@ function shift_course_mod_dates($modname, $fields, $timeshift, $courseid) {
foreach ($fields as $field) {
$updatesql = "UPDATE {".$modname."}
SET $field = $field + ?
- WHERE course=? AND $field<>0 AND $field<>0";
+ WHERE course=? AND $field<>0";
$return = $DB->execute($updatesql, array($timeshift, $courseid)) && $return;
}
12 lib/questionlib.php
View
@@ -1117,16 +1117,18 @@ function question_category_options($contexts, $top = false, $currentcat = 0,
// sort cats out into different contexts
$categoriesarray = array();
- foreach ($pcontexts as $pcontext) {
- $contextstring = print_context_name(
- get_context_instance_by_id($pcontext), true, true);
+ foreach ($pcontexts as $contextid) {
+ $context = context::instance_by_id($contextid);
+ $contextstring = $context->get_context_name(true, true);
foreach ($categories as $category) {
- if ($category->contextid == $pcontext) {
+ if ($category->contextid == $contextid) {
$cid = $category->id;
if ($currentcat != $cid || $currentcat == 0) {
$countstring = !empty($category->questioncount) ?
" ($category->questioncount)" : '';
- $categoriesarray[$contextstring][$cid] = $category->indentedname.$countstring;
+ $categoriesarray[$contextstring][$cid] =
+ format_string($category->indentedname, true,
+ array('context' => $context)) . $countstring;
}
}
}
2  mod/data/field/checkbox/mod.html
View
@@ -9,6 +9,6 @@
</tr>
<tr>
<td class="c0" valign="top"><label for="param1"><?php echo get_string('fieldoptions', 'data'); ?></label></td>
- <td class="c1"><textarea style="width:300px; height:150px;" name="param1" id="param1" cols="80" rows="10"><?php if($this->field->param1) {p($this->field->param1);} ?></textarea></td>
+ <td class="c1"><textarea class="optionstextarea" name="param1" id="param1" cols="80" rows="10"><?php if($this->field->param1) {p($this->field->param1);} ?></textarea></td>
</tr>
</table>
9 mod/data/field/latlong/field.class.php
View
@@ -130,7 +130,6 @@ function display_browse_field($recordid, $template) {
} else {
$compasslong = sprintf('%01.4f', $long) . '°E';
}
- $str = '<form style="display:inline;">';
// Now let's create the jump-to-services link
$servicesshown = explode(',', $this->field->param1);
@@ -148,10 +147,11 @@ function display_browse_field($recordid, $template) {
);
if(sizeof($servicesshown)==1 && $servicesshown[0]) {
- $str .= " <a href='"
+ $str = " <a href='"
. str_replace(array_keys($urlreplacements), array_values($urlreplacements), $this->linkoutservices[$servicesshown[0]])
."' title='$servicesshown[0]'>$compasslat, $compasslong</a>";
} elseif (sizeof($servicesshown)>1) {
+ $str = '<form id="latlongfieldbrowse">';
$str .= "$compasslat, $compasslong\n";
$str .= "<label class='accesshide' for='jumpto'>". get_string('jumpto') ."</label>";
$str .= "<select id='jumpto' name='jumpto'>";
@@ -164,10 +164,11 @@ function display_browse_field($recordid, $template) {
// NB! If you are editing this, make sure you don't break the javascript reference "previousSibling"
// which allows the "Go" button to refer to the drop-down selector.
$str .= "\n</select><input type='button' value='" . get_string('go') . "' onclick='if(previousSibling.value){self.location=previousSibling.value}'/>";
+ $str .= '</form>';
} else {
- $str.= "$compasslat, $compasslong";
+ $str = "$compasslat, $compasslong";
}
- $str.= '</form>';
+
return $str;
}
return false;
2  mod/data/field/menu/mod.html
View
@@ -9,6 +9,6 @@
</tr>
<tr>
<td class="c0" valign="top"><label for="param1"><?php echo get_string('fieldoptions', 'data'); ?></label></td>
- <td class="c1"><textarea style="width:300px; height:150px;" name="param1" id="param1" cols="80" rows="10"><?php if($this->field->param1) {p($this->field->param1);} ?></textarea></td>
+ <td class="c1"><textarea class="optionstextarea" name="param1" id="param1" cols="80" rows="10"><?php if($this->field->param1) {p($this->field->param1);} ?></textarea></td>
</tr>
</table>
2  mod/data/field/multimenu/mod.html
View
@@ -9,6 +9,6 @@
</tr>
<tr>
<td class="c0" valign="top"><label for="param1"><?php echo get_string('fieldoptions', 'data'); ?></label></td>
- <td class="c1"><textarea style="width:300px; height:150px;" name="param1" id="param1" cols="80" rows="10"><?php if($this->field->param1) {p($this->field->param1);} ?></textarea></td>
+ <td class="c1"><textarea class="optionstextarea" name="param1" id="param1" cols="80" rows="10"><?php if($this->field->param1) {p($this->field->param1);} ?></textarea></td>
</tr>
</table>
4 mod/data/field/picture/field.class.php
View
@@ -152,13 +152,13 @@ function display_browse_field($recordid, $template) {
if ($template == 'listtemplate') {
$src = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/mod_data/content/'.$content->id.'/'.'thumb_'.$content->content);
// no need to add width/height, because the thumb is resized properly
- $str = '<a href="view.php?d='.$this->field->dataid.'&amp;rid='.$recordid.'"><img src="'.$src.'" alt="'.s($alt).'" title="'.s($title).'" style="border:0px" /></a>';
+ $str = '<a href="view.php?d='.$this->field->dataid.'&amp;rid='.$recordid.'"><img src="'.$src.'" alt="'.s($alt).'" title="'.s($title).'" class="list_picture"/></a>';
} else {
$src = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/mod_data/content/'.$content->id.'/'.$content->content);
$width = $this->field->param1 ? ' width="'.s($this->field->param1).'" ':' ';
$height = $this->field->param2 ? ' height="'.s($this->field->param2).'" ':' ';
- $str = '<a href="'.$src.'"><img '.$width.$height.' src="'.$src.'" alt="'.s($alt).'" title="'.s($title).'" style="border:0px" /></a>';
+ $str = '<a href="'.$src.'"><img '.$width.$height.' src="'.$src.'" alt="'.s($alt).'" title="'.s($title).'" class="list_picture"/></a>';
}
return $str;
8 mod/data/field/picture/mod.html
View
@@ -16,27 +16,27 @@
<td class="c0"><label for="param1">
<?php echo get_string('fieldwidthsingleview', 'data');?></label></td>
<td class="c1">
- <input style="width:70px;" type="text" name="param1" id="param1" value="<?php if (!empty($this->field->param1)) p($this->field->param1); ?>" />
+ <input class="picturefieldsize" type="text" name="param1" id="param1" value="<?php if (!empty($this->field->param1)) p($this->field->param1); ?>" />
</td>
</tr>
<tr>
<td class="c0"><label for="param2">
<?php echo get_string('fieldheightsingleview', 'data');?></label></td>
<td class="c1">
- <input style="width:70px;" type="text" name="param2" id="param2" value="<?php if (!empty($this->field->param2)) p($this->field->param2); ?>" />
+ <input class="picturefieldsize" type="text" name="param2" id="param2" value="<?php if (!empty($this->field->param2)) p($this->field->param2); ?>" />
</td>
</tr>
<tr>
<td class="c0"><label for="param4">
<?php echo get_string('fieldwidthlistview', 'data');?></label></td>
- <td class="c1"><input style="width:70px;" type="text" name="param4" id="param4" value="<?php if (!empty($this->field->param4)) p($this->field->param4); ?>" />
+ <td class="c1"><input class="picturefieldsize" type="text" name="param4" id="param4" value="<?php if (!empty($this->field->param4)) p($this->field->param4); ?>" />
</td>
</tr>
<tr>
<td class="c0"><label for="param5">
<?php echo get_string('fieldheightlistview', 'data');?></label></td>
<td class="c1">
- <input style="width:70px;" type="text" name="param5" id="param5" value="<?php if (!empty($this->field->param5)) p($this->field->param5); ?>" />
+ <input class="picturefieldsize" type="text" name="param5" id="param5" value="<?php if (!empty($this->field->param5)) p($this->field->param5); ?>" />
</td>
</tr>
<tr>
2  mod/data/field/radiobutton/mod.html
View
@@ -9,6 +9,6 @@
</tr>
<tr>
<td class="c0" valign="top"><label for="param1"><?php echo get_string('fieldoptions', 'data'); ?></label></td>
- <td class="c1"><textarea style="width:300px; height:150px;" name="param1" id="param1" cols="80" rows="10"><?php if($this->field->param1) {p($this->field->param1);} ?></textarea></td>
+ <td class="c1"><textarea class="optionstextarea" name="param1" id="param1" cols="80" rows="10"><?php if($this->field->param1) {p($this->field->param1);} ?></textarea></td>
</tr>
</table>
4 mod/data/field/textarea/mod.html
View
@@ -14,7 +14,7 @@
<td class="c0"><label for="param2">
<?php echo get_string('fieldwidth', 'data'); ?></label></td>
<td class="c1">
- <input style="width:50px;" type="text" name="param2" id="param2" value=
+ <input class="textareafieldsize" type="text" name="param2" id="param2" value=
<?php
if (empty($this->field->param2)) {
echo('"60"');
@@ -28,7 +28,7 @@
<td class="c0"><label for="param3">
<?php echo get_string('fieldheight', 'data'); ?></label></td>
<td class="c1">
- <input style="width:50px;" type="text" name="param3" id="param3" value=
+ <input class="textareafieldsize" type="text" name="param3" id="param3" value=
<?php
if (empty($this->field->param3)) {
echo('"35"');
28 mod/data/lib.php
View
@@ -244,7 +244,7 @@ function display_add_field($recordid=0){
$str = '<div title="'.s($this->field->description).'">';
$str .= '<label class="accesshide" for="field_'.$this->field->id.'">'.$this->field->description.'</label>';
- $str .= '<input style="width:300px;" type="text" name="field_'.$this->field->id.'" id="field_'.$this->field->id.'" value="'.s($content).'" />';
+ $str .= '<input class="basefieldinput" type="text" name="field_'.$this->field->id.'" id="field_'.$this->field->id.'" value="'.s($content).'" />';
$str .= '</div>';
return $str;
@@ -1546,14 +1546,16 @@ function data_print_preference_form($data, $perpage, $search, $sort='', $order='
$pagesizes = array(2=>2,3=>3,4=>4,5=>5,6=>6,7=>7,8=>8,9=>9,10=>10,15=>15,
20=>20,30=>30,40=>40,50=>50,100=>100,200=>200,300=>300,400=>400,500=>500,1000=>1000);
echo html_writer::select($pagesizes, 'perpage', $perpage, false, array('id'=>'pref_perpage'));
- echo '<div id="reg_search" style="display: ';
+
if ($advanced) {
- echo 'none';
- }
- else {
- echo 'inline';
+ $regsearchclass = 'search_none';
+ $advancedsearchclass = 'search_inline';
+ } else {
+ $regsearchclass = 'search_inline';
+ $advancedsearchclass = 'search_none';
}
- echo ';" >&nbsp;&nbsp;&nbsp;<label for="pref_search">'.get_string('search').'</label> <input type="text" size="16" name="search" id= "pref_search" value="'.s($search).'" /></div>';
+ echo '<div id="reg_search" class="' . $regsearchclass . '" >&nbsp;&nbsp;&nbsp;';
+ echo '<label for="pref_search">'.get_string('search').'</label> <input type="text" size="16" name="search" id= "pref_search" value="'.s($search).'" /></div>';
echo '&nbsp;&nbsp;&nbsp;<label for="pref_sortby">'.get_string('sortby').'</label> ';
// foreach field, print the option
echo '<select name="sort" id="pref_sortby">';
@@ -1613,15 +1615,7 @@ function data_print_preference_form($data, $perpage, $search, $sort='', $order='
echo '&nbsp;<input type="submit" value="'.get_string('savesettings','data').'" />';
echo '<br />';
- echo '<div class="dataadvancedsearch" id="data_adv_form" style="display: ';
-
- if ($advanced) {
- echo 'inline';
- }
- else {
- echo 'none';
- }
- echo ';margin-left:auto;margin-right:auto;" >';
+ echo '<div class="' . $advancedsearchclass . '" id="data_adv_form">';
echo '<table class="boxaligncenter">';
// print ASC or DESC
@@ -1686,7 +1680,7 @@ function data_print_preference_form($data, $perpage, $search, $sort='', $order='
echo format_text($newtext, FORMAT_HTML, $options);
echo '</td></tr>';
- echo '<tr><td colspan="4" style="text-align: center;"><br/><input type="submit" value="'.get_string('savesettings','data').'" /><input type="submit" name="resetadv" value="'.get_string('resetsettings','data').'" /></td></tr>';
+ echo '<tr><td colspan="4"><br/><input type="submit" value="'.get_string('savesettings','data').'" /><input type="submit" name="resetadv" value="'.get_string('resetsettings','data').'" /></td></tr>';
echo '</table>';
echo '</div>';
echo '</div>';
32 mod/data/styles.css
View
@@ -7,6 +7,24 @@
.path-mod-data-field .c0,
#page-mod-data-view #sortsearch .c0 {text-align: right;}
#page-mod-data-view .approve img.icon {width:34px;height:34px;}
+#page-mod-data-view img.list_picture {
+ border:0px;
+}
+#page-mod-data-view div.search_none {
+ display: none;
+}
+#page-mod-data-view div.search_inline,
+#page-mod-data-view form#latlongfieldbrowse {
+ display: inline;
+}
+#page-mod-data-view div#data_adv_form {
+ margin-left:auto;
+ margin-right:auto;
+}
+
+#page-mod-data-edit .basefieldinput {
+ width:300px;
+}
/** Styles for preset.php **/
#page-mod-data-preset .presetmapping table {text-align: left;margin-left: auto;margin-right: auto;}
@@ -20,6 +38,16 @@
.path-mod-data-field .sortdefault select {margin-left: 1em;}
.path-mod-data-field .fieldname,
.path-mod-data-field .fielddescription {width:300px;}
+.path-mod-data-field textarea.optionstextarea {
+ width:300px;
+ height:150px;
+}
+.path-mod-data-field input.textareafieldsize {
+ width:50px;
+}
+.path-mod-data-field input.picturefieldsize {
+ width:70px;
+}
/** UI Usability Hacks **/
#page-mod-data-export #notice span {padding:0 10px;}
@@ -30,6 +58,10 @@
.mod-data-default-template .template-token {text-align:left;}
.mod-data-default-template .controls {text-align:center;}
.mod-data-default-template searchcontrols {text-align:right;}
+#page-mod-data-templates td.save_template,
+#page-mod-data-templates .template_heading {
+ text-align:center;
+}
.dir-rtl .mod-data-default-template .template-field {text-align:left;}
.dir-rtl .mod-data-default-template .template-token {text-align:right;}
14 mod/data/templates.php
View
@@ -141,7 +141,7 @@
}
}
} else {
- echo '<div class="littleintro" style="text-align:center">'.get_string('header'.$mode,'data').'</div>';
+ echo '<div class="template_heading">'.get_string('header'.$mode,'data').'</div>';
}
/// If everything is empty then generate some defaults
@@ -198,7 +198,7 @@
echo '<tr>';
echo '<td>&nbsp;</td>';
echo '<td>';
- echo '<div style="text-align:center"><label for="edit-listtemplateheader">'.get_string('header','data').'</label></div>';
+ echo '<div class="template_heading"><label for="edit-listtemplateheader">'.get_string('header','data').'</label></div>';
$field = 'listtemplateheader';
$editor->use_editor($field, $options);
@@ -290,9 +290,9 @@
echo '<td valign="top">';
if ($mode == 'listtemplate'){
- echo '<div style="text-align:center"><label for="edit-template">'.get_string('multientry','data').'</label></div>';
+ echo '<div class="template_heading"><label for="edit-template">'.get_string('multientry','data').'</label></div>';
} else {
- echo '<div style="text-align:center"><label for="edit-template">'.get_string($mode,'data').'</label></div>';
+ echo '<div class="template_heading"><label for="edit-template">'.get_string($mode,'data').'</label></div>';
}
$field = 'template';
@@ -305,7 +305,7 @@
echo '<tr>';
echo '<td>&nbsp;</td>';
echo '<td>';
- echo '<div style="text-align:center"><label for="edit-listtemplatefooter">'.get_string('footer','data').'</label></div>';
+ echo '<div class="template_heading"><label for="edit-listtemplatefooter">'.get_string('footer','data').'</label></div>';
$field = 'listtemplatefooter';
$editor->use_editor($field, $options);
@@ -316,7 +316,7 @@
echo '<tr>';
echo '<td>&nbsp;</td>';
echo '<td>';
- echo '<div style="text-align:center"><label for="edit-rsstitletemplate">'.get_string('rsstitletemplate','data').'</label></div>';
+ echo '<div class="template_heading"><label for="edit-rsstitletemplate">'.get_string('rsstitletemplate','data').'</label></div>';
$field = 'rsstitletemplate';
$editor->use_editor($field, $options);
@@ -325,7 +325,7 @@
echo '</tr>';
}
-echo '<tr><td style="text-align:center" colspan="2">';
+echo '<tr><td class="save_template" colspan="2">';
echo '<input type="submit" value="'.get_string('savetemplate','data').'" />&nbsp;';
echo '</td></tr></table>';
18 mod/quiz/lib.php
View
@@ -1135,8 +1135,14 @@ function quiz_update_events($quiz, $override = null) {
$addopen = empty($current->id) || !empty($current->timeopen);
$addclose = empty($current->id) || !empty($current->timeclose);
+ if (!empty($quiz->coursemodule)) {
+ $cmid = $quiz->coursemodule;
+ } else {
+ $cmid = get_coursemodule_from_instance('quiz', $quiz->id, $quiz->course)->id;
+ }
+
$event = new stdClass();
- $event->description = format_module_intro('quiz', $quiz, $quiz->coursemodule);
+ $event->description = format_module_intro('quiz', $quiz, $cmid);
// Events module won't show user events when the courseid is nonzero.
$event->courseid = ($userid) ? 0 : $quiz->course;
$event->groupid = $groupid;
@@ -1328,8 +1334,18 @@ function quiz_reset_userdata($data) {
// Updating dates - shift may be negative too
if ($data->timeshift) {
+ $DB->execute("UPDATE {quiz_overrides}
+ SET timeopen = timeopen + ?
+ WHERE quiz IN (SELECT id FROM {quiz} WHERE course = ?)
+ AND timeopen <> 0", array($data->timeshift, $data->courseid));
+ $DB->execute("UPDATE {quiz_overrides}
+ SET timeclose = timeclose + ?
+ WHERE quiz IN (SELECT id FROM {quiz} WHERE course = ?)
+ AND timeclose <> 0", array($data->timeshift, $data->courseid));
+
shift_course_mod_dates('quiz', array('timeopen', 'timeclose'),
$data->timeshift, $data->courseid);
+
$status[] = array(
'component' => $componentstr,
'item' => get_string('openclosedatesupdated', 'quiz'),
1  mod/quiz/styles.css
View
@@ -179,6 +179,7 @@ table.quizreviewsummary td.cell {padding: 1px 1em 1px 0.5em;text-align: left;bac
#page-mod-quiz-edit h2.main{display:inline;padding-right:1em;clear:left;}
#categoryquestions .r1 {background: #e4e4e4;}
+#categoryquestions .r1.highlight {background-color:#AAFFAA;}
#categoryquestions .header {text-align: center;padding: 0 2px;border: 0 none;}
#categoryquestions th.modifiername .sorters,
#categoryquestions th.creatorname .sorters {font-weight: normal;font-size: 0.8em;}
7 question/category_class.php