</FEEDBACK>
</PHP_SETTING>
</PHP_SETTINGS>
-</MOODLE>
+ </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>
+ <PCREUNICODE level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="pcreunicodewarning" />
+ </FEEDBACK>
+ </PCREUNICODE>
+ <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>
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);
+ }
}
}
--- /dev/null
+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.
\ No newline at end of file
* @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(' ', '_', textlib::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:
$shortname = textlib::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'));
* @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
// this is a regular file, it must be present in the backup pool
$backuppath = $basepath . backup_file_manager::get_backup_content_file_location($file->contenthash);
+ // The file is not found in the backup.
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;
}
// create the file in the filepool if it does not exist yet
}
}
$rs->close();
+ return $results;
}
/**
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;
$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);
$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);
$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);
$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) {
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');
*/
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);
$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;
}
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();
}
}
$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();
return $outcome;
}
+ /**
+ * 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.
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 = textlib::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
// 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;
}
/**
*/
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);
}
/**
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.
$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');
$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;
if (!empty($this->results['include_file_references_to_external_content'])) {
$output .= $renderer->notification(get_string('filereferencesincluded', 'backup'), 'notifyproblem');
}
+ if (!empty($this->results['missing_files_in_pool'])) {
+ $output .= $renderer->notification(get_string('missingfilesinpool', 'backup'), 'notifyproblem');
+ }
$output .= $renderer->notification(get_string('executionsuccess', 'backup'), 'notifysuccess');
$output .= $renderer->continue_button($restorerul);
$output .= $renderer->box_end();
$html .= $renderer->box_end();
}
$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');
*
* @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
class block_completionstatus extends block_base {
public function init() {
- $this->title = get_string('pluginname', 'block_completionstatus');
+ $this->title = get_string('pluginname', 'block_completionstatus');
}
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()) {
// 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;
}
// Load course completion
$params = array(
'userid' => $USER->id,
- 'course' => $COURSE->id
+ 'course' => $course->id
);
$ccompletion = new completion_completion($params);
$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;
}
*
* @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) {
// 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) {
///
/// Display page
///
+$PAGE->set_context(context_course::instance($course->id));
// Print header
$page = get_string('completionprogressdetails', 'block_completionstatus');
$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();
// 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();
$string['firstofsecond'] = '{$a->first} of {$a->second}';
$string['pluginname'] = 'Course completion status';
$string['requirement'] = 'Requirement';
+$string['returntocourse'] = 'Return to course';
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');
}
}
* @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());
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)))
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
$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;
/// 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!
}
}
-fclose($fp);
exit;
'<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>',
$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';
$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';
$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';
$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';
if ($withprefix){
$name = get_string('modulename', $cm->modname).': ';
}
- $name .= $mod->name;
+ $name .= format_string($mod->name, true, array('context' => $this));
}
}
return $name;
$course = $COURSE;
} else {
$course = $DB->get_record('course', array('id' => $this->item->course),
- 'id, enablecompletion, modinfo', MUST_EXIST);
+ 'id, enablecompletion, modinfo, sectioncache', MUST_EXIST);
}
foreach ($this->item->conditionscompletion as $cmid => $expectedcompletion) {
if (!$modinfo) {
$course = $COURSE;
} else {
$course = $DB->get_record('course', array('id' => $this->item->course),
- 'id, enablecompletion, modinfo', MUST_EXIST);
+ 'id, enablecompletion, modinfo, sectioncache', MUST_EXIST);
}
$completion = new completion_info($course);
upgrade_main_savepoint(true, 2012062501.08);
}
+ if ($oldversion < 2012062501.14) {
+ $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, 2012062501.14);
+ }
+
return true;
}
defined('MOODLE_INTERNAL') || die();
+/**
+ * Returns all non-view and non-temp tables with sane names.
+ * Prints list of non-supported tables using $OUTPUT->notification()
+ *
+ * @return array
+ */
+function upgrade_mysql_get_supported_tables() {
+ global $OUTPUT, $DB;
+
+ $tables = array();
+ $patprefix = str_replace('_', '\\_', $DB->get_prefix());
+ $pregprefix = preg_quote($DB->get_prefix(), '/');
+
+ $sql = "SHOW FULL TABLES LIKE '$patprefix%'";
+ $rs = $DB->get_recordset_sql($sql);
+ foreach ($rs as $record) {
+ $record = array_change_key_case((array)$record, CASE_LOWER);
+ $type = $record['table_type'];
+ unset($record['table_type']);
+ $fullname = array_shift($record);
+
+ if ($pregprefix === '') {
+ $name = $fullname;
+ } else {
+ $count = null;
+ $name = preg_replace("/^$pregprefix/", '', $fullname, -1, $count);
+ if ($count !== 1) {
+ continue;
+ }
+ }
+
+ if (!preg_match("/^[a-z][a-z0-9_]*$/", $name)) {
+ echo $OUTPUT->notification("Database table with invalid name '$fullname' detected, skipping.", 'notifyproblem');
+ continue;
+ }
+ if ($type === 'VIEW') {
+ echo $OUTPUT->notification("Unsupported database table view '$fullname' detected, skipping.", 'notifyproblem');
+ continue;
+ }
+ $tables[$name] = $name;
+ }
+ $rs->close();
+
+ return $tables;
+}
/**
* Remove all signed numbers from current database - mysql only.
$pbar = new progress_bar('mysqlconvertunsigned', 500, true);
$prefix = $DB->get_prefix();
- $tables = $DB->get_tables();
+ $tables = upgrade_mysql_get_supported_tables();
$tablecount = count($tables);
$i = 0;
$pbar = new progress_bar('mysqlconvertlobs', 500, true);
$prefix = $DB->get_prefix();
- $tables = $DB->get_tables();
+ $tables = upgrade_mysql_get_supported_tables();
asort($tables);
$tablecount = count($tables);
protected function embed_external(moodle_url $url, $name, $width, $height, $options) {
global $CFG;
- $site = $this->matches[1];
- $videoid = $this->matches[3];
+ $site = 'www.youtube.com';
+ $videoid = end($this->matches);
$info = trim($name);
if (empty($info) or strpos($info, 'http') === 0) {
}
protected function get_regex() {
+ // Regex for standard youtube link
+ $link = '(youtube(-nocookie)?\.com/(?:watch\?v=|v/))';
+ // Regex for shortened youtube link
+ $shortlink = '((youtu|y2u)\.be/)';
+
// Initial part of link.
- $start = '~^https?://(www\.youtube(-nocookie)?\.com)/';
- // Middle bit: either watch?v= or v/.
- $middle = '(?:watch\?v=|v/)([a-z0-9\-_]+)';
+ $start = '~^https?://(www\.)?(' . $link . '|' . $shortlink . ')';
+ // Middle bit: Video key value
+ $middle = '([a-z0-9\-_]+)';
return $start . $middle . core_media_player_external::END_LINK_REGEX_PART;
}
}
public function get_embeddable_markers() {
- return array('youtube');
+ return array('youtube.com', 'youtube-nocookie.com', 'youtu.be', 'y2u.be');
}
}
* 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);
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;
}
return true;
}
- if ($now - $recent > HOURSECS) {
+ if ($now - $recent > 24 * HOURSECS) {
return false;
}
// 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;
}
}
}
$this->assertEquals('5.43000', format_float(5.43, 5, false));
$this->assertEquals('5.43', format_float(5.43, 5, false, true));
}
+
+ /**
+ * Test deleting of users.
+ */
+ public function test_delete_user() {
+ global $DB, $CFG;
+
+ $this->resetAfterTest();
+
+ $guest = $DB->get_record('user', array('id'=>$CFG->siteguest), '*', MUST_EXIST);
+ $admin = $DB->get_record('user', array('id'=>$CFG->siteadmins), '*', MUST_EXIST);
+ $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
+
+ $user = $this->getDataGenerator()->create_user(array('idnumber'=>'abc'));
+ $user2 = $this->getDataGenerator()->create_user(array('idnumber'=>'xyz'));
+
+ $result = delete_user($user);
+ $this->assertTrue($result);
+ $deluser = $DB->get_record('user', array('id'=>$user->id), '*', MUST_EXIST);
+ $this->assertEquals(1, $deluser->deleted);
+ $this->assertEquals(0, $deluser->picture);
+ $this->assertSame('', $deluser->idnumber);
+ $this->assertSame(md5($user->username), $deluser->email);
+ $this->assertRegExp('/^'.preg_quote($user->email, '/').'\.\d*$/', $deluser->username);
+
+ $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
+
+ // Try invalid params.
+
+ $record = new stdClass();
+ $record->grrr = 1;
+ try {
+ delete_user($record);
+ $this->fail('Expecting exception for invalid delete_user() $user parameter');
+ } catch (coding_exception $e) {
+ $this->assertTrue(true);
+ }
+ $record->id = 1;
+ try {
+ delete_user($record);
+ $this->fail('Expecting exception for invalid delete_user() $user parameter');
+ } catch (coding_exception $e) {
+ $this->assertTrue(true);
+ }
+
+ $CFG->debug = DEBUG_MINIMAL; // Prevent standard debug warnings.
+
+ $record = new stdClass();
+ $record->id = 666;
+ $record->username = 'xx';
+ $this->assertFalse($DB->record_exists('user', array('id'=>666))); // Any non-existent id is ok.
+ $result = delete_user($record);
+ $this->assertFalse($result);
+
+ $result = delete_user($guest);
+ $this->assertFalse($result);
+
+ $result = delete_user($admin);
+ $this->assertFalse($result);
+ }
}
*/
public function test_cron_has_fresh_fetch() {
$provider = testable_available_update_checker::instance();
- $provider->fakerecentfetch = time() - 59 * MINSECS; // fetched an hour ago
+ $provider->fakerecentfetch = time() - 23 * HOURSECS; // fetched 23 hours ago
$provider->fakecurrenttimestamp = -1;
$provider->cron();
$this->assertTrue(true); // we should get here with no exception thrown
*/
public function test_cron_offset_execution_not_yet() {
$provider = testable_available_update_checker::instance();
- $provider->fakerecentfetch = time() - 24 * HOURSECS;
- $provider->fakecurrenttimestamp = mktime(1, 40, 02); // 01:40:02 AM
+ $provider->fakecurrenttimestamp = mktime(1, 40, 02); // 01:40:02 AM today
+ $provider->fakerecentfetch = $provider->fakecurrenttimestamp - 24 * HOURSECS;
$provider->cron();
$this->assertTrue(true); // we should get here with no exception thrown
}
/**
- * The first cron after 01:42 AM today should fetch the data
+ * The first cron after 01:42 AM today should fetch the data and then
+ * it is supposed to wait next 24 hours.
*
* @see testable_available_update_checker::cron_execution_offset()
*/
public function test_cron_offset_execution() {
$provider = testable_available_update_checker::instance();
- $provider->fakerecentfetch = time() - 24 * HOURSECS;
- $provider->fakecurrenttimestamp = mktime(1, 45, 02); // 01:45:02 AM
- $this->setExpectedException('testable_available_update_checker_cron_executed');
- $provider->cron();
+
+ // the cron at 01:45 should fetch the data
+ $provider->fakecurrenttimestamp = mktime(1, 45, 02); // 01:45:02 AM today
+ $provider->fakerecentfetch = $provider->fakecurrenttimestamp - 24 * HOURSECS - 1;
+ $executed = false;
+ try {
+ $provider->cron();
+ } catch (testable_available_update_checker_cron_executed $e) {
+ $executed = true;
+ }
+ $this->assertTrue($executed, 'Cron should be executed at 01:45:02 but it was not.');
+
+ // another cron at 06:45 should still consider data as fresh enough
+ $provider->fakerecentfetch = $provider->fakecurrenttimestamp;
+ $provider->fakecurrenttimestamp = mktime(6, 45, 03); // 06:45:03 AM
+ $executed = false;
+ try {
+ $provider->cron();
+ } catch (testable_available_update_checker_cron_executed $e) {
+ $executed = true;
+ }
+ $this->assertFalse($executed, 'Cron should not be executed at 06:45:03 but it was.');
+
+ // the next scheduled execution should happen the next day
+ $provider->fakecurrenttimestamp = $provider->fakerecentfetch + 24 * HOURSECS + 1;
+ $executed = false;
+ try {
+ $provider->cron();
+ } catch (testable_available_update_checker_cron_executed $e) {
+ $executed = true;
+ }
+ $this->assertTrue($executed, 'Cron should be executed the next night but it was not.');
}
public function test_compare_responses_both_empty() {
}
protected function cron_execute() {
- throw new testable_available_update_checker_cron_executed('Cron executed but it should not!');
+ throw new testable_available_update_checker_cron_executed('Cron executed!');
}
}
}
}
- //Sort the conversations. This is a bit complicated as we need to sort by $conversation->timecreated
- //and there may be multiple conversations with the same timecreated value.
- //The conversations array contains both read and unread messages (different tables) so sorting by ID won't work
- usort($conversations, "conversationsort");
+ // Sort the conversations by $conversation->timecreated, newest to oldest
+ // There may be multiple conversations with the same timecreated
+ // The conversations array contains both read and unread messages (different tables) so sorting by ID won't work
+ $result = collatorlib::asort_objects_by_property($conversations, 'timecreated', collatorlib::SORT_NUMERIC);
+ $conversations = array_reverse($conversations);
return $conversations;
}
-/**
- * Sort function used to order conversations
- *
- * @param object $a A conversation object
- * @param object $b A conversation object
- * @return integer
- */
-function conversationsort($a, $b)
-{
- if ($a->timecreated == $b->timecreated) {
- return 0;
- }
- return ($a->timecreated > $b->timecreated) ? -1 : 1;
-}
-
/**
* Get the users recent event notifications
*
array($user1->id, $user2->id, $user2->id, $user1->id, $user1->id),
"timecreated $sort", '*', 0, $limitnum)) {
foreach ($messages_read as $message) {
- $messages[$message->timecreated] = $message;
+ $messages[] = $message;
}
}
if ($messages_new = $DB->get_records_select('message', "((useridto = ? AND useridfrom = ?) OR
array($user1->id, $user2->id, $user2->id, $user1->id, $user1->id),
"timecreated $sort", '*', 0, $limitnum)) {
foreach ($messages_new as $message) {
- $messages[$message->timecreated] = $message;
+ $messages[] = $message;
}
}
+ $result = collatorlib::asort_objects_by_property($messages, 'timecreated', collatorlib::SORT_NUMERIC);
+
//if we only want the last $limitnum messages
- ksort($messages);
$messagecount = count($messages);
- if ($limitnum>0 && $messagecount>$limitnum) {
- $messages = array_slice($messages, $messagecount-$limitnum, $limitnum, true);
+ if ($limitnum > 0 && $messagecount > $limitnum) {
+ $messages = array_slice($messages, $messagecount - $limitnum, $limitnum, true);
}
return $messages;
</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>
} 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);
);
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'>";
// 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;
</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>
</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>
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.'&rid='.$recordid.'"><img src="'.$src.'" alt="'.s($alt).'" title="'.s($title).'" style="border:0px" /></a>';
+ $str = '<a href="view.php?d='.$this->field->dataid.'&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;
<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>
</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>
<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"');
<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"');
$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;
$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 ';" > <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 . '" > ';
+ echo '<label for="pref_search">'.get_string('search').'</label> <input type="text" size="16" name="search" id= "pref_search" value="'.s($search).'" /></div>';
echo ' <label for="pref_sortby">'.get_string('sortby').'</label> ';
// foreach field, print the option
echo '<select name="sort" id="pref_sortby">';
echo ' <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
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>';
.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;}
.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;}
.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;}
}
}
} 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
echo '<tr>';
echo '<td> </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);
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';
echo '<tr>';
echo '<td> </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);
echo '<tr>';
echo '<td> </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);
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').'" /> ';
echo '</td></tr></table>';
$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;
// 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'),
#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;}
/**
* Converts <ELEMENT> into <workshopform_comments_dimension>
+ *
+ * @param array $data legacy element data
+ * @param array $raw raw element data
+ *
+ * @return array converted
*/
- public function process_legacy_element($data, $raw) {
+ public function process_legacy_element(array $data, array $raw) {
// prepare a fake record and re-use the upgrade logic
$fakerecord = (object)$data;
$converted = (array)workshopform_comments_upgrade_element($fakerecord, 12345678);
/**
* Converts <ELEMENT> into <workshopform_numerrors_dimension> and stores it for later writing
*
+ * @param array $data legacy element data
+ * @param array $raw raw element data
+ *
* @return array to be written to workshop.xml
*/
- public function process_legacy_element($data, $raw) {
+ public function process_legacy_element(array $data, array $raw) {
$workshop = $this->parenthandler->get_current_workshop();
/**
* Processes one <ELEMENT>
+ *
+ * @param array $data legacy element data
+ * @param array $raw raw element data
*/
- public function process_legacy_element($data, $raw) {
+ public function process_legacy_element(array $data, array $raw) {
$this->elements[] = $data;
$this->rubrics[$data['id']] = array();
}
/// Each section adds html to be displayed as part of this list item
$questionbankurl = new moodle_url("/question/edit.php", ($this->parentlist->pageurl->params() + array('category'=>"$category->id,$category->contextid")));
$catediturl = $this->parentlist->pageurl->out(true, array('edit' => $this->id));
- $item = "<b><a title=\"{$str->edit}\" href=\"$catediturl\">".$category->name ."</a></b> <a title=\"$editqestions\" href=\"$questionbankurl\">".'('.$category->questioncount.')</a>';
+ $item = "<b><a title=\"{$str->edit}\" href=\"$catediturl\">" .
+ format_string($category->name, true, array('context' => $this->parentlist->context)) .
+ "</a></b> <a title=\"$editqestions\" href=\"$questionbankurl\">".'('.$category->questioncount.')</a>';
- $item .= ' '. $category->info;
+ $item .= ' ' . format_text($category->info, $category->infoformat,
+ array('context' => $this->parentlist->context, 'noclean' => true));
// don't allow delete if this is the last category in this context.
if (count($this->parentlist->records) != 1) {
.que.multichoice .answer div.r1 {
padding: 0.3em;
}
+.que.multichoice .feedback .rightanswer * {
+ display: inline;
+}
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->dirroot.'/backup/lib.php');
+// Required for constants in backup_cron_automated_helper
+require_once($CFG->dirroot.'/backup/util/helper/backup_cron_helper.class.php');
+
admin_externalpage_setup('reportbackups', '', null, '', array('pagelayout'=>'report'));
$table = new html_table;
$strok = get_string("ok");
$strunfinished = get_string("unfinished");
$strskipped = get_string("skipped");
+$strwarning = get_string("warning");
list($select, $join) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
$sql = "SELECT bc.*, c.fullname $select
context_instance_preload($backuprow);
// Prepare a cell to display the status of the entry
- if ($backuprow->laststatus == 1) {
+ if ($backuprow->laststatus == backup_cron_automated_helper::BACKUP_STATUS_OK) {
$status = $strok;
$statusclass = 'backup-ok'; // Green
- } else if ($backuprow->laststatus == 2) {
+ } else if ($backuprow->laststatus == backup_cron_automated_helper::BACKUP_STATUS_UNFINISHED) {
$status = $strunfinished;
$statusclass = 'backup-unfinished'; // Red
- } else if ($backuprow->laststatus == 3) {
+ } else if ($backuprow->laststatus == backup_cron_automated_helper::BACKUP_STATUS_SKIPPED) {
$status = $strskipped;
$statusclass = 'backup-skipped'; // Green
+ } else if ($backuprow->laststatus == backup_cron_automated_helper::BACKUP_STATUS_WARNING) {
+ $status = $strwarning;
+ $statusclass = 'backup-warning'; // Orange
} else {
$status = $strerror;
$statusclass = 'backup-error'; // Red
*/
function report_stats_extend_navigation_course($navigation, $course, $context) {
global $CFG;
- if (!empty($CFG->enablestats)) {
+ if (empty($CFG->enablestats)) {
return;
}
if (has_capability('report/stats:view', $context)) {
*/
function report_stats_extend_navigation_user($navigation, $user, $course) {
global $CFG;
- if (!empty($CFG->enablestats)) {
+ if (empty($CFG->enablestats)) {
return;
}
if (report_stats_can_access_user_report($user, $course)) {
defined('MOODLE_INTERNAL') || die;
// just a link to course report
-$ADMIN->add('reports', new admin_externalpage('reportstats', get_string('pluginname', 'report_stats'), "$CFG->wwwroot/report/stats/index.php", 'report/stats:view'));
+$ADMIN->add('reports', new admin_externalpage('reportstats', get_string('pluginname', 'report_stats'), "$CFG->wwwroot/report/stats/index.php", 'report/stats:view', empty($CFG->enablestats)));
// no report settings
$settings = null;
private $flickr;
public $photos;
+ /**
+ * Stores sizes of images to prevent multiple API call
+ */
+ static private $sizes = array();
+
/**
*
* @param int $repositoryid
* @return string
*/
private function build_photo_url($photoid) {
- $result = $this->flickr->photos_getSizes($photoid);
- $url = '';
- if(!empty($result[4])) {
- $url = $result[4]['source'];
- } elseif(!empty($result[3])) {
- $url = $result[3]['source'];
- } elseif(!empty($result[2])) {
- $url = $result[2]['source'];
+ $bestsize = $this->get_best_size($photoid);
+ if (!isset($bestsize['source'])) {
+ throw new repository_exception('cannotdownload', 'repository');
+ }
+ return $bestsize['source'];
+ }
+
+ /**
+ * Returns the best size for a photo
+ *
+ * @param string $photoid the photo identifier
+ * @return array of information provided by the API
+ */
+ protected function get_best_size($photoid) {
+ if (!isset(self::$sizes[$photoid])) {
+ // Sizes are returned from smallest to greatest.
+ self::$sizes[$photoid] = $this->flickr->photos_getSizes($photoid);
+ }
+ $sizes = self::$sizes[$photoid];
+ $bestsize = array();
+ if (is_array($sizes)) {
+ while ($bestsize = array_pop($sizes)) {
+ // Make sure the source is set. Exit the loop if found.
+ if (isset($bestsize['source'])) {
+ break;
+ }
+ }
}
- return $url;
+ return $bestsize;
}
public function get_link($photoid) {
private $flickr;
public $photos;
+ /**
+ * Stores sizes of images to prevent multiple API call
+ */
+ static private $sizes = array();
+
/**
* constructor method
*
public function license4moodle ($license_id) {
$license = array(
+ '0' => 'allrightsreserved',
'1' => 'cc-nc-sa',
'2' => 'cc-nc',
'3' => 'cc-nc-nd',
'4' => 'cc',
'5' => 'cc-sa',
'6' => 'cc-nd',
- '7' => 'allrightsreserved'
+ '7' => 'other'
);
return $license[$license_id];
}
* @return string
*/
private function build_photo_url($photoid) {
- $result = $this->flickr->photos_getSizes($photoid);
- $url = '';
- if(!empty($result[4])) {
- $url = $result[4]['source'];
- } elseif(!empty($result[3])) {
- $url = $result[3]['source'];
- } elseif(!empty($result[2])) {
- $url = $result[2]['source'];
+ $bestsize = $this->get_best_size($photoid);
+ if (!isset($bestsize['source'])) {
+ throw new repository_exception('cannotdownload', 'repository');
+ }
+ return $bestsize['source'];
+ }
+
+ /**
+ * Returns the best size for a photo
+ *
+ * @param string $photoid the photo identifier
+ * @return array of information provided by the API
+ */
+ protected function get_best_size($photoid) {
+ if (!isset(self::$sizes[$photoid])) {
+ // Sizes are returned from smallest to greatest.
+ self::$sizes[$photoid] = $this->flickr->photos_getSizes($photoid);
+ }
+ $sizes = self::$sizes[$photoid];
+ $bestsize = array();
+ if (is_array($sizes)) {
+ while ($bestsize = array_pop($sizes)) {
+ // Make sure the source is set. Exit the loop if found.
+ if (isset($bestsize['source'])) {
+ break;
+ }
+ }
}
- return $url;
+ return $bestsize;
}
public function get_link($photoid) {
$author = $info['owner']['username'];
}
$copyright = get_string('author', 'repository') . ': ' . $author;
- $result = $this->flickr->photos_getSizes($photoid);
- // download link
- $source = '';
- // flickr photo page
- $url = '';
- if (!empty($result[4])) {
- $source = $result[4]['source'];
- $url = $result[4]['url'];
- } elseif(!empty($result[3])) {
- $source = $result[3]['source'];
- $url = $result[3]['url'];
- } elseif(!empty($result[2])) {
- $source = $result[2]['source'];
- $url = $result[2]['url'];
+
+ // If we can read the original secret, it means that we have access to the original picture.
+ if (isset($info['originalsecret'])) {
+ $source = $this->flickr->buildPhotoURL($info, 'original');
+ } else {
+ $source = $this->build_photo_url($photoid);
}
+
$result = parent::get_file($source, $file);
$path = $result['path'];
+
if (!empty($this->usewatermarks)) {
$img = new moodle_image($path);
$img->watermark($copyright, array(10,10), array('ttf'=>true, 'fontsize'=>12))->saveas($path);
}
- return array('path'=>$path, 'url'=>$url, 'author'=>$info['owner']['realname'], 'license'=>$this->license4moodle($info['license']));
+ return array('path'=>$path, 'author'=>$info['owner']['realname'], 'license'=>$this->license4moodle($info['license']));
}
/**
background-color: #34637f;
color: #fff;
}
+.ie7 select { /* fixes compatibility view */
+ background-color: #eee;
+ color: #036;
+}
#loginbtn, input, button, select {
cursor: pointer;
margin-left: 5px;
.file-picker button,
.file-picker textarea {
background-color: #EEE;
-}
\ No newline at end of file
+}
padding-top: 10px;
}
-.forumpost .content .shortenedpost a,
-.forumpost .content p {
+.forumpost .content .shortenedpost a {
margin: 0 10px;
padding: 0;
}
#page-admin-report-backups-index .backup-unfinished {color: #f00000;}
#page-admin-report-backups-index .backup-skipped,
#page-admin-report-backups-index .backup-ok {color: #006400;}
+#page-admin-report-backups-index .backup-warning {color: #ff9900;}
#page-admin-qbehaviours .disabled {color: gray;}
#page-admin-qbehaviours th {white-space: normal;}
$contentfile = "$CFG->libdir/yui/$part";
}
if (!file_exists($contentfile) or !is_file($contentfile)) {
- $content .= "\n// Combo resource $part ($contentfile) not found!\n";
+ $location = '$CFG->dirroot'.preg_replace('/^'.preg_quote($CFG->dirroot, '/').'/', '', $contentfile);
+ $content .= "\n// Combo resource $part ($location) not found!\n";
continue;
}
$filecontent = file_get_contents($contentfile);
$hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
}
+if (has_capability('moodle/site:viewuseridentity', $context)) {
+ $identityfields = array_flip(explode(',', $CFG->showuseridentity));
+} else {
+ $identityfields = array();
+}
+
// Start setting up the page
$strpublicprofile = get_string('publicprofile');
print_row(get_string('city') . ':', $user->city);
}
-if (has_capability('moodle/user:viewhiddendetails', $context)) {
- if ($user->address) {
- print_row(get_string("address").":", "$user->address");
- }
- if ($user->phone1) {
- print_row(get_string("phone").":", "$user->phone1");
- }
- if ($user->phone2) {
- print_row(get_string("phone2").":", "$user->phone2");
- }
+if (isset($identityfields['address']) && $user->address) {
+ print_row(get_string("address").":", "$user->address");
+}
+
+if (isset($identityfields['phone1']) && $user->phone1) {
+ print_row(get_string("phone").":", "$user->phone1");
}
-if ($currentuser
+if (isset($identityfields['phone2']) && $user->phone2) {
+ print_row(get_string("phone2").":", "$user->phone2");
+}
+
+if (isset($identityfields['institution']) && $user->institution) {
+ print_row(get_string("institution").":", "$user->institution");
+}
+
+if (isset($identityfields['department']) && $user->department) {
+ print_row(get_string("department").":", "$user->department");
+}
+
+if (isset($identityfields['idnumber']) && $user->idnumber) {
+ print_row(get_string("idnumber").":", "$user->idnumber");
+}
+
+if (isset($identityfields['email']) and ($currentuser
or $user->maildisplay == 1
or has_capability('moodle/course:useremail', $context)
- or ($user->maildisplay == 2 and enrol_sharing_course($user, $USER))) {
-
+ or ($user->maildisplay == 2 and enrol_sharing_course($user, $USER)))) {
print_row(get_string("email").":", obfuscate_mailto($user->email, ''));
}
var clearbtn = Y.one('#'+this.name + '_clearbutton');
this.clearbutton = Y.Node.create('<input type="button" value="'+clearbtn.get('value')+'" />');
clearbtn.replace(Y.Node.getDOMNode(this.clearbutton));
- this.clearbutton.set('id',+this.name+"_clearbutton");
+ this.clearbutton.set('id', this.name+"_clearbutton");
this.clearbutton.on('click', this.handle_clear, this);
+ this.clearbutton.set('disabled', (this.get_search_text() == ''));
this.send_query(false);
},
defined('MOODLE_INTERNAL') || die();
-$version = 2012062501.12; // YYYYMMDD = weekly release date of this DEV branch
+$version = 2012062501.14; // YYYYMMDD = weekly release date of this DEV branch
// RR = release increments - 00 in DEV branches
// .XX = incremental changes