diff --git a/admin/environment.xml b/admin/environment.xml index 004cab09808..584a9b0e617 100644 --- a/admin/environment.xml +++ b/admin/environment.xml @@ -659,5 +659,121 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backup/moodle2/backup_custom_fields.php b/backup/moodle2/backup_custom_fields.php index e0ceb334d95..12929de6578 100644 --- a/backup/moodle2/backup_custom_fields.php +++ b/backup/moodle2/backup_custom_fields.php @@ -96,14 +96,19 @@ class file_nested_element extends backup_nested_element { 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); + } } } diff --git a/backup/upgrade.txt b/backup/upgrade.txt new file mode 100644 index 00000000000..cb020956ed0 --- /dev/null +++ b/backup/upgrade.txt @@ -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. \ No newline at end of file diff --git a/backup/util/dbops/backup_plan_dbops.class.php b/backup/util/dbops/backup_plan_dbops.class.php index e169a8a8ba1..2e2faaeb05e 100644 --- a/backup/util/dbops/backup_plan_dbops.class.php +++ b/backup/util/dbops/backup_plan_dbops.class.php @@ -197,19 +197,19 @@ abstract class backup_plan_dbops extends backup_dbops { * @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: @@ -231,7 +231,11 @@ abstract class backup_plan_dbops extends backup_dbops { $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')); diff --git a/backup/util/dbops/restore_dbops.class.php b/backup/util/dbops/restore_dbops.class.php index 3bd86e374db..4b6c63c3e20 100644 --- a/backup/util/dbops/restore_dbops.class.php +++ b/backup/util/dbops/restore_dbops.class.php @@ -818,10 +818,13 @@ abstract class restore_dbops { * @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 @@ -901,8 +904,14 @@ abstract class restore_dbops { // 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 @@ -959,6 +968,7 @@ abstract class restore_dbops { } } $rs->close(); + return $results; } /** diff --git a/backup/util/helper/backup_cron_helper.class.php b/backup/util/helper/backup_cron_helper.class.php index ea8fda08577..e3ada4f6b0d 100644 --- a/backup/util/helper/backup_cron_helper.class.php +++ b/backup/util/helper/backup_cron_helper.class.php @@ -46,6 +46,8 @@ abstract class backup_cron_automated_helper { 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 @@ abstract class backup_cron_automated_helper { $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 @@ abstract class backup_cron_automated_helper { $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 @@ abstract class backup_cron_automated_helper { $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 @@ abstract class backup_cron_automated_helper { $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 @@ abstract class backup_cron_automated_helper { 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 @@ abstract class backup_cron_automated_helper { */ 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 @@ abstract class backup_cron_automated_helper { $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 @@ abstract class backup_cron_automated_helper { } 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 @@ abstract class backup_cron_automated_helper { $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(); @@ -396,6 +403,30 @@ abstract class backup_cron_automated_helper { 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. @@ -530,18 +561,7 @@ abstract class backup_cron_automated_helper { 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 diff --git a/backup/util/plan/backup_structure_step.class.php b/backup/util/plan/backup_structure_step.class.php index f62fee936a1..964dd3f9885 100644 --- a/backup/util/plan/backup_structure_step.class.php +++ b/backup/util/plan/backup_structure_step.class.php @@ -94,11 +94,22 @@ abstract class backup_structure_step extends backup_step { // 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; } /** diff --git a/backup/util/plan/restore_structure_step.class.php b/backup/util/plan/restore_structure_step.class.php index 42491cf432b..7a1cfa28fdf 100644 --- a/backup/util/plan/restore_structure_step.class.php +++ b/backup/util/plan/restore_structure_step.class.php @@ -218,8 +218,14 @@ abstract class restore_structure_step extends restore_step { */ 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); } /** diff --git a/backup/util/structure/backup_nested_element.class.php b/backup/util/structure/backup_nested_element.class.php index 9a479013d91..8557ec8ae53 100644 --- a/backup/util/structure/backup_nested_element.class.php +++ b/backup/util/structure/backup_nested_element.class.php @@ -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 @@ class backup_nested_element extends base_nested_element implements processable { $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 @@ class backup_nested_element extends base_nested_element implements processable { $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; diff --git a/backup/util/ui/backup_ui_stage.class.php b/backup/util/ui/backup_ui_stage.class.php index 472294af5a7..4065212648c 100644 --- a/backup/util/ui/backup_ui_stage.class.php +++ b/backup/util/ui/backup_ui_stage.class.php @@ -487,6 +487,9 @@ class backup_ui_stage_complete extends backup_ui_stage_final { 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(); diff --git a/backup/util/ui/restore_ui_stage.class.php b/backup/util/ui/restore_ui_stage.class.php index 6c282544f05..a7464bf49ae 100644 --- a/backup/util/ui/restore_ui_stage.class.php +++ b/backup/util/ui/restore_ui_stage.class.php @@ -772,6 +772,9 @@ class restore_ui_stage_complete extends restore_ui_stage_process { $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'); diff --git a/blocks/completionstatus/block_completionstatus.php b/blocks/completionstatus/block_completionstatus.php index 2b598ce767f..b339e9a74cd 100644 --- a/blocks/completionstatus/block_completionstatus.php +++ b/blocks/completionstatus/block_completionstatus.php @@ -19,15 +19,14 @@ * * @package block * @subpackage completion - * @copyright 2009 Catalyst IT Ltd + * @copyright 2009-2012 Catalyst IT Ltd * @author Aaron Barnes * @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 @@ -36,25 +35,28 @@ require_once($CFG->libdir.'/completionlib.php'); 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; + $context = context_course::instance($course->id); + // Create empty content - $this->content = new stdClass; + $this->content = new stdClass(); // Can edit settings? - $can_edit = has_capability('moodle/course:update', context_course::instance($this->page->course->id)); + $can_edit = has_capability('moodle/course:update', $context); // 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 +86,9 @@ class block_completionstatus extends block_base { // 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', context_course::instance($COURSE->id))) { - $this->content->text = ''.get_string('viewcoursereport', 'completion').''; + if (has_capability('report/completion:view', $context)) { + $report = new moodle_url('/report/completion/index.php', array('course' => $course->id)); + $this->content->text = ''.get_string('viewcoursereport', 'completion').''; return $this->content; } @@ -187,7 +189,7 @@ class block_completionstatus extends block_base { // Load course completion $params = array( 'userid' => $USER->id, - 'course' => $COURSE->id + 'course' => $course->id ); $ccompletion = new completion_completion($params); @@ -221,7 +223,8 @@ class block_completionstatus extends block_base { $this->content->text .= $shtml.''; // Display link to detailed view - $this->content->footer = '
'.get_string('moredetails', 'completion').''; + $details = new moodle_url('/blocks/completionstatus/details.php', array('course' => $course->id)); + $this->content->footer = '
'.get_string('moredetails', 'completion').''; return $this->content; } diff --git a/blocks/completionstatus/details.php b/blocks/completionstatus/details.php index a71da02635a..bb1b051562f 100644 --- a/blocks/completionstatus/details.php +++ b/blocks/completionstatus/details.php @@ -19,27 +19,23 @@ * * @package block * @subpackage completion - * @copyright 2009 Catalyst IT Ltd + * @copyright 2009-2012 Catalyst IT Ltd * @author Aaron Barnes * @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 @@ if (!$can_view) { // 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 @@ if (!$info->is_tracked_user($user->id)) { /// /// Display page /// +$PAGE->set_context(context_course::instance($course->id)); // Print header $page = get_string('completionprogressdetails', 'block_completionstatus'); @@ -111,7 +100,7 @@ $title = format_string($course->fullname) . ': ' . $page; $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 @@ $coursecomplete = $info->is_course_complete($user->id); // 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 ''.get_string('notyetstarted', 'completion').''; } else { echo ''.get_string('inprogress','completion').''; } echo ''; -echo ''.get_string('required').': '; -// 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 '
'; + echo $OUTPUT->box(get_string('err_nocriteria', 'completion'), 'noticebox'); + echo ''; } else { - echo get_string('criteriarequiredany', 'completion'); -} + echo ''.get_string('required').': '; -echo ''; + // Get overall aggregation method + $overall = $info->get_aggregation_method(); -// Generate markup for criteria statuses -echo ''; -echo ''; -echo ''; -echo ''; -echo ''; -echo ''; -echo ''; -echo ''; -echo ''; - -// 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; -} - -// Print table -$last_type = ''; -$agg_type = false; - -foreach ($rows as $row) { - - // Criteria group - echo ''; - // Criteria title - echo ''; + echo '
'.get_string('criteriagroup', 'block_completionstatus').''.get_string('criteria', 'completion').''.get_string('requirement', 'block_completionstatus').''.get_string('status').''.get_string('complete').''.get_string('completiondate', 'report_completion').'
'; - if ($last_type !== $row['details']['type']) { - $last_type = $row['details']['type']; - echo $last_type; - - // Reset agg type - $agg_type = true; + if ($overall == COMPLETION_AGGREGATION_ALL) { + echo get_string('criteriarequiredall', 'completion'); } else { - // Display aggregation type - if ($agg_type) { - $agg = $info->get_aggregation_method($row['type']); - - echo '('; - - if ($agg == COMPLETION_AGGREGATION_ALL) { - echo strtolower(get_string('all', 'completion')); - } else { - echo strtolower(get_string('any', 'completion')); - } - - echo ' '.strtolower(get_string('required')).')'; - $agg_type = false; - } + echo get_string('criteriarequiredany', 'completion'); } - echo ''; - echo $row['details']['criteria']; - echo '
'; - // Requirement - echo ''; - echo $row['details']['requirement']; - echo ''; - - // Status - echo ''; - echo $row['details']['status']; - echo ''; - - // Is complete - echo ''; - echo ($row['status'] === get_string('yes')) ? get_string('yes') : get_string('no'); - echo ''; - - // Completion data - echo ''; - if ($row['timecompleted']) { - echo userdate($row['timecompleted'], '%e %B %G'); - } else { - echo '-'; - } - echo ''; + // Generate markup for criteria statuses + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; echo ''; + + // 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; + } + + // Print table + $last_type = ''; + $agg_type = false; + $oddeven = 0; + + foreach ($rows as $row) { + + echo ''; + + // Criteria group + echo ''; + + // Criteria title + echo ''; + + // Requirement + echo ''; + + // Status + echo ''; + + // Is complete + echo ''; + + // Completion data + echo ''; + echo ''; + // for row striping + $oddeven = $oddeven ? 0 : 1; + } + + echo '
'.get_string('criteriagroup', 'block_completionstatus').''.get_string('criteria', 'completion').''.get_string('requirement', 'block_completionstatus').''.get_string('status').''.get_string('complete').''.get_string('completiondate', 'report_completion').'
'; + 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 '('; + + if ($agg == COMPLETION_AGGREGATION_ALL) { + echo strtolower(get_string('aggregateall', 'completion')); + } else { + echo strtolower(get_string('aggregateany', 'completion')); + } + + echo ' '.strtolower(get_string('required')).')'; + $agg_type = false; + } + } + echo ''; + echo $row['details']['criteria']; + echo ''; + echo $row['details']['requirement']; + echo ''; + echo $row['details']['status']; + echo ''; + echo $row['complete'] ? get_string('yes') : get_string('no'); + echo ''; + if ($row['timecompleted']) { + echo userdate($row['timecompleted'], get_string('strftimedate', 'langconfig')); + } else { + echo '-'; + } + echo '
'; } -echo ''; +echo '
'; +$courseurl = new moodle_url("/course/view.php", array('id' => $course->id)); +echo $OUTPUT->single_button($courseurl, get_string('returntocourse', 'block_completionstatus'), 'get'); +echo '
'; echo $OUTPUT->footer(); diff --git a/blocks/completionstatus/lang/en/block_completionstatus.php b/blocks/completionstatus/lang/en/block_completionstatus.php index fcc965ac7fd..6658c17dff4 100644 --- a/blocks/completionstatus/lang/en/block_completionstatus.php +++ b/blocks/completionstatus/lang/en/block_completionstatus.php @@ -5,3 +5,4 @@ $string['criteriagroup'] = 'Criteria group'; $string['firstofsecond'] = '{$a->first} of {$a->second}'; $string['pluginname'] = 'Course completion status'; $string['requirement'] = 'Requirement'; +$string['returntocourse'] = 'Return to course'; diff --git a/blog/external_blogs.php b/blog/external_blogs.php index bdf30ef0c48..bc91d4061dd 100644 --- a/blog/external_blogs.php +++ b/blog/external_blogs.php @@ -44,7 +44,16 @@ $message = null; 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'); } } diff --git a/blog/locallib.php b/blog/locallib.php index 43b36af0418..9a07fbace76 100644 --- a/blog/locallib.php +++ b/blog/locallib.php @@ -292,11 +292,10 @@ class blog_entry implements renderable { * @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()); diff --git a/enrol/manual/yui/quickenrolment/quickenrolment.js b/enrol/manual/yui/quickenrolment/quickenrolment.js index 08f8d702823..4efe9674723 100644 --- a/enrol/manual/yui/quickenrolment/quickenrolment.js +++ b/enrol/manual/yui/quickenrolment/quickenrolment.js @@ -339,7 +339,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) { count++; var user = result.response.users[i]; users.append(create('
') - .addClass((i%2)?CSS.ODD:CSS.EVEN) + .addClass((count%2)?CSS.ODD:CSS.EVEN) .append(create('
'+count+'
')) .append(create('
') .append(create(user.picture))) diff --git a/enrol/paypal/ipn.php b/enrol/paypal/ipn.php index 2a28c245e90..67fb06fa997 100644 --- a/enrol/paypal/ipn.php +++ b/enrol/paypal/ipn.php @@ -34,6 +34,7 @@ require("../../config.php"); 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 @@ if (! $plugin_instance = $DB->get_record("enrol", array("id"=>$data->instanceid, $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 "

Error: could not access paypal.com

"; message_paypal_error_to_admin("Could not access paypal.com to verify payment", $data); die; @@ -104,12 +108,9 @@ if (!$fp) { /// Could not open a socket to PayPal - FAIL /// 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 @@ while (!feof($fp)) { } } -fclose($fp); exit; diff --git a/filter/mediaplugin/tests/filter_test.php b/filter/mediaplugin/tests/filter_test.php index 86876102204..c0044fb6ab5 100644 --- a/filter/mediaplugin/tests/filter_test.php +++ b/filter/mediaplugin/tests/filter_test.php @@ -57,6 +57,9 @@ class filter_mediaplugin_testcase extends advanced_testcase { 'test mpg', 'test', 'test file', + 'test file', + 'test file', + 'test file', 'test file', 'test flv', 'test file', diff --git a/grade/edit/tree/category_form.php b/grade/edit/tree/category_form.php index 5a1d8f7c30e..c155565ffe4 100644 --- a/grade/edit/tree/category_form.php +++ b/grade/edit/tree/category_form.php @@ -225,7 +225,7 @@ class edit_category_form extends moodleform { $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) { @@ -238,6 +238,7 @@ class edit_category_form extends moodleform { 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')); } diff --git a/install/lang/ko/install.php b/install/lang/ko/install.php index c8574b51f01..0dd2488181e 100644 --- a/install/lang/ko/install.php +++ b/install/lang/ko/install.php @@ -34,7 +34,8 @@ $string['admindirname'] = '관리 디렉토리'; $string['availablelangs'] = '가능한 언어 목록'; $string['chooselanguagehead'] = '언어를 선택하시오'; $string['chooselanguagesub'] = '설치 과정에서 사용할 언어를 선택하십시오. 선택한 언어는 사이트의 기본 언어로 사용할 수 있으며, 추후 다른 언어로 바꿀 수도 있습니다.'; -$string['clialreadyinstalled'] = '이미 config.php 파일이 존재함. 사이트를 업데이트하려면 admin/cli/upgrade.php를 사용하십시오'; +$string['clialreadyconfigured'] = '만일 이 사이트를 설치하고 싶은데 이미 config.php파일이 있다면, admin/cli/install_database.php 를 이용하시기 바랍니다.'; +$string['clialreadyinstalled'] = '이미 config.php 파일이 존재함. 사이트를 업그레이드하려면 admin/cli/upgrade.php를 사용하시기 바랍니다.'; $string['cliinstallheader'] = '무들 {$a} 명령 입력 설치 프로그램'; $string['databasehost'] = '데이터베이스 호스트'; $string['databasename'] = '데이터베이스 명칭'; @@ -82,10 +83,10 @@ $string['phpversionhelp'] = '

무들은 적어도 PHP4.3.0 혹은 5.1.0. 이

PHP를 업그레이드 하시거나 새버전을 제공하는 웹호스팅 업체로 이전하기를 권합니다!
(만일 5.0.x버전을 사용 중이라면 4.4.x 버전으로 다운그레이드 할 수 있습니다)

'; $string['welcomep10'] = '{$a->installername} ({$a->installerversion})'; $string['welcomep20'] = '당신의 컴퓨터에 {$a->packname} {$a->packversion} 패키지를 성공적으로 설치한 것을 축하합니다!'; -$string['welcomep30'] = '{$a->installername} 의 이 릴리스는 무들이 그 속에서 동작하는 환경을 생성하기 위한 어플리케이션을 포함하고 있습니다.'; +$string['welcomep30'] = '{$a->installername} 판본은 무들이 동작하는 환경을 생성하기 위한 어플리케이션을 포함하고 있습니다.'; $string['welcomep40'] = '이 패키지는 무들 {$a->moodlerelease} ({$a->moodleversion}) 을 포함하고 있습니다.'; $string['welcomep50'] = '이 패키지에 있는 모든 어플리케이션을 사용하는 것은 각각의 라이센스에의해 지배받습니다. 완전한{$a->installername} 패키지는 공개 소스이며 GPL 라이선스에 의해 배포됩니다.'; -$string['welcomep60'] = '다음 페이지들은 당신의 컴퓨터에 무들을 설정하고 설치하는 길라잡이 역할을 할 것입니다. 기본 설정을 선택하거나 목적에 맞게 선택적으로 수정할 수 있습니다.'; +$string['welcomep60'] = '다음 페이지들은 컴퓨터에 무들을 설치하고 설정하는 길라잡이 역할을 할 것입니다. 기본 설정을 선택하거나 목적에 맞게 선택적으로 수정할 수 있습니다.'; $string['welcomep70'] = '무들 설정을 계속하기 위해서는 "다음" 버튼을 클릭하세요.'; $string['wwwroot'] = '웹 주소'; diff --git a/lang/en/admin.php b/lang/en/admin.php index 7f8972a8564..5e7b0d98264 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -68,7 +68,7 @@ $string['availablelicenses'] = 'Available licences'; $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'; diff --git a/lang/en/backup.php b/lang/en/backup.php index 5fde19e8722..e053c96bdee 100644 --- a/lang/en/backup.php +++ b/lang/en/backup.php @@ -163,6 +163,7 @@ $string['lockedbypermission'] = 'You don\'t have sufficient permissions to chang $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'; @@ -177,6 +178,7 @@ $string['restoreactivity'] = 'Restore activity'; $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'; diff --git a/lang/en/block.php b/lang/en/block.php index c0b8f01c1f6..a685f0ef0e6 100644 --- a/lang/en/block.php +++ b/lang/en/block.php @@ -37,6 +37,8 @@ $string['defaultregion'] = 'Default region'; $string['defaultregion_help'] = 'Themes may define one or more named block regions where blocks are displayed. This setting defines which of these you want this block to appear in by default. The region may be overridden on specific pages if required.'; $string['defaultweight'] = 'Default weight'; $string['defaultweight_help'] = 'The default weight allows you to choose roughly where you want the block to appear in the chosen region, either at the top or the bottom. The final location is calculated from all the blocks in that region (for example, only one block can actually be at the top). This value can be overridden on specific pages if required.'; +$string['deletecheck'] = 'Delete {$a} block?'; +$string['deleteblockcheck'] = 'Are you sure that you want to delete this block titled {$a}?'; $string['moveblockhere'] = 'Move block here'; $string['movingthisblockcancel'] = 'Moving this block ({$a})'; $string['onthispage'] = 'On this page'; diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 2fc4fac2df5..f7f308401d6 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -1802,6 +1802,7 @@ $string['virusfounduser'] = 'The file you have uploaded, {$a->filename}, has bee $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'; diff --git a/lib/accesslib.php b/lib/accesslib.php index 523086375fe..2163435043d 100644 --- a/lib/accesslib.php +++ b/lib/accesslib.php @@ -6676,7 +6676,7 @@ class context_module extends context { if ($withprefix){ $name = get_string('modulename', $cm->modname).': '; } - $name .= $mod->name; + $name .= format_string($mod->name, true, array('context' => $this)); } } return $name; diff --git a/lib/adodb/adodb-active-record.inc.php b/lib/adodb/adodb-active-record.inc.php index bf20639d3f4..9ea8175a4c4 100644 --- a/lib/adodb/adodb-active-record.inc.php +++ b/lib/adodb/adodb-active-record.inc.php @@ -1,7 +1,7 @@ RecordCount() is used. @@ -3499,23 +3499,22 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * * $upper 0 = lowercase, 1 = uppercase, 2 = whatever is returned by FetchField */ - function GetRowAssoc($upper=1) + function GetRowAssoc($upper=1) { $record = array(); - // if (!$this->fields) return $record; - - if (!$this->bind) { + if (!$this->bind) { $this->GetAssocKeys($upper); } - foreach($this->bind as $k => $v) { - $record[$k] = $this->fields[$v]; + if( isset( $this->fields[$v] ) ) { + $record[$k] = $this->fields[$v]; + } else if (isset($this->fields[$k])) { + $record[$k] = $this->fields[$k]; + } } - return $record; } - /** * Clean up recordset * diff --git a/lib/adodb/drivers/adodb-access.inc.php b/lib/adodb/drivers/adodb-access.inc.php index f5685f347e5..c9efb1fd8f8 100644 --- a/lib/adodb/drivers/adodb-access.inc.php +++ b/lib/adodb/drivers/adodb-access.inc.php @@ -1,6 +1,6 @@ $argDatabasename,'UID'=>$argUsername,'PWD'=>$argPassword); + $connectionInfo = $this->connectionInfo; + $connectionInfo["Database"]=$argDatabasename; + $connectionInfo["UID"]=$argUsername; + $connectionInfo["PWD"]=$argPassword; if ($this->debug) error_log("
connecting... hostname: $argHostname params: ".var_export($connectionInfo,true)); //if ($this->debug) error_log("
_connectionID before: ".serialize($this->_connectionID)); if(!($this->_connectionID = sqlsrv_connect($argHostname,$connectionInfo))) { diff --git a/lib/adodb/drivers/adodb-mssqlpo.inc.php b/lib/adodb/drivers/adodb-mssqlpo.inc.php index 76dfe245a21..60bc9c7c3aa 100644 --- a/lib/adodb/drivers/adodb-mssqlpo.inc.php +++ b/lib/adodb/drivers/adodb-mssqlpo.inc.php @@ -1,6 +1,6 @@ rsPrefix .= 'ext_'; } - + + + // SetCharSet - switch the client encoding + function SetCharSet($charset_name) + { + if (!function_exists('mysql_set_charset')) + return false; + + if ($this->charSet !== $charset_name) { + $ok = @mysql_set_charset($charset_name,$this->_connectionID); + if ($ok) { + $this->charSet = $charset_name; + return true; + } + return false; + } + return true; + } + function ServerInfo() { $arr['description'] = ADOConnection::GetOne("select version()"); diff --git a/lib/adodb/drivers/adodb-mysqli.inc.php b/lib/adodb/drivers/adodb-mysqli.inc.php index b1d5d4d35d8..bb556a131a9 100644 --- a/lib/adodb/drivers/adodb-mysqli.inc.php +++ b/lib/adodb/drivers/adodb-mysqli.inc.php @@ -1,6 +1,6 @@ _hasOCIFetchStatement = ADODB_PHPVER >= 0x4200; + $this->_hasOciFetchStatement = ADODB_PHPVER >= 0x4200; if (defined('ADODB_EXTENSION')) $this->rsPrefix .= 'ext_'; } @@ -201,7 +201,7 @@ NATSOFT.DOMAIN = */ function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$mode=0) { - if (!function_exists('OCIPLogon')) return null; + if (!function_exists('oci_pconnect')) return null; #adodb_backtrace(); $this->_errorMsg = false; @@ -235,22 +235,22 @@ NATSOFT.DOMAIN = //if ($argHostname) print "

Connect: 1st argument should be left blank for $this->databaseType

"; if ($mode==1) { $this->_connectionID = ($this->charSet) ? - OCIPLogon($argUsername,$argPassword, $argDatabasename,$this->charSet) + oci_pconnect($argUsername,$argPassword, $argDatabasename,$this->charSet) : - OCIPLogon($argUsername,$argPassword, $argDatabasename) + oci_pconnect($argUsername,$argPassword, $argDatabasename) ; - if ($this->_connectionID && $this->autoRollback) OCIrollback($this->_connectionID); + if ($this->_connectionID && $this->autoRollback) oci_rollback($this->_connectionID); } else if ($mode==2) { $this->_connectionID = ($this->charSet) ? - OCINLogon($argUsername,$argPassword, $argDatabasename,$this->charSet) + oci_new_connect($argUsername,$argPassword, $argDatabasename,$this->charSet) : - OCINLogon($argUsername,$argPassword, $argDatabasename); + oci_new_connect($argUsername,$argPassword, $argDatabasename); } else { $this->_connectionID = ($this->charSet) ? - OCILogon($argUsername,$argPassword, $argDatabasename,$this->charSet) + oci_connect($argUsername,$argPassword, $argDatabasename,$this->charSet) : - OCILogon($argUsername,$argPassword, $argDatabasename); + oci_connect($argUsername,$argPassword, $argDatabasename); } if (!$this->_connectionID) return false; if ($this->_initdate) { @@ -259,7 +259,7 @@ NATSOFT.DOMAIN = // looks like: // Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production - // $vers = OCIServerVersion($this->_connectionID); + // $vers = oci_server_version($this->_connectionID); // if (strpos($vers,'8i') !== false) $this->ansiOuter = true; return true; } @@ -267,7 +267,7 @@ NATSOFT.DOMAIN = function ServerInfo() { $arr['compat'] = $this->GetOne('select value from sys.database_compatible_level'); - $arr['description'] = @OCIServerVersion($this->_connectionID); + $arr['description'] = @oci_server_version($this->_connectionID); $arr['version'] = ADOConnection::_findvers($arr['description']); return $arr; } @@ -285,7 +285,7 @@ NATSOFT.DOMAIN = function _affectedrows() { - if (is_resource($this->_stmt)) return @OCIRowCount($this->_stmt); + if (is_resource($this->_stmt)) return @oci_num_rows($this->_stmt); return 0; } @@ -386,6 +386,13 @@ NATSOFT.DOMAIN = $false = false; $rs = $this->Execute(sprintf("SELECT * FROM ALL_CONSTRAINTS WHERE UPPER(TABLE_NAME)='%s' AND CONSTRAINT_TYPE='P'",$table)); + if (!is_object($rs)) { + if (isset($savem)) + $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + return $false; + } + if ($row = $rs->FetchRow()) $primary_key = $row[1]; //constraint_name @@ -451,7 +458,7 @@ NATSOFT.DOMAIN = if (!$ok) return $this->RollbackTrans(); if ($this->transCnt) $this->transCnt -= 1; - $ret = OCIcommit($this->_connectionID); + $ret = oci_commit($this->_connectionID); $this->_commit = OCI_COMMIT_ON_SUCCESS; $this->autoCommit = true; return $ret; @@ -461,7 +468,7 @@ NATSOFT.DOMAIN = { if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; - $ret = OCIrollback($this->_connectionID); + $ret = oci_rollback($this->_connectionID); $this->_commit = OCI_COMMIT_ON_SUCCESS; $this->autoCommit = true; return $ret; @@ -477,10 +484,10 @@ NATSOFT.DOMAIN = { if ($this->_errorMsg !== false) return $this->_errorMsg; - if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt); + if (is_resource($this->_stmt)) $arr = @oci_error($this->_stmt); if (empty($arr)) { - if (is_resource($this->_connectionID)) $arr = @OCIError($this->_connectionID); - else $arr = @OCIError(); + if (is_resource($this->_connectionID)) $arr = @oci_error($this->_connectionID); + else $arr = @oci_error(); if ($arr === false) return ''; } $this->_errorMsg = $arr['message']; @@ -492,10 +499,10 @@ NATSOFT.DOMAIN = { if ($this->_errorCode !== false) return $this->_errorCode; - if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt); + if (is_resource($this->_stmt)) $arr = @oci_error($this->_stmt); if (empty($arr)) { - $arr = @OCIError($this->_connectionID); - if ($arr == false) $arr = @OCIError(); + $arr = @oci_error($this->_connectionID); + if ($arr == false) $arr = @oci_error(); if ($arr == false) return ''; } @@ -651,34 +658,34 @@ NATSOFT.DOMAIN = foreach($inputarr as $k => $v) { if (is_array($v)) { if (sizeof($v) == 2) // suggested by g.giunta@libero. - OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]); + oci_bind_by_name($stmt,":$k",$inputarr[$k][0],$v[1]); else - OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]); + oci_bind_by_name($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]); } else { $len = -1; if ($v === ' ') $len = 1; - if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again + if (isset($bindarr)) { // is prepared sql, so no need to oci_bind_by_name again $bindarr[$k] = $v; } else { // dynamic sql, so rebind every time - OCIBindByName($stmt,":$k",$inputarr[$k],$len); + oci_bind_by_name($stmt,":$k",$inputarr[$k],$len); } } } } - if (!OCIExecute($stmt, OCI_DEFAULT)) { - OCIFreeStatement($stmt); + if (!oci_execute($stmt, OCI_DEFAULT)) { + oci_free_statement($stmt); return $false; } - $ncols = OCINumCols($stmt); + $ncols = oci_num_fields($stmt); for ( $i = 1; $i <= $ncols; $i++ ) { - $cols[] = '"'.OCIColumnName($stmt, $i).'"'; + $cols[] = '"'.oci_field_name($stmt, $i).'"'; } $result = false; - OCIFreeStatement($stmt); + oci_free_statement($stmt); $fields = implode(',', $cols); if ($nrows <= 0) $nrows = 999999999999; else $nrows += $offset; @@ -741,7 +748,7 @@ NATSOFT.DOMAIN = else $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob"; - $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB); + $desc = oci_new_descriptor($this->_connectionID, OCI_D_LOB); $arr['blob'] = array($desc,-1,$type); if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT'); $commit = $this->autoCommit; @@ -772,7 +779,7 @@ NATSOFT.DOMAIN = else $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob"; - $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB); + $desc = oci_new_descriptor($this->_connectionID, OCI_D_LOB); $arr['blob'] = array($desc,-1,$type); $this->BeginTrans(); @@ -873,12 +880,12 @@ NATSOFT.DOMAIN = { static $BINDNUM = 0; - $stmt = OCIParse($this->_connectionID,$sql); + $stmt = oci_parse($this->_connectionID,$sql); if (!$stmt) { $this->_errorMsg = false; $this->_errorCode = false; - $arr = @OCIError($this->_connectionID); + $arr = @oci_error($this->_connectionID); if ($arr === false) return false; $this->_errorMsg = $arr['message']; @@ -888,9 +895,9 @@ NATSOFT.DOMAIN = $BINDNUM += 1; - $sttype = @OCIStatementType($stmt); + $sttype = @oci_statement_type($stmt); if ($sttype == 'BEGIN' || $sttype == 'DECLARE') { - return array($sql,$stmt,0,$BINDNUM, ($cursor) ? OCINewCursor($this->_connectionID) : false); + return array($sql,$stmt,0,$BINDNUM, ($cursor) ? oci_new_cursor($this->_connectionID) : false); } return array($sql,$stmt,0,$BINDNUM); } @@ -912,7 +919,7 @@ NATSOFT.DOMAIN = function ExecuteCursor($sql,$cursorName='rs',$params=false) { if (is_array($sql)) $stmt = $sql; - else $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate OCINewCursor + else $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate oci_new_cursor if (is_array($stmt) && sizeof($stmt) >= 5) { $hasref = true; @@ -928,7 +935,7 @@ NATSOFT.DOMAIN = $rs = $this->Execute($stmt); if ($rs) { - if ($rs->databaseType == 'array') OCIFreeCursor($stmt[4]); + if ($rs->databaseType == 'array') oci_free_cursor($stmt[4]); else if ($hasref) $rs->_refcursor = $stmt[4]; } return $rs; @@ -955,13 +962,13 @@ NATSOFT.DOMAIN = Some timings: ** Test table has 3 cols, and 1 index. Test to insert 1000 records - Time 0.6081s (1644.60 inserts/sec) with direct OCIParse/OCIExecute + Time 0.6081s (1644.60 inserts/sec) with direct oci_parse/oci_execute Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute Now if PHP only had batch/bulk updating like Java or PL/SQL... - Note that the order of parameters differs from OCIBindByName, + Note that the order of parameters differs from oci_bind_by_name, because we default the names to :0, :1, :2 */ function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false) @@ -970,12 +977,12 @@ NATSOFT.DOMAIN = if (!is_array($stmt)) return false; if (($type == OCI_B_CURSOR) && sizeof($stmt) >= 5) { - return OCIBindByName($stmt[1],":".$name,$stmt[4],$size,$type); + return oci_bind_by_name($stmt[1],":".$name,$stmt[4],$size,$type); } if ($name == false) { - if ($type !== false) $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size,$type); - else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator + if ($type !== false) $rez = oci_bind_by_name($stmt[1],":".$stmt[2],$var,$size,$type); + else $rez = oci_bind_by_name($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator $stmt[2] += 1; } else if (oci_lob_desc($type)) { if ($this->debug) { @@ -983,11 +990,11 @@ NATSOFT.DOMAIN = } //we have to create a new Descriptor here $numlob = count($this->_refLOBs); - $this->_refLOBs[$numlob]['LOB'] = OCINewDescriptor($this->_connectionID, oci_lob_desc($type)); + $this->_refLOBs[$numlob]['LOB'] = oci_new_descriptor($this->_connectionID, oci_lob_desc($type)); $this->_refLOBs[$numlob]['TYPE'] = $isOutput; $tmp = $this->_refLOBs[$numlob]['LOB']; - $rez = OCIBindByName($stmt[1], ":".$name, $tmp, -1, $type); + $rez = oci_bind_by_name($stmt[1], ":".$name, $tmp, -1, $type); if ($this->debug) { ADOConnection::outp("Bind: descriptor has been allocated, var (".$name.") binded"); } @@ -1008,8 +1015,8 @@ NATSOFT.DOMAIN = if ($this->debug) ADOConnection::outp("Bind: name = $name"); - if ($type !== false) $rez = OCIBindByName($stmt[1],":".$name,$var,$size,$type); - else $rez = OCIBindByName($stmt[1],":".$name,$var,$size); // +1 byte for null terminator + if ($type !== false) $rez = oci_bind_by_name($stmt[1],":".$name,$var,$size,$type); + else $rez = oci_bind_by_name($stmt[1],":".$name,$var,$size); // +1 byte for null terminator } return $rez; @@ -1034,7 +1041,7 @@ NATSOFT.DOMAIN = @param [$maxLen] Holds an maximum length of the variable. @param [$type] The data type of $var. Legal values depend on driver. - See OCIBindByName documentation at php.net. + See oci_bind_by_name documentation at php.net. */ function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false) { @@ -1066,7 +1073,7 @@ NATSOFT.DOMAIN = if (is_array($sql)) { // is prepared sql $stmt = $sql[1]; - // we try to bind to permanent array, so that OCIBindByName is persistent + // we try to bind to permanent array, so that oci_bind_by_name is persistent // and carried out once only - note that max array element size is 4000 chars if (is_array($inputarr)) { $bindpos = $sql[3]; @@ -1078,27 +1085,27 @@ NATSOFT.DOMAIN = $bindarr = array(); foreach($inputarr as $k => $v) { $bindarr[$k] = $v; - OCIBindByName($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ? -1 : 4000); + oci_bind_by_name($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ? -1 : 4000); } $this->_bind[$bindpos] = $bindarr; } } } else { - $stmt=OCIParse($this->_connectionID,$sql); + $stmt=oci_parse($this->_connectionID,$sql); } $this->_stmt = $stmt; if (!$stmt) return false; - if (defined('ADODB_PREFETCH_ROWS')) @OCISetPrefetch($stmt,ADODB_PREFETCH_ROWS); + if (defined('ADODB_PREFETCH_ROWS')) @oci_set_prefetch($stmt,ADODB_PREFETCH_ROWS); if (is_array($inputarr)) { foreach($inputarr as $k => $v) { if (is_array($v)) { if (sizeof($v) == 2) // suggested by g.giunta@libero. - OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]); + oci_bind_by_name($stmt,":$k",$inputarr[$k][0],$v[1]); else - OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]); + oci_bind_by_name($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]); if ($this->debug==99) { if (is_object($v[0])) @@ -1110,10 +1117,10 @@ NATSOFT.DOMAIN = } else { $len = -1; if ($v === ' ') $len = 1; - if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again + if (isset($bindarr)) { // is prepared sql, so no need to oci_bind_by_name again $bindarr[$k] = $v; } else { // dynamic sql, so rebind every time - OCIBindByName($stmt,":$k",$inputarr[$k],$len); + oci_bind_by_name($stmt,":$k",$inputarr[$k],$len); } } } @@ -1121,8 +1128,8 @@ NATSOFT.DOMAIN = $this->_errorMsg = false; $this->_errorCode = false; - if (OCIExecute($stmt,$this->_commit)) { -//OCIInternalDebug(1); + if (oci_execute($stmt,$this->_commit)) { + if (count($this -> _refLOBs) > 0) { foreach ($this -> _refLOBs as $key => $value) { @@ -1144,7 +1151,7 @@ NATSOFT.DOMAIN = } } - switch (@OCIStatementType($stmt)) { + switch (@oci_statement_type($stmt)) { case "SELECT": return $stmt; @@ -1153,20 +1160,20 @@ NATSOFT.DOMAIN = if (is_array($sql) && !empty($sql[4])) { $cursor = $sql[4]; if (is_resource($cursor)) { - $ok = OCIExecute($cursor); + $ok = oci_execute($cursor); return $cursor; } return $stmt; } else { if (is_resource($stmt)) { - OCIFreeStatement($stmt); + oci_free_statement($stmt); return true; } return $stmt; } break; default : - // ociclose -- no because it could be used in a LOB? + return true; } } @@ -1204,14 +1211,14 @@ NATSOFT.DOMAIN = { if (!$this->_connectionID) return; - if (!$this->autoCommit) OCIRollback($this->_connectionID); + if (!$this->autoCommit) oci_rollback($this->_connectionID); if (count($this->_refLOBs) > 0) { foreach ($this ->_refLOBs as $key => $value) { $this->_refLOBs[$key]['LOB']->free(); unset($this->_refLOBs[$key]); } } - OCILogoff($this->_connectionID); + oci_close($this->_connectionID); $this->_stmt = false; $this->_connectionID = false; @@ -1383,7 +1390,7 @@ class ADORecordset_oci8 extends ADORecordSet { /* // based on idea by Gaetano Giunta to detect unusual oracle errors // see http://phplens.com/lens/lensforum/msgs.php?id=6771 - $err = OCIError($this->_queryID); + $err = oci_error($this->_queryID); if ($err && $this->connection->debug) ADOConnection::outp($err); */ @@ -1402,7 +1409,7 @@ class ADORecordset_oci8 extends ADORecordSet { function _initrs() { $this->_numOfRows = -1; - $this->_numOfFields = OCInumcols($this->_queryID); + $this->_numOfFields = oci_num_fields($this->_queryID); if ($this->_numOfFields>0) { $this->_fieldobjs = array(); $max = $this->_numOfFields; @@ -1419,13 +1426,13 @@ class ADORecordset_oci8 extends ADORecordSet { { $fld = new ADOFieldObject; $fieldOffset += 1; - $fld->name =OCIcolumnname($this->_queryID, $fieldOffset); - $fld->type = OCIcolumntype($this->_queryID, $fieldOffset); - $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset); + $fld->name =oci_field_name($this->_queryID, $fieldOffset); + $fld->type = oci_field_type($this->_queryID, $fieldOffset); + $fld->max_length = oci_field_size($this->_queryID, $fieldOffset); switch($fld->type) { case 'NUMBER': - $p = OCIColumnPrecision($this->_queryID, $fieldOffset); - $sc = OCIColumnScale($this->_queryID, $fieldOffset); + $p = oci_field_precision($this->_queryID, $fieldOffset); + $sc = oci_field_scale($this->_queryID, $fieldOffset); if ($p != 0 && $sc == 0) $fld->type = 'INT'; $fld->scale = $p; break; @@ -1439,7 +1446,7 @@ class ADORecordset_oci8 extends ADORecordSet { return $fld; } - /* For some reason, OCIcolumnname fails when called after _initrs() so we cache it */ + /* For some reason, oci_field_name fails when called after _initrs() so we cache it */ function FetchField($fieldOffset = -1) { return $this->_fieldobjs[$fieldOffset]; @@ -1455,7 +1462,7 @@ class ADORecordset_oci8 extends ADORecordSet { if ($this->EOF) return false; $this->_currentRow++; - if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) + if($this->fields = @oci_fetch_array($this->_queryID,$this->fetchMode)) return true; $this->EOF = true; @@ -1465,7 +1472,7 @@ class ADORecordset_oci8 extends ADORecordSet { function MoveNext() { - if (@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) { + if ($this->fields = @oci_fetch_array($this->_queryID,$this->fetchMode)) { $this->_currentRow += 1; return true; } @@ -1485,18 +1492,18 @@ class ADORecordset_oci8 extends ADORecordSet { if (true || !empty($ADODB_OCI8_GETARRAY)) { # does not support $ADODB_ANSI_PADDING_OFF - //OCI_RETURN_NULLS and OCI_RETURN_LOBS is set by OCIfetchstatement + //OCI_RETURN_NULLS and OCI_RETURN_LOBS is set by oci_fetch_all switch($this->adodbFetchMode) { case ADODB_FETCH_NUM: - $ncols = @OCIfetchstatement($this->_queryID, $results, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW+OCI_NUM); + $ncols = @oci_fetch_all($this->_queryID, $results, 0, $nRows, oci_fetch_all_BY_ROW+OCI_NUM); $results = array_merge(array($this->fields),$results); return $results; case ADODB_FETCH_ASSOC: if (ADODB_ASSOC_CASE != 2 || $this->databaseType != 'oci8') break; - $ncols = @OCIfetchstatement($this->_queryID, $assoc, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW); + $ncols = @oci_fetch_all($this->_queryID, $assoc, 0, $nRows, oci_fetch_all_BY_ROW); $results = array_merge(array($this->fields),$assoc); return $results; @@ -1510,7 +1517,7 @@ class ADORecordset_oci8 extends ADORecordSet { } */ - /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */ + /* Optimize SelectLimit() by using oci_fetch() */ function GetArrayLimit($nrows,$offset=-1) { if ($offset <= 0) { @@ -1519,9 +1526,9 @@ class ADORecordset_oci8 extends ADORecordSet { } $arr = array(); for ($i=1; $i < $offset; $i++) - if (!@OCIFetch($this->_queryID)) return $arr; + if (!@oci_fetch($this->_queryID)) return $arr; - if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return $arr;; + if (!$this->fields = @oci_fetch_array($this->_queryID,$this->fetchMode)) return $arr;; $results = array(); $cnt = 0; while (!$this->EOF && $nrows != $cnt) { @@ -1556,7 +1563,7 @@ class ADORecordset_oci8 extends ADORecordSet { function _fetch() { - return @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode); + return $this->fields = @oci_fetch_array($this->_queryID,$this->fetchMode); } /* close() only needs to be called if you are worried about using too much memory while your script @@ -1566,10 +1573,10 @@ class ADORecordset_oci8 extends ADORecordSet { { if ($this->connection->_stmt === $this->_queryID) $this->connection->_stmt = false; if (!empty($this->_refcursor)) { - OCIFreeCursor($this->_refcursor); + oci_free_cursor($this->_refcursor); $this->_refcursor = false; } - @OCIFreeStatement($this->_queryID); + @oci_free_statement($this->_queryID); $this->_queryID = false; } diff --git a/lib/adodb/drivers/adodb-oci805.inc.php b/lib/adodb/drivers/adodb-oci805.inc.php index d8a5589db66..1f5c8c70818 100644 --- a/lib/adodb/drivers/adodb-oci805.inc.php +++ b/lib/adodb/drivers/adodb-oci805.inc.php @@ -1,6 +1,6 @@ debug && $argDatabasename && $this->databaseType != 'vfp') { - ADOConnection::outp("For odbc Connect(), $argDatabasename is not used. Place dsn in 1st parameter."); + if (!empty($argDatabasename) && stristr($argDSN, 'Database=') === false) { + $argDSN = trim($argDSN); + $endDSN = substr($argDSN, strlen($argDSN) - 1); + if ($endDSN != ';') $argDSN .= ';'; + $argDSN .= 'Database='.$argDatabasename; } + if (isset($php_errormsg)) $php_errormsg = ''; if ($this->curmode === false) $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword); else $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword,$this->curmode); diff --git a/lib/adodb/drivers/adodb-odbc_db2.inc.php b/lib/adodb/drivers/adodb-odbc_db2.inc.php index a05a3333ab2..fd039ba460c 100644 --- a/lib/adodb/drivers/adodb-odbc_db2.inc.php +++ b/lib/adodb/drivers/adodb-odbc_db2.inc.php @@ -1,6 +1,6 @@ -$ADODB_LANG_ARRAY = array ( - 'LANG' => 'ar', - DB_ERROR => ' ', - DB_ERROR_ALREADY_EXISTS => ' ', - DB_ERROR_CANNOT_CREATE => ' ', - DB_ERROR_CANNOT_DELETE => ' ', - DB_ERROR_CANNOT_DROP => ' ', - DB_ERROR_CONSTRAINT => ' ', - DB_ERROR_DIVZERO => ' ', - DB_ERROR_INVALID => ' ', - DB_ERROR_INVALID_DATE => ' ', - DB_ERROR_INVALID_NUMBER => ' ', - DB_ERROR_MISMATCH => ' ', - DB_ERROR_NODBSELECTED => ' ', - DB_ERROR_NOSUCHFIELD => ' ', - DB_ERROR_NOSUCHTABLE => ' ', - DB_ERROR_NOT_CAPABLE => ' ', - DB_ERROR_NOT_FOUND => ' ', - DB_ERROR_NOT_LOCKED => ' ', - DB_ERROR_SYNTAX => ' ', - DB_ERROR_UNSUPPORTED => ' ', - DB_ERROR_VALUE_COUNT_ON_ROW => ' ', - DB_ERROR_INVALID_DSN => 'DSN ', - DB_ERROR_CONNECT_FAILED => ' ', - 0 => ' ', // DB_OK - DB_ERROR_NEED_MORE_DATA => ' ', - DB_ERROR_EXTENSION_NOT_FOUND=> ' ', - DB_ERROR_NOSUCHDB => ' ', - DB_ERROR_ACCESS_VIOLATION => ' ' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-bg.inc.php b/lib/adodb/lang/adodb-bg.inc.php deleted file mode 100644 index ee307c13fed..00000000000 --- a/lib/adodb/lang/adodb-bg.inc.php +++ /dev/null @@ -1,37 +0,0 @@ - -*/ - -$ADODB_LANG_ARRAY = array ( - 'LANG' => 'bg', - DB_ERROR => ' ', - DB_ERROR_ALREADY_EXISTS => ' ', - DB_ERROR_CANNOT_CREATE => ' ', - DB_ERROR_CANNOT_DELETE => ' ', - DB_ERROR_CANNOT_DROP => ' ', - DB_ERROR_CONSTRAINT => ' ', - DB_ERROR_DIVZERO => ' ', - DB_ERROR_INVALID => '', - DB_ERROR_INVALID_DATE => ' ', - DB_ERROR_INVALID_NUMBER => ' ', - DB_ERROR_MISMATCH => ' ', - DB_ERROR_NODBSELECTED => ' ', - DB_ERROR_NOSUCHFIELD => ' ', - DB_ERROR_NOSUCHTABLE => ' ', - DB_ERROR_NOT_CAPABLE => 'DB backend not capable', - DB_ERROR_NOT_FOUND => ' ', - DB_ERROR_NOT_LOCKED => ' ', - DB_ERROR_SYNTAX => ' ', - DB_ERROR_UNSUPPORTED => ' ', - DB_ERROR_VALUE_COUNT_ON_ROW => ' ', - DB_ERROR_INVALID_DSN => ' DSN', - DB_ERROR_CONNECT_FAILED => ' ', - 0 => ' ', // DB_OK - DB_ERROR_NEED_MORE_DATA => ' ', - DB_ERROR_EXTENSION_NOT_FOUND=> ' ', - DB_ERROR_NOSUCHDB => ' ', - DB_ERROR_ACCESS_VIOLATION => ' ' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-bgutf8.inc.php b/lib/adodb/lang/adodb-bgutf8.inc.php deleted file mode 100644 index 5281ed53b65..00000000000 --- a/lib/adodb/lang/adodb-bgutf8.inc.php +++ /dev/null @@ -1,37 +0,0 @@ - -*/ - -$ADODB_LANG_ARRAY = array ( - 'LANG' => 'bgutf8', - DB_ERROR => 'неизвестна грешка', - DB_ERROR_ALREADY_EXISTS => 'вече съществува', - DB_ERROR_CANNOT_CREATE => 'не може да бъде създадена', - DB_ERROR_CANNOT_DELETE => 'не може да бъде изтрита', - DB_ERROR_CANNOT_DROP => 'не може да бъде унищожена', - DB_ERROR_CONSTRAINT => 'нарушено условие', - DB_ERROR_DIVZERO => 'деление на нула', - DB_ERROR_INVALID => 'неправилно', - DB_ERROR_INVALID_DATE => 'некоректна дата или час', - DB_ERROR_INVALID_NUMBER => 'невалиден номер', - DB_ERROR_MISMATCH => 'погрешна употреба', - DB_ERROR_NODBSELECTED => 'не е избрана база данни', - DB_ERROR_NOSUCHFIELD => 'несъществуващо поле', - DB_ERROR_NOSUCHTABLE => 'несъществуваща таблица', - DB_ERROR_NOT_CAPABLE => 'DB backend not capable', - DB_ERROR_NOT_FOUND => 'не е намерена', - DB_ERROR_NOT_LOCKED => 'не е заключена', - DB_ERROR_SYNTAX => 'грешен синтаксис', - DB_ERROR_UNSUPPORTED => 'не се поддържа', - DB_ERROR_VALUE_COUNT_ON_ROW => 'некоректен брой колони в реда', - DB_ERROR_INVALID_DSN => 'невалиден DSN', - DB_ERROR_CONNECT_FAILED => 'връзката не може да бъде осъществена', - 0 => 'няма грешки', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'предоставените данни са недостатъчни', - DB_ERROR_EXTENSION_NOT_FOUND=> 'разширението не е намерено', - DB_ERROR_NOSUCHDB => 'несъществуваща база данни', - DB_ERROR_ACCESS_VIOLATION => 'нямате достатъчно права' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-ca.inc.php b/lib/adodb/lang/adodb-ca.inc.php deleted file mode 100644 index 3640ebd0bbb..00000000000 --- a/lib/adodb/lang/adodb-ca.inc.php +++ /dev/null @@ -1,34 +0,0 @@ - 'ca', - DB_ERROR => 'error desconegut', - DB_ERROR_ALREADY_EXISTS => 'ja existeix', - DB_ERROR_CANNOT_CREATE => 'no es pot crear', - DB_ERROR_CANNOT_DELETE => 'no es pot esborrar', - DB_ERROR_CANNOT_DROP => 'no es pot eliminar', - DB_ERROR_CONSTRAINT => 'violaci de constraint', - DB_ERROR_DIVZERO => 'divisi per zero', - DB_ERROR_INVALID => 'no s vlid', - DB_ERROR_INVALID_DATE => 'la data o l\'hora no sn vlides', - DB_ERROR_INVALID_NUMBER => 'el nombre no s vlid', - DB_ERROR_MISMATCH => 'no hi ha coincidncia', - DB_ERROR_NODBSELECTED => 'cap base de dades seleccionada', - DB_ERROR_NOSUCHFIELD => 'camp inexistent', - DB_ERROR_NOSUCHTABLE => 'taula inexistent', - DB_ERROR_NOT_CAPABLE => 'l\'execuci secundria de DB no pot', - DB_ERROR_NOT_FOUND => 'no trobat', - DB_ERROR_NOT_LOCKED => 'no blocat', - DB_ERROR_SYNTAX => 'error de sintaxi', - DB_ERROR_UNSUPPORTED => 'no suportat', - DB_ERROR_VALUE_COUNT_ON_ROW => 'el nombre de columnes no coincideix amb el nombre de valors en la fila', - DB_ERROR_INVALID_DSN => 'el DSN no s vlid', - DB_ERROR_CONNECT_FAILED => 'connexi fallida', - 0 => 'cap error', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'les dades subministrades sn insuficients', - DB_ERROR_EXTENSION_NOT_FOUND=> 'extensi no trobada', - DB_ERROR_NOSUCHDB => 'base de dades inexistent', - DB_ERROR_ACCESS_VIOLATION => 'permisos insuficients' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-cn.inc.php b/lib/adodb/lang/adodb-cn.inc.php deleted file mode 100644 index eb8c7de55c2..00000000000 --- a/lib/adodb/lang/adodb-cn.inc.php +++ /dev/null @@ -1,35 +0,0 @@ - 'cn', - DB_ERROR => 'δ֪', - DB_ERROR_ALREADY_EXISTS => 'Ѿ', - DB_ERROR_CANNOT_CREATE => 'ܴ', - DB_ERROR_CANNOT_DELETE => 'ɾ', - DB_ERROR_CANNOT_DROP => 'ܶ', - DB_ERROR_CONSTRAINT => 'Լ', - DB_ERROR_DIVZERO => '0', - DB_ERROR_INVALID => 'Ч', - DB_ERROR_INVALID_DATE => 'Чڻʱ', - DB_ERROR_INVALID_NUMBER => 'Ч', - DB_ERROR_MISMATCH => 'ƥ', - DB_ERROR_NODBSELECTED => 'ûݿⱻѡ', - DB_ERROR_NOSUCHFIELD => 'ûӦֶ', - DB_ERROR_NOSUCHTABLE => 'ûӦı', - DB_ERROR_NOT_CAPABLE => 'ݿ̨', - DB_ERROR_NOT_FOUND => 'ûз', - DB_ERROR_NOT_LOCKED => 'ûб', - DB_ERROR_SYNTAX => '﷨', - DB_ERROR_UNSUPPORTED => '֧', - DB_ERROR_VALUE_COUNT_ON_ROW => 'ۼֵ', - DB_ERROR_INVALID_DSN => 'ЧԴ (DSN)', - DB_ERROR_CONNECT_FAILED => 'ʧ', - 0 => 'ûд', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'ṩݲܷҪ', - DB_ERROR_EXTENSION_NOT_FOUND=> 'չûб', - DB_ERROR_NOSUCHDB => 'ûӦݿ', - DB_ERROR_ACCESS_VIOLATION => 'ûкʵȨ' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-cz.inc.php b/lib/adodb/lang/adodb-cz.inc.php deleted file mode 100644 index 2424c2446b8..00000000000 --- a/lib/adodb/lang/adodb-cz.inc.php +++ /dev/null @@ -1,40 +0,0 @@ - - -$ADODB_LANG_ARRAY = array ( - 'LANG' => 'cz', - DB_ERROR => 'neznm chyba', - DB_ERROR_ALREADY_EXISTS => 'ji? existuje', - DB_ERROR_CANNOT_CREATE => 'nelze vytvo?it', - DB_ERROR_CANNOT_DELETE => 'nelze smazat', - DB_ERROR_CANNOT_DROP => 'nelze odstranit', - DB_ERROR_CONSTRAINT => 'poru?en omezujc podmnky', - DB_ERROR_DIVZERO => 'd?len nulou', - DB_ERROR_INVALID => 'neplatn', - DB_ERROR_INVALID_DATE => 'neplatn datum nebo ?as', - DB_ERROR_INVALID_NUMBER => 'neplatn ?slo', - DB_ERROR_MISMATCH => 'nesouhlas', - DB_ERROR_NODBSELECTED => '?dn databze nen vybrna', - DB_ERROR_NOSUCHFIELD => 'pole nenalezeno', - DB_ERROR_NOSUCHTABLE => 'tabulka nenalezena', - DB_ERROR_NOT_CAPABLE => 'nepodporovno', - DB_ERROR_NOT_FOUND => 'nenalezeno', - DB_ERROR_NOT_LOCKED => 'nezam?eno', - DB_ERROR_SYNTAX => 'syntaktick chyba', - DB_ERROR_UNSUPPORTED => 'nepodporovno', - DB_ERROR_VALUE_COUNT_ON_ROW => '', - DB_ERROR_INVALID_DSN => 'neplatn DSN', - DB_ERROR_CONNECT_FAILED => 'p?ipojen selhalo', - 0 => 'bez chyb', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'mlo zdrojovch dat', - DB_ERROR_EXTENSION_NOT_FOUND=> 'roz??en nenalezeno', - DB_ERROR_NOSUCHDB => 'databze neexistuje', - DB_ERROR_ACCESS_VIOLATION => 'nedostate?n prva' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-da.inc.php b/lib/adodb/lang/adodb-da.inc.php deleted file mode 100644 index ca0e72d6148..00000000000 --- a/lib/adodb/lang/adodb-da.inc.php +++ /dev/null @@ -1,33 +0,0 @@ - 'da', - DB_ERROR => 'ukendt fejl', - DB_ERROR_ALREADY_EXISTS => 'eksisterer allerede', - DB_ERROR_CANNOT_CREATE => 'kan ikke oprette', - DB_ERROR_CANNOT_DELETE => 'kan ikke slette', - DB_ERROR_CANNOT_DROP => 'kan ikke droppe', - DB_ERROR_CONSTRAINT => 'begrænsning krænket', - DB_ERROR_DIVZERO => 'division med nul', - DB_ERROR_INVALID => 'ugyldig', - DB_ERROR_INVALID_DATE => 'ugyldig dato eller klokkeslet', - DB_ERROR_INVALID_NUMBER => 'ugyldigt tal', - DB_ERROR_MISMATCH => 'mismatch', - DB_ERROR_NODBSELECTED => 'ingen database valgt', - DB_ERROR_NOSUCHFIELD => 'felt findes ikke', - DB_ERROR_NOSUCHTABLE => 'tabel findes ikke', - DB_ERROR_NOT_CAPABLE => 'DB backend opgav', - DB_ERROR_NOT_FOUND => 'ikke fundet', - DB_ERROR_NOT_LOCKED => 'ikke låst', - DB_ERROR_SYNTAX => 'syntaksfejl', - DB_ERROR_UNSUPPORTED => 'ikke understøttet', - DB_ERROR_VALUE_COUNT_ON_ROW => 'resulterende antal felter svarer ikke til forespørgslens antal felter', - DB_ERROR_INVALID_DSN => 'ugyldig DSN', - DB_ERROR_CONNECT_FAILED => 'tilslutning mislykkedes', - 0 => 'ingen fejl', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'utilstrækkelige data angivet', - DB_ERROR_EXTENSION_NOT_FOUND=> 'udvidelse ikke fundet', - DB_ERROR_NOSUCHDB => 'database ikke fundet', - DB_ERROR_ACCESS_VIOLATION => 'utilstrækkelige rettigheder' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-de.inc.php b/lib/adodb/lang/adodb-de.inc.php deleted file mode 100644 index 244cb2f66ae..00000000000 --- a/lib/adodb/lang/adodb-de.inc.php +++ /dev/null @@ -1,33 +0,0 @@ - -$ADODB_LANG_ARRAY = array ( - 'LANG' => 'de', - DB_ERROR => 'Unbekannter Fehler', - DB_ERROR_ALREADY_EXISTS => 'existiert bereits', - DB_ERROR_CANNOT_CREATE => 'kann nicht erstellen', - DB_ERROR_CANNOT_DELETE => 'kann nicht löschen', - DB_ERROR_CANNOT_DROP => 'Tabelle oder Index konnte nicht gelöscht werden', - DB_ERROR_CONSTRAINT => 'Constraint Verletzung', - DB_ERROR_DIVZERO => 'Division durch Null', - DB_ERROR_INVALID => 'ung¨ltig', - DB_ERROR_INVALID_DATE => 'ung¨ltiges Datum oder Zeit', - DB_ERROR_INVALID_NUMBER => 'ung¨ltige Zahl', - DB_ERROR_MISMATCH => 'Unverträglichkeit', - DB_ERROR_NODBSELECTED => 'keine Dantebank ausgewählt', - DB_ERROR_NOSUCHFIELD => 'Feld nicht vorhanden', - DB_ERROR_NOSUCHTABLE => 'Tabelle nicht vorhanden', - DB_ERROR_NOT_CAPABLE => 'Funktion nicht installiert', - DB_ERROR_NOT_FOUND => 'nicht gefunden', - DB_ERROR_NOT_LOCKED => 'nicht gesperrt', - DB_ERROR_SYNTAX => 'Syntaxfehler', - DB_ERROR_UNSUPPORTED => 'nicht Unterst¨tzt', - DB_ERROR_VALUE_COUNT_ON_ROW => 'Anzahl der zur¨ckgelieferten Felder entspricht nicht der Anzahl der Felder in der Abfrage', - DB_ERROR_INVALID_DSN => 'ung¨ltiger DSN', - DB_ERROR_CONNECT_FAILED => 'Verbindung konnte nicht hergestellt werden', - 0 => 'kein Fehler', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'Nicht gen¨gend Daten geliefert', - DB_ERROR_EXTENSION_NOT_FOUND=> 'erweiterung nicht gefunden', - DB_ERROR_NOSUCHDB => 'keine Datenbank', - DB_ERROR_ACCESS_VIOLATION => 'ungen¨gende Rechte' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-es.inc.php b/lib/adodb/lang/adodb-es.inc.php deleted file mode 100644 index 1e0afbb40d9..00000000000 --- a/lib/adodb/lang/adodb-es.inc.php +++ /dev/null @@ -1,33 +0,0 @@ - -$ADODB_LANG_ARRAY = array ( - 'LANG' => 'es', - DB_ERROR => 'error desconocido', - DB_ERROR_ALREADY_EXISTS => 'ya existe', - DB_ERROR_CANNOT_CREATE => 'imposible crear', - DB_ERROR_CANNOT_DELETE => 'imposible borrar', - DB_ERROR_CANNOT_DROP => 'imposible hacer drop', - DB_ERROR_CONSTRAINT => 'violacion de constraint', - DB_ERROR_DIVZERO => 'division por cero', - DB_ERROR_INVALID => 'invalido', - DB_ERROR_INVALID_DATE => 'fecha u hora invalida', - DB_ERROR_INVALID_NUMBER => 'numero invalido', - DB_ERROR_MISMATCH => 'error', - DB_ERROR_NODBSELECTED => 'no hay base de datos seleccionada', - DB_ERROR_NOSUCHFIELD => 'campo invalido', - DB_ERROR_NOSUCHTABLE => 'tabla no existe', - DB_ERROR_NOT_CAPABLE => 'capacidad invalida para esta DB', - DB_ERROR_NOT_FOUND => 'no encontrado', - DB_ERROR_NOT_LOCKED => 'no bloqueado', - DB_ERROR_SYNTAX => 'error de sintaxis', - DB_ERROR_UNSUPPORTED => 'no soportado', - DB_ERROR_VALUE_COUNT_ON_ROW => 'la cantidad de columnas no corresponden a la cantidad de valores', - DB_ERROR_INVALID_DSN => 'DSN invalido', - DB_ERROR_CONNECT_FAILED => 'fallo la conexion', - 0 => 'sin error', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'insuficientes datos', - DB_ERROR_EXTENSION_NOT_FOUND=> 'extension no encontrada', - DB_ERROR_NOSUCHDB => 'base de datos no encontrada', - DB_ERROR_ACCESS_VIOLATION => 'permisos insuficientes' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-esperanto.inc.php b/lib/adodb/lang/adodb-esperanto.inc.php deleted file mode 100644 index 16ca00e2fac..00000000000 --- a/lib/adodb/lang/adodb-esperanto.inc.php +++ /dev/null @@ -1,35 +0,0 @@ - 'eo', - DB_ERROR => 'nekonata eraro', - DB_ERROR_ALREADY_EXISTS => 'jam ekzistas', - DB_ERROR_CANNOT_CREATE => 'maleblas krei', - DB_ERROR_CANNOT_DELETE => 'maleblas elimini', - DB_ERROR_CANNOT_DROP => 'maleblas elimini (drop)', - DB_ERROR_CONSTRAINT => 'rompo de kondicxoj de provo', - DB_ERROR_DIVZERO => 'divido per 0 (nul)', - DB_ERROR_INVALID => 'malregule', - DB_ERROR_INVALID_DATE => 'malregula dato kaj tempo', - DB_ERROR_INVALID_NUMBER => 'malregula nombro', - DB_ERROR_MISMATCH => 'eraro', - DB_ERROR_NODBSELECTED => 'datumbazo ne elektita', - DB_ERROR_NOSUCHFIELD => 'ne ekzistas kampo', - DB_ERROR_NOSUCHTABLE => 'ne ekzistas tabelo', - DB_ERROR_NOT_CAPABLE => 'DBMS ne povas', - DB_ERROR_NOT_FOUND => 'ne trovita', - DB_ERROR_NOT_LOCKED => 'ne blokita', - DB_ERROR_SYNTAX => 'sintaksa eraro', - DB_ERROR_UNSUPPORTED => 'ne apogata', - DB_ERROR_VALUE_COUNT_ON_ROW => 'nombrilo de valoroj en linio', - DB_ERROR_INVALID_DSN => 'malregula DSN-o', - DB_ERROR_CONNECT_FAILED => 'konekto malsukcesa', - 0 => 'cxio bone', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'ne suficxe da datumo', - DB_ERROR_EXTENSION_NOT_FOUND=> 'etendo ne trovita', - DB_ERROR_NOSUCHDB => 'datumbazo ne ekzistas', - DB_ERROR_ACCESS_VIOLATION => 'ne suficxe da rajto por atingo' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-fa.inc.php b/lib/adodb/lang/adodb-fa.inc.php deleted file mode 100644 index 5594313575e..00000000000 --- a/lib/adodb/lang/adodb-fa.inc.php +++ /dev/null @@ -1,35 +0,0 @@ - */ - -$ADODB_LANG_ARRAY = array ( - 'LANG' => 'fa', - DB_ERROR => 'خطای ناشناخته', - DB_ERROR_ALREADY_EXISTS => 'وجود دارد', - DB_ERROR_CANNOT_CREATE => 'امکان create وجود ندارد', - DB_ERROR_CANNOT_DELETE => 'امکان حذف وجود ندارد', - DB_ERROR_CANNOT_DROP => 'امکان drop وجود ندارد', - DB_ERROR_CONSTRAINT => 'نقض شرط', - DB_ERROR_DIVZERO => 'تقسیم بر صفر', - DB_ERROR_INVALID => 'نامعتبر', - DB_ERROR_INVALID_DATE => 'زمان یا تاریخ نامعتبر', - DB_ERROR_INVALID_NUMBER => 'عدد نامعتبر', - DB_ERROR_MISMATCH => 'عدم مطابقت', - DB_ERROR_NODBSELECTED => 'بانک اطلاعاتی انتخاب نشده است', - DB_ERROR_NOSUCHFIELD => 'چنین ستونی وجود ندارد', - DB_ERROR_NOSUCHTABLE => 'چنین جدولی وجود ندارد', - DB_ERROR_NOT_CAPABLE => 'backend بانک اطلاعاتی قادر نیست', - DB_ERROR_NOT_FOUND => 'پیدا نشد', - DB_ERROR_NOT_LOCKED => 'قفل نشده', - DB_ERROR_SYNTAX => 'خطای دستوری', - DB_ERROR_UNSUPPORTED => 'پشتیبانی نمی شود', - DB_ERROR_VALUE_COUNT_ON_ROW => 'شمارش مقادیر روی ردیف', - DB_ERROR_INVALID_DSN => 'DSN نامعتبر', - DB_ERROR_CONNECT_FAILED => 'ارتباط برقرار نشد', - 0 => 'بدون خطا', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'داده ناکافی است', - DB_ERROR_EXTENSION_NOT_FOUND=> 'extension پیدا نشد', - DB_ERROR_NOSUCHDB => 'چنین بانک اطلاعاتی وجود ندارد', - DB_ERROR_ACCESS_VIOLATION => 'حق دسترسی ناکافی' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-fr.inc.php b/lib/adodb/lang/adodb-fr.inc.php deleted file mode 100644 index 066a2a5e5b7..00000000000 --- a/lib/adodb/lang/adodb-fr.inc.php +++ /dev/null @@ -1,33 +0,0 @@ - 'fr', - DB_ERROR => 'erreur inconnue', - DB_ERROR_ALREADY_EXISTS => 'existe déjà', - DB_ERROR_CANNOT_CREATE => 'crétion impossible', - DB_ERROR_CANNOT_DELETE => 'effacement impossible', - DB_ERROR_CANNOT_DROP => 'suppression impossible', - DB_ERROR_CONSTRAINT => 'violation de contrainte', - DB_ERROR_DIVZERO => 'division par zéro', - DB_ERROR_INVALID => 'invalide', - DB_ERROR_INVALID_DATE => 'date ou heure invalide', - DB_ERROR_INVALID_NUMBER => 'nombre invalide', - DB_ERROR_MISMATCH => 'erreur de concordance', - DB_ERROR_NODBSELECTED => 'pas de base de donnéessélectionnée', - DB_ERROR_NOSUCHFIELD => 'nom de colonne invalide', - DB_ERROR_NOSUCHTABLE => 'table ou vue inexistante', - DB_ERROR_NOT_CAPABLE => 'fonction optionnelle non installée', - DB_ERROR_NOT_FOUND => 'pas trouvé', - DB_ERROR_NOT_LOCKED => 'non verrouillé', - DB_ERROR_SYNTAX => 'erreur de syntaxe', - DB_ERROR_UNSUPPORTED => 'non supporté', - DB_ERROR_VALUE_COUNT_ON_ROW => 'valeur insérée trop grande pour colonne', - DB_ERROR_INVALID_DSN => 'DSN invalide', - DB_ERROR_CONNECT_FAILED => 'échec à la connexion', - 0 => "pas d'erreur", // DB_OK - DB_ERROR_NEED_MORE_DATA => 'données fournies insuffisantes', - DB_ERROR_EXTENSION_NOT_FOUND=> 'extension non trouvée', - DB_ERROR_NOSUCHDB => 'base de données inconnue', - DB_ERROR_ACCESS_VIOLATION => 'droits insuffisants' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-hu.inc.php b/lib/adodb/lang/adodb-hu.inc.php deleted file mode 100644 index d6f0ef82da6..00000000000 --- a/lib/adodb/lang/adodb-hu.inc.php +++ /dev/null @@ -1,34 +0,0 @@ - -$ADODB_LANG_ARRAY = array ( - 'LANG' => 'hu', - DB_ERROR => 'ismeretlen hiba', - DB_ERROR_ALREADY_EXISTS => 'mr ltezik', - DB_ERROR_CANNOT_CREATE => 'nem sikerlt ltrehozni', - DB_ERROR_CANNOT_DELETE => 'nem sikerlt trlni', - DB_ERROR_CANNOT_DROP => 'nem sikerlt eldobni', - DB_ERROR_CONSTRAINT => 'szablyok megszegse', - DB_ERROR_DIVZERO => 'oszts nullval', - DB_ERROR_INVALID => 'rvnytelen', - DB_ERROR_INVALID_DATE => 'rvnytelen dtum vagy id', - DB_ERROR_INVALID_NUMBER => 'rvnytelen szm', - DB_ERROR_MISMATCH => 'nem megfelel', - DB_ERROR_NODBSELECTED => 'nincs kivlasztott adatbzis', - DB_ERROR_NOSUCHFIELD => 'nincs ilyen mez', - DB_ERROR_NOSUCHTABLE => 'nincs ilyen tbla', - DB_ERROR_NOT_CAPABLE => 'DB backend nem tmogatja', - DB_ERROR_NOT_FOUND => 'nem tallhat', - DB_ERROR_NOT_LOCKED => 'nincs lezrva', - DB_ERROR_SYNTAX => 'szintaktikai hiba', - DB_ERROR_UNSUPPORTED => 'nem tmogatott', - DB_ERROR_VALUE_COUNT_ON_ROW => 'soron vgzett rtk szmlls', - DB_ERROR_INVALID_DSN => 'hibs DSN', - DB_ERROR_CONNECT_FAILED => 'sikertelen csatlakozs', - 0 => 'nincs hiba', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'tl kevs az adat', - DB_ERROR_EXTENSION_NOT_FOUND=> 'bvtmny nem tallhat', - DB_ERROR_NOSUCHDB => 'nincs ilyen adatbzis', - DB_ERROR_ACCESS_VIOLATION => 'nincs jogosultsg' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-it.inc.php b/lib/adodb/lang/adodb-it.inc.php deleted file mode 100644 index 20c5b93b630..00000000000 --- a/lib/adodb/lang/adodb-it.inc.php +++ /dev/null @@ -1,34 +0,0 @@ - 'it', - DB_ERROR => 'errore sconosciuto', - DB_ERROR_ALREADY_EXISTS => 'esiste già', - DB_ERROR_CANNOT_CREATE => 'non posso creare', - DB_ERROR_CANNOT_DELETE => 'non posso cancellare', - DB_ERROR_CANNOT_DROP => 'non posso eliminare', - DB_ERROR_CONSTRAINT => 'violazione constraint', - DB_ERROR_DIVZERO => 'divisione per zero', - DB_ERROR_INVALID => 'non valido', - DB_ERROR_INVALID_DATE => 'data od ora non valida', - DB_ERROR_INVALID_NUMBER => 'numero non valido', - DB_ERROR_MISMATCH => 'diversi', - DB_ERROR_NODBSELECTED => 'nessun database selezionato', - DB_ERROR_NOSUCHFIELD => 'nessun campo trovato', - DB_ERROR_NOSUCHTABLE => 'nessuna tabella trovata', - DB_ERROR_NOT_CAPABLE => 'DB backend non abilitato', - DB_ERROR_NOT_FOUND => 'non trovato', - DB_ERROR_NOT_LOCKED => 'non bloccato', - DB_ERROR_SYNTAX => 'errore di sintassi', - DB_ERROR_UNSUPPORTED => 'non supportato', - DB_ERROR_VALUE_COUNT_ON_ROW => 'valore inserito troppo grande per una colonna', - DB_ERROR_INVALID_DSN => 'DSN non valido', - DB_ERROR_CONNECT_FAILED => 'connessione fallita', - 0 => 'nessun errore', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'dati inseriti insufficienti', - DB_ERROR_EXTENSION_NOT_FOUND=> 'estensione non trovata', - DB_ERROR_NOSUCHDB => 'database non trovato', - DB_ERROR_ACCESS_VIOLATION => 'permessi insufficienti' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-nl.inc.php b/lib/adodb/lang/adodb-nl.inc.php deleted file mode 100644 index abe77b5282e..00000000000 --- a/lib/adodb/lang/adodb-nl.inc.php +++ /dev/null @@ -1,33 +0,0 @@ - 'nl', - DB_ERROR => 'onbekende fout', - DB_ERROR_ALREADY_EXISTS => 'bestaat al', - DB_ERROR_CANNOT_CREATE => 'kan niet aanmaken', - DB_ERROR_CANNOT_DELETE => 'kan niet wissen', - DB_ERROR_CANNOT_DROP => 'kan niet verwijderen', - DB_ERROR_CONSTRAINT => 'constraint overtreding', - DB_ERROR_DIVZERO => 'poging tot delen door nul', - DB_ERROR_INVALID => 'ongeldig', - DB_ERROR_INVALID_DATE => 'ongeldige datum of tijd', - DB_ERROR_INVALID_NUMBER => 'ongeldig nummer', - DB_ERROR_MISMATCH => 'is incorrect', - DB_ERROR_NODBSELECTED => 'geen database geselecteerd', - DB_ERROR_NOSUCHFIELD => 'onbekend veld', - DB_ERROR_NOSUCHTABLE => 'onbekende tabel', - DB_ERROR_NOT_CAPABLE => 'database systeem is niet tot uitvoer in staat', - DB_ERROR_NOT_FOUND => 'niet gevonden', - DB_ERROR_NOT_LOCKED => 'niet vergrendeld', - DB_ERROR_SYNTAX => 'syntaxis fout', - DB_ERROR_UNSUPPORTED => 'niet ondersteund', - DB_ERROR_VALUE_COUNT_ON_ROW => 'waarde telling op rij', - DB_ERROR_INVALID_DSN => 'ongeldige DSN', - DB_ERROR_CONNECT_FAILED => 'connectie mislukt', - 0 => 'geen fout', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'onvoldoende data gegeven', - DB_ERROR_EXTENSION_NOT_FOUND=> 'extensie niet gevonden', - DB_ERROR_NOSUCHDB => 'onbekende database', - DB_ERROR_ACCESS_VIOLATION => 'onvoldoende rechten' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-pl.inc.php b/lib/adodb/lang/adodb-pl.inc.php deleted file mode 100644 index 9d9e3906762..00000000000 --- a/lib/adodb/lang/adodb-pl.inc.php +++ /dev/null @@ -1,35 +0,0 @@ - - -$ADODB_LANG_ARRAY = array ( - 'LANG' => 'pl', - DB_ERROR => 'niezidentyfikowany bd', - DB_ERROR_ALREADY_EXISTS => 'ju istniej', - DB_ERROR_CANNOT_CREATE => 'nie mona stworzy', - DB_ERROR_CANNOT_DELETE => 'nie mona usun', - DB_ERROR_CANNOT_DROP => 'nie mona porzuci', - DB_ERROR_CONSTRAINT => 'pogwacenie uprawnie', - DB_ERROR_DIVZERO => 'dzielenie przez zero', - DB_ERROR_INVALID => 'bdny', - DB_ERROR_INVALID_DATE => 'bdna godzina lub data', - DB_ERROR_INVALID_NUMBER => 'bdny numer', - DB_ERROR_MISMATCH => 'niedopasowanie', - DB_ERROR_NODBSELECTED => 'baza danych nie zostaa wybrana', - DB_ERROR_NOSUCHFIELD => 'nie znaleziono pola', - DB_ERROR_NOSUCHTABLE => 'nie znaleziono tabeli', - DB_ERROR_NOT_CAPABLE => 'nie zdolny', - DB_ERROR_NOT_FOUND => 'nie znaleziono', - DB_ERROR_NOT_LOCKED => 'nie zakmnity', - DB_ERROR_SYNTAX => 'bd skadni', - DB_ERROR_UNSUPPORTED => 'nie obsuguje', - DB_ERROR_VALUE_COUNT_ON_ROW => 'warto liczona w szeregu', - DB_ERROR_INVALID_DSN => 'bdny DSN', - DB_ERROR_CONNECT_FAILED => 'poczenie nie zostao zrealizowane', - 0 => 'brak bdw', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'niedostateczna ilo informacji', - DB_ERROR_EXTENSION_NOT_FOUND=> 'nie znaleziono rozszerzenia', - DB_ERROR_NOSUCHDB => 'nie znaleziono bazy', - DB_ERROR_ACCESS_VIOLATION => 'niedostateczne uprawnienia' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-pt-br.inc.php b/lib/adodb/lang/adodb-pt-br.inc.php deleted file mode 100644 index cd28f7e55ca..00000000000 --- a/lib/adodb/lang/adodb-pt-br.inc.php +++ /dev/null @@ -1,35 +0,0 @@ - 'pt-br', - DB_ERROR => 'erro desconhecido', - DB_ERROR_ALREADY_EXISTS => 'j existe', - DB_ERROR_CANNOT_CREATE => 'impossvel criar', - DB_ERROR_CANNOT_DELETE => 'impossvel exclur', - DB_ERROR_CANNOT_DROP => 'impossvel remover', - DB_ERROR_CONSTRAINT => 'violao do confinamente', - DB_ERROR_DIVZERO => 'diviso por zero', - DB_ERROR_INVALID => 'invlido', - DB_ERROR_INVALID_DATE => 'data ou hora invlida', - DB_ERROR_INVALID_NUMBER => 'nmero invlido', - DB_ERROR_MISMATCH => 'erro', - DB_ERROR_NODBSELECTED => 'nenhum banco de dados selecionado', - DB_ERROR_NOSUCHFIELD => 'campo invlido', - DB_ERROR_NOSUCHTABLE => 'tabela inexistente', - DB_ERROR_NOT_CAPABLE => 'capacidade invlida para este BD', - DB_ERROR_NOT_FOUND => 'no encontrado', - DB_ERROR_NOT_LOCKED => 'no bloqueado', - DB_ERROR_SYNTAX => 'erro de sintaxe', - DB_ERROR_UNSUPPORTED => -'no suportado', - DB_ERROR_VALUE_COUNT_ON_ROW => 'a quantidade de colunas no corresponde ao de valores', - DB_ERROR_INVALID_DSN => 'DSN invlido', - DB_ERROR_CONNECT_FAILED => 'falha na conexo', - 0 => 'sem erro', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'dados insuficientes', - DB_ERROR_EXTENSION_NOT_FOUND=> 'extenso no encontrada', - DB_ERROR_NOSUCHDB => 'banco de dados no encontrado', - DB_ERROR_ACCESS_VIOLATION => 'permisso insuficiente' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-ro.inc.php b/lib/adodb/lang/adodb-ro.inc.php deleted file mode 100644 index bcd7d13228c..00000000000 --- a/lib/adodb/lang/adodb-ro.inc.php +++ /dev/null @@ -1,35 +0,0 @@ - */ - -$ADODB_LANG_ARRAY = array ( - 'LANG' => 'ro', - DB_ERROR => 'eroare necunoscuta', - DB_ERROR_ALREADY_EXISTS => 'deja exista', - DB_ERROR_CANNOT_CREATE => 'nu se poate creea', - DB_ERROR_CANNOT_DELETE => 'nu se poate sterge', - DB_ERROR_CANNOT_DROP => 'nu se poate executa drop', - DB_ERROR_CONSTRAINT => 'violare de constrain', - DB_ERROR_DIVZERO => 'se divide la zero', - DB_ERROR_INVALID => 'invalid', - DB_ERROR_INVALID_DATE => 'data sau timp invalide', - DB_ERROR_INVALID_NUMBER => 'numar invalid', - DB_ERROR_MISMATCH => 'nepotrivire-mismatch', - DB_ERROR_NODBSELECTED => 'nu exista baza de date selectata', - DB_ERROR_NOSUCHFIELD => 'camp inexistent', - DB_ERROR_NOSUCHTABLE => 'tabela inexistenta', - DB_ERROR_NOT_CAPABLE => 'functie optionala neinstalata', - DB_ERROR_NOT_FOUND => 'negasit', - DB_ERROR_NOT_LOCKED => 'neblocat', - DB_ERROR_SYNTAX => 'eroare de sintaxa', - DB_ERROR_UNSUPPORTED => 'nu e suportat', - DB_ERROR_VALUE_COUNT_ON_ROW => 'valoare prea mare pentru coloana', - DB_ERROR_INVALID_DSN => 'DSN invalid', - DB_ERROR_CONNECT_FAILED => 'conectare esuata', - 0 => 'fara eroare', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'data introduse insuficiente', - DB_ERROR_EXTENSION_NOT_FOUND=> 'extensie negasita', - DB_ERROR_NOSUCHDB => 'nu exista baza de date', - DB_ERROR_ACCESS_VIOLATION => 'permisiuni insuficiente' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-ru1251.inc.php b/lib/adodb/lang/adodb-ru1251.inc.php deleted file mode 100644 index 3a20538a020..00000000000 --- a/lib/adodb/lang/adodb-ru1251.inc.php +++ /dev/null @@ -1,35 +0,0 @@ - 'ru1251', - DB_ERROR => ' ', - DB_ERROR_ALREADY_EXISTS => ' ', - DB_ERROR_CANNOT_CREATE => ' ', - DB_ERROR_CANNOT_DELETE => ' ', - DB_ERROR_CANNOT_DROP => ' (drop)', - DB_ERROR_CONSTRAINT => ' ', - DB_ERROR_DIVZERO => ' 0', - DB_ERROR_INVALID => '', - DB_ERROR_INVALID_DATE => ' ', - DB_ERROR_INVALID_NUMBER => ' ', - DB_ERROR_MISMATCH => '', - DB_ERROR_NODBSELECTED => ' ', - DB_ERROR_NOSUCHFIELD => ' ', - DB_ERROR_NOSUCHTABLE => ' ', - DB_ERROR_NOT_CAPABLE => ' ', - DB_ERROR_NOT_FOUND => ' ', - DB_ERROR_NOT_LOCKED => ' ', - DB_ERROR_SYNTAX => ' ', - DB_ERROR_UNSUPPORTED => ' ', - DB_ERROR_VALUE_COUNT_ON_ROW => ' ', - DB_ERROR_INVALID_DSN => ' DSN', - DB_ERROR_CONNECT_FAILED => ' ', - 0 => ' ', // DB_OK - DB_ERROR_NEED_MORE_DATA => ' ', - DB_ERROR_EXTENSION_NOT_FOUND=> ' ', - DB_ERROR_NOSUCHDB => ' ', - DB_ERROR_ACCESS_VIOLATION => ' ' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-sv.inc.php b/lib/adodb/lang/adodb-sv.inc.php deleted file mode 100644 index a9fd69816c4..00000000000 --- a/lib/adodb/lang/adodb-sv.inc.php +++ /dev/null @@ -1,33 +0,0 @@ - 'en', - DB_ERROR => 'Oknt fel', - DB_ERROR_ALREADY_EXISTS => 'finns redan', - DB_ERROR_CANNOT_CREATE => 'kan inte skapa', - DB_ERROR_CANNOT_DELETE => 'kan inte ta bort', - DB_ERROR_CANNOT_DROP => 'kan inte slppa', - DB_ERROR_CONSTRAINT => 'begrnsning krnkt', - DB_ERROR_DIVZERO => 'division med noll', - DB_ERROR_INVALID => 'ogiltig', - DB_ERROR_INVALID_DATE => 'ogiltigt datum eller tid', - DB_ERROR_INVALID_NUMBER => 'ogiltigt tal', - DB_ERROR_MISMATCH => 'felaktig matchning', - DB_ERROR_NODBSELECTED => 'ingen databas vald', - DB_ERROR_NOSUCHFIELD => 'inget sdant flt', - DB_ERROR_NOSUCHTABLE => 'ingen sdan tabell', - DB_ERROR_NOT_CAPABLE => 'DB backend klarar det inte', - DB_ERROR_NOT_FOUND => 'finns inte', - DB_ERROR_NOT_LOCKED => 'inte lst', - DB_ERROR_SYNTAX => 'syntaxfel', - DB_ERROR_UNSUPPORTED => 'stds ej', - DB_ERROR_VALUE_COUNT_ON_ROW => 'vrde rknat p rad', - DB_ERROR_INVALID_DSN => 'ogiltig DSN', - DB_ERROR_CONNECT_FAILED => 'anslutning misslyckades', - 0 => 'inget fel', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'otillrckligt med data angivet', - DB_ERROR_EXTENSION_NOT_FOUND=> 'utkning hittades ej', - DB_ERROR_NOSUCHDB => 'ingen sdan databas', - DB_ERROR_ACCESS_VIOLATION => 'otillrckliga rttigheter' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-uk1251.inc.php b/lib/adodb/lang/adodb-uk1251.inc.php deleted file mode 100644 index 675016d125e..00000000000 --- a/lib/adodb/lang/adodb-uk1251.inc.php +++ /dev/null @@ -1,35 +0,0 @@ - 'uk1251', - DB_ERROR => ' ', - DB_ERROR_ALREADY_EXISTS => ' ', - DB_ERROR_CANNOT_CREATE => ' ', - DB_ERROR_CANNOT_DELETE => ' ', - DB_ERROR_CANNOT_DROP => ' (drop)', - DB_ERROR_CONSTRAINT => ' ', - DB_ERROR_DIVZERO => ' 0', - DB_ERROR_INVALID => '', - DB_ERROR_INVALID_DATE => ' ', - DB_ERROR_INVALID_NUMBER => ' ', - DB_ERROR_MISMATCH => '', - DB_ERROR_NODBSELECTED => ' ', - DB_ERROR_NOSUCHFIELD => ' ', - DB_ERROR_NOSUCHTABLE => ' ', - DB_ERROR_NOT_CAPABLE => ' ', - DB_ERROR_NOT_FOUND => ' ', - DB_ERROR_NOT_LOCKED => ' ', - DB_ERROR_SYNTAX => ' ', - DB_ERROR_UNSUPPORTED => ' ', - DB_ERROR_VALUE_COUNT_ON_ROW => ' ', - DB_ERROR_INVALID_DSN => ' DSN', - DB_ERROR_CONNECT_FAILED => '\' ', - 0 => ' ', // DB_OK - DB_ERROR_NEED_MORE_DATA => ' ', - DB_ERROR_EXTENSION_NOT_FOUND=> ' ', - DB_ERROR_NOSUCHDB => ' ', - DB_ERROR_ACCESS_VIOLATION => ' ' -); -?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb_th.inc.php b/lib/adodb/lang/adodb_th.inc.php deleted file mode 100644 index 3fdd99705f7..00000000000 --- a/lib/adodb/lang/adodb_th.inc.php +++ /dev/null @@ -1,33 +0,0 @@ - -$ADODB_LANG_ARRAY = array ( - 'LANG' => 'th', - DB_ERROR => 'error ไม่รู้สาเหตุ', - DB_ERROR_ALREADY_EXISTS => 'มี?ล้ว', - DB_ERROR_CANNOT_CREATE => 'สร้างไม่ได้', - DB_ERROR_CANNOT_DELETE => 'ลบไม่ได้', - DB_ERROR_CANNOT_DROP => 'drop ไม่ได้', - DB_ERROR_CONSTRAINT => 'constraint violation', - DB_ERROR_DIVZERO => 'หา?ด้วยสู?', - DB_ERROR_INVALID => 'ไม่ valid', - DB_ERROR_INVALID_DATE => 'วันที่ เวลา ไม่ valid', - DB_ERROR_INVALID_NUMBER => 'เลขไม่ valid', - DB_ERROR_MISMATCH => 'mismatch', - DB_ERROR_NODBSELECTED => 'ไม่ได้เลือ??านข้อมูล', - DB_ERROR_NOSUCHFIELD => 'ไม่มีฟีลด์นี้', - DB_ERROR_NOSUCHTABLE => 'ไม่มีตารางนี้', - DB_ERROR_NOT_CAPABLE => 'DB backend not capable', - DB_ERROR_NOT_FOUND => 'ไม่พบ', - DB_ERROR_NOT_LOCKED => 'ไม่ได้ล๊อ?', - DB_ERROR_SYNTAX => 'ผิด syntax', - DB_ERROR_UNSUPPORTED => 'ไม่ support', - DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', - DB_ERROR_INVALID_DSN => 'invalid DSN', - DB_ERROR_CONNECT_FAILED => 'ไม่สามารถ connect', - 0 => 'no error', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'ข้อมูลไม่เพียงพอ', - DB_ERROR_EXTENSION_NOT_FOUND=> 'ไม่พบ extension', - DB_ERROR_NOSUCHDB => 'ไม่มีข้อมูลนี้', - DB_ERROR_ACCESS_VIOLATION => 'permissions ไม่พอ' -); -?> \ No newline at end of file diff --git a/lib/adodb/perf/perf-db2.inc.php b/lib/adodb/perf/perf-db2.inc.php index 3823420a635..94ac8564a1a 100644 --- a/lib/adodb/perf/perf-db2.inc.php +++ b/lib/adodb/perf/perf-db2.inc.php @@ -1,6 +1,6 @@ page->blocks->find_instance($blockid); - if (!$block->user_can_edit() || !$this->page->user_can_edit_blocks() || !$block->user_can_addto($this->page)) { throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('deleteablock')); } - blocks_delete_instance($block->instance); + if (!$confirmdelete) { + $deletepage = new moodle_page(); + $deletepage->set_pagelayout('admin'); + $deletepage->set_course($this->page->course); + $deletepage->set_context($this->page->context); + if ($this->page->cm) { + $deletepage->set_cm($this->page->cm); + } - // If the page URL was a guess, it will contain the bui_... param, so we must make sure it is not there. - $this->page->ensure_param_not_in_url('bui_deleteid'); + $deleteurlbase = str_replace($CFG->wwwroot . '/', '/', $this->page->url->out_omit_querystring()); + $deleteurlparams = $this->page->url->params(); + $deletepage->set_url($deleteurlbase, $deleteurlparams); + $deletepage->set_block_actions_done(); + // At this point we are either going to redirect, or display the form, so + // overwrite global $PAGE ready for this. (Formslib refers to it.) + $PAGE = $deletepage; + //some functions like MoodleQuickForm::addHelpButton use $OUTPUT so we need to replace that too + $output = $deletepage->get_renderer('core'); + $OUTPUT = $output; - return true; + $site = get_site(); + $blocktitle = $block->get_title(); + $strdeletecheck = get_string('deletecheck', 'block', $blocktitle); + $message = get_string('deleteblockcheck', 'block', $blocktitle); + + $PAGE->navbar->add($strdeletecheck); + $PAGE->set_title($blocktitle . ': ' . $strdeletecheck); + $PAGE->set_heading($site->fullname); + echo $OUTPUT->header(); + $confirmurl = new moodle_url("$deletepage->url?", array('sesskey' => sesskey(), 'bui_deleteid' => $block->instance->id, 'bui_confirm' => 1)); + $cancelurl = new moodle_url($deletepage->url); + $yesbutton = new single_button($confirmurl, get_string('yes')); + $nobutton = new single_button($cancelurl, get_string('no')); + echo $OUTPUT->confirm($message, $yesbutton, $nobutton); + echo $OUTPUT->footer(); + // Make sure that nothing else happens after we have displayed this form. + exit; + } else { + blocks_delete_instance($block->instance); + // bui_deleteid and bui_confirm should not be in the PAGE url. + $this->page->ensure_param_not_in_url('bui_deleteid'); + $this->page->ensure_param_not_in_url('bui_confirm'); + return true; + } } /** diff --git a/lib/conditionlib.php b/lib/conditionlib.php index 7557190f189..96b958069d2 100644 --- a/lib/conditionlib.php +++ b/lib/conditionlib.php @@ -767,7 +767,7 @@ abstract class condition_info_base { $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) { @@ -929,7 +929,7 @@ abstract class condition_info_base { $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); diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index c243d8b00e6..f3b34fdc6c1 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -1184,5 +1184,15 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2012082300.02); } + if ($oldversion < 2012090500.00) { + $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, 2012090500.00); + } + return true; } diff --git a/lib/db/upgradelib.php b/lib/db/upgradelib.php index 5103e0ba784..5247e49429e 100644 --- a/lib/db/upgradelib.php +++ b/lib/db/upgradelib.php @@ -30,6 +30,51 @@ 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. @@ -50,7 +95,7 @@ function upgrade_mysql_fix_unsigned_columns() { $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; @@ -115,7 +160,7 @@ function upgrade_mysql_fix_lob_columns() { $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); diff --git a/lib/editor/tinymce/adminlib.php b/lib/editor/tinymce/adminlib.php index b145f996743..2aa9caa49df 100644 --- a/lib/editor/tinymce/adminlib.php +++ b/lib/editor/tinymce/adminlib.php @@ -145,7 +145,7 @@ class tiynce_subplugins_settings extends admin_setting { * @return string */ public function output_html($data, $query='') { - global $CFG, $OUTPUT; + global $CFG, $OUTPUT, $PAGE; require_once("$CFG->libdir/editorlib.php"); require_once("$CFG->libdir/pluginlib.php"); require_once(__DIR__.'/lib.php'); @@ -198,6 +198,13 @@ class tiynce_subplugins_settings extends admin_setting { $displayname = html_writer::tag('span', $namestr, array('class'=>'dimmed_text')); } + if ($PAGE->theme->resolve_image_location('icon', 'tinymce_' . $name)) { + $icon = $OUTPUT->pix_icon('icon', '', 'tinymce_' . $name, array('class' => 'smallicon pluginicon')); + } else { + $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'smallicon pluginicon noicon')); + } + $displayname = $icon . ' ' . $displayname; + // Add available buttons. $buttons = implode(', ', $plugin->get_buttons()); $buttons = html_writer::tag('span', $buttons, array('class'=>'tinymcebuttons')); diff --git a/lib/editor/tinymce/classes/plugin.php b/lib/editor/tinymce/classes/plugin.php index 305f8275c7b..35fe4f2a3ed 100644 --- a/lib/editor/tinymce/classes/plugin.php +++ b/lib/editor/tinymce/classes/plugin.php @@ -152,7 +152,12 @@ abstract class editor_tinymce_plugin { */ protected function add_button_after(array &$params, $row, $button, $after = '', $alwaysadd = true) { - $this->check_row($row); + + if ($this->is_button_present($params, $button)) { + return true; + } + + $row = $this->fix_row($params, $row); $field = 'theme_advanced_buttons' . $row; $old = $params[$field]; @@ -190,7 +195,7 @@ abstract class editor_tinymce_plugin { * to see if it succeeded. * * @param array $params TinyMCE init parameters array - * @param int $row Row to add button to (1 to 3) + * @param int $row Row to add button to (1 to 10) * @param string $button Identifier of button/plugin * @param string $before Adds button directly before the named plugin * @param bool $alwaysadd If specified $after string not found, add at start @@ -198,7 +203,11 @@ abstract class editor_tinymce_plugin { */ protected function add_button_before(array &$params, $row, $button, $before = '', $alwaysadd = true) { - $this->check_row($row); + + if ($this->is_button_present($params, $button)) { + return true; + } + $row = $this->fix_row($params, $row); $field = 'theme_advanced_buttons' . $row; $old = $params[$field]; @@ -226,15 +235,47 @@ abstract class editor_tinymce_plugin { } /** - * Checks the row value is valid. - * - * @param int $row Row to add button to (1 to 3) - * @throws coding_exception If row value is outside the range 1-3 + * Tests if button already present. + * @param array $params + * @param string $button + * @return bool */ - private function check_row($row) { - if ($row < 1 || $row > 3) { - throw new coding_exception("Invalid row option: $row"); + private function is_button_present(array $params, $button) { + for($i=1; $i<=10; $i++) { + $field = 'theme_advanced_buttons' . $i; + if (!isset($params[$field])) { + continue; + } + $buttons = explode(',', $params[$field]); + if (in_array($button, $buttons)) { + return true; + } } + return false; + } + + /** + * Checks the row value is valid, fix if necessary. + * + * @param array $params TinyMCE init parameters array + * @param int $row Row to add button if exists + * @return int requested row if exists, lower number if does not exist. + */ + private function fix_row(array &$params, $row) { + $row = ($row < 1) ? 1 : (int)$row; + $row = ($row > 10) ? 10 : $row; + + $field = 'theme_advanced_buttons' . $row; + if (isset($params[$field])) { + return $row; + } + for($i=$row; $i>=1; $i--) { + if (isset($params[$field])) { + return $row; + } + } + // This should not happen. + return 1; } /** diff --git a/lib/editor/tinymce/db/upgrade.php b/lib/editor/tinymce/db/upgrade.php new file mode 100644 index 00000000000..42d931e7807 --- /dev/null +++ b/lib/editor/tinymce/db/upgrade.php @@ -0,0 +1,41 @@ +. + +/** + * TinyMCE editor integration upgrade. + * + * @package editor_tinymce + * @copyright 2012 Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +function xmldb_editor_tinymce_upgrade($oldversion) { + global $CFG, $DB; + + $dbman = $DB->get_manager(); + + + if ($oldversion < 2012083100) { + // Reset redesigned editor toolbar setting. + unset_config('customtoolbar', 'editor_tinymce'); + upgrade_plugin_savepoint(true, 2012083100, 'editor', 'tinymce'); + } + + + return true; +} diff --git a/lib/editor/tinymce/lang/en/editor_tinymce.php b/lib/editor/tinymce/lang/en/editor_tinymce.php index d08d0f11745..fa01e4e1a12 100644 --- a/lib/editor/tinymce/lang/en/editor_tinymce.php +++ b/lib/editor/tinymce/lang/en/editor_tinymce.php @@ -27,8 +27,8 @@ $string['availablebuttons'] = 'Available buttons'; $string['common:browseimage'] = 'Find or upload an image...'; $string['common:browsemedia'] = 'Find or upload a sound, video or applet...'; -$string['customtoolbar'] = 'Custom editor toolbar'; -$string['customtoolbar_desc'] = 'Each line contains a list of comma separated button names, use "|" as a group separator. Leave empty if you want standard toolbar. See {$a} for the list of default TinyMCE buttons.'; +$string['customtoolbar'] = 'Editor toolbar'; +$string['customtoolbar_desc'] = 'Each line contains a list of comma separated button names, use "|" as a group separator, empty lines are ignored. See {$a} for the list of default TinyMCE buttons.'; $string['fontselectlist'] = 'Available fonts list'; $string['media_dlg:filename'] = 'Filename'; $string['pluginname'] = 'TinyMCE HTML editor'; diff --git a/lib/editor/tinymce/lib.php b/lib/editor/tinymce/lib.php index d079dae7498..ae784d3a1d9 100644 --- a/lib/editor/tinymce/lib.php +++ b/lib/editor/tinymce/lib.php @@ -127,7 +127,6 @@ class tinymce_texteditor extends texteditor { } $fontselectlist = empty($config->fontselectlist) ? '' : $config->fontselectlist; - $fontbutton = ($fontselectlist === '') ? '' : 'fontselect,'; $params = array( 'moodle_config' => $config, @@ -154,13 +153,6 @@ class tinymce_texteditor extends texteditor { 'theme_advanced_font_sizes' => "1,2,3,4,5,6,7", 'theme_advanced_layout_manager' => "SimpleLayout", 'theme_advanced_toolbar_align' => "left", - 'theme_advanced_buttons1' => $fontbutton . 'fontsizeselect,formatselect,|,' . - 'undo,redo,|,search,replace,|,fullscreen', - 'theme_advanced_buttons2' => 'bold,italic,underline,strikethrough,sub,sup,|,' . - 'justifyleft,justifycenter,justifyright,|,' . - 'cleanup,removeformat,pastetext,pasteword,|,forecolor,backcolor,|,ltr,rtl', - 'theme_advanced_buttons3' => 'bullist,numlist,outdent,indent,|,' . - 'link,unlink,|,image,nonbreaking,charmap,table,|,code', 'theme_advanced_fonts' => $fontselectlist, 'theme_advanced_resize_horizontal' => true, 'theme_advanced_resizing' => true, @@ -170,6 +162,19 @@ class tinymce_texteditor extends texteditor { 'theme_advanced_statusbar_location' => "bottom", ); + // Should we override the default toolbar layout unconditionally? + $customtoolbar = self::parse_toolbar_setting($config->customtoolbar); + if ($customtoolbar) { + $i = 1; + foreach ($customtoolbar as $line) { + $params['theme_advanced_buttons'.$i] = $line; + $i++; + } + } else { + // At least one line is required. + $params['theme_advanced_buttons1'] = ''; + } + if (!empty($options['legacy']) or !empty($options['noclean']) or !empty($options['trusted'])) { // now deal somehow with non-standard tags, people scream when we do not make moodle code xtml strict, // but they scream even more when we strip all tags that are not strict :-( @@ -188,20 +193,6 @@ class tinymce_texteditor extends texteditor { // Allow plugins to adjust parameters. editor_tinymce_plugin::all_update_init_params($params, $context, $options); - // Should we override the default toolbar layout unconditionally? - $customtoolbar = self::parse_toolbar_setting($config->customtoolbar); - if ($customtoolbar) { - unset($params['theme_advanced_buttons1']); - unset($params['theme_advanced_buttons2']); - unset($params['theme_advanced_buttons3']); - unset($params['theme_advanced_buttons4']); - $i = 1; - foreach ($customtoolbar as $line) { - $params['theme_advanced_buttons'.$i] = $line; - $i++; - } - } - // Remove temporary parameters. unset($params['moodle_config']); @@ -221,6 +212,7 @@ class tinymce_texteditor extends texteditor { } $customtoolbar = str_replace("\r", "\n", $customtoolbar); $customtoolbar = strtolower($customtoolbar); + $i = 0; foreach (explode("\n", $customtoolbar) as $line) { $line = preg_replace('/[^a-z0-9_,\|\-]/', ',', $line); $line = str_replace('|', ',|,', $line); @@ -229,7 +221,13 @@ class tinymce_texteditor extends texteditor { if ($line === '') { continue; } - $result[] = $line; + if ($i == 10) { + // Maximum is ten lines, merge the rest to the last line. + $result[9] = $result[9].','.$line; + } else { + $result[] = $line; + $i++; + } } return $result; } diff --git a/lib/editor/tinymce/plugins/dragmath/pix/icon.png b/lib/editor/tinymce/plugins/dragmath/pix/icon.png new file mode 100644 index 00000000000..3fd55ee7361 Binary files /dev/null and b/lib/editor/tinymce/plugins/dragmath/pix/icon.png differ diff --git a/lib/editor/tinymce/plugins/moodleemoticon/pix/icon.png b/lib/editor/tinymce/plugins/moodleemoticon/pix/icon.png new file mode 100644 index 00000000000..6d6ffc69393 Binary files /dev/null and b/lib/editor/tinymce/plugins/moodleemoticon/pix/icon.png differ diff --git a/lib/editor/tinymce/plugins/moodleimage/pix/icon.png b/lib/editor/tinymce/plugins/moodleimage/pix/icon.png new file mode 100644 index 00000000000..60bc72349fb Binary files /dev/null and b/lib/editor/tinymce/plugins/moodleimage/pix/icon.png differ diff --git a/lib/editor/tinymce/plugins/moodlemedia/pix/icon.png b/lib/editor/tinymce/plugins/moodlemedia/pix/icon.png new file mode 100644 index 00000000000..c7ebc40d3ea Binary files /dev/null and b/lib/editor/tinymce/plugins/moodlemedia/pix/icon.png differ diff --git a/lib/editor/tinymce/plugins/moodlenolink/pix/icon.png b/lib/editor/tinymce/plugins/moodlenolink/pix/icon.png new file mode 100644 index 00000000000..3f21ab4b4ea Binary files /dev/null and b/lib/editor/tinymce/plugins/moodlenolink/pix/icon.png differ diff --git a/lib/editor/tinymce/plugins/spellchecker/pix/icon.png b/lib/editor/tinymce/plugins/spellchecker/pix/icon.png new file mode 100644 index 00000000000..7124c6acb0d Binary files /dev/null and b/lib/editor/tinymce/plugins/spellchecker/pix/icon.png differ diff --git a/lib/editor/tinymce/settings.php b/lib/editor/tinymce/settings.php index d7ffb88332d..56ee3d03fab 100644 --- a/lib/editor/tinymce/settings.php +++ b/lib/editor/tinymce/settings.php @@ -31,8 +31,13 @@ if ($ADMIN->fulltree) { require_once(__DIR__.'/adminlib.php'); $settings->add(new tiynce_subplugins_settings()); $settings->add(new admin_setting_heading('tinymcegeneralheader', new lang_string('settings'), '')); + $default = "fontselect,fontsizeselect,formatselect,|,undo,redo,|,search,replace,|,fullscreen + +bold,italic,underline,strikethrough,sub,sup,|,justifyleft,justifycenter,justifyright,|,cleanup,removeformat,pastetext,pasteword,|,forecolor,backcolor,|,ltr,rtl + +bullist,numlist,outdent,indent,|,link,unlink,|,image,nonbreaking,charmap,table,|,code"; $settings->add(new admin_setting_configtextarea('editor_tinymce/customtoolbar', - get_string('customtoolbar', 'editor_tinymce'), get_string('customtoolbar_desc', 'editor_tinymce', 'http://www.tinymce.com/wiki.php/Buttons/controls'), '', PARAM_RAW, 100, 6)); + get_string('customtoolbar', 'editor_tinymce'), get_string('customtoolbar_desc', 'editor_tinymce', 'http://www.tinymce.com/wiki.php/Buttons/controls'), $default, PARAM_RAW, 100, 8)); $settings->add(new admin_setting_configtextarea('editor_tinymce/fontselectlist', get_string('fontselectlist', 'editor_tinymce'), '', 'Trebuchet=Trebuchet MS,Verdana,Arial,Helvetica,sans-serif;Arial=arial,helvetica,sans-serif;Courier New=courier new,courier,monospace;Georgia=georgia,times new roman,times,serif;Tahoma=tahoma,arial,helvetica,sans-serif;Times New Roman=times new roman,times,serif;Verdana=verdana,arial,helvetica,sans-serif;Impact=impact;Wingdings=wingdings', PARAM_RAW)); diff --git a/lib/editor/tinymce/tests/editor_test.php b/lib/editor/tinymce/tests/editor_test.php index 27db61bb075..1704bab5813 100644 --- a/lib/editor/tinymce/tests/editor_test.php +++ b/lib/editor/tinymce/tests/editor_test.php @@ -49,5 +49,8 @@ class editor_tinymce_testcase extends advanced_testcase { $result = tinymce_texteditor::parse_toolbar_setting("| \n\n| \n \r"); $this->assertSame(array(), $result); + + $result = tinymce_texteditor::parse_toolbar_setting("one\ntwo\n\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten"); + $this->assertSame(array('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'), $result); } } diff --git a/lib/editor/tinymce/version.php b/lib/editor/tinymce/version.php index 345b637d280..4d537de6065 100644 --- a/lib/editor/tinymce/version.php +++ b/lib/editor/tinymce/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2012081000; // The current plugin version (Date: YYYYMMDDXX) -$plugin->requires = 2012061700; // Requires this Moodle version +$plugin->version = 2012083100; // The current plugin version (Date: YYYYMMDDXX) +$plugin->requires = 2012083100; // Requires this Moodle version $plugin->component = 'editor_tinymce'; // Full name of the plugin (used for diagnostics) $plugin->release = '3.6.0'; // This is NOT a directory name, see lib.php if you need to know where is the editor code! diff --git a/lib/flowplayer/README.txt b/lib/flowplayer/README.txt index 7bdb7bd1c5c..742633c27a5 100644 --- a/lib/flowplayer/README.txt +++ b/lib/flowplayer/README.txt @@ -1,8 +1,35 @@ Version history: +3.2.13 +------ +- Updated to automatically load the latest controls and audio plugins + +3.2.12 +------ +- new flowplayer.js version 3.2.11, + fixes removing the player in fullscreen mode leaves Android locked in landscape orientation (#511) +- #586 add a bitrate label with a new namespace attribute fp:bitratelabel. +- #583 fixes for handling the fullscreenOnly property better +- #494 with relative filenames with a root path strip the baseurl of paths first. + +3.2.11 +------ +- new flowplayer.js, now requires Flash 10.1 as the minimum flash version +- #526 allow click through event for flash installation message when using div containers. +- #508 disabling the stagevideo screen mask, canvas is visible without it, this was causing issues with the display list. +- #443 adding accessibility option to the playbuttonoverlay. + + +3.2.10 +------ +- Fixed #514, scrubbing was broken +- new flowplayer.js version 3.2.9, fixes #510 + 3.2.9 ----- - Fixed #490, controlbar background, buffer bar and progress bar colors were all reset to white +- #503 Update viewport when stage is added to obtain the coordnates correctly. Update viewport when in and out of fullscreen. +- #508 stage video mask was being added to the top layer and hiding all children. 3.2.8 ----- @@ -69,6 +96,7 @@ this.loadPlugin("content","../flowplayer.content.swf", { html: "test" }, functi - #461 when we have a clip base url set, we need the complete clip url sent to play2 for http streams. - #470 check for a playlist when replacing the playlist with an rss feed. - #494 regression issued caused by #412, enable base url correctly. +- #30 regression caused by character replacements, removing for now and let end user deal with them. 3.2.7 ----- diff --git a/lib/flowplayer/README_audio.txt b/lib/flowplayer/README_audio.txt index 04a2f27b6d8..746c5ee8bbd 100644 --- a/lib/flowplayer/README_audio.txt +++ b/lib/flowplayer/README_audio.txt @@ -1,5 +1,18 @@ Version history: +3.2.10 +------ +- #575 send the start event after begin +- #569 if the playlist has been reset but the audio has been already buffered, set the duration and start event. +- #582 fixes for metadata events dispatching in playlists and when replaying same audio item, cleanup duration updating once download has completed, +fixes for clearing the previous cover image display. +- #611 close the channel and sound on stream not found errors. + +3.2.9 +----- +- #501 fixes to dispatch start state correctly. +- #501 use the sound channel to listen for a complete event to finish correctly. + 3.2.8 ----- Fixes: diff --git a/lib/flowplayer/flowplayer-3.2.8.js b/lib/flowplayer/flowplayer-3.2.11.js similarity index 96% rename from lib/flowplayer/flowplayer-3.2.8.js rename to lib/flowplayer/flowplayer-3.2.11.js index ee42b3c991f..c6e51cf5061 100644 --- a/lib/flowplayer/flowplayer-3.2.8.js +++ b/lib/flowplayer/flowplayer-3.2.11.js @@ -1,5 +1,5 @@ /* - * flowplayer.js 3.2.8. The Flowplayer API + * flowplayer.js 3.2.11. The Flowplayer API * * Copyright 2009-2011 Flowplayer Oy * @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with Flowplayer. If not, see . * - * Date: 2011-12-30 12:34:08 -0500 (Fri, 30 Dec 2011) - * Revision: 761 + * Date: 2012-06-16 10:34:45 -0400 (Sat, 16 Jun 2012) + * Revision: 808 */ (function () { function g(o) { @@ -373,6 +373,9 @@ D = true; try { if (v) { + if (v.fp_isFullscreen()) { + v.fp_toggleFullscreen() + } v.fp_close(); w._fireEvent("onUnload") } @@ -468,7 +471,7 @@ } return w }, getVersion:function () { - var I = "flowplayer.js 3.2.8"; + var I = "flowplayer.js 3.2.11"; if (w.isLoaded()) { var H = v.fp_getVersion(); H.push(I); @@ -615,7 +618,7 @@ return P }; function B() { - q.innerHTML = ''; // Moodle hack - we do not want splashscreens, unfortunately there is not switch to disable them + q.innerHTML=''; // Moodle hack - we do not want splashscreens, unfortunately there is not switch to disable them if ($f(q)) { $f(q).getParent().innerHTML = ""; p = $f(q).getIndex(); @@ -776,7 +779,7 @@ if (typeof t == "string") { t = {src:t} } - t = i({bgcolor:"#000000", version:[9, 0], expressInstall:"http://static.flowplayer.org/swf/expressinstall.swf", cachebusting:false}, t); + t = i({bgcolor:"#000000", version:[10, 1], expressInstall:"http://releases.flowplayer.org/swf/expressinstall.swf", cachebusting:false}, t); if (typeof o == "string") { if (o.indexOf(".") != -1) { var s = []; @@ -823,7 +826,7 @@ } })(); (function () { - var h = document.all, j = "http://www.adobe.com/go/getflashplayer", c = typeof jQuery == "function", e = /(\d+)[^\d]+(\d+)[^\d]*(\d*)/, b = {width:"100%", height:"100%", id:"_" + ("" + Math.random()).slice(9), allowfullscreen:true, allowscriptaccess:"always", quality:"high", version:[3, 0], onFail:null, expressInstall:null, w3c:false, cachebusting:false}; + var h = document.all, j = "http://get.adobe.com/flashplayer", c = typeof jQuery == "function", e = /(\d+)[^\d]+(\d+)[^\d]*(\d*)/, b = {width:"100%", height:"100%", id:"_" + ("" + Math.random()).slice(9), allowfullscreen:true, allowscriptaccess:"always", quality:"high", version:[3, 0], onFail:null, expressInstall:null, w3c:false, cachebusting:false}; if (window.attachEvent) { window.attachEvent("onbeforeunload", function () { __flash_unloadHandler = function () { @@ -882,7 +885,7 @@ } } f = e.exec(f); - return f ? [f[1], f[3]] : [0, 0] + return f ? [1 * f[1], 1 * f[(f[1] * 1 > 9 ? 2 : 3)] * 1] : [0, 0] }, asString:function (l) { if (l === null || l === undefined) { return null @@ -895,13 +898,11 @@ case"string": l = l.replace(new RegExp('(["\\\\])', "g"), "\\$1"); l = l.replace(/^\s?(\d+\.?\d*)%/, "$1pct"); - l = l.replace(/(%)/g, "%25").replace(/'/g, "\\u0027").replace(/"/g, "\\u0022").replace(/&/g, "%26"); return'"' + l + '"'; case"array": - return"[" + a(l, - function (o) { - return g.asString(o) - }).join(",") + "]"; + return"[" + a(l,function (o) { + return g.asString(o) + }).join(",") + "]"; case"function": return'"function()"'; case"object": @@ -963,7 +964,7 @@ } else { if (!f.innerHTML.replace(/\s/g, "")) { f.innerHTML = "

Flash version " + n.version + " or greater is required

" + (k[0] > 0 ? "Your version is " + k : "You have no flash plugin installed") + "

" + (f.tagName == "A" ? "

Click here to download latest version

" : "

Download latest version from here

"); - if (f.tagName == "A") { + if (f.tagName == "A" || f.tagName == "DIV") { f.onclick = function () { location.href = j } @@ -992,7 +993,7 @@ } if (c) { - jQuery.tools = jQuery.tools || {version:"3.2.8"}; + jQuery.tools = jQuery.tools || {version:"3.2.11"}; jQuery.tools.flashembed = {conf:b}; jQuery.fn.flashembed = function (l, f) { return this.each(function () { diff --git a/lib/flowplayer/flowplayer-3.2.11.min.js b/lib/flowplayer/flowplayer-3.2.11.min.js new file mode 100644 index 00000000000..c03505f4b02 --- /dev/null +++ b/lib/flowplayer/flowplayer-3.2.11.min.js @@ -0,0 +1,24 @@ +/* + * flowplayer.js 3.2.11. The Flowplayer API + * + * Copyright 2009-2011 Flowplayer Oy + * + * This file is part of Flowplayer. + * + * Flowplayer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Flowplayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Flowplayer. If not, see . + * + * Date: 2012-06-16 10:34:45 -0400 (Sat, 16 Jun 2012) + * Revision: 808 + */ +(function(){function g(o){console.log("$f.fireEvent",[].slice.call(o))}function k(q){if(!q||typeof q!="object"){return q}var o=new q.constructor();for(var p in q){if(q.hasOwnProperty(p)){o[p]=k(q[p])}}return o}function m(t,q){if(!t){return}var o,p=0,r=t.length;if(r===undefined){for(o in t){if(q.call(t[o],o,t[o])===false){break}}}else{for(var s=t[0];p1){var t=arguments[1],q=(arguments.length==3)?arguments[2]:{};if(typeof t=="string"){t={src:t}}t=i({bgcolor:"#000000",version:[10,1],expressInstall:"http://releases.flowplayer.org/swf/expressinstall.swf",cachebusting:false},t);if(typeof o=="string"){if(o.indexOf(".")!=-1){var s=[];m(n(o),function(){s.push(new b(this,k(t),k(q)))});return new d(s)}else{var r=c(o);return new b(r!==null?r:k(o),k(t),k(q))}}else{if(o){return new b(o,k(t),k(q))}}}return null};i(window.$f,{fireEvent:function(){var o=[].slice.call(arguments);var q=$f(o[0]);return q?q._fireEvent(o.slice(1)):null},addPlugin:function(o,p){b.prototype[o]=p;return $f},each:m,extend:i});if(typeof jQuery=="function"){jQuery.fn.flowplayer=function(q,p){if(!arguments.length||typeof arguments[0]=="number"){var o=[];this.each(function(){var r=$f(this);if(r){o.push(r)}});return arguments.length?o[arguments[0]]:new d(o)}return this.each(function(){$f(this,k(q),p?k(p):{})})}}})();(function(){var h=document.all,j="http://get.adobe.com/flashplayer",c=typeof jQuery=="function",e=/(\d+)[^\d]+(\d+)[^\d]*(\d*)/,b={width:"100%",height:"100%",id:"_"+(""+Math.random()).slice(9),allowfullscreen:true,allowscriptaccess:"always",quality:"high",version:[3,0],onFail:null,expressInstall:null,w3c:false,cachebusting:false};if(window.attachEvent){window.attachEvent("onbeforeunload",function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){}})}function i(m,l){if(l){for(var f in l){if(l.hasOwnProperty(f)){m[f]=l[f]}}}return m}function a(f,n){var m=[];for(var l in f){if(f.hasOwnProperty(l)){m[l]=n(f[l])}}return m}window.flashembed=function(f,m,l){if(typeof f=="string"){f=document.getElementById(f.replace("#",""))}if(!f){return}if(typeof m=="string"){m={src:m}}return new d(f,i(i({},b),m),l)};var g=i(window.flashembed,{conf:b,getVersion:function(){var m,f;try{f=navigator.plugins["Shockwave Flash"].description.slice(16)}catch(o){try{m=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");f=m&&m.GetVariable("$version")}catch(n){try{m=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");f=m&&m.GetVariable("$version")}catch(l){}}}f=e.exec(f);return f?[1*f[1],1*f[(f[1]*1>9?2:3)]*1]:[0,0]},asString:function(l){if(l===null||l===undefined){return null}var f=typeof l;if(f=="object"&&l.push){f="array"}switch(f){case"string":l=l.replace(new RegExp('(["\\\\])',"g"),"\\$1");l=l.replace(/^\s?(\d+\.?\d*)%/,"$1pct");return'"'+l+'"';case"array":return"["+a(l,function(o){return g.asString(o)}).join(",")+"]";case"function":return'"function()"';case"object":var m=[];for(var n in l){if(l.hasOwnProperty(n)){m.push('"'+n+'":'+g.asString(l[n]))}}return"{"+m.join(",")+"}"}return String(l).replace(/\s/g," ").replace(/\'/g,'"')},getHTML:function(o,l){o=i({},o);var n=''}o.width=o.height=o.id=o.w3c=o.src=null;o.onFail=o.version=o.expressInstall=null;for(var m in o){if(o[m]){n+=''}}var p="";if(l){for(var f in l){if(l[f]){var q=l[f];p+=f+"="+(/function|object/.test(typeof q)?g.asString(q):q)+"&"}}p=p.slice(0,-1);n+='"}n+="";return n},isSupported:function(f){return k[0]>f[0]||k[0]==f[0]&&k[1]>=f[1]}});var k=g.getVersion();function d(f,n,m){if(g.isSupported(n.version)){f.innerHTML=g.getHTML(n,m)}else{if(n.expressInstall&&g.isSupported([6,65])){f.innerHTML=g.getHTML(i(n,{src:n.expressInstall}),{MMredirectURL:encodeURIComponent(location.href),MMplayerType:"PlugIn",MMdoctitle:document.title})}else{if(!f.innerHTML.replace(/\s/g,"")){f.innerHTML="

Flash version "+n.version+" or greater is required

"+(k[0]>0?"Your version is "+k:"You have no flash plugin installed")+"

"+(f.tagName=="A"?"

Click here to download latest version

":"

Download latest version from here

");if(f.tagName=="A"||f.tagName=="DIV"){f.onclick=function(){location.href=j}}}if(n.onFail){var l=n.onFail.call(this);if(typeof l=="string"){f.innerHTML=l}}}}if(h){window[n.id]=document.getElementById(n.id)}i(this,{getRoot:function(){return f},getOptions:function(){return n},getConf:function(){return m},getApi:function(){return f.firstChild}})}if(c){jQuery.tools=jQuery.tools||{version:"3.2.11"};jQuery.tools.flashembed={conf:b};jQuery.fn.flashembed=function(l,f){return this.each(function(){$(this).data("flashembed",flashembed(this,l,f))})}}})(); \ No newline at end of file diff --git a/lib/flowplayer/flowplayer-3.2.14.swf b/lib/flowplayer/flowplayer-3.2.14.swf new file mode 100644 index 00000000000..bee3b6030bb Binary files /dev/null and b/lib/flowplayer/flowplayer-3.2.14.swf differ diff --git a/lib/flowplayer/flowplayer-3.2.8.min.js b/lib/flowplayer/flowplayer-3.2.8.min.js deleted file mode 100644 index 93feb7385dd..00000000000 --- a/lib/flowplayer/flowplayer-3.2.8.min.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * flowplayer.js 3.2.8. The Flowplayer API - * - * Copyright 2009-2011 Flowplayer Oy - * - * This file is part of Flowplayer. - * - * Flowplayer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Flowplayer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Flowplayer. If not, see . - * - * Date: 2011-12-30 12:34:08 -0500 (Fri, 30 Dec 2011) - * Revision: 761 - * - * this file was modified for Moodle - see flowplayer-3.2.8.js - */ -(function(){function g(o){console.log("$f.fireEvent",[].slice.call(o))}function k(q){if(!q||typeof q!="object"){return q}var o=new q.constructor();for(var p in q){if(q.hasOwnProperty(p)){o[p]=k(q[p])}}return o}function m(t,q){if(!t){return}var o,p=0,r=t.length;if(r===undefined){for(o in t){if(q.call(t[o],o,t[o])===false){break}}}else{for(var s=t[0];p1){var t=arguments[1],q=(arguments.length==3)?arguments[2]:{};if(typeof t=="string"){t={src:t}}t=i({bgcolor:"#000000",version:[9,0],expressInstall:"http://static.flowplayer.org/swf/expressinstall.swf",cachebusting:false},t);if(typeof o=="string"){if(o.indexOf(".")!=-1){var s=[];m(n(o),function(){s.push(new b(this,k(t),k(q)))});return new d(s)}else{var r=c(o);return new b(r!==null?r:k(o),k(t),k(q))}}else{if(o){return new b(o,k(t),k(q))}}}return null};i(window.$f,{fireEvent:function(){var o=[].slice.call(arguments);var q=$f(o[0]);return q?q._fireEvent(o.slice(1)):null},addPlugin:function(o,p){b.prototype[o]=p;return $f},each:m,extend:i});if(typeof jQuery=="function"){jQuery.fn.flowplayer=function(q,p){if(!arguments.length||typeof arguments[0]=="number"){var o=[];this.each(function(){var r=$f(this);if(r){o.push(r)}});return arguments.length?o[arguments[0]]:new d(o)}return this.each(function(){$f(this,k(q),p?k(p):{})})}}})();(function(){var h=document.all,j="http://www.adobe.com/go/getflashplayer",c=typeof jQuery=="function",e=/(\d+)[^\d]+(\d+)[^\d]*(\d*)/,b={width:"100%",height:"100%",id:"_"+(""+Math.random()).slice(9),allowfullscreen:true,allowscriptaccess:"always",quality:"high",version:[3,0],onFail:null,expressInstall:null,w3c:false,cachebusting:false};if(window.attachEvent){window.attachEvent("onbeforeunload",function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){}})}function i(m,l){if(l){for(var f in l){if(l.hasOwnProperty(f)){m[f]=l[f]}}}return m}function a(f,n){var m=[];for(var l in f){if(f.hasOwnProperty(l)){m[l]=n(f[l])}}return m}window.flashembed=function(f,m,l){if(typeof f=="string"){f=document.getElementById(f.replace("#",""))}if(!f){return}if(typeof m=="string"){m={src:m}}return new d(f,i(i({},b),m),l)};var g=i(window.flashembed,{conf:b,getVersion:function(){var m,f;try{f=navigator.plugins["Shockwave Flash"].description.slice(16)}catch(o){try{m=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");f=m&&m.GetVariable("$version")}catch(n){try{m=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");f=m&&m.GetVariable("$version")}catch(l){}}}f=e.exec(f);return f?[f[1],f[3]]:[0,0]},asString:function(l){if(l===null||l===undefined){return null}var f=typeof l;if(f=="object"&&l.push){f="array"}switch(f){case"string":l=l.replace(new RegExp('(["\\\\])',"g"),"\\$1");l=l.replace(/^\s?(\d+\.?\d*)%/,"$1pct");l=l.replace(/(%)/g,"%25").replace(/'/g,"\\u0027").replace(/"/g,"\\u0022").replace(/&/g,"%26");return'"'+l+'"';case"array":return"["+a(l,function(o){return g.asString(o)}).join(",")+"]";case"function":return'"function()"';case"object":var m=[];for(var n in l){if(l.hasOwnProperty(n)){m.push('"'+n+'":'+g.asString(l[n]))}}return"{"+m.join(",")+"}"}return String(l).replace(/\s/g," ").replace(/\'/g,'"')},getHTML:function(o,l){o=i({},o);var n=''}o.width=o.height=o.id=o.w3c=o.src=null;o.onFail=o.version=o.expressInstall=null;for(var m in o){if(o[m]){n+=''}}var p="";if(l){for(var f in l){if(l[f]){var q=l[f];p+=f+"="+(/function|object/.test(typeof q)?g.asString(q):q)+"&"}}p=p.slice(0,-1);n+='"}n+="";return n},isSupported:function(f){return k[0]>f[0]||k[0]==f[0]&&k[1]>=f[1]}});var k=g.getVersion();function d(f,n,m){if(g.isSupported(n.version)){f.innerHTML=g.getHTML(n,m)}else{if(n.expressInstall&&g.isSupported([6,65])){f.innerHTML=g.getHTML(i(n,{src:n.expressInstall}),{MMredirectURL:encodeURIComponent(location.href),MMplayerType:"PlugIn",MMdoctitle:document.title})}else{if(!f.innerHTML.replace(/\s/g,"")){f.innerHTML="

Flash version "+n.version+" or greater is required

"+(k[0]>0?"Your version is "+k:"You have no flash plugin installed")+"

"+(f.tagName=="A"?"

Click here to download latest version

":"

Download latest version from here

");if(f.tagName=="A"){f.onclick=function(){location.href=j}}}if(n.onFail){var l=n.onFail.call(this);if(typeof l=="string"){f.innerHTML=l}}}}if(h){window[n.id]=document.getElementById(n.id)}i(this,{getRoot:function(){return f},getOptions:function(){return n},getConf:function(){return m},getApi:function(){return f.firstChild}})}if(c){jQuery.tools=jQuery.tools||{version:"3.2.8"};jQuery.tools.flashembed={conf:b};jQuery.fn.flashembed=function(l,f){return this.each(function(){$(this).data("flashembed",flashembed(this,l,f))})}}})(); \ No newline at end of file diff --git a/lib/flowplayer/flowplayer-3.2.9.swf b/lib/flowplayer/flowplayer-3.2.9.swf deleted file mode 100644 index 9bdb3f9f546..00000000000 Binary files a/lib/flowplayer/flowplayer-3.2.9.swf and /dev/null differ diff --git a/lib/flowplayer/flowplayer.audio-3.2.10.swf b/lib/flowplayer/flowplayer.audio-3.2.10.swf new file mode 100644 index 00000000000..6dd0af32ba6 Binary files /dev/null and b/lib/flowplayer/flowplayer.audio-3.2.10.swf differ diff --git a/lib/flowplayer/flowplayer.audio-3.2.8.swf b/lib/flowplayer/flowplayer.audio-3.2.8.swf deleted file mode 100644 index df9692ae591..00000000000 Binary files a/lib/flowplayer/flowplayer.audio-3.2.8.swf and /dev/null differ diff --git a/lib/flowplayer/flowplayer.controls-3.2.13.swf b/lib/flowplayer/flowplayer.controls-3.2.13.swf new file mode 100644 index 00000000000..61e95d478a4 Binary files /dev/null and b/lib/flowplayer/flowplayer.controls-3.2.13.swf differ diff --git a/lib/flowplayer/flowplayer.controls-3.2.9.swf b/lib/flowplayer/flowplayer.controls-3.2.9.swf deleted file mode 100644 index a3d34c7bfcb..00000000000 Binary files a/lib/flowplayer/flowplayer.controls-3.2.9.swf and /dev/null differ diff --git a/lib/googleapi.php b/lib/googleapi.php index c2d529fd6b7..3ace6542d4f 100644 --- a/lib/googleapi.php +++ b/lib/googleapi.php @@ -109,23 +109,21 @@ class google_docs { $source = 'https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key='.$docid.'&exportFormat=xls'; break; case 'pdf': - $title = (string)$gdoc->title; - $source = (string)$gdoc->content[0]->attributes()->src; - break; case 'file': - $title = (string)$gdoc->title; - $source = (string)$gdoc->content[0]->attributes()->src; + $title = (string)$gdoc->title; + // 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; diff --git a/lib/javascript-static.js b/lib/javascript-static.js index 4be74efc0be..b8fc705b3ea 100644 --- a/lib/javascript-static.js +++ b/lib/javascript-static.js @@ -1754,9 +1754,9 @@ M.util.load_flowplayer = function() { for(var i=0; i 0 && video.height > 0) { - var src = {src: M.cfg.wwwroot + '/lib/flowplayer/flowplayer-3.2.9.swf', width: video.width, height: video.height}; + var src = {src: M.cfg.wwwroot + '/lib/flowplayer/flowplayer-3.2.14.swf', width: video.width, height: video.height}; } else { - var src = M.cfg.wwwroot + '/lib/flowplayer/flowplayer-3.2.9.swf'; + var src = M.cfg.wwwroot + '/lib/flowplayer/flowplayer-3.2.14.swf'; } flowplayer(video.id, src, { plugins: {controls: controls}, @@ -1856,17 +1856,17 @@ M.util.load_flowplayer = function() { controls.height = 25; controls.time = true; } - flowplayer(audio.id, M.cfg.wwwroot + '/lib/flowplayer/flowplayer-3.2.9.swf', { - plugins: {controls: controls, audio: {url: M.cfg.wwwroot + '/lib/flowplayer/flowplayer.audio-3.2.8.swf'}}, + flowplayer(audio.id, M.cfg.wwwroot + '/lib/flowplayer/flowplayer-3.2.14.swf', { + plugins: {controls: controls, audio: {url: M.cfg.wwwroot + '/lib/flowplayer/flowplayer.audio-3.2.10.swf'}}, clip: {url: audio.fileurl, provider: "audio", autoPlay: false} }); } } - if (M.cfg.jsrev == -10) { - var jsurl = M.cfg.wwwroot + '/lib/flowplayer/flowplayer-3.2.8.min.js'; + if (M.cfg.jsrev == -1) { + var jsurl = M.cfg.wwwroot + '/lib/flowplayer/flowplayer-3.2.11.js'; } else { - var jsurl = M.cfg.wwwroot + '/lib/javascript.php?jsfile=/lib/flowplayer/flowplayer-3.2.8.min.js&rev=' + M.cfg.jsrev; + var jsurl = M.cfg.wwwroot + '/lib/javascript.php?jsfile=/lib/flowplayer/flowplayer-3.2.11.min.js&rev=' + M.cfg.jsrev; } var fileref = document.createElement('script'); fileref.setAttribute('type','text/javascript'); diff --git a/lib/medialib.php b/lib/medialib.php index fb8cdf6b7ae..e75e07743be 100644 --- a/lib/medialib.php +++ b/lib/medialib.php @@ -526,8 +526,7 @@ OET; */ class core_media_player_youtube extends core_media_player_external { protected function embed_external(moodle_url $url, $name, $width, $height, $options) { - $site = $this->matches[1]; - $videoid = $this->matches[3]; + $videoid = end($this->matches); $info = trim($name); if (empty($info) or strpos($info, 'http') === 0) { @@ -540,17 +539,22 @@ class core_media_player_youtube extends core_media_player_external { return << + src="https://www.youtube.com/embed/$videoid?rel=0&wmode=transparent" frameborder="0" allowfullscreen="1"> OET; } 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; } @@ -561,7 +565,7 @@ OET; } public function get_embeddable_markers() { - return array('youtube'); + return array('youtube.com', 'youtube-nocookie.com', 'youtu.be', 'y2u.be'); } } diff --git a/lib/moodlelib.php b/lib/moodlelib.php index aaeddaaf6e5..095cea432f2 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -3927,15 +3927,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); @@ -4788,7 +4818,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; } diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index 17cd590268a..28b8490a571 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -508,23 +508,32 @@ class core_renderer extends renderer_base { /** * Return the standard string that says whether you are logged in (and switched * roles/logged in as another user). - * + * @param bool $withlinks if false, then don't include any links in the HTML produced. + * If not set, the default is the nologinlinks option from the theme config.php file, + * and if that is not set, then links are included. * @return string HTML fragment. */ - public function login_info() { + public function login_info($withlinks = null) { global $USER, $CFG, $DB, $SESSION; if (during_initial_install()) { return ''; } + if (is_null($withlinks)) { + $withlinks = empty($this->page->layout_options['nologinlinks']); + } + $loginpage = ((string)$this->page->url === get_login_url()); $course = $this->page->course; - if (session_is_loggedinas()) { $realuser = session_get_realuser(); $fullname = fullname($realuser, true); - $realuserinfo = " [wwwroot/course/loginas.php?id=$course->id&sesskey=".sesskey()."\">$fullname] "; + if ($withlinks) { + $realuserinfo = " [wwwroot/course/loginas.php?id=$course->id&sesskey=".sesskey()."\">$fullname] "; + } else { + $realuserinfo = " [$fullname] "; + } } else { $realuserinfo = ''; } @@ -539,13 +548,21 @@ class core_renderer extends renderer_base { $fullname = fullname($USER, true); // Since Moodle 2.0 this link always goes to the public profile page (not the course profile page) - $username = "wwwroot/user/profile.php?id=$USER->id\">$fullname"; + if ($withlinks) { + $username = "wwwroot/user/profile.php?id=$USER->id\">$fullname"; + } else { + $username = $fullname; + } if (is_mnet_remote_user($USER) and $idprovider = $DB->get_record('mnet_host', array('id'=>$USER->mnethostid))) { - $username .= " from wwwroot}\">{$idprovider->name}"; + if ($withlinks) { + $username .= " from wwwroot}\">{$idprovider->name}"; + } else { + $username .= " from {$idprovider->name}"; + } } if (isguestuser()) { $loggedinas = $realuserinfo.get_string('loggedinasguest'); - if (!$loginpage) { + if (!$loginpage && $withlinks) { $loggedinas .= " (".get_string('login').')'; } } else if (is_role_switched($course->id)) { // Has switched roles @@ -553,15 +570,19 @@ class core_renderer extends renderer_base { if ($role = $DB->get_record('role', array('id'=>$USER->access['rsw'][$context->path]))) { $rolename = ': '.format_string($role->name); } - $loggedinas = get_string('loggedinas', 'moodle', $username).$rolename. - " (wwwroot/course/view.php?id=$course->id&switchrole=0&sesskey=".sesskey()."\">".get_string('switchrolereturn').')'; + $loggedinas = get_string('loggedinas', 'moodle', $username).$rolename; + if ($withlinks) { + $loggedinas .= " (wwwroot/course/view.php?id=$course->id&switchrole=0&sesskey=".sesskey()."\">".get_string('switchrolereturn').')'; + } } else { - $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username).' '. - " (wwwroot/login/logout.php?sesskey=".sesskey()."\">".get_string('logout').')'; + $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username); + if ($withlinks) { + $loggedinas .= " (wwwroot/login/logout.php?sesskey=".sesskey()."\">".get_string('logout').')'; + } } } else { $loggedinas = get_string('loggedinnot', 'moodle'); - if (!$loginpage) { + if (!$loginpage && $withlinks) { $loggedinas .= " (".get_string('login').')'; } } diff --git a/lib/phpmailer/README b/lib/phpmailer/README index 8d48dc05f52..f66012d40e8 100644 --- a/lib/phpmailer/README +++ b/lib/phpmailer/README @@ -1,14 +1,21 @@ /******************************************************************* -* The http://phpmailer.codeworxtech.com/ website now carries a few * -* advertisements through the Google Adsense network. Please visit * -* the advertiser sites and help us offset some of our costs. * -* Thanks .... * +* http://code.google.com/a/apache-extras.org/p/phpmailer/ * ********************************************************************/ PHPMailer Full Featured Email Transfer Class for PHP ========================================== +Version 5.2.1 (January 16, 2012) + +Patch release (see changelog.txt). + +Version 5.2.0 (July 19, 2011) + +With the release of this version, PHPMailer has moved to Apache +Extras: + http://code.google.com/a/apache-extras.org/p/phpmailer/ + Version 5.0.0 (April 02, 2009) With the release of this version, we are initiating a new version numbering diff --git a/lib/phpmailer/README_MOODLE.txt b/lib/phpmailer/README_MOODLE.txt index 57d158c95da..107d288e9df 100644 --- a/lib/phpmailer/README_MOODLE.txt +++ b/lib/phpmailer/README_MOODLE.txt @@ -1,4 +1,4 @@ -Description of PHPMailer 5.1 library import into Moodle +Description of PHPMailer 5.2.1 library import into Moodle We now use a vanilla version of phpmailer and do our customisations in a subclass. diff --git a/lib/phpmailer/changelog.txt b/lib/phpmailer/changelog.txt index a5c0cb57d06..e2c982db4f3 100644 --- a/lib/phpmailer/changelog.txt +++ b/lib/phpmailer/changelog.txt @@ -3,6 +3,19 @@ ChangeLog NOTE: THIS VERSION OF PHPMAILER IS DESIGNED FOR PHP5/PHP6. IT WILL NOT WORK WITH PHP4. +Version 5.2.1 (January 16, 2012) +* Closed several bugs +* Performance improvements +* MsgHTML() now returns the message as required. +* New method: GetSentMIMEMessage() (returns full copy of sent message) + +Version 5.2 (July 19, 2011) +* protected MIME body and header +* better DKIM DNS Resource Record support +* better aly handling +* htmlfilter class added to extras +* moved to Apache Extras + Version 5.1 (October 20, 2009) * fixed filename issue with AddStringAttachment (thanks to Tony) * fixed "SingleTo" property, now works with Senmail, Qmail, and SMTP in diff --git a/lib/phpmailer/class.phpmailer.php b/lib/phpmailer/class.phpmailer.php index 430cbc9ab81..af089d59789 100644 --- a/lib/phpmailer/class.phpmailer.php +++ b/lib/phpmailer/class.phpmailer.php @@ -2,15 +2,15 @@ /*~ class.phpmailer.php .---------------------------------------------------------------------------. | Software: PHPMailer - PHP email class | -| Version: 5.1 | -| Contact: via sourceforge.net support pages (also www.worxware.com) | -| Info: http://phpmailer.sourceforge.net | -| Support: http://sourceforge.net/projects/phpmailer/ | +| Version: 5.2.1 | +| Site: https://code.google.com/a/apache-extras.org/p/phpmailer/ | | ------------------------------------------------------------------------- | -| Admin: Andy Prevost (project admininistrator) | +| Admin: Jim Jagielski (project admininistrator) | | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | | : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | +| : Jim Jagielski (jimjag) jimjag@gmail.com | | Founder: Brent R. Matzelle (original founder) | +| Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. | | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. | | Copyright (c) 2001-2003, Brent R. Matzelle | | ------------------------------------------------------------------------- | @@ -19,11 +19,6 @@ | This program is distributed in the hope that it will be useful - WITHOUT | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | | FITNESS FOR A PARTICULAR PURPOSE. | -| ------------------------------------------------------------------------- | -| We offer a number of paid services (www.worxware.com): | -| - Web Hosting on highly optimized fast and secure servers | -| - Technology Consulting | -| - Oursourcing (highly qualified programmers and graphic designers) | '---------------------------------------------------------------------------' */ @@ -33,8 +28,10 @@ * @package PHPMailer * @author Andy Prevost * @author Marcus Bointon + * @author Jim Jagielski + * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost - * @version $Id$ + * @version $Id: class.phpmailer.php 450 2010-06-23 16:46:33Z coolbru $ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ @@ -118,6 +115,27 @@ class PHPMailer { */ public $AltBody = ''; + /** + * Stores the complete compiled MIME message body. + * @var string + * @access protected + */ + protected $MIMEBody = ''; + + /** + * Stores the complete compiled MIME message headers. + * @var string + * @access protected + */ + protected $MIMEHeader = ''; + + /** + * Stores the complete sent MIME message (Body and Headers) + * @var string + * @access protected + */ + protected $SentMIMEMessage = ''; + /** * Sets word wrapping on the body of the message to a given number of * characters. @@ -269,6 +287,12 @@ class PHPMailer { */ public $DKIM_identity = ''; + /** + * Used with DKIM DNS Resource Record + * @var string + */ + public $DKIM_passphrase = ''; + /** * Used with DKIM DNS Resource Record * optional, in format of email address 'you@yourdomain.com' @@ -300,28 +324,34 @@ class PHPMailer { * Sets the PHPMailer Version number * @var string */ - public $Version = '5.1'; + public $Version = '5.2.1'; + + /** + * What to use in the X-Mailer header + * @var string + */ + public $XMailer = ''; ///////////////////////////////////////////////// // PROPERTIES, PRIVATE AND PROTECTED ///////////////////////////////////////////////// - private $smtp = NULL; - private $to = array(); - private $cc = array(); - private $bcc = array(); - private $ReplyTo = array(); - private $all_recipients = array(); - private $attachment = array(); - private $CustomHeader = array(); - private $message_type = ''; - private $boundary = array(); - protected $language = array(); - private $error_count = 0; - private $sign_cert_file = ""; - private $sign_key_file = ""; - private $sign_key_pass = ""; - private $exceptions = false; + protected $smtp = NULL; + protected $to = array(); + protected $cc = array(); + protected $bcc = array(); + protected $ReplyTo = array(); + protected $all_recipients = array(); + protected $attachment = array(); + protected $CustomHeader = array(); + protected $message_type = ''; + protected $boundary = array(); + protected $language = array(); + protected $error_count = 0; + protected $sign_cert_file = ''; + protected $sign_key_file = ''; + protected $sign_key_pass = ''; + protected $exceptions = false; ///////////////////////////////////////////////// // CONSTANTS @@ -437,7 +467,7 @@ class PHPMailer { * @return boolean */ public function AddReplyTo($address, $name = '') { - return $this->AddAnAddress('ReplyTo', $address, $name); + return $this->AddAnAddress('Reply-To', $address, $name); } /** @@ -447,11 +477,17 @@ class PHPMailer { * @param string $address The email address to send to * @param string $name * @return boolean true on success, false if address already used or invalid in some way - * @access private + * @access protected */ - private function AddAnAddress($kind, $address, $name = '') { - if (!preg_match('/^(to|cc|bcc|ReplyTo)$/', $kind)) { - echo 'Invalid recipient array: ' . kind; + protected function AddAnAddress($kind, $address, $name = '') { + if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) { + $this->SetError($this->Lang('Invalid recipient array').': '.$kind); + if ($this->exceptions) { + throw new phpmailerException('Invalid recipient array: ' . $kind); + } + if ($this->SMTPDebug) { + echo $this->Lang('Invalid recipient array').': '.$kind; + } return false; } $address = trim($address); @@ -461,10 +497,12 @@ class PHPMailer { if ($this->exceptions) { throw new phpmailerException($this->Lang('invalid_address').': '.$address); } - echo $this->Lang('invalid_address').': '.$address; + if ($this->SMTPDebug) { + echo $this->Lang('invalid_address').': '.$address; + } return false; } - if ($kind != 'ReplyTo') { + if ($kind != 'Reply-To') { if (!isset($this->all_recipients[strtolower($address)])) { array_push($this->$kind, array($address, $name)); $this->all_recipients[strtolower($address)] = true; @@ -485,7 +523,7 @@ class PHPMailer { * @param string $name * @return boolean */ - public function SetFrom($address, $name = '',$auto=1) { + public function SetFrom($address, $name = '', $auto = 1) { $address = trim($address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim if (!self::ValidateAddress($address)) { @@ -493,14 +531,16 @@ class PHPMailer { if ($this->exceptions) { throw new phpmailerException($this->Lang('invalid_address').': '.$address); } - echo $this->Lang('invalid_address').': '.$address; + if ($this->SMTPDebug) { + echo $this->Lang('invalid_address').': '.$address; + } return false; } $this->From = $address; $this->FromName = $name; if ($auto) { if (empty($this->ReplyTo)) { - $this->AddAnAddress('ReplyTo', $address, $name); + $this->AddAnAddress('Reply-To', $address, $name); } if (empty($this->Sender)) { $this->Sender = $address; @@ -544,6 +584,21 @@ class PHPMailer { */ public function Send() { try { + if(!$this->PreSend()) return false; + return $this->PostSend(); + } catch (phpmailerException $e) { + $this->SentMIMEMessage = ''; + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + return false; + } + } + + protected function PreSend() { + try { + $mailHeader = ""; if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL); } @@ -555,27 +610,58 @@ class PHPMailer { $this->error_count = 0; // reset errors $this->SetMessageType(); - $header = $this->CreateHeader(); - $body = $this->CreateBody(); - + //Refuse to send an empty message if (empty($this->Body)) { throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL); } - // digitally sign with DKIM if enabled - if ($this->DKIM_domain && $this->DKIM_private) { - $header_dkim = $this->DKIM_Add($header,$this->Subject,$body); - $header = str_replace("\r\n","\n",$header_dkim) . $header; + $this->MIMEHeader = $this->CreateHeader(); + $this->MIMEBody = $this->CreateBody(); + + // To capture the complete message when using mail(), create + // an extra header list which CreateHeader() doesn't fold in + if ($this->Mailer == 'mail') { + if (count($this->to) > 0) { + $mailHeader .= $this->AddrAppend("To", $this->to); + } else { + $mailHeader .= $this->HeaderLine("To", "undisclosed-recipients:;"); + } + $mailHeader .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader(trim($this->Subject)))); + // if(count($this->cc) > 0) { + // $mailHeader .= $this->AddrAppend("Cc", $this->cc); + // } } + // digitally sign with DKIM if enabled + if ($this->DKIM_domain && $this->DKIM_private) { + $header_dkim = $this->DKIM_Add($this->MIMEHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody); + $this->MIMEHeader = str_replace("\r\n", "\n", $header_dkim) . $this->MIMEHeader; + } + + $this->SentMIMEMessage = sprintf("%s%s\r\n\r\n%s",$this->MIMEHeader,$mailHeader,$this->MIMEBody); + return true; + + } catch (phpmailerException $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + return false; + } + } + + protected function PostSend() { + try { // Choose the mailer and send through it switch($this->Mailer) { case 'sendmail': - return $this->SendmailSend($header, $body); + return $this->SendmailSend($this->MIMEHeader, $this->MIMEBody); case 'smtp': - return $this->SmtpSend($header, $body); + return $this->SmtpSend($this->MIMEHeader, $this->MIMEBody); + case 'mail': + return $this->MailSend($this->MIMEHeader, $this->MIMEBody); default: - return $this->MailSend($header, $body); + return $this->MailSend($this->MIMEHeader, $this->MIMEBody); } } catch (phpmailerException $e) { @@ -583,7 +669,9 @@ class PHPMailer { if ($this->exceptions) { throw $e; } - echo $e->getMessage()."\n"; + if ($this->SMTPDebug) { + echo $e->getMessage()."\n"; + } return false; } } @@ -612,7 +700,7 @@ class PHPMailer { $result = pclose($mail); // implement call back function if it exists $isSent = ($result == 0) ? 1 : 0; - $this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body); + $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); if($result != 0) { throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } @@ -626,7 +714,7 @@ class PHPMailer { $result = pclose($mail); // implement call back function if it exists $isSent = ($result == 0) ? 1 : 0; - $this->doCallback($isSent,$this->to,$this->cc,$this->bcc,$this->Subject,$body); + $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body); if($result != 0) { throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } @@ -648,8 +736,12 @@ class PHPMailer { } $to = implode(', ', $toArr); - $params = sprintf("-oi -f %s", $this->Sender); - if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) { + if (empty($this->Sender)) { + $params = "-oi "; + } else { + $params = sprintf("-oi -f %s", $this->Sender); + } + if ($this->Sender != '' and !ini_get('safe_mode')) { $old_from = ini_get('sendmail_from'); ini_set('sendmail_from', $this->Sender); if ($this->SingleTo === true && count($toArr) > 1) { @@ -657,13 +749,13 @@ class PHPMailer { $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); // implement call back function if it exists $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body); + $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); } } else { $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); // implement call back function if it exists $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent,$to,$this->cc,$this->bcc,$this->Subject,$body); + $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body); } } else { if ($this->SingleTo === true && count($toArr) > 1) { @@ -671,13 +763,13 @@ class PHPMailer { $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); // implement call back function if it exists $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body); + $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); } } else { - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header); + $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); // implement call back function if it exists $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent,$to,$this->cc,$this->bcc,$this->Subject,$body); + $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body); } } if (isset($old_from)) { @@ -716,11 +808,11 @@ class PHPMailer { $bad_rcpt[] = $to[0]; // implement call back function if it exists $isSent = 0; - $this->doCallback($isSent,$to[0],'','',$this->Subject,$body); + $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); } else { // implement call back function if it exists $isSent = 1; - $this->doCallback($isSent,$to[0],'','',$this->Subject,$body); + $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); } } foreach($this->cc as $cc) { @@ -728,11 +820,11 @@ class PHPMailer { $bad_rcpt[] = $cc[0]; // implement call back function if it exists $isSent = 0; - $this->doCallback($isSent,'',$cc[0],'',$this->Subject,$body); + $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); } else { // implement call back function if it exists $isSent = 1; - $this->doCallback($isSent,'',$cc[0],'',$this->Subject,$body); + $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); } } foreach($this->bcc as $bcc) { @@ -740,11 +832,11 @@ class PHPMailer { $bad_rcpt[] = $bcc[0]; // implement call back function if it exists $isSent = 0; - $this->doCallback($isSent,'','',$bcc[0],$this->Subject,$body); + $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); } else { // implement call back function if it exists $isSent = 1; - $this->doCallback($isSent,'','',$bcc[0],$this->Subject,$body); + $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); } } @@ -822,7 +914,9 @@ class PHPMailer { } } catch (phpmailerException $e) { $this->smtp->Reset(); - throw $e; + if ($this->exceptions) { + throw $e; + } } return true; } @@ -942,7 +1036,7 @@ class PHPMailer { $line = explode($this->LE, $message); $message = ''; - for ($i=0 ;$i < count($line); $i++) { + for ($i = 0 ;$i < count($line); $i++) { $line_part = explode(' ', $line[$i]); $buf = ''; for ($e = 0; $emessage_type) { case 'alt': - case 'alt_attachments': + case 'alt_inline': + case 'alt_attach': + case 'alt_inline_attach': $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); break; default: @@ -1077,12 +1173,13 @@ class PHPMailer { $uniq_id = md5(uniqid(time())); $this->boundary[1] = 'b1_' . $uniq_id; $this->boundary[2] = 'b2_' . $uniq_id; + $this->boundary[3] = 'b3_' . $uniq_id; $result .= $this->HeaderLine('Date', self::RFCDate()); if($this->Sender == '') { - $result .= $this->HeaderLine('Return-Path', trim($this->SecureHeader($this->From))); // Moodle modification + $result .= $this->HeaderLine('Return-Path', trim($this->From)); } else { - $result .= $this->HeaderLine('Return-Path', trim($this->SecureHeader($this->Sender))); // Moodle modification + $result .= $this->HeaderLine('Return-Path', trim($this->Sender)); } // To be created automatically by mail() @@ -1098,7 +1195,7 @@ class PHPMailer { $result .= $this->HeaderLine('To', 'undisclosed-recipients:;'); } } - } + } $from = array(); $from[0][0] = trim($this->From); @@ -1116,7 +1213,7 @@ class PHPMailer { } if(count($this->ReplyTo) > 0) { - $result .= $this->AddrAppend('Reply-to', $this->ReplyTo); + $result .= $this->AddrAppend('Reply-To', $this->ReplyTo); } // mail() sets the subject itself @@ -1125,12 +1222,16 @@ class PHPMailer { } if($this->MessageID != '') { - $result .= $this->HeaderLine('Message-ID',$this->MessageID); + $result .= $this->HeaderLine('Message-ID', $this->MessageID); } else { $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); } $result .= $this->HeaderLine('X-Priority', $this->Priority); - $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (phpmailer.worxware.com)'); + if($this->XMailer) { + $result .= $this->HeaderLine('X-Mailer', $this->XMailer); + } else { + $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (http://code.google.com/a/apache-extras.org/p/phpmailer/)'); + } if($this->ConfirmReadingTo != '') { $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); @@ -1158,18 +1259,21 @@ class PHPMailer { switch($this->message_type) { case 'plain': $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); - $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet); + $result .= $this->TextLine('Content-Type: '.$this->ContentType.'; charset="'.$this->CharSet.'"'); break; - case 'attachments': - case 'alt_attachments': - if($this->InlineImageExists()){ - $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE); - } else { - $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); - } + case 'inline': + $result .= $this->HeaderLine('Content-Type', 'multipart/related;'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + case 'attach': + case 'inline_attach': + case 'alt_attach': + case 'alt_inline_attach': + $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); break; case 'alt': + case 'alt_inline': $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); break; @@ -1182,6 +1286,16 @@ class PHPMailer { return $result; } + /** + * Returns the MIME message (headers and body). Only really valid post PreSend(). + * @access public + * @return string + */ + public function GetSentMIMEMessage() { + return $this->SentMIMEMessage; + } + + /** * Assembles the message body. Returns an empty string on failure. * @access public @@ -1197,6 +1311,33 @@ class PHPMailer { $this->SetWordWrap(); switch($this->message_type) { + case 'plain': + $body .= $this->EncodeString($this->Body, $this->Encoding); + break; + case 'inline': + $body .= $this->GetBoundary($this->boundary[1], '', '', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("inline", $this->boundary[1]); + break; + case 'attach': + $body .= $this->GetBoundary($this->boundary[1], '', '', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("attachment", $this->boundary[1]); + break; + case 'inline_attach': + $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', '', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("inline", $this->boundary[2]); + $body .= $this->LE; + $body .= $this->AttachAll("attachment", $this->boundary[1]); + break; case 'alt': $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); $body .= $this->EncodeString($this->AltBody, $this->Encoding); @@ -1206,26 +1347,56 @@ class PHPMailer { $body .= $this->LE.$this->LE; $body .= $this->EndBoundary($this->boundary[1]); break; - case 'plain': - $body .= $this->EncodeString($this->Body, $this->Encoding); - break; - case 'attachments': - $body .= $this->GetBoundary($this->boundary[1], '', '', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE; - $body .= $this->AttachAll(); - break; - case 'alt_attachments': - $body .= sprintf("--%s%s", $this->boundary[1], $this->LE); - $body .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE); - $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body + case 'alt_inline': + $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); $body .= $this->EncodeString($this->AltBody, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body + $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("inline", $this->boundary[2]); + $body .= $this->LE; + $body .= $this->EndBoundary($this->boundary[1]); + break; + case 'alt_attach': + $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); + $body .= $this->EncodeString($this->AltBody, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; $body .= $this->EndBoundary($this->boundary[2]); - $body .= $this->AttachAll(); + $body .= $this->LE; + $body .= $this->AttachAll("attachment", $this->boundary[1]); + break; + case 'alt_inline_attach': + $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); + $body .= $this->EncodeString($this->AltBody, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->TextLine("--" . $this->boundary[2]); + $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[3], '', 'text/html', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("inline", $this->boundary[3]); + $body .= $this->LE; + $body .= $this->EndBoundary($this->boundary[2]); + $body .= $this->LE; + $body .= $this->AttachAll("attachment", $this->boundary[1]); break; } @@ -1238,8 +1409,8 @@ class PHPMailer { $signed = tempnam("", "signed"); if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) { @unlink($file); - @unlink($signed); $body = file_get_contents($signed); + @unlink($signed); } else { @unlink($file); @unlink($signed); @@ -1258,9 +1429,10 @@ class PHPMailer { /** * Returns the start of a message boundary. - * @access private + * @access protected + * @return string */ - private function GetBoundary($boundary, $charSet, $contentType, $encoding) { + protected function GetBoundary($boundary, $charSet, $contentType, $encoding) { $result = ''; if($charSet == '') { $charSet = $this->CharSet; @@ -1272,7 +1444,7 @@ class PHPMailer { $encoding = $this->Encoding; } $result .= $this->TextLine('--' . $boundary); - $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet); + $result .= sprintf("Content-Type: %s; charset=\"%s\"", $contentType, $charSet); $result .= $this->LE; $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding); $result .= $this->LE; @@ -1282,31 +1454,25 @@ class PHPMailer { /** * Returns the end of a message boundary. - * @access private + * @access protected + * @return string */ - private function EndBoundary($boundary) { + protected function EndBoundary($boundary) { return $this->LE . '--' . $boundary . '--' . $this->LE; } /** * Sets the message type. - * @access private + * @access protected * @return void */ - private function SetMessageType() { - if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) { - $this->message_type = 'plain'; - } else { - if(count($this->attachment) > 0) { - $this->message_type = 'attachments'; - } - if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) { - $this->message_type = 'alt'; - } - if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) { - $this->message_type = 'alt_attachments'; - } - } + protected function SetMessageType() { + $this->message_type = array(); + if($this->AlternativeExists()) $this->message_type[] = "alt"; + if($this->InlineImageExists()) $this->message_type[] = "inline"; + if($this->AttachmentExists()) $this->message_type[] = "attach"; + $this->message_type = implode("_", $this->message_type); + if($this->message_type == "") $this->message_type = "plain"; } /** @@ -1367,7 +1533,9 @@ class PHPMailer { if ($this->exceptions) { throw $e; } - echo $e->getMessage()."\n"; + if ($this->SMTPDebug) { + echo $e->getMessage()."\n"; + } if ( $e->getCode() == self::STOP_CRITICAL ) { return false; } @@ -1386,10 +1554,10 @@ class PHPMailer { /** * Attaches all fs, string, and binary attachments to the message. * Returns an empty string on failure. - * @access private + * @access protected * @return string */ - private function AttachAll() { + protected function AttachAll($disposition_type, $boundary) { // Return text of body $mime = array(); $cidUniq = array(); @@ -1397,54 +1565,58 @@ class PHPMailer { // Add all attachments foreach ($this->attachment as $attachment) { - // Check for string attachment - $bString = $attachment[5]; - if ($bString) { - $string = $attachment[0]; - } else { - $path = $attachment[0]; - } - - if (in_array($attachment[0], $incl)) { continue; } - $filename = $attachment[1]; - $name = $attachment[2]; - $encoding = $attachment[3]; - $type = $attachment[4]; - $disposition = $attachment[6]; - $cid = $attachment[7]; - $incl[] = $attachment[0]; - if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; } - $cidUniq[$cid] = true; - - $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE); - $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE); - $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); - - if($disposition == 'inline') { - $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); - } - - $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); - - // Encode as string attachment - if($bString) { - $mime[] = $this->EncodeString($string, $encoding); - if($this->IsError()) { - return ''; + // CHECK IF IT IS A VALID DISPOSITION_FILTER + if($attachment[6] == $disposition_type) { + // Check for string attachment + $bString = $attachment[5]; + if ($bString) { + $string = $attachment[0]; + } else { + $path = $attachment[0]; } - $mime[] = $this->LE.$this->LE; - } else { - $mime[] = $this->EncodeFile($path, $encoding); - if($this->IsError()) { - return ''; + + $inclhash = md5(serialize($attachment)); + if (in_array($inclhash, $incl)) { continue; } + $incl[] = $inclhash; + $filename = $attachment[1]; + $name = $attachment[2]; + $encoding = $attachment[3]; + $type = $attachment[4]; + $disposition = $attachment[6]; + $cid = $attachment[7]; + if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; } + $cidUniq[$cid] = true; + + $mime[] = sprintf("--%s%s", $boundary, $this->LE); + $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE); + $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); + + if($disposition == 'inline') { + $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); + } + + $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); + + // Encode as string attachment + if($bString) { + $mime[] = $this->EncodeString($string, $encoding); + if($this->IsError()) { + return ''; + } + $mime[] = $this->LE.$this->LE; + } else { + $mime[] = $this->EncodeFile($path, $encoding); + if($this->IsError()) { + return ''; + } + $mime[] = $this->LE.$this->LE; } - $mime[] = $this->LE.$this->LE; } } - $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE); + $mime[] = sprintf("--%s--%s", $boundary, $this->LE); - return join('', $mime); + return implode("", $mime); } /** @@ -1453,10 +1625,10 @@ class PHPMailer { * @param string $path The full path to the file * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' * @see EncodeFile() - * @access private + * @access protected * @return string */ - private function EncodeFile($path, $encoding = 'base64') { + protected function EncodeFile($path, $encoding = 'base64') { try { if (!is_readable($path)) { throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE); @@ -1466,13 +1638,23 @@ class PHPMailer { return false; } } - if (PHP_VERSION < 6) { - $magic_quotes = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - } + $magic_quotes = get_magic_quotes_runtime(); + if ($magic_quotes) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime(0); + } else { + ini_set('magic_quotes_runtime', 0); + } + } $file_buffer = file_get_contents($path); $file_buffer = $this->EncodeString($file_buffer, $encoding); - if (PHP_VERSION < 6) { set_magic_quotes_runtime($magic_quotes); } + if ($magic_quotes) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime($magic_quotes); + } else { + ini_set('magic_quotes_runtime', $magic_quotes); + } + } return $file_buffer; } catch (Exception $e) { $this->SetError($e->getMessage()); @@ -1488,7 +1670,7 @@ class PHPMailer { * @access public * @return string */ - public function EncodeString ($str, $encoding = 'base64') { + public function EncodeString($str, $encoding = 'base64') { $encoded = ''; switch(strtolower($encoding)) { case 'base64': @@ -1637,7 +1819,7 @@ class PHPMailer { * @return string */ public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) { - $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); + $hex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); $lines = preg_split('/(?:\r\n|\r|\n)/', $input); $eol = "\r\n"; $escape = '='; @@ -1718,7 +1900,7 @@ class PHPMailer { * @access public * @return string */ - public function EncodeQ ($str, $position = 'text') { + public function EncodeQ($str, $position = 'text') { // There should not be any EOL in the string $encoded = preg_replace('/[\r\n]*/', '', $str); @@ -1733,7 +1915,7 @@ class PHPMailer { // Replace every high ascii, control =, ? and _ characters //TODO using /e (equivalent to eval()) is probably not a good idea $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e', - "'='.sprintf('%02X', ord('\\1'))", $encoded); + "'='.sprintf('%02X', ord(stripslashes('\\1')))", $encoded); break; } @@ -1807,6 +1989,20 @@ class PHPMailer { return true; } + public function AddStringEmbeddedImage($string, $cid, $filename = '', $encoding = 'base64', $type = 'application/octet-stream') { + // Append to $attachment array + $this->attachment[] = array( + 0 => $string, + 1 => $filename, + 2 => basename($filename), + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => 'inline', + 7 => $cid + ); + } + /** * Returns true if an inline attachment is present. * @access public @@ -1821,6 +2017,19 @@ class PHPMailer { return false; } + public function AttachmentExists() { + foreach($this->attachment as $attachment) { + if ($attachment[6] == 'attachment') { + return true; + } + } + return false; + } + + public function AlternativeExists() { + return strlen($this->AltBody)>0; + } + ///////////////////////////////////////////////// // CLASS METHODS, MESSAGE RESET ///////////////////////////////////////////////// @@ -1933,10 +2142,10 @@ class PHPMailer { /** * Returns the server hostname or 'localhost.localdomain' if unknown. - * @access private + * @access protected * @return string */ - private function ServerHostname() { + protected function ServerHostname() { if (!empty($this->Hostname)) { $result = $this->Hostname; } elseif (isset($_SERVER['SERVER_NAME'])) { @@ -1950,10 +2159,10 @@ class PHPMailer { /** * Returns a message in the appropriate language. - * @access private + * @access protected * @return string */ - private function Lang($key) { + protected function Lang($key) { if(count($this->language) < 1) { $this->SetLanguage('en'); // set the default language } @@ -1976,10 +2185,10 @@ class PHPMailer { /** * Changes every end of line from CR or LF to CRLF. - * @access private + * @access public * @return string */ - private function FixEOL($str) { + public function FixEOL($str) { $str = str_replace("\r\n", "\n", $str); $str = str_replace("\r", "\n", $str); $str = str_replace("\n", $this->LE, $str); @@ -2001,34 +2210,37 @@ class PHPMailer { * @return $message */ public function MsgHTML($message, $basedir = '') { - preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images); + preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images); if(isset($images[2])) { foreach($images[2] as $i => $url) { // do not change urls for absolute images (thanks to corvuscorax) - if (!preg_match('#^[A-z]+://#',$url)) { + if (!preg_match('#^[A-z]+://#', $url)) { $filename = basename($url); $directory = dirname($url); - ($directory == '.')?$directory='':''; + ($directory == '.') ? $directory='': ''; $cid = 'cid:' . md5($filename); $ext = pathinfo($filename, PATHINFO_EXTENSION); $mimeType = self::_mime_types($ext); - if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; } - if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; } - if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) { - $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message); + if ( strlen($basedir) > 1 && substr($basedir, -1) != '/') { $basedir .= '/'; } + if ( strlen($directory) > 1 && substr($directory, -1) != '/') { $directory .= '/'; } + if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64', $mimeType) ) { + $message = preg_replace("/".$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui", $images[1][$i]."=\"".$cid."\"", $message); } } } } $this->IsHTML(true); $this->Body = $message; - $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message))); - if (!empty($textMsg) && empty($this->AltBody)) { - $this->AltBody = html_entity_decode($textMsg); - } + if (empty($this->AltBody)) { + $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s', '', $message))); + if (!empty($textMsg)) { + $this->AltBody = html_entity_decode($textMsg, ENT_QUOTES, $this->CharSet); + } + } if (empty($this->AltBody)) { $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; } + return $message; } /** @@ -2192,14 +2404,14 @@ class PHPMailer { * @param string $key_pass Password for private key */ public function DKIM_QP($txt) { - $tmp=""; - $line=""; - for ($i=0;$iDKIM_private); - if ($this->DKIM_passphrase!='') { - $privKey = openssl_pkey_get_private($privKeyStr,$this->DKIM_passphrase); + if ($this->DKIM_passphrase != '') { + $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); } else { $privKey = $privKeyStr; } @@ -2230,15 +2442,15 @@ class PHPMailer { * @param string $s Header */ public function DKIM_HeaderC($s) { - $s=preg_replace("/\r\n\s+/"," ",$s); - $lines=explode("\r\n",$s); - foreach ($lines as $key=>$line) { - list($heading,$value)=explode(":",$line,2); - $heading=strtolower($heading); - $value=preg_replace("/\s+/"," ",$value) ; // Compress useless spaces - $lines[$key]=$heading.":".trim($value) ; // Don't forget to remove WSP around the value + $s = preg_replace("/\r\n\s+/", " ", $s); + $lines = explode("\r\n", $s); + foreach ($lines as $key => $line) { + list($heading, $value) = explode(":", $line, 2); + $heading = strtolower($heading); + $value = preg_replace("/\s+/", " ", $value) ; // Compress useless spaces + $lines[$key] = $heading.":".trim($value) ; // Don't forget to remove WSP around the value } - $s=implode("\r\n",$lines); + $s = implode("\r\n", $lines); return $s; } @@ -2251,11 +2463,11 @@ class PHPMailer { public function DKIM_BodyC($body) { if ($body == '') return "\r\n"; // stabilize line endings - $body=str_replace("\r\n","\n",$body); - $body=str_replace("\n","\r\n",$body); + $body = str_replace("\r\n", "\n", $body); + $body = str_replace("\n", "\r\n", $body); // END stabilize line endings - while (substr($body,strlen($body)-4,4) == "\r\n\r\n") { - $body=substr($body,0,strlen($body)-2); + while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { + $body = substr($body, 0, strlen($body) - 2); } return $body; } @@ -2268,23 +2480,23 @@ class PHPMailer { * @param string $subject Subject * @param string $body Body */ - public function DKIM_Add($headers_line,$subject,$body) { + public function DKIM_Add($headers_line, $subject, $body) { $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body $DKIMquery = 'dns/txt'; // Query method $DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) $subject_header = "Subject: $subject"; - $headers = explode("\r\n",$headers_line); + $headers = explode($this->LE, $headers_line); foreach($headers as $header) { - if (strpos($header,'From:') === 0) { - $from_header=$header; - } elseif (strpos($header,'To:') === 0) { - $to_header=$header; + if (strpos($header, 'From:') === 0) { + $from_header = $header; + } elseif (strpos($header, 'To:') === 0) { + $to_header = $header; } } - $from = str_replace('|','=7C',$this->DKIM_QP($from_header)); - $to = str_replace('|','=7C',$this->DKIM_QP($to_header)); - $subject = str_replace('|','=7C',$this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable + $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); + $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); + $subject = str_replace('|', '=7C', $this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable $body = $this->DKIM_BodyC($body); $DKIMlen = strlen($body) ; // Length of body $DKIMb64 = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body @@ -2303,10 +2515,10 @@ class PHPMailer { return "X-PHPMAILER-DKIM: phpmailer.worxware.com\r\n".$dkimhdrs.$signed."\r\n"; } - protected function doCallback($isSent,$to,$cc,$bcc,$subject,$body) { + protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body) { if (!empty($this->action_function) && function_exists($this->action_function)) { - $params = array($isSent,$to,$cc,$bcc,$subject,$body); - call_user_func_array($this->action_function,$params); + $params = array($isSent, $to, $cc, $bcc, $subject, $body); + call_user_func_array($this->action_function, $params); } } } diff --git a/lib/phpmailer/class.smtp.php b/lib/phpmailer/class.smtp.php index 6d3f24eff85..6977bffad14 100644 --- a/lib/phpmailer/class.smtp.php +++ b/lib/phpmailer/class.smtp.php @@ -2,15 +2,15 @@ /*~ class.smtp.php .---------------------------------------------------------------------------. | Software: PHPMailer - PHP email class | -| Version: 5.1 | -| Contact: via sourceforge.net support pages (also www.codeworxtech.com) | -| Info: http://phpmailer.sourceforge.net | -| Support: http://sourceforge.net/projects/phpmailer/ | +| Version: 5.2.1 | +| Site: https://code.google.com/a/apache-extras.org/p/phpmailer/ | | ------------------------------------------------------------------------- | -| Admin: Andy Prevost (project admininistrator) | +| Admin: Jim Jagielski (project admininistrator) | | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | | : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | +| : Jim Jagielski (jimjag) jimjag@gmail.com | | Founder: Brent R. Matzelle (original founder) | +| Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. | | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. | | Copyright (c) 2001-2003, Brent R. Matzelle | | ------------------------------------------------------------------------- | @@ -19,11 +19,6 @@ | This program is distributed in the hope that it will be useful - WITHOUT | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | | FITNESS FOR A PARTICULAR PURPOSE. | -| ------------------------------------------------------------------------- | -| We offer a number of paid services (www.codeworxtech.com): | -| - Web Hosting on highly optimized fast and secure servers | -| - Technology Consulting | -| - Oursourcing (highly qualified programmers and graphic designers) | '---------------------------------------------------------------------------' */ @@ -34,8 +29,10 @@ * @author Andy Prevost * @author Marcus Bointon * @copyright 2004 - 2008 Andy Prevost + * @author Jim Jagielski + * @copyright 2010 - 2012 Jim Jagielski * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) - * @version $Id$ + * @version $Id: class.smtp.php 450 2010-06-23 16:46:33Z coolbru $ */ /** @@ -71,6 +68,12 @@ class SMTP { */ public $do_verp = false; + /** + * Sets the SMTP PHPMailer Version number + * @var string + */ + public $Version = '5.2.1'; + ///////////////////////////////////////////////// // PROPERTIES, PRIVATE AND PROTECTED ///////////////////////////////////////////////// @@ -794,7 +797,8 @@ class SMTP { */ private function get_lines() { $data = ""; - while($str = @fgets($this->smtp_conn,515)) { + while(!feof($this->smtp_conn)) { + $str = @fgets($this->smtp_conn,515); if($this->do_debug >= 4) { echo "SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '
'; echo "SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '
'; @@ -811,4 +815,4 @@ class SMTP { } -?> \ No newline at end of file +?> diff --git a/lib/phpmailer/moodle_phpmailer.php b/lib/phpmailer/moodle_phpmailer.php index debfb6b7676..03d88eeb46c 100644 --- a/lib/phpmailer/moodle_phpmailer.php +++ b/lib/phpmailer/moodle_phpmailer.php @@ -1,31 +1,30 @@ . /** - * Moodle - Modular Object-Oriented Dynamic Learning Environment - * http://moodle.org - * Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * Customised version of phpmailer for Moodle * * @package moodle * @subpackage lib * @author Dan Poltawski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * - * Customised version of phpmailer for Moodle */ +defined('MOODLE_INTERNAL') || die(); + // PLEASE NOTE: we use the phpmailer class _unmodified_ // through the joys of OO. Distros are free to use their stock // version of this file. @@ -73,7 +72,7 @@ class moodle_phpmailer extends PHPMailer { * Use internal moodles own textlib to encode mimeheaders. * Fall back to phpmailers inbuilt functions if not */ - public function EncodeHeader ($str, $position = 'text') { + public function EncodeHeader($str, $position = 'text') { $encoded = textlib::encode_mimeheader($str, $this->CharSet); if ($encoded !== false) { $encoded = str_replace("\n", $this->LE, $encoded); diff --git a/lib/pluginlib.php b/lib/pluginlib.php index 61d2a6ea609..b1139ebe8e0 100644 --- a/lib/pluginlib.php +++ b/lib/pluginlib.php @@ -1085,7 +1085,7 @@ class available_update_checker { return true; } - if ($now - $recent > HOURSECS) { + if ($now - $recent > 24 * HOURSECS) { return false; } diff --git a/lib/questionlib.php b/lib/questionlib.php index 7dc749afab8..7298d75c34e 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -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( - 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; } } } diff --git a/lib/sessionlib.php b/lib/sessionlib.php index 4729b6bea81..9e5c29f7384 100644 --- a/lib/sessionlib.php +++ b/lib/sessionlib.php @@ -605,7 +605,11 @@ class database_session extends session_stub { $ignoretimeout = false; if (!empty($record->userid)) { // skips not logged in if ($user = $this->database->get_record('user', array('id'=>$record->userid))) { - if (!isguestuser($user)) { + + // Refresh session if logged as a guest + if (isguestuser($user)) { + $ignoretimeout = true; + } else { $authsequence = get_enabled_auth_plugins(); // auths, in sequence foreach($authsequence as $authname) { $authplugin = get_auth_plugin($authname); @@ -925,9 +929,12 @@ function session_gc() { } $rs->close(); + // Extending the timeout period for guest sessions as they are renewed. $purgebefore = time() - $maxlifetime; + $purgebeforeguests = time() - ($maxlifetime * 5); + // delete expired sessions for guest user account - $DB->delete_records_select('sessions', 'userid = ? AND timemodified < ?', array($CFG->siteguest, $purgebefore)); + $DB->delete_records_select('sessions', 'userid = ? AND timemodified < ?', array($CFG->siteguest, $purgebeforeguests)); // delete expired sessions for userid = 0 (not logged in) $DB->delete_records_select('sessions', 'userid = 0 AND timemodified < ?', array($purgebefore)); } catch (dml_exception $ex) { diff --git a/lib/tcpdf/2dbarcodes.php b/lib/tcpdf/2dbarcodes.php index e04b3073ce1..6490dfa7eaf 100644 --- a/lib/tcpdf/2dbarcodes.php +++ b/lib/tcpdf/2dbarcodes.php @@ -1,9 +1,9 @@ barcode_array['bcode'][$r][$c] == 1) { // draw a single barcode cell if ($imagick) { - $bar->rectangle($x, $y, ($x + $w), ($y + $h)); + $bar->rectangle($x, $y, ($x + $w - 1), ($y + $h - 1)); } else { - imagefilledrectangle($png, $x, $y, ($x + $w), ($y + $h), $fgcol); + imagefilledrectangle($png, $x, $y, ($x + $w - 1), ($y + $h - 1), $fgcol); } } $x += $w; diff --git a/lib/tcpdf/CHANGELOG.TXT b/lib/tcpdf/CHANGELOG.TXT index 23a14f9991d..3b87c8c356d 100644 --- a/lib/tcpdf/CHANGELOG.TXT +++ b/lib/tcpdf/CHANGELOG.TXT @@ -1,3 +1,98 @@ +5.9.181 (2012-08-31) + - composer.json file was added. + - Bug item #3563369 "Cached images are not unlinked some time" was fixed. + +5.9.180 (2012-08-22) + - Bug item #3560493 "Problems with nested cells in HTML" was fixed. + +5.9.179 (2012-08-04) + - SVG 'use' tag was fixed for 'circle' and 'ellipse' shift problem. + - Alpha status is now correctly stored and restored by getGraphicVars() and SetGraphicVars() methods. + +5.9.178 (2012-08-02) + - SVG 'use' tag was fixed for 'circle' and 'ellipse'. + +5.9.177 (2012-08-02) + - An additional control on annotations was fixed. + +5.9.176 (2012-07-25) + - A bug related to stroke width was fixed. + - A problem related to font spacing in HTML was fixed. + +5.9.175 (2012-07-25) + - The problem of missing letter on hyphen break was fixed. + +5.9.174 (2012-07-25) + - The problem of wrong filename when downloading PDF from an Android device was fixed. + - The method setHeaderData() was extended to set text and line color for header (see example n. 1). + - The method setFooterData() was added to set text and line color for footer (see example n. 1). + - The methods setTextShadow() and getTextShadow() were added to set text shadows (see example n. 1). + - The GetCharWidth() method was fixed for negative character spacing. + - A 'none' border mode is now correctly recognized. + - Break on hyphen problem was fixed. + +5.9.173 (2012-07-23) + - Some additional control wher added on barcode methods. + - The option CURLOPT_FOLLOWLOCATION on Image method is now disabled if PHP safe_mode is on or open_basedir is set. + - Method Bookmark() was extended to include X parameter. + - Method setDestination() was extended to include X parameter. + - A problem with Thai language was fixed. + +5.9.172 (2012-07-02) + - A PNG color profile issue was fixed. + +5.9.171 (2012-07-01) + - Some SVG rendering problems were fixed. + +5.9.170 (2012-06-27) + - Bug #3538227 "Numerous errors inserting shared images" was fixed. + +5.9.169 (2012-06-25) + - Some SVG rendering problems were fixed. + +5.9.168 (2012-06-22) + - Thai language rendering was fixed. + +5.9.167 (2012-06-22) + - Thai language rendering was fixed and improved. + - Method isCharDefined() was improved. + - Protected method replaceChar() was added. + - Font "kerning" word was corrected to "tracking". + +5.9.166 (2012-06-21) + - Array to string conversion on file_id creation was fixed. + - Thai language rendering was fixed (thanks to Atsawin Chaowanakritsanakul). + +5.9.165 (2012-06-07) + - Some HTML form related bugs were fixed. + +5.9.164 (2012-06-06) + - A bug introduced on the latest release was fixed. + +5.9.163 (2012-06-05) + - Method getGDgamma() was changed. + - Rendering performances of PNG images with alpha channel were improved. + +5.9.162 (2012-05-11) + - A bug related to long text on TD cells was fixed. + +5.9.161 (2012-05-09) + - A bug on XREF table was fixed (Bug ID: 3525051). + - Deprecated Imagick:clone was replaced. + - Method objclone() was fixed for PHP4. + +5.9.160 (2012-05-03) + - A bug on tcpdf_parser.php was fixed. + +5.9.159 (2012-04-30) + - Barcode classes were updated to fix PNG export Bug (ID: 3522291). + +5.9.158 (2012-04-22) + - Some SVG-related bugs were fixed. + +5.9.157 (2012-04-16) + - Some SVG-related bugs were fixed. + 5.9.156 (2012-04-10) - Bug item #3515885 "TOC and booklet: left and right page exchanged". - SetAutoPageBreak(false) now works also in multicolumn mode. @@ -567,7 +662,7 @@ - The problem of blank page for nobr table higher than a single page was fixed. 5.9.000 (2010-10-06) - - Support for text stretching and spacing (kerning) was added, see example n. 63 and methods setFontStretching(), getFontStretching(), setFontSpacing(), getFontSpacing(). + - Support for text stretching and spacing (tracking) was added, see example n. 63 and methods setFontStretching(), getFontStretching(), setFontSpacing(), getFontSpacing(). - Support for CSS properties 'font-stretch' and 'letter-spacing' was added (see example n. 63). - The cMargin state was replaced by cell_padding array that can be set/get using setCellPadding() and getCellPadding() methods. - Methods getCellPaddings() and setCellPaddings() were added to fine tune cell paddings (see example n. 5). diff --git a/lib/tcpdf/README.TXT b/lib/tcpdf/README.TXT index 83520bb1272..1d0fc015deb 100644 --- a/lib/tcpdf/README.TXT +++ b/lib/tcpdf/README.TXT @@ -8,8 +8,8 @@ http://sourceforge.net/donate/index.php?group_id=128076 ------------------------------------------------------------ Name: TCPDF -Version: 5.9.156 -Release date: 2012-04-10 +Version: 5.9.181 +Release date: 2012-08-31 Author: Nicola Asuni Copyright (c) 2002-2012: @@ -47,7 +47,7 @@ Main Features: * no-write page regions; * bookmarks, named destinations and table of content; * text hyphenation; - * text stretching and spacing (tracking/kerning); + * text stretching and spacing (tracking); * automatic page break, line break and text alignments including justification; * automatic page numbering and page groups; * move and delete pages; diff --git a/lib/tcpdf/barcodes.php b/lib/tcpdf/barcodes.php index 4027aa7109a..c6c0172e4a3 100644 --- a/lib/tcpdf/barcodes.php +++ b/lib/tcpdf/barcodes.php @@ -1,9 +1,9 @@ * @package com.tecnick.tcpdf - * @version 1.0.023 + * @version 1.0.024 * @author Nicola Asuni */ class TCPDFBarcode { @@ -201,9 +201,9 @@ class TCPDFBarcode { $y = round(($v['p'] * $h / $this->barcode_array['maxh']), 3); // draw a vertical bar if ($imagick) { - $bar->rectangle($x, $y, ($x + $bw), ($y + $bh)); + $bar->rectangle($x, $y, ($x + $bw - 1), ($y + $bh - 1)); } else { - imagefilledrectangle($png, $x, $y, ($x + $bw), ($y + $bh), $fgcol); + imagefilledrectangle($png, $x, $y, ($x + $bw - 1), ($y + $bh - 1), $fgcol); } } $x += $bw; diff --git a/lib/tcpdf/composer.json b/lib/tcpdf/composer.json new file mode 100644 index 00000000000..4cbb538103d --- /dev/null +++ b/lib/tcpdf/composer.json @@ -0,0 +1,38 @@ +{ + "name": "tcpdf/tcpdf", + "version": "5.9.181", + "homepage": "http://www.tcpdf.org/", + "type": "library", + "description": "TCPDF is a PHP class for generating PDF files on-the-fly without requiring external extensions.", + "keywords": ["pdf"], + "license": "LGPLv3", + "authors": [ + { + "name": "Nicola Asuni", + "email": "info@tecnick.com", + "homepage": "http://nicolaasuni.tecnick.com" + } + ], + "require": { + "php": ">5.2" + }, + "autoload": { + "classmap": [ + "fonts", + "config/lang", + "config", + "2dbarcodes.php", + "barcodes.php", + "datamatrix.php", + "encodings_maps.php", + "htmlcolors.php", + "pdf417.php", + "qrcode.php", + "spotcolors.php", + "tcpdf.php", + "tcpdf_filters.php", + "tcpdf_parser.php", + "unicode_data.php" + ] + } +} diff --git a/lib/tcpdf/qrcode.php b/lib/tcpdf/qrcode.php index 1fab6b58a77..93b05cbe0a2 100644 --- a/lib/tcpdf/qrcode.php +++ b/lib/tcpdf/qrcode.php @@ -1,9 +1,9 @@ dataStr) > 0) { - if ($this->dataStr == '') { - return 0; - } $mode = $this->identifyMode(0); switch ($mode) { case QR_MODE_NM: { @@ -1476,6 +1474,7 @@ class QRcode { } $this->dataStr = substr($this->dataStr, $length); } + return 0; } /** @@ -2028,7 +2027,7 @@ class QRcode { if ($ver > $this->version) { $this->version = $ver; } - for (;;) { + while (true) { $cbs = $this->createBitStream($items); $items = $cbs[0]; $bits = $cbs[1]; @@ -2315,17 +2314,18 @@ class QRcode { /** * Return a version number that satisfies the input code length. - * @param $size (int) input code length (byte) + * @param $size (int) input code length (bytes) * @param $level (int) error correction level * @return int version number */ protected function getMinimumVersion($size, $level) { - for ($i=1; $i <= QRSPEC_VERSION_MAX; ++$i) { - $words = $this->capacity[$i][QRCAP_WORDS] - $this->capacity[$i][QRCAP_EC][$level]; + for ($i = 1; $i <= QRSPEC_VERSION_MAX; ++$i) { + $words = ($this->capacity[$i][QRCAP_WORDS] - $this->capacity[$i][QRCAP_EC][$level]); if ($words >= $size) { return $i; } } + // the size of input data is greater than QR capacity, try to lover the error correction mode return -1; } diff --git a/lib/tcpdf/readme_moodle.txt b/lib/tcpdf/readme_moodle.txt index 1b6d92480dd..e953af35e22 100644 --- a/lib/tcpdf/readme_moodle.txt +++ b/lib/tcpdf/readme_moodle.txt @@ -1,5 +1,5 @@ -Description of TCPDF library import 5.9.156 -=================================== +Description of TCPDF library import 5.9.181 +=========================================== * delete cache/ doc/ examples/ config/tcpdf_config_alt.php config/lang/ images/ * remove all fonts but the core ones (courier.php, helveticabi.php, helveticab.php, helveticai.php, helvetica.php, symbol.php, timesbi.php, diff --git a/lib/tcpdf/tcpdf.php b/lib/tcpdf/tcpdf.php index 7c18096a52a..c5109bf4a68 100644 --- a/lib/tcpdf/tcpdf.php +++ b/lib/tcpdf/tcpdf.php @@ -1,9 +1,9 @@ no-write page regions; *
  • bookmarks, named destinations and table of content;
  • *
  • text hyphenation;
  • - *
  • text stretching and spacing (tracking/kerning);
  • + *
  • text stretching and spacing (tracking);
  • *
  • automatic page break, line break and text alignments including justification;
  • *
  • automatic page numbering and page groups;
  • *
  • move and delete pages;
  • @@ -137,7 +138,7 @@ * Tools to encode your unicode fonts are on fonts/utils directory.

    * @package com.tecnick.tcpdf * @author Nicola Asuni - * @version 5.9.156 + * @version 5.9.181 */ // Main configuration file. Define the K_TCPDF_EXTERNAL_CONFIG constant to skip this file. @@ -149,7 +150,7 @@ require_once(dirname(__FILE__).'/config/tcpdf_config.php'); * TCPDF project (http://www.tcpdf.org) has been originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.
    * @package com.tecnick.tcpdf * @brief PHP class for generating PDF documents without requiring external extensions. - * @version 5.9.156 + * @version 5.9.181 * @author Nicola Asuni - info@tecnick.com */ class TCPDF { @@ -160,7 +161,7 @@ class TCPDF { * Current TCPDF version. * @private */ - private $tcpdf_version = '5.9.156'; + private $tcpdf_version = '5.9.181'; // Protected properties @@ -180,7 +181,13 @@ class TCPDF { * Array of object offsets. * @protected */ - protected $offsets; + protected $offsets = array(); + + /** + * Array of object IDs for each page. + * @protected + */ + protected $pageobjects = array(); /** * Buffer holding in-memory PDF. @@ -352,6 +359,12 @@ class TCPDF { */ protected $images = array(); + /** + * Array of cached files. + * @protected + */ + protected $cached_files = array(); + /** * Array of Annotations in pages. * @protected @@ -705,6 +718,41 @@ class TCPDF { */ protected $header_string = ''; + /** + * Color for header text (RGB array). + * @since 5.9.174 (2012-07-25) + * @protected + */ + protected $header_text_color = array(0,0,0); + + /** + * Color for header line (RGB array). + * @since 5.9.174 (2012-07-25) + * @protected + */ + protected $header_line_color = array(0,0,0); + + /** + * Color for footer text (RGB array). + * @since 5.9.174 (2012-07-25) + * @protected + */ + protected $footer_text_color = array(0,0,0); + + /** + * Color for footer line (RGB array). + * @since 5.9.174 (2012-07-25) + * @protected + */ + protected $footer_line_color = array(0,0,0); + + /** + * Text shadow data array. + * @since 5.9.174 (2012-07-25) + * @protected + */ + protected $txtshadow = array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal'); + /** * Default number of columns for html table. * @protected @@ -1590,7 +1638,7 @@ class TCPDF { protected $font_stretching = 100; /** - * Increases or decreases the space between characters in a text by the specified amount (tracking/kerning). + * Increases or decreases the space between characters in a text by the specified amount (tracking). * @protected * @since 5.9.000 (2010-09-29) */ @@ -1873,6 +1921,13 @@ class TCPDF { */ protected $tcpdflink = true; + /** + * Cache array for computed GD gamma values. + * @protected + * @since 5.9.1632 (2012-06-05) + */ + protected $gdgammacache = array(); + //------------------------------------------------------------ // METHODS //------------------------------------------------------------ @@ -2006,6 +2061,7 @@ class TCPDF { $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0); $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255); $this->extgstates = array(); + $this->setTextShadow(); // user's rights $this->sign = false; $this->ur['enabled'] = false; @@ -2037,7 +2093,8 @@ class TCPDF { } $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); // set file ID for trailer - $this->file_id = md5($this->getRandomSeed('TCPDF'.$orientation.$unit.$format.$encoding)); + $serformat = (is_array($format) ? serialize($format) : $format); + $this->file_id = md5($this->getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding)); // set document creation and modification timestamp $this->doc_creation_timestamp = time(); $this->doc_modification_timestamp = $this->doc_creation_timestamp; @@ -3059,8 +3116,8 @@ class TCPDF { // swap X and Y coordinates (change page orientation) $this->swapPageBoxCoordinates($this->page); } - $this->w = $this->wPt / $this->k; - $this->h = $this->hPt / $this->k; + $this->w = ($this->wPt / $this->k); + $this->h = ($this->hPt / $this->k); if ($this->empty_string($autopagebreak)) { if (isset($this->AutoPageBreak)) { $autopagebreak = $this->AutoPageBreak; @@ -4083,13 +4140,28 @@ class TCPDF { * @param $lw (string) header image logo width in mm * @param $ht (string) string to print as title on document header * @param $hs (string) string to print on document header + * @param $tc (array) RGB array color for text. + * @param $lc (array) RGB array color for line. * @public */ - public function setHeaderData($ln='', $lw=0, $ht='', $hs='') { + public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) { $this->header_logo = $ln; $this->header_logo_width = $lw; $this->header_title = $ht; $this->header_string = $hs; + $this->header_text_color = $tc; + $this->header_line_color = $lc; + } + + /** + * Set footer data. + * @param $tc (array) RGB array color for text. + * @param $lc (array) RGB array color for line. + * @public + */ + public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) { + $this->footer_text_color = $tc; + $this->footer_line_color = $lc; } /** @@ -4105,6 +4177,8 @@ class TCPDF { $ret['logo_width'] = $this->header_logo_width; $ret['title'] = $this->header_title; $ret['string'] = $this->header_string; + $ret['text_color'] = $this->header_text_color; + $ret['line_color'] = $this->header_line_color; return $ret; } @@ -4238,7 +4312,7 @@ class TCPDF { $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1); } $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1); - $this->SetTextColor(0, 0, 0); + $this->SetTextColorArray($this->header_text_color); // header title $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1); $this->SetX($header_x); @@ -4248,7 +4322,7 @@ class TCPDF { $this->SetX($header_x); $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false); // print an ending header line - $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))); + $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color'])); $this->SetY((2.835 / $this->k) + max($imgy, $this->y)); if ($this->rtl) { $this->SetX($this->original_rMargin); @@ -4284,10 +4358,10 @@ class TCPDF { */ public function Footer() { $cur_y = $this->y; - $this->SetTextColor(0, 0, 0); + $this->SetTextColorArray($this->footer_text_color); //set style for cell border - $line_width = 0.85 / $this->k; - $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))); + $line_width = (0.85 / $this->k); + $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color)); //print document barcode $barcode = $this->getBarcode(); if (!empty($barcode)) { @@ -4919,9 +4993,9 @@ class TCPDF { } /** - * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking/kerning). + * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking). * @param $char (int) The char code whose length is to be returned - * @param $notlast (boolean) set to false for the latest character on string, true otherwise (default) + * @param $notlast (boolean) If false ignore the font-spacing. * @return float char width * @author Nicola Asuni * @public @@ -4930,7 +5004,7 @@ class TCPDF { public function GetCharWidth($char, $notlast=true) { // get raw width $chw = $this->getRawCharWidth($char); - if (($this->font_spacing != 0) AND $notlast) { + if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) { // increase/decrease font spacing $chw += $this->font_spacing; } @@ -5400,7 +5474,7 @@ class TCPDF { } /** - * Return the font ascent value + * Return the font ascent value. * @param $font (string) font name * @param $style (string) font style * @param $size (float) The size (in points) @@ -5421,7 +5495,7 @@ class TCPDF { } /** - * Return the font descent value + * Return true in the character is present in the specified font. * @param $char (mixed) Character to check (integer value or string) * @param $font (string) Font name (family name). * @param $style (string) Font style. @@ -5436,6 +5510,9 @@ class TCPDF { $char = $char[0]; } if ($this->empty_string($font)) { + if ($this->empty_string($style)) { + return (isset($this->CurrentFont['cw'][intval($char)])); + } $font = $this->FontFamily; } $fontdata = $this->AddFont($font, $style); @@ -5828,6 +5905,35 @@ class TCPDF { } } $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']); + // apply text shadow if enabled + if ($this->txtshadow['enabled']) { + // save data + $x = $this->x; + $y = $this->y; + $bc = $this->bgcolor; + $fc = $this->fgcolor; + $sc = $this->strokecolor; + $alpha = $this->alpha; + // print shadow + $this->x += $this->txtshadow['depth_w']; + $this->y += $this->txtshadow['depth_h']; + $this->SetFillColorArray($this->txtshadow['color']); + $this->SetTextColorArray($this->txtshadow['color']); + $this->SetDrawColorArray($this->txtshadow['color']); + if ($this->txtshadow['opacity'] != $alpha['CA']) { + $this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']); + } + $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign)); + //restore data + $this->x = $x; + $this->y = $y; + $this->SetFillColorArray($bc); + $this->SetTextColorArray($fc); + $this->SetDrawColorArray($sc); + if ($this->txtshadow['opacity'] != $alpha['CA']) { + $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']); + } + } $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign)); $this->cell_padding = $prev_cell_padding; $this->cell_margin = $prev_cell_margin; @@ -6023,34 +6129,97 @@ class TCPDF { } else { $unicode = $this->UTF8StringToArray($txt); // array of UTF-8 unicode values $unicode = $this->utf8Bidi($unicode, '', $this->tmprtl); + // replace thai chars (if any) if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) { - // ---- Fix for bug #2977340 "Incorrect Thai characters position arrangement" ---- - // NOTE: this doesn't work with HTML justification - // Symbols that could overlap on the font top (only works in LTR) - $topchar = array(3611, 3613, 3615, 3650, 3651, 3652); // chars that extends on top - $topsym = array(3633, 3636, 3637, 3638, 3639, 3655, 3656, 3657, 3658, 3659, 3660, 3661, 3662); // symbols with top position - $numchars = count($unicode); // number of chars - $unik = 0; - $uniblock = array(); - $uniblock[$unik] = array(); - $uniblock[$unik][] = $unicode[0]; - // resolve overlapping conflicts by splitting the string in several parts - for ($i = 1; $i < $numchars; ++$i) { - // check if symbols overlaps at top - if (in_array($unicode[$i], $topsym) AND (in_array($unicode[($i - 1)], $topsym) OR in_array($unicode[($i - 1)], $topchar))) { - // move symbols to another array - ++$unik; - $uniblock[$unik] = array(); - $uniblock[$unik][] = $unicode[$i]; - ++$unik; - $uniblock[$unik] = array(); - $unicode[$i] = 0x200b; // Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B) + // number of chars + $numchars = count($unicode); + // po pla, for far, for fan + $longtail = array(0x0e1b, 0x0e1d, 0x0e1f); + // do chada, to patak + $lowtail = array(0x0e0e, 0x0e0f); + // mai hun arkad, sara i, sara ii, sara ue, sara uee + $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37); + // mai ek, mai tho, mai tri, mai chattawa, karan + $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c); + // sara u, sara uu, pinthu + $lowvowel = array(0x0e38, 0x0e39, 0x0e3a); + $output = array(); + for ($i = 0; $i < $numchars; $i++) { + if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) { + $ch0 = $unicode[$i]; + $ch1 = ($i > 0) ? $unicode[($i - 1)] : 0; + $ch2 = ($i > 1) ? $unicode[($i - 2)] : 0; + $chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0; + if (in_array($ch0, $tonemark)) { + if ($chn == 0x0e33) { + // sara um + if (in_array($ch1, $longtail)) { + // tonemark at upper left + $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48)); + } else { + // tonemark at upper right (normal position) + $output[] = $ch0; + } + } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) { + // tonemark at lower left + $output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48)); + } elseif (in_array($ch1, $upvowel)) { + if (in_array($ch2, $longtail)) { + // tonemark at upper left + $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48)); + } else { + // tonemark at upper right (normal position) + $output[] = $ch0; + } + } else { + // tonemark at lower right + $output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48)); + } + } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) { + // add lower left nikhahit and sara aa + if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) { + $output[] = 0xf711; + $this->CurrentFont['subsetchars'][0xf711] = true; + $output[] = 0x0e32; + $this->CurrentFont['subsetchars'][0x0e32] = true; + } else { + $output[] = $ch0; + } + } elseif (in_array($ch1, $longtail)) { + if ($ch0 == 0x0e31) { + // lower left mai hun arkad + $output[] = $this->replaceChar($ch0, 0xf710); + } elseif (in_array($ch0, $upvowel)) { + // lower left + $output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34)); + } elseif ($ch0 == 0x0e47) { + // lower left mai tai koo + $output[] = $this->replaceChar($ch0, 0xf712); + } else { + // normal character + $output[] = $ch0; + } + } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) { + // lower vowel + $output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38)); + } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) { + // yo ying without lower part + $output[] = $this->replaceChar($ch0, 0xf70f); + } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) { + // tho santan without lower part + $output[] = $this->replaceChar($ch0, 0xf700); + } else { + $output[] = $ch0; + } } else { - $uniblock[$unik][] = $unicode[$i]; + // non-thai character + $output[] = $unicode[$i]; } } - // ---- END OF Fix for bug #2977340 - } + $unicode = $output; + // update font subsetchars + $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); + } // end of K_THAI_TOPCHARS $txt2 = $this->arrUTF8ToUTF16BE($unicode, false); } } @@ -6094,7 +6263,7 @@ class TCPDF { $s .= 'q '.$this->TextColor.' '; } // rendering mode - $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, $this->textstrokewidth); + $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k)); // count number of spaces $ns = substr_count($txt, chr(32)); // Justification @@ -6250,6 +6419,25 @@ class TCPDF { return $rs; } + /** + * Replace a char if is defined on the current font. + * @param $oldchar (int) Integer code (unicode) of the character to replace. + * @param $newchar (int) Integer code (unicode) of the new character. + * @return int the replaced char or the old char in case the new char i not defined + * @protected + * @since 5.9.167 (2012-06-22) + */ + protected function replaceChar($oldchar, $newchar) { + if ($this->isCharDefined($newchar)) { + // add the new char on the subset list + $this->CurrentFont['subsetchars'][$newchar] = true; + // return the new character + return $newchar; + } + // return the old char + return $oldchar; + } + /** * Returns the code to draw the cell border * @param $x (float) X coordinate. @@ -7069,7 +7257,7 @@ class TCPDF { $w = $this->w - $this->rMargin - $this->x; } // max column width - $wmax = $w - $wadj; + $wmax = ($w - $wadj); if (!$firstline) { $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']); } @@ -7155,18 +7343,27 @@ class TCPDF { $this->rMargin += $margin['R']; } $w = $this->getRemainingWidth(); - $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R']; + $wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']); } else { // 160 is the non-breaking space. // 173 is SHY (Soft Hypen). // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. - if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) { + if (($c != 160) + AND (($c == 173) + OR preg_match($this->re_spaces, $this->unichr($c)) + OR (($c == 45) + AND ($i < ($nb - 1)) + AND @preg_match('/[\p{L}]/'.$this->re_space['m'], $this->unichr($pc)) + AND @preg_match('/[\p{L}]/'.$this->re_space['m'], $this->unichr($chars[($i + 1)])) + ) + ) + ) { // update last blank space position $sep = $i; // check if is a SHY - if ($c == 173) { + if (($c == 173) OR ($c == 45)) { $shy = true; if ($pc == 45) { $tmp_shy_replacement_width = 0; @@ -7191,8 +7388,8 @@ class TCPDF { // we have reached the end of column if ($sep == -1) { // check if the line was already started - if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth))) - OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) { + if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth))) + OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) { // print a void cell and go to next line $this->Cell($w, $h, '', 0, 1); $linebreak = true; @@ -7255,9 +7452,9 @@ class TCPDF { $linew = $this->GetArrStringWidth($tmparr); unset($tmparr); if ($this->rtl) { - $this->endlinex = $startx - $linew; + $this->endlinex = ($startx - $linew); } else { - $this->endlinex = $startx + $linew; + $this->endlinex = ($startx + $linew); } $w = $linew; $tmpcellpadding = $this->cell_padding; @@ -7320,6 +7517,9 @@ class TCPDF { $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch); unset($tmpstr); if ($firstline) { + if ($chars[$sep] == 45) { + $endspace += 1; + } // return the remaining text $this->cell_padding = $tmpcellpadding; return ($this->UniArrSubString($uchars, ($sep + $endspace))); @@ -7327,7 +7527,7 @@ class TCPDF { $i = $sep; $sep = -1; $shy = false; - $j = ($i+1); + $j = ($i + 1); } } // account for margin changes @@ -7655,22 +7855,21 @@ class TCPDF { } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($h, $x, $y); - $cached_file = false; // true when the file is cached $exurl = ''; // external streams // check if we are passing an image as file or string if ($file[0] === '@') { // image from string $imgdata = substr($file, 1); - $file = K_PATH_CACHE.'img_'.md5($imgdata); + $file = $this->getObjFilename('img'); $fp = fopen($file, 'w'); fwrite($fp, $imgdata); fclose($fp); unset($imgdata); - $cached_file = true; $imsize = @getimagesize($file); if ($imsize === FALSE) { unlink($file); - $cached_file = false; + } else { + $this->cached_files[] = $file; } } else { // image file if ($file{0} === '*') { @@ -7697,7 +7896,9 @@ class TCPDF { curl_setopt($cs, CURLOPT_BINARYTRANSFER, true); curl_setopt($cs, CURLOPT_FAILONERROR, true); curl_setopt($cs, CURLOPT_RETURNTRANSFER, true); - curl_setopt($cs, CURLOPT_FOLLOWLOCATION, true); + if ((ini_get('open_basedir') == '') AND (ini_get('safe_mode') == 'Off')) { + curl_setopt($cs, CURLOPT_FOLLOWLOCATION, true); + } curl_setopt($cs, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($cs, CURLOPT_TIMEOUT, 30); curl_setopt($cs, CURLOPT_SSL_VERIFYPEER, false); @@ -7707,16 +7908,16 @@ class TCPDF { curl_close($cs); if ($imgdata !== FALSE) { // copy image to cache - $file = K_PATH_CACHE.'img_'.md5($imgdata); + $file = $this->getObjFilename('img'); $fp = fopen($file, 'w'); fwrite($fp, $imgdata); fclose($fp); unset($imgdata); - $cached_file = true; $imsize = @getimagesize($file); if ($imsize === FALSE) { unlink($file); - $cached_file = false; + } else { + $this->cached_files[] = $file; } } } elseif (($w > 0) AND ($h > 0)) { @@ -7738,7 +7939,7 @@ class TCPDF { } } // file hash - $filehash = md5($file); + $filehash = md5($this->file_id.$file); // get original image width and height in pixels list($pixw, $pixh) = $imsize; // calculate image width and height on document @@ -7976,10 +8177,6 @@ class TCPDF { // add image to document $this->setImageBuffer($file, $info); } - if ($cached_file) { - // remove cached file - unlink($file); - } // set alignment $this->img_rb_y = $y + $h; // set alignment @@ -8320,11 +8517,13 @@ class TCPDF { $data .= $this->rfread($f, $n); fread($f, 4); } elseif ($type == 'iCCP') { - // skip profile name and null separator + // skip profile name $len = 0; while ((ord(fread($f, 1)) > 0) AND ($len < 80)) { ++$len; } + // skip null separator + fread($f, 1); // get compression method if (ord(fread($f, 1)) != 0) { //$this->Error('Unknown filter method: '.$file); @@ -8396,7 +8595,7 @@ class TCPDF { */ protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') { if (empty($filehash)) { - $filehash = md5($file); + $filehash = md5($this->file_id.$file); } // create temp image file (without alpha channel) $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash; @@ -8407,7 +8606,7 @@ class TCPDF { $img = new Imagick(); $img->readImage($file); // clone image object - $imga = $img->clone(); + $imga = $this->objclone($img); // extract alpha channel $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE); $img->negateImage(true); @@ -8429,9 +8628,7 @@ class TCPDF { for ($xpx = 0; $xpx < $wpx; ++$xpx) { for ($ypx = 0; $ypx < $hpx; ++$ypx) { $color = imagecolorat($img, $xpx, $ypx); - $alpha = ($color >> 24); // shifts off the first 24 bits (where 8x3 are used for each color), and returns the remaining 7 allocated bits (commonly used for alpha) - $alpha = (((127 - $alpha) / 127) * 255); // GD alpha is only 7 bit (0 -> 127) - $alpha = $this->getGDgamma($alpha); // correct gamma + $alpha = $this->getGDgamma($color); // correct gamma imagesetpixel($imgalpha, $xpx, $ypx, $alpha); } } @@ -8455,13 +8652,27 @@ class TCPDF { } /** - * Correct the gamma value to be used with GD library - * @param $v (float) the gamma value to be corrected + * Get the GD-corrected PNG gamma value from alpha color + * @param $c (int) alpha color * @protected * @since 4.3.007 (2008-12-04) */ - protected function getGDgamma($v) { - return (pow(($v / 255), 2.2) * 255); + protected function getGDgamma($c) { + if (!isset($this->gdgammacache["'".$c."'"])) { + // shifts off the first 24 bits (where 8x3 are used for each color), + // and returns the remaining 7 allocated bits (commonly used for alpha) + $alpha = ($c >> 24); + // GD alpha is only 7 bit (0 -> 127) + $alpha = (((127 - $alpha) / 127) * 255); + // correct gamma + $this->gdgammacache["'".$c."'"] = (pow(($alpha / 255), 2.2) * 255); + // store the latest values on cache to improve performances + if (count($this->gdgammacache) > 8) { + // remove one element from the cache array + array_shift($this->gdgammacache); + } + } + return $this->gdgammacache["'".$c."'"]; } /** @@ -8736,7 +8947,7 @@ class TCPDF { header('Pragma: public'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); - header('Content-Disposition: inline; filename="'.basename($name).'";'); + header('Content-Disposition: inline; filename="'.basename($name).'"'); $this->sendOutputData($this->getBuffer(), $this->bufferlen); } else { echo $this->getBuffer(); @@ -8767,7 +8978,7 @@ class TCPDF { header('Content-Type: application/pdf'); } // use the Content-Disposition header to supply a recommended filename - header('Content-Disposition: attachment; filename="'.basename($name).'";'); + header('Content-Disposition: attachment; filename="'.basename($name).'"'); header('Content-Transfer-Encoding: binary'); $this->sendOutputData($this->getBuffer(), $this->bufferlen); break; @@ -8794,7 +9005,7 @@ class TCPDF { header('Pragma: public'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); - header('Content-Disposition: inline; filename="'.basename($name).'";'); + header('Content-Disposition: inline; filename="'.basename($name).'"'); $this->sendOutputData(file_get_contents($name), filesize($name)); } elseif ($dest == 'FD') { // send headers to browser @@ -8819,7 +9030,7 @@ class TCPDF { header('Content-Type: application/pdf'); } // use the Content-Disposition header to supply a recommended filename - header('Content-Disposition: attachment; filename="'.basename($name).'";'); + header('Content-Disposition: attachment; filename="'.basename($name).'"'); header('Content-Transfer-Encoding: binary'); $this->sendOutputData(file_get_contents($name), filesize($name)); } @@ -8865,6 +9076,7 @@ class TCPDF { AND ($val != 'bufferlen') AND ($val != 'buffer') AND ($val != 'diskcache') + AND ($val != 'cached_files') AND ($val != 'sign') AND ($val != 'signature_data') AND ($val != 'signature_max_length') @@ -8875,6 +9087,15 @@ class TCPDF { } } } + if (isset($this->cached_files) AND !empty($this->cached_files)) { + // remove cached files + foreach ($this->cached_files as $cachefile) { + if (is_file($cachefile)) { + unlink($cachefile); + } + } + unset($this->cached_files); + } } /** @@ -9430,7 +9651,7 @@ class TCPDF { if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) { $annots .= ' /BE <<'; $bstyles = array('S', 'C'); - if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) { + if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) { $annots .= ' /S /'.$pl['opt']['bs']['s']; } else { $annots .= ' /S /S'; @@ -9509,9 +9730,11 @@ class TCPDF { $annots .= ' /A <_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>'; } else { // internal link - $l = $this->links[$pl['txt']]; - if (isset($this->page_obj_id[($l[0])])) { - $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k))); + if (isset($this->links[$pl['txt']])) { + $l = $this->links[$pl['txt']]; + if (isset($this->page_obj_id[($l[0])])) { + $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k))); + } } } $hmodes = array('N', 'I', 'O', 'P'); @@ -12855,11 +13078,14 @@ class TCPDF { $this->_out('xref'); $this->_out('0 '.($this->n + 1)); $this->_out('0000000000 65535 f '); + $freegen = ($this->n + 2); for ($i=1; $i <= $this->n; ++$i) { if (!isset($this->offsets[$i]) AND ($i > 1)) { - $this->offsets[$i] = $this->offsets[($i - 1)]; + $this->_out(sprintf('0000000000 %05d f ', $freegen)); + ++$freegen; + } else { + $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i])); } - $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i])); } // TRAILER $out = 'trailer'."\n"; @@ -12899,6 +13125,7 @@ class TCPDF { */ protected function _beginpage($orientation='', $format='') { ++$this->page; + $this->pageobjects[$this->page] = array(); $this->setPageBuffer($this->page, ''); // initialize array for graphics tranformation positions inside a page buffer $this->transfmrk[$this->page] = array(); @@ -12967,6 +13194,7 @@ class TCPDF { $objid = $this->n; } $this->offsets[$objid] = $this->bufferlen; + $this->pageobjects[$this->page][] = $objid; return $objid.' 0 obj'; } @@ -15012,7 +15240,7 @@ class TCPDF { * @since 2.1.000 (2008-01-08) */ protected function _outPoint($x, $y) { - $this->_out(sprintf('%F %F m', $x * $this->k, ($this->h - $y) * $this->k)); + $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k))); } /** @@ -15024,7 +15252,7 @@ class TCPDF { * @since 2.1.000 (2008-01-08) */ protected function _outLine($x, $y) { - $this->_out(sprintf('%F %F l', $x * $this->k, ($this->h - $y) * $this->k)); + $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k))); } /** @@ -16404,12 +16632,13 @@ class TCPDF { * @param $name (string) Destination name. * @param $y (float) Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;). * @param $page (int) Target page number (leave empty for current page). + * @param $x (float) X position in user units of the destiantion on the selected page (default = -1 = current position;). * @return (string) Stripped named destination identifier or false in case of error. * @public * @author Christian Deligant, Nicola Asuni * @since 5.9.097 (2011-06-23) */ - public function setDestination($name, $y=-1, $page='') { + public function setDestination($name, $y=-1, $page='', $x=-1) { // remove unsupported characters $name = $this->encodeNameObject($name); if ($this->empty_string($name)) { @@ -16417,6 +16646,17 @@ class TCPDF { } if ($y == -1) { $y = $this->GetY(); + } elseif ($y < 0) { + $y = 0; + } elseif ($y > $this->h) { + $y = $this->h; + } + if ($x == -1) { + $x = $this->GetX(); + } elseif ($x < 0) { + $x = 0; + } elseif ($x > $this->w) { + $x = $this->w; } if (empty($page)) { $page = $this->PageNo(); @@ -16424,7 +16664,7 @@ class TCPDF { return; } } - $this->dests[$name] = array('y' => $y, 'p' => $page); + $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page); return $name; } @@ -16452,7 +16692,7 @@ class TCPDF { $this->n_dests = $this->_newobj(); $out = ' <<'; foreach($this->dests as $name => $o) { - $out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($o['p'])], ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))); + $out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))); } $out .= ' >>'; $out .= "\n".'endobj'; @@ -16481,11 +16721,12 @@ class TCPDF { * @param $page (int) Target page number (leave empty for current page). * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic. * @param $color (array) RGB color array (values from 0 to 255). + * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;). * @public * @author Olivier Plathey, Nicola Asuni * @since 2.1.002 (2008-02-12) */ - public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0)) { + public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1) { if ($level < 0) { $level = 0; } @@ -16500,6 +16741,17 @@ class TCPDF { } if ($y == -1) { $y = $this->GetY(); + } elseif ($y < 0) { + $y = 0; + } elseif ($y > $this->h) { + $y = $this->h; + } + if ($x == -1) { + $x = $this->GetX(); + } elseif ($x < 0) { + $x = 0; + } elseif ($x > $this->w) { + $x = $this->w; } if (empty($page)) { $page = $this->PageNo(); @@ -16507,7 +16759,7 @@ class TCPDF { return; } } - $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page, 's' => strtoupper($style), 'c' => $color); + $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 's' => strtoupper($style), 'c' => $color); } /** @@ -16590,7 +16842,7 @@ class TCPDF { $out .= ' /Last '.($n + $o['last']).' 0 R'; } if (isset($this->page_obj_id[($o['p'])])) { - $out .= ' '.sprintf('/Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($o['p'])], ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))); + $out .= ' '.sprintf('/Dest [%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))); } // set font style $style = 0; @@ -19169,7 +19421,7 @@ class TCPDF { */ public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) { if ($this->rtl) { - $xc = $this->w - $xc; + $xc = ($this->w - $xc); } $op = $this->getPathPaintOperator($style); if ($op == 'f') { @@ -19177,8 +19429,8 @@ class TCPDF { } if ($cw) { $d = $b; - $b = 360 - $a + $o; - $a = 360 - $d + $o; + $b = (360 - $a + $o); + $a = (360 - $d + $o); } else { $b += $o; $a += $o; @@ -19578,7 +19830,7 @@ class TCPDF { // create new barcode object $barcodeobj = new TCPDFBarcode($code, $type); $arrcode = $barcodeobj->getBarcodeArray(); - if ($arrcode === false) { + if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] == 0)) { $this->Error('Error in 1D barcode string'); } // set default values @@ -19949,7 +20201,7 @@ class TCPDF { // create new barcode object $barcodeobj = new TCPDF2DBarcode($code, $type); $arrcode = $barcodeobj->getBarcodeArray(); - if (($arrcode === false) OR empty($arrcode)) { + if (($arrcode === false) OR empty($arrcode) OR !isset($arrcode['num_rows']) OR ($arrcode['num_rows'] == 0) OR !isset($arrcode['num_cols']) OR ($arrcode['num_cols'] == 0)) { $this->Error('Error in 2D barcode string'); } // set default values @@ -20004,6 +20256,9 @@ class TCPDF { // module width and height $mw = $style['module_width']; $mh = $style['module_height']; + if (($mw == 0) OR ($mh == 0)) { + $this->Error('Error in 2D barcode string'); + } // get max dimensions if ($this->rtl) { $maxw = $x - $this->lMargin; @@ -20886,7 +21141,7 @@ class TCPDF { /** * Returns the letter-spacing value from CSS value * @param $spacing (string) letter-spacing value - * @param $parent (float) font spacing (tracking/kerning) value of the parent element + * @param $parent (float) font spacing (tracking) value of the parent element * @return float quantity to increases or decreases the space between characters in a text. * @protected * @since 5.9.000 (2010-10-02) @@ -21499,7 +21754,7 @@ class TCPDF { } if (isset($dom[$key]['style']['border-style'])) { $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style'])); - if (isset($brd_styles[3])) { + if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) { $dom[$key]['border']['L']['cap'] = 'square'; $dom[$key]['border']['L']['join'] = 'miter'; $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]); @@ -21834,7 +22089,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize); $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize); $curfontstretcing = $this->font_stretching; - $curfontkerning = $this->font_spacing; + $curfonttracking = $this->font_spacing; $this->newline = true; $newline = true; $startlinepage = $this->page; @@ -21973,7 +22228,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $this_method_vars['curfontascent'] = $curfontascent; $this_method_vars['curfontdescent'] = $curfontdescent; $this_method_vars['curfontstretcing'] = $curfontstretcing; - $this_method_vars['curfontkerning'] = $curfontkerning; + $this_method_vars['curfonttracking'] = $curfonttracking; $this_method_vars['minstartliney'] = $minstartliney; $this_method_vars['maxbottomliney'] = $maxbottomliney; $this_method_vars['yshift'] = $yshift; @@ -23051,9 +23306,9 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: } else { $wadj = 0; // space to leave for block continuity if ($this->rtl) { - $cwa = $this->x - $this->lMargin; + $cwa = ($this->x - $this->lMargin); } else { - $cwa = $this->w - $this->rMargin - $this->x; + $cwa = ($this->w - $this->rMargin - $this->x); } if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) { // check the next text blocks for continuity @@ -23143,6 +23398,14 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: } } else { $loop = 0; + // add the positive font spacing of the last character (if any) + if ($this->font_spacing > 0) { + if ($this->rtl) { + $this->x -= $this->font_spacing; + } else { + $this->x += $this->font_spacing; + } + } } } ++$key; @@ -23740,7 +24003,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $value = $tag['attribute']['value']; } if (isset($tag['attribute']['maxlength']) AND !$this->empty_string($tag['attribute']['maxlength'])) { - $opt['maxlen'] = intval($tag['attribute']['value']); + $opt['maxlen'] = intval($tag['attribute']['maxlength']); } $h = $this->FontSize * $this->cell_height_ratio; if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) { @@ -23786,14 +24049,23 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: break; } case 'checkbox': { + if (!isset($value)) { + break; + } $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false); break; } case 'radio': { + if (!isset($value)) { + break; + } $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false); break; } case 'submit': { + if (!isset($value)) { + $value = 'submit'; + } $w = $this->GetStringWidth($value) * 1.5; $h *= 1.6; $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); @@ -23810,6 +24082,9 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: break; } case 'reset': { + if (!isset($value)) { + $value = 'reset'; + } $w = $this->GetStringWidth($value) * 1.5; $h *= 1.6; $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); @@ -23855,6 +24130,9 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: break; } case 'button': { + if (!isset($value)) { + $value = ' '; + } $w = $this->GetStringWidth($value) * 1.5; $h *= 1.6; $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); @@ -24328,13 +24606,10 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: if (end($this->transfmrk[$this->page]) !== false) { $pagemarkkey = key($this->transfmrk[$this->page]); $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; - $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen; } elseif ($this->InFooter) { $pagemark = $this->footerpos[$this->page]; - $this->footerpos[$this->page] += $offsetlen; } else { $pagemark = $this->intmrk[$this->page]; - $this->intmrk[$this->page] += $offsetlen; } $pagebuff = $this->getPageBuffer($this->page); $pstart = substr($pagebuff, 0, $pagemark); @@ -24738,17 +25013,14 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: if (end($this->transfmrk[$this->page]) !== false) { $pagemarkkey = key($this->transfmrk[$this->page]); $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; - $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen; } elseif ($this->InFooter) { $pagemark = $this->footerpos[$this->page]; - $this->footerpos[$this->page] += $offsetlen; } else { $pagemark = $this->intmrk[$this->page]; - $this->intmrk[$this->page] += $offsetlen; } $pagebuff = $this->getPageBuffer($this->page); - $pstart = substr($pagebuff, 0, $this->bordermrk[$this->page]); - $pend = substr($pagebuff, $this->bordermrk[$this->page]); + $pstart = substr($pagebuff, 0, $pagemark); + $pend = substr($pagebuff, $pagemark); $this->setPageBuffer($this->page, $pstart.$ccode.$pend); $this->bordermrk[$this->page] += $offsetlen; $this->cntmrk[$this->page] += $offsetlen; @@ -25278,6 +25550,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 'cell_height_ratio' => $this->cell_height_ratio, 'font_stretching' => $this->font_stretching, 'font_spacing' => $this->font_spacing, + 'alpha' => $this->alpha, // extended 'lasth' => $this->lasth, 'tMargin' => $this->tMargin, @@ -25337,6 +25610,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $this->cell_height_ratio = $gvars['cell_height_ratio']; $this->font_stretching = $gvars['font_stretching']; $this->font_spacing = $gvars['font_spacing']; + $this->alpha = $gvars['alpha']; if ($extended) { // restore extended values $this->lasth = $gvars['lasth']; @@ -25651,6 +25925,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $tmpintmrk = $this->intmrk[$frompage]; $tmpbordermrk = $this->bordermrk[$frompage]; $tmpcntmrk = $this->cntmrk[$frompage]; + $tmppageobjects = $this->pageobjects[$frompage]; if (isset($this->footerpos[$frompage])) { $tmpfooterpos = $this->footerpos[$frompage]; } @@ -25686,6 +25961,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $this->intmrk[$i] = $this->intmrk[$j]; $this->bordermrk[$i] = $this->bordermrk[$j]; $this->cntmrk[$i] = $this->cntmrk[$j]; + $this->pageobjects[$i] = $this->pageobjects[$j]; if (isset($this->footerpos[$j])) { $this->footerpos[$i] = $this->footerpos[$j]; } elseif (isset($this->footerpos[$i])) { @@ -25720,6 +25996,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $this->intmrk[$topage] = $tmpintmrk; $this->bordermrk[$topage] = $tmpbordermrk; $this->cntmrk[$topage] = $tmpcntmrk; + $this->pageobjects[$topage] = $tmppageobjects; if (isset($tmpfooterpos)) { $this->footerpos[$topage] = $tmpfooterpos; } elseif (isset($this->footerpos[$topage])) { @@ -25807,6 +26084,12 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: unset($this->intmrk[$page]); unset($this->bordermrk[$page]); unset($this->cntmrk[$page]); + foreach ($this->pageobjects[$page] as $oid) { + if (isset($this->offsets[$oid])){ + unset($this->offsets[$oid]); + } + } + unset($this->pageobjects[$page]); if (isset($this->footerpos[$page])) { unset($this->footerpos[$page]); } @@ -25841,6 +26124,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $this->intmrk[$i] = $this->intmrk[$j]; $this->bordermrk[$i] = $this->bordermrk[$j]; $this->cntmrk[$i] = $this->cntmrk[$j]; + $this->pageobjects[$i] = $this->pageobjects[$j]; if (isset($this->footerpos[$j])) { $this->footerpos[$i] = $this->footerpos[$j]; } elseif (isset($this->footerpos[$i])) { @@ -25881,6 +26165,12 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: unset($this->intmrk[$this->numpages]); unset($this->bordermrk[$this->numpages]); unset($this->cntmrk[$this->numpages]); + foreach ($this->pageobjects[$this->numpages] as $oid) { + if (isset($this->offsets[$oid])){ + unset($this->offsets[$oid]); + } + } + unset($this->pageobjects[$this->numpages]); if (isset($this->footerpos[$this->numpages])) { unset($this->footerpos[$this->numpages]); } @@ -25982,6 +26272,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $this->intmrk[$this->page] = $this->intmrk[$page]; $this->bordermrk[$this->page] = $this->bordermrk[$page]; $this->cntmrk[$this->page] = $this->cntmrk[$page]; + $this->pageobjects[$this->page] = $this->pageobjects[$page]; $this->pageopen[$this->page] = false; if (isset($this->footerpos[$page])) { $this->footerpos[$this->page] = $this->footerpos[$page]; @@ -26007,7 +26298,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $tmpoutlines = $this->outlines; foreach ($tmpoutlines as $key => $outline) { if ($outline['p'] == $page) { - $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'y' => $outline['y'], 'p' => $this->page, 's' => $outline['s'], 'c' => $outline['c']); + $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'x' => $outline['x'], 'y' => $outline['y'], 'p' => $this->page, 's' => $outline['s'], 'c' => $outline['c']); } } // copy links @@ -26695,7 +26986,59 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: } } $this->textrendermode = $textrendermode; - $this->textstrokewidth = $stroke * $this->k; + $this->textstrokewidth = $stroke; + } + + /** + * Set parameters for drop shadow effect for text. + * @param $params (array) Array of parameters: enabled (boolean) set to true to enable shadow; depth_w (float) shadow width in user units; depth_h (float) shadow height in user units; color (array) shadow color or false to use the stroke color; opacity (float) Alpha value: real value from 0 (transparent) to 1 (opaque); blend_mode (string) blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity. + * @since 5.9.174 (2012-07-25) + * @public + */ + public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) { + if (isset($params['enabled'])) { + $this->txtshadow['enabled'] = $params['enabled']?true:false; + } else { + $this->txtshadow['enabled'] = false; + } + if (isset($params['depth_w'])) { + $this->txtshadow['depth_w'] = floatval($params['depth_w']); + } else { + $this->txtshadow['depth_w'] = 0; + } + if (isset($params['depth_h'])) { + $this->txtshadow['depth_h'] = floatval($params['depth_h']); + } else { + $this->txtshadow['depth_h'] = 0; + } + if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) { + $this->txtshadow['color'] = $params['color']; + } else { + $this->txtshadow['color'] = $this->strokecolor; + } + if (isset($params['opacity'])) { + $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity']))); + } else { + $this->txtshadow['opacity'] = 1; + } + if (isset($params['blend_mode']) AND in_array($params['blend_mode'], array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) { + $this->txtshadow['blend_mode'] = $params['blend_mode']; + } else { + $this->txtshadow['blend_mode'] = 'Normal'; + } + if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) { + $this->txtshadow['enabled'] = false; + } + } + + /** + * Return the text shadow parameters array. + * @return Array of parameters. + * @since 5.9.174 (2012-07-25) + * @public + */ + public function getTextShadow() { + return $this->txtshadow; } /** @@ -27337,7 +27680,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: /** * Get the amount to increase or decrease the space between characters in a text. - * @return int font spacing (tracking/kerning) value + * @return int font spacing (tracking) value * @author Nicola Asuni * @public * @since 5.9.000 (2010-09-29) @@ -27741,7 +28084,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: // scale and translate $e = $ox * $this->k * (1 - $svgscale_x); $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y); - $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, $e + $svgoffset_x, $f + $svgoffset_y)); + $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y))); // creates a new XML parser to be used by the other XML functions $this->parser = xml_parser_create('UTF-8'); // the following function allows to use parser inside object @@ -28089,10 +28432,10 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $gradient['coords'][3] += $y; } // calculate percentages - $gradient['coords'][0] = ($gradient['coords'][0] - $x) / $w; - $gradient['coords'][1] = ($gradient['coords'][1] - $y) / $h; - $gradient['coords'][2] = ($gradient['coords'][2] - $x) / $w; - $gradient['coords'][3] = ($gradient['coords'][3] - $y) / $h; + $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w); + $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h); + $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w); + $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h); if (isset($gradient['coords'][4])) { $gradient['coords'][4] /= $w; } @@ -28121,9 +28464,9 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: if ($gradient['type'] == 3) { // circular gradient $cy = $this->h - $y - ($gradient['coords'][1] * ($w + $h)); - $this->_out(sprintf('%F 0 0 %F %F %F cm', $w*$this->k, $w*$this->k, $x*$this->k, $cy*$this->k)); + $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($w * $this->k), ($x * $this->k), ($cy * $this->k))); } else { - $this->_out(sprintf('%F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k)); + $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), (($this->h - ($y + $h)) * $this->k))); } if (count($gradient['stops']) > 1) { $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false); @@ -28331,6 +28674,8 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: } else { $this->_outLine($x, $y); } + $x0 = $x; + $y0 = $y; } $xmin = min($xmin, $x); $ymin = min($ymin, $y); @@ -28352,6 +28697,8 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $y = $cp + $yoffset; if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { $this->_outLine($x, $y); + $x0 = $x; + $y0 = $y; } $xmin = min($xmin, $x); $ymin = min($ymin, $y); @@ -28370,6 +28717,8 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $x = $cp + $xoffset; if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { $this->_outLine($x, $y); + $x0 = $x; + $y0 = $y; } $xmin = min($xmin, $x); $xmax = max($xmax, $x); @@ -28384,6 +28733,8 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $y = $cp + $yoffset; if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { $this->_outLine($x, $y); + $x0 = $x; + $y0 = $y; } $ymin = min($ymin, $y); $ymax = max($ymax, $y); @@ -28661,6 +29012,10 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: // get styling properties $prev_svgstyle = $this->svgstyles[(count($this->svgstyles) - 1)]; // previous style $svgstyle = $this->svgstyles[0]; // set default style + if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) { + // default fill attribute for clipping + $attribs['fill'] = 'none'; + } if (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) { // fix style for regular expression $attribs['style'] = ';'.$attribs['style']; @@ -28693,7 +29048,8 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: if (!empty($ctm)) { $tm = $ctm; } else { - $tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix']; + //$tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix']; + $tm = array(1,0,0,1,0,0); } if (isset($attribs['transform']) AND !empty($attribs['transform'])) { $tm = $this->getTransformationMatrixProduct($tm, $this->getSVGTransformMatrix($attribs['transform'])); @@ -28732,6 +29088,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: // group together related graphics elements array_push($this->svgstyles, $svgstyle); $this->StartTransform(); + $this->SVGTransform($tm); $this->setSVGStyles($svgstyle, $prev_svgstyle); break; } @@ -28752,15 +29109,19 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; } //$attribs['spreadMethod'] - $x1 = (isset($attribs['x1'])?$attribs['x1']:'0%'); - $y1 = (isset($attribs['y1'])?$attribs['y1']:'0%'); - $x2 = (isset($attribs['x2'])?$attribs['x2']:'100%'); - $y2 = (isset($attribs['y2'])?$attribs['y2']:'0%'); - if (substr($x1, -1) != '%') { - $this->svggradients[$this->svggradientid]['mode'] = 'measure'; - } else { + if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2']))) + OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%')) + OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%')) + OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%')) + OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) { $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; + } else { + $this->svggradients[$this->svggradientid]['mode'] = 'measure'; } + $x1 = (isset($attribs['x1'])?$attribs['x1']:'0'); + $y1 = (isset($attribs['y1'])?$attribs['y1']:'0'); + $x2 = (isset($attribs['x2'])?$attribs['x2']:'100'); + $y2 = (isset($attribs['y2'])?$attribs['y2']:'0'); if (isset($attribs['gradientTransform'])) { $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']); } @@ -28788,16 +29149,18 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; } //$attribs['spreadMethod'] - $cx = (isset($attribs['cx'])?$attribs['cx']:0.5); - $cy = (isset($attribs['cy'])?$attribs['cy']:0.5); - $fx = (isset($attribs['fx'])?$attribs['fx']:$cx); - $fy = (isset($attribs['fy'])?$attribs['fy']:$cy); - $r = (isset($attribs['r'])?$attribs['r']:0.5); - if (isset($attribs['cx']) AND (substr($attribs['cx'], -1) != '%')) { - $this->svggradients[$this->svggradientid]['mode'] = 'measure'; - } else { + if (((!isset($attribs['cx'])) AND (!isset($attribs['cy']))) + OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%')) + OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')) )) { $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; + } else { + $this->svggradients[$this->svggradientid]['mode'] = 'measure'; } + $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5); + $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5); + $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx); + $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy); + $r = (isset($attribs['r']) ? $attribs['r'] : 0.5); if (isset($attribs['gradientTransform'])) { $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']); } @@ -28876,12 +29239,12 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: if ($invisible) { break; } - $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0); - $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0); - $r = (isset($attribs['r'])?$this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false):0); - $x = $cx - $r; - $y = $cy - $r; - $w = 2 * $r; + $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0); + $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0)); + $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0)); + $x = ($cx - $r); + $y = ($cy - $r); + $w = (2 * $r); $h = $w; if ($clipping) { $this->SVGTransform($tm); @@ -28901,14 +29264,14 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: if ($invisible) { break; } - $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0); - $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0); - $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0); - $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):0); - $x = $cx - $rx; - $y = $cy - $ry; - $w = 2 * $rx; - $h = 2 * $ry; + $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0); + $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0); + $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0)); + $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0)); + $x = ($cx - $rx); + $y = ($cy - $ry); + $w = (2 * $rx); + $h = (2 * $ry); if ($clipping) { $this->SVGTransform($tm); $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8); @@ -28981,7 +29344,9 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ')); - $this->PolyLine($p, 'D', array(), array()); + if (!empty($obstyle)) { + $this->PolyLine($p, $obstyle, array(), array()); + } $this->StopTransform(); } else { // polygon if ($clipping) { @@ -29050,14 +29415,26 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: // text case 'text': case 'tspan': { + // only basic support - advanced features must be implemented $this->svgtextmode['invisible'] = $invisible; if ($invisible) { break; } array_push($this->svgstyles, $svgstyle); - // only basic support - advanced features must be implemented - $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):$this->x); - $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):$this->y); + if (isset($attribs['x'])) { + $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false); + } elseif ($name == 'tspan') { + $x = $this->x; + } else { + $x = 0; + } + if (isset($attribs['y'])) { + $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false); + } elseif ($name == 'tspan') { + $y = $this->y; + } else { + $y = 0; + } $svgstyle['text-color'] = $svgstyle['fill']; $this->svgtext = ''; if (isset($svgstyle['text-anchor'])) { @@ -29088,16 +29465,19 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: } // use case 'use': { - if (isset($attribs['xlink:href'])) { - $use = $this->svgdefs[substr($attribs['xlink:href'], 1)]; - if (isset($attribs['xlink:href'])) { - unset($attribs['xlink:href']); + if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { + $svgdefid = substr($attribs['xlink:href'], 1); + if (isset($this->svgdefs[$svgdefid])) { + $use = $this->svgdefs[$svgdefid]; + if (isset($attribs['xlink:href'])) { + unset($attribs['xlink:href']); + } + if (isset($attribs['id'])) { + unset($attribs['id']); + } + $attribs = array_merge($attribs, $use['attribs']); + $this->startSVGElementHandler($parser, $use['name'], $attribs); } - if (isset($attribs['id'])) { - unset($attribs['id']); - } - $attribs = array_merge($use['attribs'], $attribs); - $this->startSVGElementHandler($parser, $use['name'], $use['attribs']); } break; } @@ -29162,7 +29542,17 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $textrendermode = $this->textrendermode; $textstrokewidth = $this->textstrokewidth; $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false); + if ($name == 'text') { + // store current coordinates + $tmpx = $this->x; + $tmpy = $this->y; + } $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T'); + if ($name == 'text') { + // restore coordinates + $this->x = $tmpx; + $this->y = $tmpy; + } // restore previous rendering mode $this->textrendermode = $textrendermode; $this->textstrokewidth = $textstrokewidth; diff --git a/lib/tcpdf/tcpdf_parser.php b/lib/tcpdf/tcpdf_parser.php index 122676034d3..f17359f2134 100644 --- a/lib/tcpdf/tcpdf_parser.php +++ b/lib/tcpdf/tcpdf_parser.php @@ -1,9 +1,9 @@ * @package com.tecnick.tcpdf * @author Nicola Asuni - * @version 1.0.000 + * @version 1.0.001 */ // include class for decoding filters @@ -48,7 +48,7 @@ require_once(dirname(__FILE__).'/tcpdf_filters.php'); * This is a PHP class for parsing PDF documents.
    * @package com.tecnick.tcpdf * @brief This is a PHP class for parsing PDF documents.. - * @version 1.0.000 + * @version 1.0.001 * @author Nicola Asuni - info@tecnick.com */ class TCPDF_PARSER { @@ -127,20 +127,29 @@ class TCPDF_PARSER { * @since 1.0.000 (2011-05-24) */ protected function getXrefData($offset=0, $xref=array()) { - // find last startxref - if (preg_match_all('/[\r\n]startxref[\s]*[\r\n]+([0-9]+)[\s]*[\r\n]+%%EOF/i', $this->pdfdata, $matches, PREG_SET_ORDER, $offset) == 0) { - $this->Error('Unable to find startxref'); + if ($offset == 0) { + // find last startxref + if (preg_match_all('/[\r\n]startxref[\s]*[\r\n]+([0-9]+)[\s]*[\r\n]+%%EOF/i', $this->pdfdata, $matches, PREG_SET_ORDER, $offset) == 0) { + $this->Error('Unable to find startxref'); + } + $matches = array_pop($matches); + $startxref = $matches[1]; + } else { + // get the first xref at the specified offset + if (preg_match('/[\r\n]startxref[\s]*[\r\n]+([0-9]+)[\s]*[\r\n]+%%EOF/i', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $offset) == 0) { + $this->Error('Unable to find startxref'); + } + $startxref = $matches[1][0]; } - $matches = array_pop($matches); - $startxref = $matches[1]; // check xref position if (strpos($this->pdfdata, 'xref', $startxref) != $startxref) { $this->Error('Unable to find xref'); } // extract xref data (object indexes and offsets) - $offset = $startxref + 5; + $xoffset = $startxref + 5; // initialize object number $obj_num = 0; + $offset = $xoffset; while (preg_match('/^([0-9]+)[\s]([0-9]+)[\s]?([nf]?)/im', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { $offset = (strlen($matches[0][0]) + $matches[0][1]); if ($matches[3][0] == 'n') { @@ -162,7 +171,7 @@ class TCPDF_PARSER { } } // get trailer data - if (preg_match('/trailer[\s]*<<(.*)>>[\s]*[\r\n]+startxref[\s]*[\r\n]+/isU', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { + if (preg_match('/trailer[\s]*<<(.*)>>[\s]*[\r\n]+startxref[\s]*[\r\n]+/isU', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $xoffset) > 0) { $trailer_data = $matches[1][0]; if (!isset($xref['trailer'])) { // get only the last updated version @@ -188,7 +197,7 @@ class TCPDF_PARSER { } if (preg_match('/Prev[\s]+([0-9]+)/i', $trailer_data, $matches) > 0) { // get previous xref - $xref = getXrefData(substr($this->pdfdata, 0, $startxref), intval($matches[1]), $xref); + $xref = $this->getXrefData(intval($matches[1]), $xref); } } else { $this->Error('Unable to find trailer'); @@ -399,7 +408,7 @@ class TCPDF_PARSER { $offset = $element[2]; // decode stream using stream's dictionary information if ($decoding AND ($element[0] == 'stream') AND (isset($objdata[($i - 1)][0])) AND ($objdata[($i - 1)][0] == '<<')) { - $element[3] = $this->decodeStream($objdata[($i - 1)][1], $element[1]); + $element[3] = $this->decodeStream($objdata[($i - 1)][1], substr($element[1], 1)); } $objdata[$i] = $element; ++$i; diff --git a/lib/tests/moodlelib_test.php b/lib/tests/moodlelib_test.php index dd9665a5d59..1abdf8098e4 100644 --- a/lib/tests/moodlelib_test.php +++ b/lib/tests/moodlelib_test.php @@ -1910,4 +1910,64 @@ class moodlelib_testcase extends advanced_testcase { $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); + } } diff --git a/lib/tests/pluginlib_test.php b/lib/tests/pluginlib_test.php index eb29268a55b..2ec4d0dd2f8 100644 --- a/lib/tests/pluginlib_test.php +++ b/lib/tests/pluginlib_test.php @@ -103,7 +103,7 @@ class available_update_checker_test extends advanced_testcase { */ 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 @@ -127,23 +127,52 @@ class available_update_checker_test extends advanced_testcase { */ 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() { @@ -503,7 +532,7 @@ class testable_available_update_checker extends available_update_checker { } 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!'); } } diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml index 48811300c00..cb47dbb50f9 100644 --- a/lib/thirdpartylibs.xml +++ b/lib/thirdpartylibs.xml @@ -4,7 +4,7 @@ adodb AdoDB GPL/BSD - 5.16 + 5.17 2.1+ @@ -88,7 +88,7 @@ flowplayer Flowplayer GPL - 3.2.9 + 3.2.14 3 @@ -165,7 +165,7 @@ phpmailer PHPMailer LGPL - 5.1 + 5.2.1 2.1 @@ -179,14 +179,14 @@ tcpdf TCPDF LGPL - 5.9.156 + 5.9.181 3 typo3 Typo3 GPL - 4.6.8 + 4.7.4 2.0+ diff --git a/lib/typo3/class.t3lib_cs.php b/lib/typo3/class.t3lib_cs.php index 75273e2fb53..2bb2cc742f4 100644 --- a/lib/typo3/class.t3lib_cs.php +++ b/lib/typo3/class.t3lib_cs.php @@ -194,6 +194,7 @@ class t3lib_cs { // mapping of iso-639-1 language codes to script names var $lang_to_script = array( // iso-639-1 language codes, see http://www.loc.gov/standards/iso639-2/php/code_list.php + 'af' => 'west_european', //Afrikaans 'ar' => 'arabic', 'bg' => 'cyrillic', // Bulgarian 'bs' => 'east_european', // Bosnian @@ -244,6 +245,7 @@ class t3lib_cs { 'zh' => 'chinese', // MS language codes, see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt_language_strings.asp // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceinternational5/html/wce50conLanguageIdentifiersandLocales.asp + 'afk'=> 'west_european', // Afrikaans 'ara' => 'arabic', 'bgr' => 'cyrillic', // Bulgarian 'cat' => 'west_european', // Catalan @@ -304,6 +306,7 @@ class t3lib_cs { 'trk' => 'turkish', 'ukr' => 'cyrillic', // Ukrainian // English language names + 'afrikaans' => 'west_european', 'albanian' => 'albanian', 'arabic' => 'arabic', 'basque' => 'west_european', @@ -412,6 +415,7 @@ class t3lib_cs { // TYPO3 specific: Array with the system charsets used for each system language in TYPO3: // Empty values means "iso-8859-1" var $charSetArray = array( + 'af' => '', 'ar' => 'iso-8859-6', 'ba' => 'iso-8859-2', 'bg' => 'windows-1251', @@ -481,7 +485,7 @@ class t3lib_cs { // TYPO3 specific: Array with the iso names used for each system language in TYPO3: // Missing keys means: same as TYPO3 - // @deprecated since TYPO3 4.6, will be removed in TYPO3 4.8 - use t3lib_l10n_Locales::getIsoMapping() + // @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 - use t3lib_l10n_Locales::getIsoMapping() var $isoArray = array( 'ba' => 'bs', 'br' => 'pt_BR', @@ -568,7 +572,7 @@ class t3lib_cs { if (TYPO3_OS == 'WIN') { $cs = $this->script_to_charset_windows[$script] ? $this->script_to_charset_windows[$script] : 'windows-1252'; } else { - $cs = $this->script_to_charset_unix[$script] ? $this->script_to_charset_unix[$script] : 'iso-8859-1'; + $cs = $this->script_to_charset_unix[$script] ? $this->script_to_charset_unix[$script] : 'utf-8'; } return $cs; @@ -810,26 +814,32 @@ class t3lib_cs { * @param boolean If set, then all string-HTML entities (like & or £ will be converted as well) * @return string Output string */ - function entities_to_utf8($str, $alsoStdHtmlEnt = 0) { + function entities_to_utf8($str, $alsoStdHtmlEnt = FALSE) { if ($alsoStdHtmlEnt) { - $trans_tbl = array_flip(get_html_translation_table(HTML_ENTITIES)); // Getting them in iso-8859-1 - but thats ok since this is observed below. + $trans_tbl = array_flip(get_html_translation_table(HTML_ENTITIES, ENT_COMPAT, 'UTF-8')); } $token = md5(microtime()); $parts = explode($token, preg_replace('/(&([#[:alnum:]]*);)/', $token . '${2}' . $token, $str)); foreach ($parts as $k => $v) { - if ($k % 2) { - if (substr($v, 0, 1) == '#') { // Dec or hex entities: - if (substr($v, 1, 1) == 'x') { - $parts[$k] = $this->UnumberToChar(hexdec(substr($v, 2))); - } else { - $parts[$k] = $this->UnumberToChar(substr($v, 1)); - } - } elseif ($alsoStdHtmlEnt && $trans_tbl['&' . $v . ';']) { // Other entities: - $parts[$k] = $this->utf8_encode($trans_tbl['&' . $v . ';'], 'iso-8859-1'); - } else { // No conversion: - $parts[$k] = '&' . $v . ';'; + // only take every second element + if ($k % 2 === 0) { + continue; + } + + $position = 0; + if (substr($v, $position, 1) == '#') { // Dec or hex entities: + $position++; + if (substr($v, $position, 1) == 'x') { + $v = hexdec(substr($v, ++$position)); + } else { + $v = substr($v, $position); } + $parts[$k] = $this->UnumberToChar($v); + } elseif ($alsoStdHtmlEnt && isset($trans_tbl['&' . $v . ';'])) { // Other entities: + $parts[$k] = $trans_tbl['&' . $v . ';']; + } else { // No conversion: + $parts[$k] = '&' . $v . ';'; } } @@ -1696,9 +1706,9 @@ class t3lib_cs { /** * Converts special chars (like æøåÆØÅ, umlauts etc) to ascii equivalents (usually double-bytes, like æ => ae etc.) * - * @param string Character set of string - * @param string Input string to convert - * @return string The converted string + * @param string $charset Character set of string + * @param string $string Input string to convert + * @return string The converted string */ function specCharsToASCII($charset, $string) { if ($charset == 'utf-8') { @@ -2342,4 +2352,4 @@ if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLA include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_cs.php']); } -?> \ No newline at end of file +?> diff --git a/lib/typo3/class.t3lib_div.php b/lib/typo3/class.t3lib_div.php index 6a6c241e0ee..1d7ddb28f39 100644 --- a/lib/typo3/class.t3lib_div.php +++ b/lib/typo3/class.t3lib_div.php @@ -375,7 +375,7 @@ final class t3lib_div { } else { // this case should not happen $csConvObj = self::makeInstance('t3lib_cs'); - return $csConvObj->crop('iso-8859-1', $string, $chars, $appendString); + return $csConvObj->crop('utf-8', $string, $chars, $appendString); } } @@ -386,7 +386,7 @@ final class t3lib_div { * @param string $newlineChar The string to implode the broken lines with (default/typically \n) * @param integer $lineWidth The line width * @return string reformatted text - * @deprecated since TYPO3 4.6, will be removed in TYPO3 4.8 - Use t3lib_utility_Mail::breakLinesForEmail() + * @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 - Use t3lib_utility_Mail::breakLinesForEmail() */ public static function breakLinesForEmail($str, $newlineChar = LF, $lineWidth = 76) { self::logDeprecatedFunction(); @@ -856,7 +856,7 @@ final class t3lib_div { * @param integer $max Higher limit * @param integer $zeroValue Default value if input is FALSE. * @return integer The input value forced into the boundaries of $min and $max - * @deprecated since TYPO3 4.6, will be removed in TYPO3 4.8 - Use t3lib_utility_Math::forceIntegerInRange() instead + * @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 - Use t3lib_utility_Math::forceIntegerInRange() instead */ public static function intInRange($theInt, $min, $max = 2000000000, $zeroValue = 0) { self::logDeprecatedFunction(); @@ -868,7 +868,7 @@ final class t3lib_div { * * @param integer $theInt Integer string to process * @return integer - * @deprecated since TYPO3 4.6, will be removed in TYPO3 4.8 - Use t3lib_utility_Math::convertToPositiveInteger() instead + * @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 - Use t3lib_utility_Math::convertToPositiveInteger() instead */ public static function intval_positive($theInt) { self::logDeprecatedFunction(); @@ -880,7 +880,7 @@ final class t3lib_div { * * @param string $verNumberStr Version number on format x.x.x * @return integer Integer version of version number (where each part can count to 999) - * @deprecated since TYPO3 4.6, will be removed in TYPO3 4.9 - Use t3lib_utility_VersionNumber::convertVersionNumberToInteger() instead + * @deprecated since TYPO3 4.6, will be removed in TYPO3 6.1 - Use t3lib_utility_VersionNumber::convertVersionNumberToInteger() instead */ public static function int_from_ver($verNumberStr) { // Deprecation log is activated only for TYPO3 4.7 and above @@ -1066,25 +1066,12 @@ final class t3lib_div { return self::modifyHTMLColor($color, $all, $all, $all); } - /** - * Removes comma (if present) in the end of string - * - * @param string $string String from which the comma in the end (if any) will be removed. - * @return string - * @deprecated since TYPO3 4.5, will be removed in TYPO3 4.7 - Use rtrim() directly - */ - public static function rm_endcomma($string) { - self::logDeprecatedFunction(); - - return rtrim($string, ','); - } - /** * Tests if the input can be interpreted as integer. * * @param mixed $var Any input variable to test * @return boolean Returns TRUE if string is an integer - * @deprecated since TYPO3 4.6, will be removed in TYPO3 4.8 - Use t3lib_utility_Math::canBeInterpretedAsInteger() instead + * @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 - Use t3lib_utility_Math::canBeInterpretedAsInteger() instead */ public static function testInt($var) { self::logDeprecatedFunction(); @@ -1177,7 +1164,7 @@ final class t3lib_div { * @param string $string Input string, eg "123 + 456 / 789 - 4" * @return integer Calculated value. Or error string. * @see calcParenthesis() - * @deprecated since TYPO3 4.6, will be removed in TYPO3 4.8 - Use t3lib_utility_Math::calculateWithPriorityToAdditionAndSubtraction() instead + * @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 - Use t3lib_utility_Math::calculateWithPriorityToAdditionAndSubtraction() instead */ public static function calcPriority($string) { self::logDeprecatedFunction(); @@ -1191,7 +1178,7 @@ final class t3lib_div { * @param string $string Input string, eg "(123 + 456) / 789 - 4" * @return integer Calculated value. Or error string. * @see calcPriority(), tslib_cObj::stdWrap() - * @deprecated since TYPO3 4.6, will be removed in TYPO3 4.8 - Use t3lib_utility_Math::calculateWithParentheses() instead + * @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 - Use t3lib_utility_Math::calculateWithParentheses() instead */ public static function calcParenthesis($string) { self::logDeprecatedFunction(); @@ -1272,7 +1259,10 @@ final class t3lib_div { if (strlen($email) > 320) { return FALSE; } - return (filter_var($email, FILTER_VALIDATE_EMAIL) !== FALSE); + require_once(PATH_typo3 . 'contrib/idna/idna_convert.class.php'); + $IDN = new idna_convert(array('idn_version' => 2008)); + + return (filter_var($IDN->encode($email), FILTER_VALIDATE_EMAIL) !== FALSE); } /** @@ -1522,7 +1512,10 @@ final class t3lib_div { * @return boolean Whether the given URL is valid */ public static function isValidUrl($url) { - return (filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED) !== FALSE); + require_once(PATH_typo3 . 'contrib/idna/idna_convert.class.php'); + $IDN = new idna_convert(array('idn_version' => 2008)); + + return (filter_var($IDN->encode($url), FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED) !== FALSE); } @@ -1862,28 +1855,30 @@ final class t3lib_div { * @param array $arr1 Second array, overruling the first array * @param boolean $notAddKeys If set, keys that are NOT found in $arr0 (first array) will not be set. Thus only existing value can/will be overruled from second array. * @param boolean $includeEmptyValues If set, values from $arr1 will overrule if they are empty or zero. Default: TRUE + * @param boolean $enableUnsetFeature If set, special values "__UNSET" can be used in the second array in order to unset array keys in the resulting array. * @return array Resulting array where $arr1 values has overruled $arr0 values */ - public static function array_merge_recursive_overrule(array $arr0, array $arr1, $notAddKeys = FALSE, $includeEmptyValues = TRUE) { + public static function array_merge_recursive_overrule(array $arr0, array $arr1, $notAddKeys = FALSE, $includeEmptyValues = TRUE, $enableUnsetFeature = TRUE) { foreach ($arr1 as $key => $val) { if (is_array($arr0[$key])) { if (is_array($arr1[$key])) { - $arr0[$key] = self::array_merge_recursive_overrule($arr0[$key], $arr1[$key], $notAddKeys, $includeEmptyValues); + $arr0[$key] = self::array_merge_recursive_overrule( + $arr0[$key], + $arr1[$key], + $notAddKeys, + $includeEmptyValues, + $enableUnsetFeature + ); } - } else { - if ($notAddKeys) { - if (isset($arr0[$key])) { - if ($includeEmptyValues || $val) { - $arr0[$key] = $val; - } - } - } else { - if ($includeEmptyValues || $val) { - $arr0[$key] = $val; - } + } elseif (!$notAddKeys || isset($arr0[$key])) { + if ($enableUnsetFeature && $val === '__UNSET') { + unset($arr0[$key]); + } elseif ($includeEmptyValues || $val) { + $arr0[$key] = $val; } } } + reset($arr0); return $arr0; } @@ -2198,16 +2193,8 @@ final class t3lib_div { */ public static function array2xml_cs(array $array, $docTag = 'phparray', array $options = array(), $charset = '') { - // Figure out charset if not given explicitly: - if (!$charset) { - if ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']) { // First priority: forceCharset! If set, this will be authoritative! - $charset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']; - } elseif (is_object($GLOBALS['LANG'])) { - $charset = $GLOBALS['LANG']->charSet; // If "LANG" is around, that will hold the current charset - } else { - $charset = 'iso-8859-1'; // THIS is just a hopeful guess! - } - } + // Set default charset unless explicitly specified + $charset = $charset ? $charset : 'utf-8'; // Return XML: return '' . LF . @@ -2406,7 +2393,7 @@ final class t3lib_div { // default output charset is UTF-8, only ASCII, ISO-8859-1 and UTF-8 are supported!!! $match = array(); preg_match('/^[[:space:]]*<\?xml[^>]*encoding[[:space:]]*=[[:space:]]*"([^"]*)"/', substr($string, 0, 200), $match); - $theCharset = $match[1] ? $match[1] : ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'] ? $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'] : 'iso-8859-1'); + $theCharset = $match[1] ? $match[1] : 'utf-8'; xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $theCharset); // us-ascii / utf-8 / iso-8859-1 // Parse content: @@ -2742,10 +2729,10 @@ final class t3lib_div { ) ); - $content = file_get_contents($url, FALSE, $ctx); + $content = @file_get_contents($url, FALSE, $ctx); if ($content === FALSE && isset($report)) { - $report['error'] = -1; + $report['error'] = -1; $report['message'] = 'Couldn\'t get URL: ' . implode(LF, $http_response_header); } } else { @@ -2753,10 +2740,10 @@ final class t3lib_div { $report['lib'] = 'file'; } - $content = file_get_contents($url); + $content = @file_get_contents($url); if ($content === FALSE && isset($report)) { - $report['error'] = -1; + $report['error'] = -1; $report['message'] = 'Couldn\'t get URL: ' . implode(LF, $http_response_header); } } @@ -2989,7 +2976,7 @@ final class t3lib_div { $result = @mkdir($fullDirectoryPath, $permissionMask, TRUE); if (!$result) { - throw new \RuntimeException('Could not create directory!', 1170251400); + throw new \RuntimeException('Could not create directory "' . $fullDirectoryPath . '"!', 1170251400); } } return $firstCreatedPath; @@ -3090,7 +3077,7 @@ final class t3lib_div { $sortarray[$key] = filemtime($path . '/' . $entry); } elseif ($order) { - $sortarray[$key] = $entry; + $sortarray[$key] = strtolower($entry); } } } @@ -3341,99 +3328,6 @@ final class t3lib_div { return $fullName; } - - /************************* - * - * DEBUG helper FUNCTIONS - * - *************************/ - - /* Deprecated since 4.5, use t3lib_utility_Debug */ - - - /** - * Returns a string with a list of ascii-values for the first $characters characters in $string - * - * @param string $string String to show ASCII value for - * @param integer $characters Number of characters to show - * @return string The string with ASCII values in separated by a space char. - * @deprecated since TYPO3 4.5 - Use t3lib_utility_Debug::ordinalValue instead - */ - public static function debug_ordvalue($string, $characters = 100) { - self::logDeprecatedFunction(); - return t3lib_utility_Debug::ordinalValue($string, $characters); - } - - /** - * Returns HTML-code, which is a visual representation of a multidimensional array - * use t3lib_div::print_array() in order to print an array - * Returns FALSE if $array_in is not an array - * - * @param mixed $array_in Array to view - * @return string HTML output - * @deprecated since TYPO3 4.5 - Use t3lib_utility_Debug::viewArray instead - */ - public static function view_array($array_in) { - self::logDeprecatedFunction(); - return t3lib_utility_Debug::viewArray($array_in); - } - - /** - * Prints an array - * - * @param mixed $array_in Array to print visually (in a table). - * @return void - * @see view_array() - * @deprecated since TYPO3 4.5 - Use t3lib_utility_Debug::printArray instead - */ - public static function print_array($array_in) { - self::logDeprecatedFunction(); - t3lib_utility_Debug::printArray($array_in); - } - - /** - * Makes debug output - * Prints $var in bold between two vertical lines - * If not $var the word 'debug' is printed - * If $var is an array, the array is printed by t3lib_div::print_array() - * - * @param mixed $var Variable to print - * @param string $header The header. - * @param string $group Group for the debug console - * @return void - * @deprecated since TYPO3 4.5 - Use t3lib_utility_Debug::debug instead - */ - public static function debug($var = '', $header = '', $group = 'Debug') { - self::logDeprecatedFunction(); - t3lib_utility_Debug::debug($var, $header, $group); - } - - /** - * Displays the "path" of the function call stack in a string, using debug_backtrace - * - * @return string - * @deprecated since TYPO3 4.5 - Use t3lib_utility_Debug::debugTrail instead - */ - public static function debug_trail() { - self::logDeprecatedFunction(); - return t3lib_utility_Debug::debugTrail(); - } - - /** - * Displays an array as rows in a table. Useful to debug output like an array of database records. - * - * @param mixed $rows Array of arrays with similar keys - * @param string $header Table header - * @param boolean $returnHTML If TRUE, will return content instead of echo'ing out. - * @return mixed Outputs to browser or returns an HTML string if $returnHTML is TRUE - * @deprecated since TYPO3 4.5 - Use t3lib_utility_Debug::debugRows instead - */ - public static function debugRows($rows, $header = '', $returnHTML = FALSE) { - self::logDeprecatedFunction(); - return t3lib_utility_Debug::debugRows($rows, $header, $returnHTML); - } - - /************************* * * SYSTEM INFORMATION @@ -3750,7 +3644,7 @@ final class t3lib_div { if ($proxySSL == '*') { $proxySSL = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP']; } - if (self::cmpIP($_SERVER['REMOTE_ADDR'], $proxySSL)) { + if (self::cmpIP(self::getIndpEnv('REMOTE_ADDR'), $proxySSL)) { $retVal = TRUE; } else { $retVal = $_SERVER['SSL_SESSION_ID'] || !strcasecmp($_SERVER['HTTPS'], 'on') || !strcmp($_SERVER['HTTPS'], '1') ? TRUE : FALSE; // see http://bugs.typo3.org/view.php?id=3909 @@ -3959,7 +3853,7 @@ final class t3lib_div { * So it's compatible with the UNIX style path strings valid for TYPO3 internally. * * @param string $theFile File path to evaluate - * @return boolean TRUE, $theFile is allowed path string + * @return boolean TRUE, $theFile is allowed path string, FALSE otherwise * @see http://php.net/manual/en/security.filesystem.nullbytes.php * @todo Possible improvement: Should it rawurldecode the string first to check if any of these characters is encoded? */ @@ -3967,6 +3861,8 @@ final class t3lib_div { if (strpos($theFile, '//') === FALSE && strpos($theFile, '\\') === FALSE && !preg_match('#(?:^\.\.|/\.\./|[[:cntrl:]])#u', $theFile)) { return TRUE; } + + return FALSE; } /** @@ -4175,18 +4071,15 @@ final class t3lib_div { * @param string $addQueryParams Query-parameters: "&xxx=yyy&zzz=uuu" * @return array Array with key/value pairs of query-parameters WITHOUT a certain list of variable names (like id, type, no_cache etc.) and WITH a variable, encryptionKey, specific for this server/installation * @see tslib_fe::makeCacheHash(), tslib_cObj::typoLink(), t3lib_div::calculateCHash() + * @deprecated since TYPO3 4.7 - will be removed in TYPO3 6.1 - use t3lib_cacheHash instead */ public static function cHashParams($addQueryParams) { + t3lib_div::logDeprecatedFunction(); $params = explode('&', substr($addQueryParams, 1)); // Splitting parameters up + /* @var $cacheHash t3lib_cacheHash */ + $cacheHash = t3lib_div::makeInstance('t3lib_cacheHash'); + $pA = $cacheHash->getRelevantParameters($addQueryParams); - // Make array: - $pA = array(); - foreach ($params as $theP) { - $pKV = explode('=', $theP); // Splitting single param by '=' sign - if (!self::inList('id,type,no_cache,cHash,MP,ftu', $pKV[0]) && !preg_match('/TSFE_ADMIN_PANEL\[.*?\]/', $pKV[0])) { - $pA[rawurldecode($pKV[0])] = (string) rawurldecode($pKV[1]); - } - } // Hook: Allows to manipulate the parameters which are taken to build the chash: if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['cHashParamsHook'])) { $cHashParamsHook =& $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['cHashParamsHook']; @@ -4202,9 +4095,6 @@ final class t3lib_div { } } } - // Finish and sort parameters array by keys: - $pA['encryptionKey'] = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']; - ksort($pA); return $pA; } @@ -4215,11 +4105,13 @@ final class t3lib_div { * @param string $addQueryParams Query-parameters: "&xxx=yyy&zzz=uuu" * @return string Hash of all the values * @see t3lib_div::cHashParams(), t3lib_div::calculateCHash() + * @deprecated since TYPO3 4.7 - will be removed in TYPO3 6.1 - use t3lib_cacheHash instead */ public static function generateCHash($addQueryParams) { - $cHashParams = self::cHashParams($addQueryParams); - $cHash = self::calculateCHash($cHashParams); - return $cHash; + t3lib_div::logDeprecatedFunction(); + /* @var $cacheHash t3lib_cacheHash */ + $cacheHash = t3lib_div::makeInstance('t3lib_cacheHash'); + return $cacheHash->generateForParameters($addQueryParams); } /** @@ -4227,10 +4119,13 @@ final class t3lib_div { * * @param array $params Array of key-value pairs * @return string Hash of all the values + * @deprecated since TYPO3 4.7 - will be removed in TYPO3 6.1 - use t3lib_cacheHash instead */ public static function calculateCHash($params) { - $cHash = md5(serialize($params)); - return $cHash; + t3lib_div::logDeprecatedFunction(); + /* @var $cacheHash t3lib_cacheHash */ + $cacheHash = t3lib_div::makeInstance('t3lib_cacheHash'); + return $cacheHash->calculateCacheHash($params); } /** @@ -4282,7 +4177,7 @@ final class t3lib_div { * @param string $langKey TYPO3 language key, eg. "dk" or "de" or "default" * @param string $charset Character set (optional) * @return array LOCAL_LANG array in return. - * @deprecated since TYPO3 4.6, will be removed in TYPO3 4.8 - use t3lib_l10n_parser_Llphp::getParsedData() from now on + * @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 - use t3lib_l10n_parser_Llphp::getParsedData() from now on */ public static function readLLPHPfile($fileRef, $langKey, $charset = '') { t3lib_div::logDeprecatedFunction(); @@ -4298,14 +4193,11 @@ final class t3lib_div { if (@is_file($fileRef) && $langKey) { // Set charsets: - $sourceCharset = $csConvObj->parse_charset($csConvObj->charSetArray[$langKey] ? $csConvObj->charSetArray[$langKey] : 'iso-8859-1'); + $sourceCharset = $csConvObj->parse_charset($csConvObj->charSetArray[$langKey] ? $csConvObj->charSetArray[$langKey] : 'utf-8'); if ($charset) { $targetCharset = $csConvObj->parse_charset($charset); - } elseif ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']) { - // when forceCharset is set, we store ALL labels in this charset!!! - $targetCharset = $csConvObj->parse_charset($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']); } else { - $targetCharset = $csConvObj->parse_charset($csConvObj->charSetArray[$langKey] ? $csConvObj->charSetArray[$langKey] : 'iso-8859-1'); + $targetCharset = 'utf-8'; } // Cache file name: @@ -4328,9 +4220,9 @@ final class t3lib_div { // converting the default language (English) // this needs to be done for a few accented loan words and extension names - if (is_array($LOCAL_LANG['default']) && $targetCharset != 'iso-8859-1') { + if (is_array($LOCAL_LANG['default']) && $targetCharset != 'utf-8') { foreach ($LOCAL_LANG['default'] as &$labelValue) { - $labelValue = $csConvObj->conv($labelValue, 'iso-8859-1', $targetCharset); + $labelValue = $csConvObj->conv($labelValue, 'utf-8', $targetCharset); } unset($labelValue); } @@ -4369,7 +4261,7 @@ final class t3lib_div { * @param string $langKey TYPO3 language key, eg. "dk" or "de" or "default" * @param string $charset Character set (optional) * @return array LOCAL_LANG array in return. - * @deprecated since TYPO3 4.6, will be removed in TYPO3 4.8 - use t3lib_l10n_parser_Llxml::getParsedData() from now on + * @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 - use t3lib_l10n_parser_Llxml::getParsedData() from now on */ public static function readLLXMLfile($fileRef, $langKey, $charset = '') { t3lib_div::logDeprecatedFunction(); @@ -4388,11 +4280,8 @@ final class t3lib_div { // Set charset: if ($charset) { $targetCharset = $csConvObj->parse_charset($charset); - } elseif ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']) { - // when forceCharset is set, we store ALL labels in this charset!!! - $targetCharset = $csConvObj->parse_charset($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']); } else { - $targetCharset = $csConvObj->parse_charset($csConvObj->charSetArray[$langKey] ? $csConvObj->charSetArray[$langKey] : 'iso-8859-1'); + $targetCharset = 'utf-8'; } // Cache file name: @@ -5177,7 +5066,7 @@ final class t3lib_div { /** * Simple substitute for the PHP function mail() which allows you to specify encoding and character set * The fifth parameter ($encoding) will allow you to specify 'base64' encryption for the output (set $encoding=base64) - * Further the output has the charset set to ISO-8859-1 by default. + * Further the output has the charset set to UTF-8 by default. * * @param string $email Email address to send to. (see PHP function mail()) * @param string $subject Subject line, non-encoded. (see PHP function mail()) @@ -5190,7 +5079,7 @@ final class t3lib_div { */ public static function plainMailEncoded($email, $subject, $message, $headers = '', $encoding = 'quoted-printable', $charset = '', $dontEncodeHeader = FALSE) { if (!$charset) { - $charset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'] ? $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'] : 'ISO-8859-1'; + $charset = 'utf-8'; } $email = self::normalizeMailAddress($email); @@ -5305,7 +5194,7 @@ final class t3lib_div { * @param string $charset Charset used for encoding * @return string The encoded string */ - public static function encodeHeader($line, $enc = 'quoted-printable', $charset = 'iso-8859-1') { + public static function encodeHeader($line, $enc = 'quoted-printable', $charset = 'utf-8') { // Avoid problems if "###" is found in $line (would conflict with the placeholder which is used below) if (strpos($line, '###') !== FALSE) { return $line; @@ -5361,30 +5250,32 @@ final class t3lib_div { * @see makeRedirectUrl() */ public static function substUrlsInPlainText($message, $urlmode = '76', $index_script_url = '') { - // Substitute URLs with shorter links: - foreach (array('http', 'https') as $protocol) { - $urlSplit = explode($protocol . '://', $message); - foreach ($urlSplit as $c => &$v) { - if ($c) { - $newParts = preg_split('/\s|[<>"{}|\\\^`()\']/', $v, 2); - $newURL = $protocol . '://' . $newParts[0]; + $lengthLimit = FALSE; - switch ((string) $urlmode) { - case 'all': - $newURL = self::makeRedirectUrl($newURL, 0, $index_script_url); - break; - case '76': - $newURL = self::makeRedirectUrl($newURL, 76, $index_script_url); - break; - } - $v = $newURL . substr($v, strlen($newParts[0])); - } - } - unset($v); - $message = implode('', $urlSplit); + switch ((string) $urlmode) { + case '': + $lengthLimit = FALSE; + break; + case 'all': + $lengthLimit = 0; + break; + case '76': + default: + $lengthLimit = (int) $urlmode; } - return $message; + if ($lengthLimit === FALSE) { + // no processing + $messageSubstituted = $message; + } else { + $messageSubstituted = preg_replace( + '/(http|https):\/\/.+(?=[\]\.\?]*([\! \'"()<>]+|$))/eiU', + 'self::makeRedirectUrl("\\0",' . $lengthLimit . ',"' . $index_script_url . '")', + $message + ); + } + + return $messageSubstituted; } /** @@ -5775,25 +5666,15 @@ final class t3lib_div { /** - * Quotes a string for usage as JS parameter. Depends whether the value is - * used in script tags (it doesn't need/must not get htmlspecialchar'ed in - * this case). + * Quotes a string for usage as JS parameter. * * @param string $value the string to encode, may be empty - * @param boolean $withinCData - * whether the escaped data is expected to be used as CDATA and thus - * does not need to be htmlspecialchared * * @return string the encoded value already quoted (with single quotes), * will not be empty */ - static public function quoteJSvalue($value, $withinCData = FALSE) { - $escapedValue = addcslashes( - $value, '\'' . '"' . '\\' . TAB . LF . CR - ); - if (!$withinCData) { - $escapedValue = htmlspecialchars($escapedValue); - } + public static function quoteJSvalue($value) { + $escapedValue = t3lib_div::makeInstance('t3lib_codec_JavaScriptEncoder')->encode($value); return '\'' . $escapedValue . '\''; } diff --git a/lib/typo3/class.t3lib_l10n_locales.php b/lib/typo3/class.t3lib_l10n_locales.php index 160f0ffddaa..535bc5ccad2 100644 --- a/lib/typo3/class.t3lib_l10n_locales.php +++ b/lib/typo3/class.t3lib_l10n_locales.php @@ -47,6 +47,7 @@ class t3lib_l10n_Locales implements t3lib_Singleton { */ protected $languages = array( 'default' => 'English', + 'af' => 'Afrikaans', 'ar' => 'Arabic', 'bs' => 'Bosnian', 'bg' => 'Bulgarian', @@ -101,7 +102,7 @@ class t3lib_l10n_Locales implements t3lib_Singleton { /** * Supported TYPO3 locales - * @deprecated since TYPO3 4.6, will be removed in TYPO3 4.8 + * @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 * @var array */ protected $locales = array(); @@ -176,12 +177,12 @@ class t3lib_l10n_Locales implements t3lib_Singleton { } /** - * @deprecated since TYPO3 4.6, will be removed in TYPO3 4.8 + * @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 */ $instance->locales = array_keys($instance->languages); /** - * @deprecated since TYPO3 4.6, will be removed in TYPO3 4.8 + * @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 */ define('TYPO3_languages', implode('|', $instance->getLocales())); } diff --git a/lib/typo3/readme_moodle.txt b/lib/typo3/readme_moodle.txt index 7f390fdeb97..d45a1835b7b 100644 --- a/lib/typo3/readme_moodle.txt +++ b/lib/typo3/readme_moodle.txt @@ -1,10 +1,12 @@ -Description of Typo3 libraries (v 4.6.8) import into Moodle +Description of Typo3 libraries (v 4.7.4) import into Moodle Changes: none skodak, stronk7 +Previous changes: + 25 June 2010 - Martin D (4.3.0RC1) I renamed getURL to getUrl since it was being called that way everywhere. I added a check to avoid notices on lib/typo3/class.t3lib_cs.php line 976 diff --git a/message/lib.php b/message/lib.php index db95cea31a9..38a6d31d321 100644 --- a/message/lib.php +++ b/message/lib.php @@ -749,29 +749,15 @@ function message_get_recent_conversations($user, $limitfrom=0, $limitto=100) { } } - //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 * @@ -844,7 +830,7 @@ function message_print_recent_notifications($user=null) { $showicontext = false; $showotheruser = false; - message_print_recent_messages_table($notifications, $user, $showotheruser, $showicontext); + message_print_recent_messages_table($notifications, $user, $showotheruser, $showicontext, true); } /** @@ -854,9 +840,10 @@ function message_print_recent_notifications($user=null) { * @param object $user the current user * @param bool $showotheruser display information on the other user? * @param bool $showicontext show text next to the action icons? + * @param bool $forcetexttohtml Force text to go through @see text_to_html() via @see format_text() * @return void */ -function message_print_recent_messages_table($messages, $user=null, $showotheruser=true, $showicontext=false) { +function message_print_recent_messages_table($messages, $user=null, $showotheruser=true, $showicontext=false, $forcetexttohtml=false) { global $OUTPUT; static $dateformat; @@ -914,7 +901,7 @@ function message_print_recent_messages_table($messages, $user=null, $showotherus } echo html_writer::tag('span', userdate($message->timecreated, $dateformat), array('class' => 'messagedate')); - echo html_writer::tag('span', format_text($messagetoprint, FORMAT_HTML), array('class' => 'themessage')); + echo html_writer::tag('span', format_text($messagetoprint, $forcetexttohtml?FORMAT_MOODLE:FORMAT_HTML), array('class' => 'themessage')); echo message_format_contexturl($message); echo html_writer::end_tag('div');//end singlemessage } @@ -1804,7 +1791,7 @@ function message_get_history($user1, $user2, $limitnum=0, $viewingnewmessages=fa 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 @@ -1812,15 +1799,16 @@ function message_get_history($user1, $user2, $limitnum=0, $viewingnewmessages=fa 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; diff --git a/mod/assign/assignmentplugin.php b/mod/assign/assignmentplugin.php index dcb31e96b72..d71281b4120 100644 --- a/mod/assign/assignmentplugin.php +++ b/mod/assign/assignmentplugin.php @@ -216,7 +216,7 @@ abstract class assign_plugin { * * @return bool - if false - this plugin will not accept submissions / feedback */ - public final function is_enabled() { + public function is_enabled() { return $this->get_config('enabled'); } diff --git a/mod/assign/backup/moodle2/backup_assign_stepslib.php b/mod/assign/backup/moodle2/backup_assign_stepslib.php index 06a8fdc4d9b..494fd14b474 100644 --- a/mod/assign/backup/moodle2/backup_assign_stepslib.php +++ b/mod/assign/backup/moodle2/backup_assign_stepslib.php @@ -48,15 +48,21 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st 'intro', 'introformat', 'alwaysshowdescription', - 'preventlatesubmissions', 'submissiondrafts', 'sendnotifications', 'sendlatenotifications', 'duedate', + 'cutoffdate', 'allowsubmissionsfromdate', 'grade', 'timemodified', - 'completionsubmit')); + 'completionsubmit', + 'requiresubmissionstatement', + 'teamsubmission', + 'requireallteammemberssubmit', + 'teamsubmissiongroupingid', + 'blindmarking', + 'revealidentities')); $submissions = new backup_nested_element('submissions'); @@ -64,7 +70,8 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st array('userid', 'timecreated', 'timemodified', - 'status')); + 'status', + 'groupid')); $grades = new backup_nested_element('grades'); @@ -75,7 +82,8 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st 'grader', 'grade', 'locked', - 'mailed')); + 'mailed', + 'extensionduedate')); $pluginconfigs = new backup_nested_element('plugin_configs'); @@ -115,8 +123,10 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st // Define id annotations $submission->annotate_ids('user', 'userid'); + $submission->annotate_ids('group', 'groupid'); $grade->annotate_ids('user', 'userid'); $grade->annotate_ids('user', 'grader'); + $assign->annotate_ids('grouping', 'teamsubmissiongroupingid'); // Define file annotations $assign->annotate_files('mod_assign', 'intro', null); // This file area hasn't itemid diff --git a/mod/assign/backup/moodle2/restore_assign_stepslib.php b/mod/assign/backup/moodle2/restore_assign_stepslib.php index 6ad67254480..890c64dc37b 100644 --- a/mod/assign/backup/moodle2/restore_assign_stepslib.php +++ b/mod/assign/backup/moodle2/restore_assign_stepslib.php @@ -73,6 +73,19 @@ class restore_assign_activity_structure_step extends restore_activity_structure_ $data->timemodified = $this->apply_date_offset($data->timemodified); $data->allowsubmissionsfromdate = $this->apply_date_offset($data->allowsubmissionsfromdate); $data->duedate = $this->apply_date_offset($data->duedate); + if ($data->teamsubmissiongroupingid > 0) { + $data->teamsubmissiongroupingid = $this->get_mappingid('grouping', $data->teamsubmissiongroupingid); + } + + if (!isset($data->cutoffdate)) { + $data->cutoffdate = 0; + } + + if (!empty($data->preventlatesubmissions)) { + $data->cutoffdate = $data->duedate; + } else { + $data->cutoffdate = $this->apply_date_offset($data->cutoffdate); + } $newitemid = $DB->insert_record('assign', $data); @@ -95,7 +108,12 @@ class restore_assign_activity_structure_step extends restore_activity_structure_ $data->timemodified = $this->apply_date_offset($data->timemodified); $data->timecreated = $this->apply_date_offset($data->timecreated); - $data->userid = $this->get_mappingid('user', $data->userid); + if ($data->userid > 0) { + $data->userid = $this->get_mappingid('user', $data->userid); + } + if ($data->groupid > 0) { + $data->groupid = $this->get_mappingid('group', $data->groupid); + } $newitemid = $DB->insert_record('assign_submission', $data); @@ -121,6 +139,7 @@ class restore_assign_activity_structure_step extends restore_activity_structure_ $data->timecreated = $this->apply_date_offset($data->timecreated); $data->userid = $this->get_mappingid('user', $data->userid); $data->grader = $this->get_mappingid('user', $data->grader); + $data->extensionduedate = $this->apply_date_offset($data->extensionduedate); $newitemid = $DB->insert_record('assign_grades', $data); diff --git a/mod/assign/db/access.php b/mod/assign/db/access.php index 9f8cde73d5e..7028038907e 100644 --- a/mod/assign/db/access.php +++ b/mod/assign/db/access.php @@ -81,6 +81,28 @@ $capabilities = array( ), 'clonepermissionsfrom' => 'moodle/course:manageactivities' ), + + 'mod/assign:grantextension' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array( + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ), + 'clonepermissionsfrom' => 'gradereport/grader:view' + ), + + 'mod/assign:revealidentities' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array( + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ) + ), + + ); diff --git a/mod/assign/db/install.xml b/mod/assign/db/install.xml index 213a62b6655..95002326fea 100644 --- a/mod/assign/db/install.xml +++ b/mod/assign/db/install.xml @@ -1,5 +1,5 @@ - @@ -12,9 +12,8 @@ - - - + + @@ -22,13 +21,20 @@ - + + + + + + + - + + @@ -38,7 +44,8 @@ - + + @@ -58,7 +65,8 @@ - + + @@ -69,7 +77,7 @@
    - +
    @@ -88,5 +96,17 @@
    + + + + + + + + + + + +
    diff --git a/mod/assign/db/log.php b/mod/assign/db/log.php index 5401cb89eab..45f19580079 100644 --- a/mod/assign/db/log.php +++ b/mod/assign/db/log.php @@ -30,6 +30,7 @@ $logs = array( array('module'=>'assign', 'action'=>'download all submissions', 'mtable'=>'assign', 'field'=>'name'), array('module'=>'assign', 'action'=>'grade submission', 'mtable'=>'assign', 'field'=>'name'), array('module'=>'assign', 'action'=>'lock submission', 'mtable'=>'assign', 'field'=>'name'), + array('module'=>'assign', 'action'=>'reveal identities', 'mtable'=>'assign', 'field'=>'name'), array('module'=>'assign', 'action'=>'revert submission to draft', 'mtable'=>'assign', 'field'=>'name'), array('module'=>'assign', 'action'=>'submission statement accepted', 'mtable'=>'assign', 'field'=>'name'), array('module'=>'assign', 'action'=>'submit', 'mtable'=>'assign', 'field'=>'name'), diff --git a/mod/assign/db/upgrade.php b/mod/assign/db/upgrade.php index b80a60eb74a..20b7b915366 100644 --- a/mod/assign/db/upgrade.php +++ b/mod/assign/db/upgrade.php @@ -34,11 +34,11 @@ function xmldb_assign_upgrade($oldversion) { if ($oldversion < 2012051700) { - // Define field sendlatenotifications to be added to assign + // Define field to be added to assign. $table = new xmldb_table('assign'); $field = new xmldb_field('sendlatenotifications', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'sendnotifications'); - // Conditionally launch add field sendlatenotifications + // Conditionally launch add field. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } @@ -47,16 +47,17 @@ function xmldb_assign_upgrade($oldversion) { upgrade_mod_savepoint(true, 2012051700, 'assign'); } - // Moodle v2.3.0 release upgrade line - // Put any upgrade step following this + // Moodle v2.3.0 release upgrade line. + // Put any upgrade step following this. if ($oldversion < 2012071800) { - // Define field requiresubmissionstatement to be added to assign + // Define field requiresubmissionstatement to be added to assign. $table = new xmldb_table('assign'); $field = new xmldb_field('requiresubmissionstatement', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'timemodified'); - // Conditionally launch add field requiresubmissionstatement + // Conditionally launch add field requiresubmissionstatement. + if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } @@ -67,11 +68,11 @@ function xmldb_assign_upgrade($oldversion) { if ($oldversion < 2012081600) { - // Define field sendlatenotifications to be added to assign. + // Define field to be added to assign. $table = new xmldb_table('assign'); $field = new xmldb_field('completionsubmit', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'timemodified'); - // Conditionally launch add field sendlatenotifications. + // Conditionally launch add field. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } @@ -80,6 +81,122 @@ function xmldb_assign_upgrade($oldversion) { upgrade_mod_savepoint(true, 2012081600, 'assign'); } + // Individual extension dates support. + if ($oldversion < 2012082100) { + + // Define field cutoffdate to be added to assign. + $table = new xmldb_table('assign'); + $field = new xmldb_field('cutoffdate', XMLDB_TYPE_INTEGER, '10', null, + XMLDB_NOTNULL, null, '0', 'completionsubmit'); + + // Conditionally launch add field cutoffdate. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + // If prevent late is on - set cutoffdate to due date. + + // Now remove the preventlatesubmissions column. + $field = new xmldb_field('preventlatesubmissions', XMLDB_TYPE_INTEGER, '2', null, + XMLDB_NOTNULL, null, '0', 'nosubmissions'); + if ($dbman->field_exists($table, $field)) { + // Set the cutoffdate to the duedate if preventlatesubmissions was enabled. + $sql = 'UPDATE {assign} SET cutoffdate = duedate WHERE preventlatesubmissions = 1'; + $DB->execute($sql); + + $dbman->drop_field($table, $field); + } + + // Define field extensionduedate to be added to assign_grades + $table = new xmldb_table('assign_grades'); + $field = new xmldb_field('extensionduedate', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'mailed'); + + // Conditionally launch add field extensionduedate + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Assign savepoint reached. + upgrade_mod_savepoint(true, 2012082100, 'assign'); + } + + // Team assignment support. + if ($oldversion < 2012082300) { + + // Define field to be added to assign. + $table = new xmldb_table('assign'); + $field = new xmldb_field('teamsubmission', XMLDB_TYPE_INTEGER, '2', null, + XMLDB_NOTNULL, null, '0', 'cutoffdate'); + + // Conditionally launch add field. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $field = new xmldb_field('requireallteammemberssubmit', XMLDB_TYPE_INTEGER, '2', null, + XMLDB_NOTNULL, null, '0', 'teamsubmission'); + // Conditionally launch add field. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $field = new xmldb_field('teamsubmissiongroupingid', XMLDB_TYPE_INTEGER, '10', null, + XMLDB_NOTNULL, null, '0', 'requireallteammemberssubmit'); + // Conditionally launch add field. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $index = new xmldb_index('teamsubmissiongroupingid', XMLDB_INDEX_NOTUNIQUE, array('teamsubmissiongroupingid')); + // Conditionally launch add index. + if (!$dbman->index_exists($table, $index)) { + $dbman->add_index($table, $index); + } + $table = new xmldb_table('assign_submission'); + $field = new xmldb_field('groupid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'status'); + // Conditionally launch add field. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + upgrade_mod_savepoint(true, 2012082300, 'assign'); + } + if ($oldversion < 2012082400) { + + // Define table assign_user_mapping to be created + $table = new xmldb_table('assign_user_mapping'); + + // Adding fields to table assign_user_mapping + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('assignment', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + + // Adding keys to table assign_user_mapping + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + $table->add_key('assignment', XMLDB_KEY_FOREIGN, array('assignment'), 'assign', array('id')); + $table->add_key('user', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id')); + + // Conditionally launch create table for assign_user_mapping + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + // Define field blindmarking to be added to assign + $table = new xmldb_table('assign'); + $field = new xmldb_field('blindmarking', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'teamsubmissiongroupingid'); + + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Define field revealidentities to be added to assign + $table = new xmldb_table('assign'); + $field = new xmldb_field('revealidentities', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'blindmarking'); + + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // assign savepoint reached + upgrade_mod_savepoint(true, 2012082400, 'assign'); + } + + return true; } diff --git a/mod/assign/extensionform.php b/mod/assign/extensionform.php new file mode 100644 index 00000000000..f838638adfc --- /dev/null +++ b/mod/assign/extensionform.php @@ -0,0 +1,102 @@ +. + +/** + * This file contains the forms to create and edit an instance of this module + * + * @package mod_assign + * @copyright 2012 NetSpot {@link http://www.netspot.com.au} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); + + +require_once($CFG->libdir.'/formslib.php'); +require_once($CFG->dirroot . '/mod/assign/locallib.php'); + +/** + * Assignment extension dates form + * + * @package mod_assign + * @copyright 2012 NetSpot {@link http://www.netspot.com.au} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mod_assign_extension_form extends moodleform { + /** @var array $instance - The data passed to this form */ + private $instance; + + /** + * Define the form - called by parent constructor + */ + public function definition() { + $mform = $this->_form; + + list($coursemoduleid, $userid, $batchusers, $instance, $data) = $this->_customdata; + // Instance variable is used by the form validation function. + $this->instance = $instance; + + if ($batchusers) { + $listusersmessage = get_string('grantextensionforusers', 'assign', count(explode(',', $batchusers))); + $mform->addElement('static', 'applytoselectedusers', '', $listusersmessage); + } + if ($instance->allowsubmissionsfromdate) { + $mform->addElement('static', 'allowsubmissionsfromdate', get_string('allowsubmissionsfromdate', 'assign'), + userdate($instance->allowsubmissionsfromdate)); + } + if ($instance->duedate) { + $mform->addElement('static', 'duedate', get_string('duedate', 'assign'), userdate($instance->duedate)); + $finaldate = $instance->duedate; + } + if ($instance->cutoffdate) { + $mform->addElement('static', 'cutoffdate', get_string('cutoffdate', 'assign'), userdate($instance->cutoffdate)); + $finaldate = $instance->cutoffdate; + } + $mform->addElement('date_time_selector', 'extensionduedate', + get_string('extensionduedate', 'assign'), array('optional'=>true)); + $mform->setDefault('extensionduedate', $finaldate); + $mform->addElement('hidden', 'id', $coursemoduleid); + $mform->addElement('hidden', 'userid', $userid); + $mform->addElement('hidden', 'selectedusers', $batchusers); + $mform->addElement('hidden', 'action', 'saveextension'); + $this->add_action_buttons(true, get_string('savechanges', 'assign')); + + if ($data) { + $this->set_data($data); + } + } + + /** + * Perform validation on the extension form + * @param array $data + * @param array $files + */ + public function validation($data, $files) { + $errors = parent::validation($data, $files); + if ($this->instance->duedate && $data['extensionduedate']) { + if ($this->instance->duedate > $data['extensionduedate']) { + $errors['extensionduedate'] = get_string('extensionnotafterduedate', 'assign'); + } + } + if ($this->instance->allowsubmissionsfromdate && $data['extensionduedate']) { + if ($this->instance->allowsubmissionsfromdate > $data['extensionduedate']) { + $errors['extensionduedate'] = get_string('extensionnotafterfromdate', 'assign'); + } + } + + return $errors; + } +} diff --git a/mod/assign/feedback/file/lang/en/assignfeedback_file.php b/mod/assign/feedback/file/lang/en/assignfeedback_file.php index e62e6df8fe1..ca017e9dcc2 100644 --- a/mod/assign/feedback/file/lang/en/assignfeedback_file.php +++ b/mod/assign/feedback/file/lang/en/assignfeedback_file.php @@ -23,6 +23,7 @@ */ $string['configmaxbytes'] = 'Maximum file size'; +$string['countfiles'] = '{$a} files'; $string['default'] = 'Enabled by default'; $string['default_help'] = 'If set, this feedback method will be enabled by default for all new assignments.'; $string['enabled'] = 'File feedback'; diff --git a/mod/assign/gradingbatchoperationsform.php b/mod/assign/gradingbatchoperationsform.php index 2ae00a2d4a6..fa6a18e353d 100644 --- a/mod/assign/gradingbatchoperationsform.php +++ b/mod/assign/gradingbatchoperationsform.php @@ -52,6 +52,9 @@ class mod_assign_grading_batch_operations_form extends moodleform { if ($instance['submissiondrafts']) { $options['reverttodraft'] = get_string('reverttodraft', 'assign'); } + if ($instance['duedate']) { + $options['grantextension'] = get_string('grantextension', 'assign'); + } $mform->addElement('hidden', 'action', 'batchgradingoperation'); $mform->addElement('hidden', 'id', $instance['cm']); $mform->addElement('hidden', 'selectedusers', '', array('class'=>'selectedusers')); diff --git a/mod/assign/gradingtable.php b/mod/assign/gradingtable.php index 40bed663228..51bc69a0ee3 100644 --- a/mod/assign/gradingtable.php +++ b/mod/assign/gradingtable.php @@ -50,6 +50,13 @@ class assign_grading_table extends table_sql implements renderable { private $tablemaxrows = 10000; /** @var boolean $quickgrading */ private $quickgrading = false; + /** @var boolean $hasgrantextension - Only do the capability check once for the entire table */ + private $hasgrantextension = false; + /** @var array $groupsubmissions - A static cache of group submissions */ + private $groupsubmissions = array(); + /** @var array $submissiongroups - A static cache of submission groups */ + private $submissiongroups = array(); + /** * overridden constructor keeps a reference to the assignment class that is displaying this table @@ -88,9 +95,19 @@ class assign_grading_table extends table_sql implements renderable { $params['assignmentid1'] = (int)$this->assignment->get_instance()->id; $params['assignmentid2'] = (int)$this->assignment->get_instance()->id; - $fields = user_picture::fields('u') . ', u.id as userid, '; - $fields .= 's.status as status, s.id as submissionid, s.timecreated as firstsubmission, s.timemodified as timesubmitted, '; - $fields .= 'g.id as gradeid, g.grade as grade, g.timemodified as timemarked, g.timecreated as firstmarked, g.mailed as mailed, g.locked as locked'; + $fields = user_picture::fields('u') . ', '; + $fields .= 'u.id as userid, '; + $fields .= 's.status as status, '; + $fields .= 's.id as submissionid, '; + $fields .= 's.timecreated as firstsubmission, '; + $fields .= 's.timemodified as timesubmitted, '; + $fields .= 'g.id as gradeid, '; + $fields .= 'g.grade as grade, '; + $fields .= 'g.timemodified as timemarked, '; + $fields .= 'g.timecreated as firstmarked, '; + $fields .= 'g.mailed as mailed, '; + $fields .= 'g.locked as locked, '; + $fields .= 'g.extensionduedate as extensionduedate'; $from = '{user} u LEFT JOIN {assign_submission} s ON u.id = s.userid AND s.assignment = :assignmentid1' . ' LEFT JOIN {assign_grades} g ON u.id = g.userid AND g.assignment = :assignmentid2'; @@ -127,13 +144,19 @@ class assign_grading_table extends table_sql implements renderable { $headers[] = get_string('edit'); } - // User picture - $columns[] = 'picture'; - $headers[] = get_string('pictureofuser'); + // User picture. + if (!$this->assignment->is_blind_marking()) { + $columns[] = 'picture'; + $headers[] = get_string('pictureofuser'); - // Fullname - $columns[] = 'fullname'; - $headers[] = get_string('fullname'); + // Fullname. + $columns[] = 'fullname'; + $headers[] = get_string('fullname'); + } else { + // Record ID. + $columns[] = 'recordid'; + $headers[] = get_string('recordid', 'assign'); + } // Submission status if ($assignment->is_any_submission_plugin_enabled()) { @@ -141,6 +164,14 @@ class assign_grading_table extends table_sql implements renderable { $headers[] = get_string('status'); } + // Team submission columns + if ($assignment->get_instance()->teamsubmission) { + $columns[] = 'team'; + $headers[] = get_string('submissionteam', 'assign'); + + $columns[] = 'teamstatus'; + $headers[] = get_string('teamsubmissionstatus', 'assign'); + } // Grade $columns[] = 'grade'; @@ -177,6 +208,7 @@ class assign_grading_table extends table_sql implements renderable { // load the grading info for all users $this->gradinginfo = grade_get_grades($this->assignment->get_course()->id, 'mod', 'assign', $this->assignment->get_instance()->id, $users); + $this->hasgrantextension = has_capability('mod/assign:grantextension', $this->assignment->get_context()); if (!empty($CFG->enableoutcomes) && !empty($this->gradinginfo->outcomes)) { $columns[] = 'outcomes'; @@ -192,6 +224,11 @@ class assign_grading_table extends table_sql implements renderable { $this->no_sorting('select'); $this->no_sorting('outcomes'); + if ($assignment->get_instance()->teamsubmission) { + $this->no_sorting('team'); + $this->no_sorting('teamstatus'); + } + foreach ($this->assignment->get_submission_plugins() as $plugin) { if ($plugin->is_visible() && $plugin->is_enabled()) { $this->no_sorting('assignsubmission_' . $plugin->get_type()); @@ -205,6 +242,16 @@ class assign_grading_table extends table_sql implements renderable { } + /** + * Add a column with an ID that uniquely identifies this user in this assignment + * + * @return string + */ + function col_recordid(stdClass $row) { + return get_string('hiddenuser', 'assign', $this->assignment->get_uniqueid_for_user($row->userid)); + } + + /** * Add the userid to the row class so it can be updated via ajax * @@ -241,6 +288,71 @@ class assign_grading_table extends table_sql implements renderable { return $o; } + /** + * Get the team info for this user + * + * @param stdClass $row + * @return string The team name + */ + function col_team(stdClass $row) { + $submission = false; + $group = false; + $this->get_group_and_submission($row->id, $group, $submission); + if ($group) { + return $group->name; + } + return get_string('defaultteam', 'assign'); + } + + /** + * Use a static cache to try and reduce DB calls. + * + * @param int $userid The user id for this submission + * @param int $groupid The groupid (returned) + * @param mixed $submission The stdClass submission or false (returned) + */ + function get_group_and_submission($userid, &$group, &$submission) { + $group = false; + if (isset($this->submissiongroups[$userid])) { + $group = $this->submissiongroups[$userid]; + } else { + $group = $this->assignment->get_submission_group($userid, false); + $this->submissiongroups[$userid] = $group; + } + + $groupid = 0; + if ($group) { + $groupid = $group->id; + } + + if (isset($this->groupsubmissions[$groupid])) { + $submission = $this->groupsubmissions[$groupid]; + } else { + $submission = $this->assignment->get_group_submission($userid, $groupid, false); + $this->groupsubmissions[$groupid] = $submission; + } + } + + + /** + * Get the team status for this user + * + * @param stdClass $row + * @return string The team name + */ + function col_teamstatus(stdClass $row) { + $submission = false; + $group = false; + $this->get_group_and_submission($row->id, $group, $submission); + + $status = ''; + if ($submission) { + $status = $submission->status; + } + return get_string('submissionstatus_' . $status, 'assign'); + } + + /** * Format a list of outcomes * @@ -409,9 +521,14 @@ class assign_grading_table extends table_sql implements renderable { if ($this->assignment->is_any_submission_plugin_enabled()) { - $o .= $this->output->container(get_string('submissionstatus_' . $row->status, 'assign'), array('class'=>'submissionstatus' .$row->status)); + $o .= $this->output->container(get_string('submissionstatus_' . $row->status, 'assign'), + array('class'=>'submissionstatus' .$row->status)); if ($this->assignment->get_instance()->duedate && $row->timesubmitted > $this->assignment->get_instance()->duedate) { - $o .= $this->output->container(get_string('submittedlateshort', 'assign', format_time($row->timesubmitted - $this->assignment->get_instance()->duedate)), 'latesubmission'); + if (!$row->extensionduedate || $row->timesubmitted > $row->extensionduedate) { + $latemessage = get_string('submittedlateshort', 'assign', + format_time($row->timesubmitted - $this->assignment->get_instance()->duedate)); + $o .= $this->output->container($latemessage, 'latesubmission'); + } } if ($row->locked) { $o .= $this->output->container(get_string('submissionslockedshort', 'assign'), 'lockedsubmission'); @@ -419,6 +536,19 @@ class assign_grading_table extends table_sql implements renderable { if ($row->grade !== NULL && $row->grade >= 0) { $o .= $this->output->container(get_string('graded', 'assign'), 'submissiongraded'); } + if (!$row->timesubmitted) { + $now = time(); + $due = $this->assignment->get_instance()->duedate; + if ($row->extensionduedate) { + $due = $row->extensionduedate; + } + if ($due && ($now > $due)) { + $o .= $this->output->container(get_string('overdue', 'assign', format_time($now - $due)), 'overduesubmission'); + } + } + if ($row->extensionduedate) { + $o .= $this->output->container(get_string('userextensiondate', 'assign', userdate($row->extensionduedate)), 'extensiondate'); + } } return $o; @@ -450,23 +580,42 @@ class assign_grading_table extends table_sql implements renderable { } $actions[$url->out(false)] = $description; - if (!$row->status || $row->status == ASSIGN_SUBMISSION_STATUS_DRAFT || !$this->assignment->get_instance()->submissiondrafts) { - if (!$row->locked) { + // Hide for offline assignments. + if ($this->assignment->is_any_submission_plugin_enabled()) { + if (!$row->status || + $row->status == ASSIGN_SUBMISSION_STATUS_DRAFT || + !$this->assignment->get_instance()->submissiondrafts) { + + if (!$row->locked) { + $url = new moodle_url('/mod/assign/view.php', array('id' => $this->assignment->get_course_module()->id, + 'userid'=>$row->id, + 'action'=>'lock', + 'sesskey'=>sesskey(), + 'page'=>$this->currpage)); + $description = get_string('preventsubmissionsshort', 'assign'); + $actions[$url->out(false)] = $description; + } else { + $url = new moodle_url('/mod/assign/view.php', array('id' => $this->assignment->get_course_module()->id, + 'userid'=>$row->id, + 'action'=>'unlock', + 'sesskey'=>sesskey(), + 'page'=>$this->currpage)); + $description = get_string('allowsubmissionsshort', 'assign'); + $actions[$url->out(false)] = $description; + } + } + + if (($this->assignment->get_instance()->duedate || + $this->assignment->get_instance()->cutoffdate) && + $this->hasgrantextension) { $url = new moodle_url('/mod/assign/view.php', array('id' => $this->assignment->get_course_module()->id, 'userid'=>$row->id, - 'action'=>'lock', + 'action'=>'grantextension', 'sesskey'=>sesskey(), 'page'=>$this->currpage)); - $description = get_string('preventsubmissionsshort', 'assign'); - $actions[$url->out(false)] = $description; - } else { - $url = new moodle_url('/mod/assign/view.php', array('id' => $this->assignment->get_course_module()->id, - 'userid'=>$row->id, - 'action'=>'unlock', - 'sesskey'=>sesskey(), - 'page'=>$this->currpage)); - $description = get_string('allowsubmissionsshort', 'assign'); + $description = get_string('grantextension', 'assign'); $actions[$url->out(false)] = $description; + } } if ($row->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED && $this->assignment->get_instance()->submissiondrafts) { @@ -555,7 +704,15 @@ class assign_grading_table extends table_sql implements renderable { $plugin = $this->assignment->get_submission_plugin_by_type(substr($colname, strlen('assignsubmission_'))); if ($plugin->is_visible() && $plugin->is_enabled()) { - if ($row->submissionid) { + if ($this->assignment->get_instance()->teamsubmission) { + $group = false; + $submission = false; + $this->get_group_and_submission($row->id, $group, $submission); + if ($submission) { + return $this->format_plugin_summary_with_link($plugin, $submission, 'grading', array()); + } + } else if ($row->submissionid) { + $submission = new stdClass(); $submission->id = $row->submissionid; $submission->timecreated = $row->firstsubmission; diff --git a/mod/assign/lang/en/assign.php b/mod/assign/lang/en/assign.php index 29fbc833c51..d4b4d70c056 100644 --- a/mod/assign/lang/en/assign.php +++ b/mod/assign/lang/en/assign.php @@ -32,9 +32,12 @@ $string['allowsubmissionsfromdatesummary'] = 'This assignment will accept submis $string['allowsubmissionsanddescriptionfromdatesummary'] = 'The assignment details and submission form will be available from {$a}'; $string['alwaysshowdescription'] = 'Always show description'; $string['alwaysshowdescription_help'] = 'If disabled, the Assignment Description above will only become visible to students at the "Allow submissions from" date.'; +$string['applytoteam'] = 'Apply grades and feedback to entire team'; $string['assign:addinstance'] = 'Add a new assignment'; $string['assign:exportownsubmission'] = 'Export own submission'; $string['assign:grade'] = 'Grade assignment'; +$string['assign:grantextension'] = 'Grant extension'; +$string['assign:revealidentities'] = 'Reveal student identities'; $string['assign:submit'] = 'Submit assignment'; $string['assign:view'] = 'View assignment'; $string['assignfeedback'] = 'Feedback plugin'; @@ -60,11 +63,14 @@ $string['availability'] = 'Availability'; $string['backtoassignment'] = 'Back to assignment'; $string['batchoperationsdescription'] = 'With selected...'; $string['batchoperationconfirmlock'] = 'Lock all selected submissions?'; +$string['batchoperationconfirmgrantextension'] = 'Grant an extension to all selected submissions?'; $string['batchoperationconfirmunlock'] = 'Unlock all selected submissions?'; $string['batchoperationconfirmreverttodraft'] = 'Revert selected submissions to draft?'; $string['batchoperationlock'] = 'lock submissions'; $string['batchoperationunlock'] = 'unlock submissions'; $string['batchoperationreverttodraft'] = 'revert submissions to draft'; +$string['blindmarking'] = 'Blind marking'; +$string['blindmarking_help'] = 'Blind marking hides the identity of students to markers. Blind marking settings will be locked once a submission or grade has been made in relation to this assignment.'; $string['changegradewarning'] = 'This assignment has graded submissions and changing the grade will not automatically re-calculate existing submission grades. You must re-grade all existing submissions, if you wish to change the grade.'; $string['comment'] = 'Comment'; $string['completionsubmit'] = 'Student must submit to this activity to complete it'; @@ -78,8 +84,13 @@ $string['couldnotcreatecoursemodule'] = 'Could not create course module.'; $string['couldnotcreatenewassignmentinstance'] = 'Could not create new assignment instance.'; $string['couldnotfindassignmenttoupgrade'] = 'Could not find old assignment instance to upgrade.'; $string['currentgrade'] = 'Current grade in gradebook'; +$string['cutoffdate'] = 'Cut-off date'; +$string['cutoffdate_help'] = 'If set, the assignment will not accept submissions after this date without an extension.'; +$string['cutoffdatevalidation'] = 'Cut-off date must be after the due date.'; +$string['cutoffdatefromdatevalidation'] = 'Cut-off date must be after the allow submissions from date.'; $string['defaultplugins'] = 'Default assignment settings'; $string['defaultplugins_help'] = 'These settings define the defaults for all new assignments.'; +$string['defaultteam'] = 'Default team'; $string['deletepluginareyousure'] = 'Delete assignment plugin {$a}: are you sure?'; $string['deletepluginareyousuremessage'] = 'You are about to completely delete the assignment plugin {$a}. This will completely delete everything in the database associated with this assignment plugin. Are you SURE you want to continue?'; $string['deletingplugin'] = 'Deleting plugin {$a}.'; @@ -87,12 +98,15 @@ $string['description'] = 'Description'; $string['downloadall'] = 'Download all submissions'; $string['download all submissions'] = 'Download all submissions in a zip file.'; $string['duedate'] = 'Due date'; -$string['duedate_help'] = 'This is when the assignment is due. If late submissions are allowed, any assignments submitted after this date are marked as late.'; +$string['duedate_help'] = 'This is when the assignment is due. Submissions will still be allowed after this date but any assignments submitted after this date are marked as late. To prevent submissions after a certain date - set the assignment cut off date.'; $string['duedateno'] = 'No due date'; $string['duedatereached'] = 'The due date for this assignment has now passed'; $string['duedatevalidation'] = 'Due date must be after the allow submissions from date.'; $string['editsubmission'] = 'Edit my submission'; $string['editaction'] = 'Actions...'; +$string['extensionduedate'] = 'Extension due date'; +$string['extensionnotafterduedate'] = 'Extension date must be after the due date'; +$string['extensionnotafterfromdate'] = 'Extension date must be after the allow submissions from date'; $string['gradersubmissionupdatedtext'] = '{$a->username} has updated their assignment submission for \'{$a->assignment}\' at {$a->timeupdated} @@ -103,6 +117,8 @@ $string['gradersubmissionupdatedhtml'] = '{$a->username} has updated their assig for \'{$a->assignment}\' at {$a->timeupdated}

    It is available on the web site.'; $string['gradersubmissionupdatedsmall'] = '{$a->username} has updated their submission for assignment {$a->assignment}.'; +$string['grantextension'] = 'Grant extension'; +$string['grantextensionforusers'] = 'Grant extension for {$a} students'; $string['enabled'] = 'Enabled'; $string['errornosubmissions'] = 'There are no submissions to download'; $string['errorquickgradingvsadvancedgrading'] = 'The grades were not saved because this assignment is currently using advanced grading'; @@ -143,11 +159,14 @@ $string['gradingstatus'] = 'Grading status'; $string['gradingstudentprogress'] = 'Grading student {$a->index} of {$a->count}'; $string['gradingsummary'] = 'Grading summary'; $string['hideshow'] = 'Hide/Show'; +$string['hiddenuser'] = 'Participant {$a}'; $string['instructionfiles'] = 'Instruction files'; $string['invalidgradeforscale'] = 'The grade supplied was not valid for the current scale'; $string['invalidfloatforgrade'] = 'The grade provided could not be understood: {$a}'; $string['lastmodifiedsubmission'] = 'Last modified (submission)'; $string['lastmodifiedgrade'] = 'Last modified (grade)'; +$string['latesubmissions'] = 'Late submissions'; +$string['latesubmissionsaccepted'] = 'Only student(s) having been granted extension can still submit the assignment'; $string['locksubmissionforstudent'] = 'Prevent any more submissions for student: (id={$a->id}, fullname={$a->fullname}).'; $string['locksubmissions'] = 'Lock submissions'; $string['manageassignfeedbackplugins'] = 'Manage assignment feedback plugins'; @@ -165,9 +184,12 @@ $string['mysubmission'] = 'My submission: '; $string['newsubmissions'] = 'Assignments submitted'; $string['nofiles'] = 'No files. '; $string['nograde'] = 'No grade. '; +$string['nolatesubmissions'] = 'No late submissions accepted. '; $string['noonlinesubmissions'] = 'This assignment does not require you to submit anything online'; $string['nosavebutnext'] = 'Next'; $string['nosubmission'] = 'Nothing has been submitted for this assignment'; +$string['nosubmissionsacceptedafter'] = 'No submissions accepted after '; +$string['nomoresubmissionsaccepted'] = 'No more submissions accepted'; $string['notgraded'] = 'Not graded'; $string['notgradedyet'] = 'Not graded yet'; $string['notsubmittedyet'] = 'Not submitted yet'; @@ -177,15 +199,16 @@ $string['numberofdraftsubmissions'] = 'Drafts'; $string['numberofparticipants'] = 'Participants'; $string['numberofsubmittedassignments'] = 'Submitted'; $string['numberofsubmissionsneedgrading'] = 'Needs grading'; +$string['numberofteams'] = 'Teams'; $string['offline'] = 'No online submissions required'; +$string['open'] = 'Open'; $string['overdue'] = 'Assignment is overdue by: {$a}'; $string['outlinegrade'] = 'Grade: {$a}'; $string['page-mod-assign-x'] = 'Any assignment module page'; $string['page-mod-assign-view'] = 'Assignment module main and submission page'; +$string['participant'] = 'Participant'; $string['pluginadministration'] = 'Assignment administration'; $string['pluginname'] = 'Assignment'; -$string['preventlatesubmissions'] = 'Prevent late submissions'; -$string['preventlatesubmissions_help'] = 'If enabled, students will not be able submit after the Due Date. If disabled, students will be able to submit assignments after the due date.'; $string['preventsubmissions'] = 'Prevent the user from making any more submissions to this assignment.'; $string['preventsubmissionsshort'] = 'Prevent submission changes'; $string['previous'] = 'Previous'; @@ -195,6 +218,11 @@ $string['quickgradingchangessaved'] = 'The grade changes were saved'; $string['quickgrading_help'] = 'Quick grading allows you to assign grades (and outcomes) directly in the submissions table. Quick grading is not compatible with advanced grading and is not recommended when there are multiple markers.'; $string['requiresubmissionstatement'] = 'Require that students accept the submission statement'; $string['requiresubmissionstatement_help'] = 'Require that students accept the submission statement for all assignment submissions for this entire Moodle installation. If this setting is not enabled, then submission statements can be enabled or disabled in the settings for each assignment.'; +$string['requireallteammemberssubmit'] = 'Require all team members submit'; +$string['requireallteammemberssubmit_help'] = 'If enabled, all members of the student team must click the submit button for this assignment before the team submission will be considered as submitted. If disabled, the team submission will be considered as submitted as soon as any member of the student team clicks the submit button.'; +$string['recordid'] = 'Identifier'; +$string['revealidentities'] = 'Reveal student identities'; +$string['revealidentitiesconfirm'] = 'Are you sure you want to reveal student identities for this assignment. This operation cannot be undone. Once the student identities have been revealed, the marks will be released to the gradebook.'; $string['reverttodraftforstudent'] = 'Revert submission to draft for student: (id={$a->id}, fullname={$a->fullname}).'; $string['reverttodraft'] = 'Revert the submission to draft status.'; $string['reverttodraftshort'] = 'Revert the submission to draft'; @@ -213,6 +241,8 @@ $string['settings'] = 'Assignment settings'; $string['showrecentsubmissions'] = 'Show recent submissions'; $string['submissiondrafts'] = 'Require students click submit button'; $string['submissiondrafts_help'] = 'If enabled, students will have to click a Submit button to declare their submission as final. This allows students to keep a draft version of the submission on the system. If this setting is changed from "No" to "Yes" after students have already submitted those submissions will be regarded as final.'; +$string['submissioneditable'] = 'Student can edit this submission'; +$string['submissionnoteditable'] = 'Student cannot edit this submission'; $string['submissionnotready'] = 'This assignment is not ready to submit:'; $string['submissionplugins'] = 'Submission plugins'; $string['submissionreceipts'] = 'Send submission receipts'; @@ -242,6 +272,7 @@ $string['submissionstatus_new'] = 'New submission'; $string['submissionstatus_'] = 'No submission'; $string['submissionstatus'] = 'Submission status'; $string['submissionstatus_submitted'] = 'Submitted for grading'; +$string['submissionteam'] = 'Team'; $string['submission'] = 'Submission'; $string['submitaction'] = 'Submit'; $string['submitassignment_help'] = 'Once this assignment is submitted you will not be able to make any more changes'; @@ -250,6 +281,11 @@ $string['submittedearly'] = 'Assignment was submitted {$a} early'; $string['submittedlate'] = 'Assignment was submitted {$a} late'; $string['submittedlateshort'] = '{$a} late'; $string['submitted'] = 'Submitted'; +$string['teamsubmission'] = 'Students submit in teams'; +$string['teamsubmission_help'] = 'If enabled students will be divided into teams based on the default set of groups or a custom grouping. A team submission will be shared among team members and all members of the team will see each others changes to the submission.'; +$string['teamsubmissiongroupingid'] = 'Grouping for student teams'; +$string['teamsubmissiongroupingid_help'] = 'This is the grouping that the assignment will use to find groups for student teams. If not set - the default set of groups will be used.'; +$string['teamsubmissionstatus'] = 'Team submission status'; $string['textinstructions'] = 'Assignment instructions'; $string['timemodified'] = 'Last modified'; $string['timeremaining'] = 'Time remaining'; @@ -258,6 +294,8 @@ $string['unlocksubmissions'] = 'Unlock submissions'; $string['updategrade'] = 'Update grade'; $string['updatetable'] = 'Save and update table'; $string['upgradenotimplemented'] = 'Upgrade not implemented in plugin ({$a->type} {$a->subtype})'; +$string['userextensiondate'] = 'Extension granted until: {$a}'; +$string['userswhoneedtosubmit'] = 'Users who need to submit: {$a}'; $string['viewfeedback'] = 'View feedback'; $string['viewfeedbackforuser'] = 'View feedback for user: {$a}'; $string['viewfullgradingpage'] = 'Open the full grading page to provide feedback'; @@ -269,3 +307,5 @@ $string['viewownsubmissionstatus'] = 'View own submission status page.'; $string['viewsubmissionforuser'] = 'View submission for user: {$a}'; $string['viewsubmission'] = 'View submission'; $string['viewsubmissiongradingtable'] = 'View submission grading table.'; +$string['viewrevealidentitiesconfirm'] = 'View reveal student identities confirmation page.'; + diff --git a/mod/assign/lib.php b/mod/assign/lib.php index 18a1b11659c..1636d2e7911 100644 --- a/mod/assign/lib.php +++ b/mod/assign/lib.php @@ -113,7 +113,7 @@ function assign_grading_areas_list() { * @return void */ function assign_extend_settings_navigation(settings_navigation $settings, navigation_node $navref) { - global $PAGE; + global $PAGE, $DB; $cm = $PAGE->cm; if (!$cm) { @@ -144,6 +144,14 @@ function assign_extend_settings_navigation(settings_navigation $settings, naviga $node = $navref->add(get_string('downloadall', 'assign'), $link, navigation_node::TYPE_SETTING); } + if (has_capability('mod/assign:revealidentities', $context)) { + $assignment = $DB->get_record('assign', array('id'=>$cm->instance), 'blindmarking, revealidentities'); + + if ($assignment && $assignment->blindmarking && !$assignment->revealidentities) { + $link = new moodle_url('/mod/assign/view.php', array('id' => $cm->id,'action'=>'revealidentities')); + $node = $navref->add(get_string('revealidentities', 'assign'), $link, navigation_node::TYPE_SETTING); + } + } } @@ -216,9 +224,14 @@ function assign_print_overview($courses, &$htmlarray) { $time = time(); $isopen = false; if ($assignment->duedate) { - $isopen = $assignment->allowsubmissionsfromdate <= $time; - if ($assignment->preventlatesubmissions) { - $isopen = ($isopen && $time <= $assignment->duedate); + $duedate = false; + if ($assignment->cutoffdate) { + $duedate = $assignment->cutoffdate; + } + if ($duedate) { + $isopen = ($assignment->allowsubmissionsfromdate <= $time && $time <= $duedate); + } else { + $isopen = ($assignment->allowsubmissionsfromdate <= $time); } } if ($isopen) { @@ -232,6 +245,9 @@ function assign_print_overview($courses, &$htmlarray) { } $strduedate = get_string('duedate', 'assign'); + $strcutoffdate = get_string('nosubmissionsacceptedafter', 'assign'); + $strnolatesubmissions = get_string('nolatesubmissions', 'assign'); + $strduedateno = get_string('duedateno', 'assign'); $strduedateno = get_string('duedateno', 'assign'); $strgraded = get_string('graded', 'assign'); $strnotgradedyet = get_string('notgradedyet', 'assign'); @@ -279,6 +295,13 @@ function assign_print_overview($courses, &$htmlarray) { } else { $str .= '
    '.$strduedateno.'
    '; } + if ($assignment->cutoffdate) { + if ($assignment->cutoffdate == $assignment->duedate) { + $str .= '
    '.$strnolatesubmissions.'
    '; + } else { + $str .= '
    '.$strcutoffdate.': '.userdate($assignment->cutoffdate).'
    '; + } + } $context = context_module::instance($assignment->coursemodule); if (has_capability('mod/assign:grade', $context)) { diff --git a/mod/assign/locallib.php b/mod/assign/locallib.php index 455afd1fadf..567252f7751 100644 --- a/mod/assign/locallib.php +++ b/mod/assign/locallib.php @@ -215,6 +215,28 @@ class assign { return $this->submissionplugins; } + /** + * Is blind marking enabled and reveal identities not set yet? + * + * @return bool + */ + public function is_blind_marking() { + return $this->get_instance()->blindmarking && !$this->get_instance()->revealidentities; + } + + /** + * Does an assignment have submission(s) or grade(s) already? + * + * @return bool + */ + public function has_submissions_or_grades() { + $allgrades = $this->count_grades(); + $allsubmissions = $this->count_submissions(); + if (($allgrades == 0) && ($allsubmissions == 0)) { + return false; + } + return true; + } /** * get a specific submission plugin by its type @@ -303,25 +325,24 @@ class assign { if ($this->process_save_submission($mform)) { $action = 'view'; } - } else if ($action == 'lock') { + } else if ($action == 'lock') { $this->process_lock(); $action = 'grading'; - } else if ($action == 'reverttodraft') { + } else if ($action == 'reverttodraft') { $this->process_revert_to_draft(); $action = 'grading'; - } else if ($action == 'unlock') { + } else if ($action == 'unlock') { $this->process_unlock(); $action = 'grading'; - } else if ($action == 'confirmsubmit') { + } else if ($action == 'confirmsubmit') { $action = 'submit'; if ($this->process_submit_for_grading($mform)) { $action = 'view'; } // save and show next button - } else if ($action == 'batchgradingoperation') { - $this->process_batch_grading_operation(); - $action = 'grading'; - } else if ($action == 'submitgrade') { + } else if ($action == 'batchgradingoperation') { + $action = $this->process_batch_grading_operation(); + } else if ($action == 'submitgrade') { if (optional_param('saveandshownext', null, PARAM_ALPHA)) { //save and show next $action = 'grade'; @@ -343,12 +364,20 @@ class assign { //cancel button $action = 'grading'; } - }else if ($action == 'quickgrade') { + } else if ($action == 'quickgrade') { $message = $this->process_save_quick_grades(); $action = 'quickgradingresult'; - }else if ($action == 'saveoptions') { + } else if ($action == 'saveoptions') { $this->process_save_grading_options(); $action = 'grading'; + } else if ($action == 'saveextension') { + $action = 'grantextension'; + if ($this->process_save_extension($mform)) { + $action = 'grading'; + } + } else if ($action == 'revealidentitiesconfirm') { + $this->process_reveal_identities(); + $action = 'grading'; } $returnparams = array('rownum'=>optional_param('rownum', 0, PARAM_INT)); @@ -378,6 +407,10 @@ class assign { $o .= $this->download_submissions(); } else if ($action == 'submit') { $o .= $this->check_submit_for_grading($mform); + } else if ($action == 'grantextension') { + $o .= $this->view_grant_extension($mform); + } else if ($action == 'revealidentities') { + $o .= $this->view_reveal_identities_confirm($mform); } else { $o .= $this->view_submission_page(); } @@ -409,15 +442,20 @@ class assign { $update->intro = $formdata->intro; $update->introformat = $formdata->introformat; $update->alwaysshowdescription = $formdata->alwaysshowdescription; - $update->preventlatesubmissions = $formdata->preventlatesubmissions; $update->submissiondrafts = $formdata->submissiondrafts; $update->requiresubmissionstatement = $formdata->requiresubmissionstatement; $update->sendnotifications = $formdata->sendnotifications; $update->sendlatenotifications = $formdata->sendlatenotifications; $update->duedate = $formdata->duedate; + $update->cutoffdate = $formdata->cutoffdate; $update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate; $update->grade = $formdata->grade; $update->completionsubmit = !empty($formdata->completionsubmit); + $update->teamsubmission = $formdata->teamsubmission; + $update->requireallteammemberssubmit = $formdata->requireallteammemberssubmit; + $update->teamsubmissiongroupingid = $formdata->teamsubmissiongroupingid; + $update->blindmarking = $formdata->blindmarking; + $returnid = $DB->insert_record('assign', $update); $this->instance = $DB->get_record('assign', array('id'=>$returnid), '*', MUST_EXIST); // cache the course record @@ -629,15 +667,20 @@ class assign { $update->intro = $formdata->intro; $update->introformat = $formdata->introformat; $update->alwaysshowdescription = $formdata->alwaysshowdescription; - $update->preventlatesubmissions = $formdata->preventlatesubmissions; $update->submissiondrafts = $formdata->submissiondrafts; $update->requiresubmissionstatement = $formdata->requiresubmissionstatement; $update->sendnotifications = $formdata->sendnotifications; $update->sendlatenotifications = $formdata->sendlatenotifications; $update->duedate = $formdata->duedate; + $update->cutoffdate = $formdata->cutoffdate; $update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate; $update->grade = $formdata->grade; $update->completionsubmit = !empty($formdata->completionsubmit); + $update->teamsubmission = $formdata->teamsubmission; + $update->requireallteammemberssubmit = $formdata->requireallteammemberssubmit; + $update->teamsubmissiongroupingid = $formdata->teamsubmissiongroupingid; + $update->blindmarking = $formdata->blindmarking; + $result = $DB->update_record('assign', $update); $this->instance = $DB->get_record('assign', array('id'=>$update->id), '*', MUST_EXIST); @@ -964,6 +1007,24 @@ class assign { } } + /** + * Load a count of valid teams for this assignment + * + * @return int number of valid teams + */ + public function count_teams() { + + $groups = groups_get_all_groups($this->get_course()->id, 0, $this->get_instance()->teamsubmissiongroupingid, 'g.id'); + $count = count($groups); + + // See if there are any users in the default group. + $defaultusers = $this->get_submission_group_members(0, true); + if (count($defaultusers) > 0) { + $count += 1; + } + return $count; + } + /** * Load a count of users enrolled in the current course with the specified permission and group (0 for no group) * @@ -996,17 +1057,63 @@ class assign { } /** - * Load a count of users enrolled in the current course with the specified permission and group (optional) + * Load a count of grades + * + * @return int number of grades + */ + public function count_grades() { + global $DB; + + if (!$this->has_instance()) { + return 0; + } + + $sql = 'SELECT COUNT(id) FROM {assign_grades} WHERE assignment = ?'; + $params = array($this->get_course_module()->instance); + + return $DB->count_records_sql($sql, $params); + } + + /** + * Load a count of submissions + * + * @return int number of submissions + */ + public function count_submissions() { + global $DB; + + if (!$this->has_instance()) { + return 0; + } + + $sql = 'SELECT COUNT(id) FROM {assign_submission} WHERE assignment = ?'; + $params = array($this->get_course_module()->instance); + + if ($this->get_instance()->teamsubmission) { + // only look at team submissions + $sql .= ' AND userid = ?'; + $params[] = 0; + } + return $DB->count_records_sql($sql, $params); + } + + /** + * Load a count of submissions with a specified status * * @param string $status The submission status - should match one of the constants * @return int number of matching submissions */ public function count_submissions_with_status($status) { global $DB; - return $DB->count_records_sql("SELECT COUNT('x') - FROM {assign_submission} - WHERE assignment = ? AND - status = ?", array($this->get_course_module()->instance, $status)); + $sql = 'SELECT COUNT(id) FROM {assign_submission} WHERE assignment = ? AND status = ?'; + $params = array($this->get_course_module()->instance, $status); + + if ($this->get_instance()->teamsubmission) { + // only look at team submissions + $sql .= ' AND userid = ?'; + $params[] = 0; + } + return $DB->count_records_sql($sql, $params); } /** @@ -1015,7 +1122,7 @@ class assign { * * @return array An array of userids */ - private function get_grading_userid_list(){ + private function get_grading_userid_list() { $filter = get_user_preferences('assign_filter', ''); $table = new assign_grading_table($this, 0, $filter, 0, false); @@ -1033,7 +1140,7 @@ class assign { * @param bool $last This is set to true if this is the last user in the table * @return mixed The user id of the matching user or false if there was an error */ - private function get_userid_for_row($num, $last){ + private function get_userid_for_row($num, $last) { if (!array_key_exists('userid_for_row', $this->cache)) { $this->cache['userid_for_row'] = array(); } @@ -1110,7 +1217,8 @@ class assign { $timenow = time(); // Collect all submissions from the past 24 hours that require mailing. - $sql = "SELECT s.*, a.course, a.name, g.*, g.id as gradeid, g.timemodified as lastmodified + $sql = "SELECT s.*, a.course, a.name, a.blindmarking, a.revealidentities, + g.*, g.id as gradeid, g.timemodified as lastmodified FROM {assign} a JOIN {assign_grades} g ON g.assignment = a.id LEFT JOIN {assign_submission} s ON s.assignment = a.id AND s.userid = g.userid @@ -1212,7 +1320,15 @@ class assign { $eventtype = 'assign_notification'; $updatetime = $submission->lastmodified; $modulename = get_string('modulename', 'assign'); - self::send_assignment_notification($grader, $user, $messagetype, $eventtype, $updatetime, $mod, $contextmodule, $course, $modulename, $submission->name); + + $uniqueid = 0; + if ($submission->blindmarking && !$submission->revealidentities) { + $uniqueid = self::get_uniqueid_for_user_static($submission->assignment, $user->id); + } + self::send_assignment_notification($grader, $user, $messagetype, $eventtype, $updatetime, + $mod, $contextmodule, $course, $modulename, $submission->name, + $submission->blindmarking && !$submission->revealidentities, + $uniqueid); $grade = new stdClass(); $grade->id = $submission->gradeid; @@ -1270,6 +1386,196 @@ class assign { return $result; } + /** + * View the grant extension date page + * + * Uses url parameters 'userid' + * or from parameter 'selectedusers' + * @param moodleform $mform - Used for validation of the submitted data + * @return string + */ + private function view_grant_extension($mform) { + global $DB, $CFG; + require_once($CFG->dirroot . '/mod/assign/extensionform.php'); + + $o = ''; + $batchusers = optional_param('selectedusers', '', PARAM_TEXT); + $data = new stdClass(); + $data->extensionduedate = null; + $userid = 0; + if (!$batchusers) { + $userid = required_param('userid', PARAM_INT); + + $grade = $this->get_user_grade($userid, false); + + $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST); + + if ($grade) { + $data->extensionduedate = $grade->extensionduedate; + } + $data->userid = $userid; + } else { + $data->batchusers = $batchusers; + } + $o .= $this->output->render(new assign_header($this->get_instance(), + $this->get_context(), + $this->show_intro(), + $this->get_course_module()->id, + get_string('grantextension', 'assign'))); + + if (!$mform) { + $mform = new mod_assign_extension_form(null, array($this->get_course_module()->id, + $userid, + $batchusers, + $this->get_instance(), + $data)); + } + $o .= $this->output->render(new assign_form('extensionform', $mform)); + $o .= $this->view_footer(); + return $o; + } + + /** + * Get a list of the users in the same group as this user + * + * @param int $groupid The id of the group whose members we want or 0 for the default group + * @param bool $onlyids Whether to retrieve only the user id's + * @return array The users (possibly id's only) + */ + public function get_submission_group_members($groupid, $onlyids) { + $members = array(); + if ($groupid != 0) { + if ($onlyids) { + $allusers = groups_get_members($groupid, 'u.id'); + } else { + $allusers = groups_get_members($groupid); + } + foreach ($allusers as $user) { + if ($this->get_submission_group($user->id)) { + $members[] = $user; + } + } + } else { + $allusers = $this->list_participants(null, $onlyids); + foreach ($allusers as $user) { + if ($this->get_submission_group($user->id) == null) { + $members[] = $user; + } + } + } + return $members; + } + + /** + * Get a list of the users in the same group as this user that have not submitted the assignment + * + * @param int $groupid The id of the group whose members we want or 0 for the default group + * @param bool $onlyids Whether to retrieve only the user id's + * @return array The users (possibly id's only) + */ + public function get_submission_group_members_who_have_not_submitted($groupid, $onlyids) { + if (!$this->get_instance()->teamsubmission || !$this->get_instance()->requireallteammemberssubmit) { + return array(); + } + $members = $this->get_submission_group_members($groupid, $onlyids); + + foreach ($members as $id => $member) { + $submission = $this->get_user_submission($member->id, false); + if ($submission && $submission->status != ASSIGN_SUBMISSION_STATUS_DRAFT) { + unset($members[$id]); + } else { + if ($this->is_blind_marking()) { + $members[$id]->alias = get_string('hiddenuser', 'assign') . $this->get_uniqueid_for_user($id); + } + } + } + return $members; + } + + /** + * Load the group submission object for a particular user, optionally creating it if required + * + * This will create the user submission and the group submission if required + * + * @param int $userid The id of the user whose submission we want + * @param int $groupid The id of the group for this user - may be 0 in which case it is determined from the userid + * @param bool $create If set to true a new submission object will be created in the database + * @return stdClass The submission + */ + public function get_group_submission($userid, $groupid, $create) { + global $DB; + + if ($groupid == 0) { + $group = $this->get_submission_group($userid); + if ($group) { + $groupid = $group->id; + } + } + + if ($create) { + // Make sure there is a submission for this user. + $params = array('assignment'=>$this->get_instance()->id, 'groupid'=>0, 'userid'=>$userid); + $submission = $DB->get_record('assign_submission', $params); + + if (!$submission) { + $submission = new stdClass(); + $submission->assignment = $this->get_instance()->id; + $submission->userid = $userid; + $submission->groupid = 0; + $submission->timecreated = time(); + $submission->timemodified = $submission->timecreated; + + if ($this->get_instance()->submissiondrafts) { + $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT; + } else { + $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED; + } + $DB->insert_record('assign_submission', $submission); + } + } + // Now get the group submission. + $params = array('assignment'=>$this->get_instance()->id, 'groupid'=>$groupid, 'userid'=>0); + $submission = $DB->get_record('assign_submission', $params); + + if ($submission) { + return $submission; + } + if ($create) { + $submission = new stdClass(); + $submission->assignment = $this->get_instance()->id; + $submission->userid = 0; + $submission->groupid = $groupid; + $submission->timecreated = time(); + $submission->timemodified = $submission->timecreated; + + if ($this->get_instance()->submissiondrafts) { + $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT; + } else { + $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED; + } + $sid = $DB->insert_record('assign_submission', $submission); + $submission->id = $sid; + return $submission; + } + return false; + } + + /** + * This is used for team assignments to get the group for the specified user. + * If the user is a member of multiple or no groups this will return false + * + * @param int $userid The id of the user whose submission we want + * @return mixed The group or false + */ + public function get_submission_group($userid) { + $groups = groups_get_all_groups($this->get_course()->id, $userid, $this->get_instance()->teamsubmissiongroupingid); + if (count($groups) != 1) { + return false; + } + return array_pop($groups); + } + + /** * display the submission that is used by a plugin * Uses url parameters 'sid', 'gid' and 'plugin' @@ -1465,7 +1771,11 @@ class assign { $user = $DB->get_record("user", array("id"=>$userid),'id,username,firstname,lastname', MUST_EXIST); - $prefix = clean_filename(fullname($user) . "_" .$userid . "_"); + if ($this->is_blind_marking()) { + $prefix = clean_filename(get_string('participant', 'assign') . "_" . $this->get_uniqueid_for_user($userid) . "_"); + } else { + $prefix = clean_filename(fullname($user) . "_" . $this->get_uniqueid_for_user($userid) . "_"); + } foreach ($this->submissionplugins as $plugin) { if ($plugin->is_enabled() && $plugin->is_visible()) { @@ -1508,6 +1818,10 @@ class assign { /** * Load the submission object for a particular user, optionally creating it if required * + * For team assignments there are 2 submissions - the student submission and the team submission + * All files are associated with the team submission but the status of the students contribution is + * recorded separately. + * * @param int $userid The id of the user whose submission we want or 0 in which case USER->id is used * @param bool $create optional Defaults to false. If set to true a new submission object will be created in the database * @return stdClass The submission @@ -1518,8 +1832,9 @@ class assign { if (!$userid) { $userid = $USER->id; } - // if the userid is not null then use userid - $submission = $DB->get_record('assign_submission', array('assignment'=>$this->get_instance()->id, 'userid'=>$userid)); + // If the userid is not null then use userid. + $params = array('assignment'=>$this->get_instance()->id, 'userid'=>$userid, 'groupid'=>0); + $submission = $DB->get_record('assign_submission', $params); if ($submission) { return $submission; @@ -1640,32 +1955,75 @@ class assign { if ($offset) { $_POST = array(); } - if(!$userid){ + if (!$userid) { throw new coding_exception('Row is out of bounds for the current grading table: ' . $rownum); } $user = $DB->get_record('user', array('id' => $userid)); if ($user) { - $o .= $this->output->render(new assign_user_summary($user, $this->get_course()->id, has_capability('moodle/site:viewfullnames', $this->get_course_context()))); + $o .= $this->output->render(new assign_user_summary($user, + $this->get_course()->id, + has_capability('moodle/site:viewfullnames', + $this->get_course_context()), + $this->is_blind_marking(), + $this->get_uniqueid_for_user($user->id))); } $submission = $this->get_user_submission($userid, false); + $submissiongroup = null; + $submissiongroupmemberswhohavenotsubmitted = array(); + $teamsubmission = null; + $notsubmitted = array(); + if ($this->get_instance()->teamsubmission) { + $teamsubmission = $this->get_group_submission($userid, 0, false); + $submissiongroup = $this->get_submission_group($userid); + $groupid = 0; + if ($submissiongroup) { + $groupid = $submissiongroup->id; + } + $notsubmitted = $this->get_submission_group_members_who_have_not_submitted($groupid, false); + + } + // get the current grade $grade = $this->get_user_grade($userid, false); if ($this->can_view_submission($userid)) { $gradelocked = ($grade && $grade->locked) || $this->grading_disabled($userid); + $extensionduedate = null; + if ($grade) { + $extensionduedate = $grade->extensionduedate; + } + $showedit = $this->submissions_open($userid) && ($this->is_any_submission_plugin_enabled()); + + if ($teamsubmission) { + $showsubmit = $showedit && $teamsubmission && ($teamsubmission->status == ASSIGN_SUBMISSION_STATUS_DRAFT); + } else { + $showsubmit = $showedit && $submission && ($submission->status == ASSIGN_SUBMISSION_STATUS_DRAFT); + } + $viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_course_context()); + $o .= $this->output->render(new assign_submission_status($this->get_instance()->allowsubmissionsfromdate, $this->get_instance()->alwaysshowdescription, $submission, + $this->get_instance()->teamsubmission, + $teamsubmission, + $submissiongroup, + $notsubmitted, $this->is_any_submission_plugin_enabled(), $gradelocked, $this->is_graded($userid), $this->get_instance()->duedate, + $this->get_instance()->cutoffdate, $this->get_submission_plugins(), $this->get_return_action(), $this->get_return_params(), $this->get_course_module()->id, + $this->get_course()->id, assign_submission_status::GRADER_VIEW, - false, - false)); + $showedit, + $showsubmit, + $viewfullnames, + $extensionduedate, + $this->get_context(), + $this->is_blind_marking())); } if ($grade) { $data = new stdClass(); @@ -1679,16 +2037,51 @@ class assign { // now show the grading form if (!$mform) { - $mform = new mod_assign_grade_form(null, array($this, $data, array('rownum'=>$rownum, 'useridlist'=>$useridlist, 'last'=>$last)), 'post', '', array('class'=>'gradeform')); + $pagination = array( 'rownum'=>$rownum, 'useridlist'=>$useridlist, 'last'=>$last); + $formparams = array($this, $data, $pagination); + $mform = new mod_assign_grade_form(null, + $formparams, + 'post', + '', + array('class'=>'gradeform')); } $o .= $this->output->render(new assign_form('gradingform',$mform)); - $this->add_to_log('view grading form', get_string('viewgradingformforstudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user)))); + $msg = get_string('viewgradingformforstudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user))); + $this->add_to_log('view grading form', $msg); $o .= $this->view_footer(); return $o; } + /** + * Show a confirmation page to make sure they want to release student identities + * + * @return string + */ + private function view_reveal_identities_confirm() { + global $CFG, $USER; + + require_capability('mod/assign:revealidentities', $this->get_context()); + + $o = ''; + $o .= $this->output->render(new assign_header($this->get_instance(), + $this->get_context(), false, $this->get_course_module()->id)); + + $confirmurl = new moodle_url('/mod/assign/view.php', array('id'=>$this->get_course_module()->id, + 'action'=>'revealidentitiesconfirm', + 'sesskey'=>sesskey())); + + $cancelurl = new moodle_url('/mod/assign/view.php', array('id'=>$this->get_course_module()->id, + 'action'=>'grading')); + + $o .= $this->output->confirm(get_string('revealidentitiesconfirm', 'assign'), $confirmurl, $cancelurl); + $o .= $this->view_footer(); + $this->add_to_log('view', get_string('viewrevealidentitiesconfirm', 'assign')); + return $o; + } + + /** @@ -1732,6 +2125,10 @@ class assign { $downloadurl = '/mod/assign/view.php?id=' . $this->get_course_module()->id . '&action=downloadall'; $links[$downloadurl] = get_string('downloadall', 'assign'); } + if ($this->is_blind_marking() && has_capability('mod/assign:revealidentities', $this->get_context())) { + $revealidentitiesurl = '/mod/assign/view.php?id=' . $this->get_course_module()->id . '&action=revealidentities'; + $links[$revealidentitiesurl] = get_string('revealidentities', 'assign'); + } $gradingactions = new url_select($links); @@ -1760,7 +2157,8 @@ class assign { $gradingbatchoperationsform = new mod_assign_grading_batch_operations_form(null, array('cm'=>$this->get_course_module()->id, - 'submissiondrafts'=>$this->get_instance()->submissiondrafts), + 'submissiondrafts'=>$this->get_instance()->submissiondrafts, + 'duedate'=>$this->get_instance()->duedate), 'post', '', array('class'=>'gradingbatchoperationsform')); @@ -1946,7 +2344,7 @@ class assign { /** * Ask the user to confirm they want to perform this batch operation - * @return string + * @return string - the page to view after processing these actions */ private function process_batch_grading_operation() { global $CFG; @@ -1955,7 +2353,8 @@ class assign { $gradingbatchoperationsform = new mod_assign_grading_batch_operations_form(null, array('cm'=>$this->get_course_module()->id, - 'submissiondrafts'=>$this->get_instance()->submissiondrafts), + 'submissiondrafts'=>$this->get_instance()->submissiondrafts, + 'duedate'=>$this->get_instance()->duedate), 'post', '', array('class'=>'gradingbatchoperationsform')); @@ -1971,11 +2370,13 @@ class assign { $this->process_unlock($userid); } else if ($data->operation == 'reverttodraft') { $this->process_revert_to_draft($userid); + } else if ($data->operation == 'grantextension') { + return 'grantextension'; } } } - return true; + return 'grading'; } /** @@ -2042,26 +2443,60 @@ class assign { $submission = $this->get_user_submission($user->id, false); $o = ''; + $teamsubmission = null; + $submissiongroup = null; + $notsubmitted = array(); + if ($this->get_instance()->teamsubmission) { + $teamsubmission = $this->get_group_submission($user->id, 0, false); + $submissiongroup = $this->get_submission_group($user->id); + $groupid = 0; + if ($submissiongroup) { + $groupid = $submissiongroup->id; + } + $notsubmitted = $this->get_submission_group_members_who_have_not_submitted($groupid, false); + } + if ($this->can_view_submission($user->id)) { $showedit = has_capability('mod/assign:submit', $this->context) && - $this->submissions_open() && ($this->is_any_submission_plugin_enabled()) && $showlinks; - $showsubmit = $submission && ($submission->status == ASSIGN_SUBMISSION_STATUS_DRAFT) && $showlinks; + $this->submissions_open($user->id) && ($this->is_any_submission_plugin_enabled()) && $showlinks; $gradelocked = ($grade && $grade->locked) || $this->grading_disabled($user->id); + $showsubmit = ($submission || $teamsubmission) && $showlinks; + if ($teamsubmission && ($teamsubmission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED)) { + $showsubmit = false; + } + if ($submission && ($submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED)) { + $showsubmit = false; + } + $extensionduedate = null; + if ($grade) { + $extensionduedate = $grade->extensionduedate; + } + $viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_course_context()); $o .= $this->output->render(new assign_submission_status($this->get_instance()->allowsubmissionsfromdate, $this->get_instance()->alwaysshowdescription, $submission, + $this->get_instance()->teamsubmission, + $teamsubmission, + $submissiongroup, + $notsubmitted, $this->is_any_submission_plugin_enabled(), $gradelocked, $this->is_graded($user->id), $this->get_instance()->duedate, + $this->get_instance()->cutoffdate, $this->get_submission_plugins(), $this->get_return_action(), $this->get_return_params(), $this->get_course_module()->id, + $this->get_course()->id, assign_submission_status::STUDENT_VIEW, $showedit, - $showsubmit)); + $showsubmit, + $viewfullnames, + $extensionduedate, + $this->get_context(), + $this->is_blind_marking())); require_once($CFG->libdir.'/gradelib.php'); require_once($CFG->dirroot.'/grade/grading/lib.php'); @@ -2137,15 +2572,31 @@ class assign { $this->get_course_module()->id)); if ($this->can_grade()) { - $o .= $this->output->render(new assign_grading_summary($this->count_participants(0), - $this->get_instance()->submissiondrafts, - $this->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT), - $this->is_any_submission_plugin_enabled(), - $this->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED), - $this->get_instance()->duedate, - $this->get_course_module()->id, - $this->count_submissions_need_grading() - )); + if ($this->get_instance()->teamsubmission) { + $summary = new assign_grading_summary($this->count_teams(), + $this->get_instance()->submissiondrafts, + $this->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT), + $this->is_any_submission_plugin_enabled(), + $this->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED), + $this->get_instance()->cutoffdate, + $this->get_instance()->duedate, + $this->get_course_module()->id, + $this->count_submissions_need_grading(), + $this->get_instance()->teamsubmission); + $o .= $this->output->render($summary); + } else { + $summary = new assign_grading_summary($this->count_participants(0), + $this->get_instance()->submissiondrafts, + $this->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT), + $this->is_any_submission_plugin_enabled(), + $this->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED), + $this->get_instance()->cutoffdate, + $this->get_instance()->duedate, + $this->get_course_module()->id, + $this->count_submissions_need_grading(), + $this->get_instance()->teamsubmission); + $o .= $this->output->render($summary); + } } $grade = $this->get_user_grade($USER->id, false); $submission = $this->get_user_submission($USER->id, false); @@ -2214,9 +2665,26 @@ class assign { */ private function gradebook_item_update($submission=NULL, $grade=NULL) { - if($submission != NULL){ + // Do not push grade to gradebook if blind marking is active as the gradebook would reveal the students. + if ($this->is_blind_marking()) { + return false; + } + if ($submission != NULL) { + if ($submission->userid == 0) { + // This is a group submission update. + $team = groups_get_members($submission->groupid, 'u.id'); + + foreach ($team as $member) { + $submission->groupid = 0; + $submission->userid = $member->id; + $this->gradebook_item_update($submission, null); + } + return; + } + $gradebookgrade = $this->convert_submission_for_gradebook($submission); - }else{ + + } else { $gradebookgrade = $this->convert_grade_for_gradebook($grade); } // Grading is disabled, return. @@ -2230,15 +2698,80 @@ class assign { } /** - * update grades in the gradebook based on submission time + * update team submission * * @param stdClass $submission + * @param int $userid * @param bool $updatetime * @return bool */ - private function update_submission(stdClass $submission, $updatetime=true) { + private function update_team_submission(stdClass $submission, $userid, $updatetime) { global $DB; + if ($updatetime) { + $submission->timemodified = time(); + } + + // First update the submission for the current user. + $mysubmission = $this->get_user_submission($userid, true); + $mysubmission->status = $submission->status; + + $this->update_submission($mysubmission, 0, $updatetime, false); + + // Now check the team settings to see if this assignment qualifies as submitted or draft. + $team = $this->get_submission_group_members($submission->groupid, true); + + $allsubmitted = true; + $anysubmitted = false; + foreach ($team as $member) { + $membersubmission = $this->get_user_submission($member->id, false); + + if (!$membersubmission || $membersubmission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED) { + $allsubmitted = false; + if ($anysubmitted) { + break; + } + } else { + $anysubmitted = true; + } + } + if ($this->get_instance()->requireallteammemberssubmit) { + if ($allsubmitted) { + $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED; + } else { + $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT; + } + $result= $DB->update_record('assign_submission', $submission); + } else { + if ($anysubmitted) { + $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED; + } else { + $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT; + } + $result= $DB->update_record('assign_submission', $submission); + } + + $this->gradebook_item_update($submission); + return $result; + } + + + /** + * update grades in the gradebook based on submission time + * + * @param stdClass $submission + * @param int $userid + * @param bool $updatetime + * @param bool $teamsubmission + * @return bool + */ + private function update_submission(stdClass $submission, $userid, $updatetime, $teamsubmission) { + global $DB; + + if ($teamsubmission) { + return $this->update_team_submission($submission, $userid, $updatetime); + } + if ($updatetime) { $submission->timemodified = time(); } @@ -2257,15 +2790,35 @@ class assign { * has this person already submitted, * is the assignment locked? * + * @param int $userid - Optional userid so we can see if a different user can submit * @return bool */ - private function submissions_open() { + private function submissions_open($userid = 0) { global $USER; + if (!$userid) { + $userid = $USER->id; + } + $time = time(); $dateopen = true; - if ($this->get_instance()->preventlatesubmissions && $this->get_instance()->duedate) { - $dateopen = ($this->get_instance()->allowsubmissionsfromdate <= $time && $time <= $this->get_instance()->duedate); + $finaldate = false; + if ($this->get_instance()->cutoffdate) { + $finaldate = $this->get_instance()->cutoffdate; + } + // User extensions. + if ($finaldate) { + $grade = $this->get_user_grade($userid, false); + if ($grade && $grade->extensionduedate) { + // Extension can be before cut off date. + if ($grade->extensionduedate > $finaldate) { + $finaldate = $grade->extensionduedate; + } + } + } + + if ($finaldate) { + $dateopen = ($this->get_instance()->allowsubmissionsfromdate <= $time && $time <= $finaldate); } else { $dateopen = ($this->get_instance()->allowsubmissionsfromdate <= $time); } @@ -2274,23 +2827,30 @@ class assign { return false; } - // now check if this user has already submitted etc. - if (!is_enrolled($this->get_course_context(), $USER)) { + // Now check if this user has already submitted etc. + if (!is_enrolled($this->get_course_context(), $userid)) { return false; } - if ($submission = $this->get_user_submission($USER->id, false)) { + $submission = false; + if ($this->get_instance()->teamsubmission) { + $submission = $this->get_group_submission($USER->id, 0, false); + } else { + $submission = $this->get_user_submission($USER->id, false); + } + if ($submission) { + if ($this->get_instance()->submissiondrafts && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) { // drafts are tracked and the student has submitted the assignment return false; } } - if ($grade = $this->get_user_grade($USER->id, false)) { + if ($grade = $this->get_user_grade($userid, false)) { if ($grade->locked) { return false; } } - if ($this->grading_disabled($USER->id)) { + if ($this->grading_disabled($userid)) { return false; } @@ -2307,11 +2867,6 @@ class assign { public function render_area_files($component, $area, $submissionid) { global $USER; - if (!$submissionid) { - $submission = $this->get_user_submission($USER->id,false); - $submissionid = $submission->id; - } - $fs = get_file_storage(); $browser = get_file_browser(); $files = $fs->get_area_files($this->get_context()->id, $component, $area , $submissionid , "timemodified", false); @@ -2428,11 +2983,16 @@ class assign { */ public static function send_assignment_notification($userfrom, $userto, $messagetype, $eventtype, $updatetime, $coursemodule, $context, $course, - $modulename, $assignmentname) { + $modulename, $assignmentname, $blindmarking, + $uniqueidforuser) { global $CFG; $info = new stdClass(); - $info->username = fullname($userfrom, true); + if ($blindmarking) { + $info->username = get_string('participant', 'assign') . ' ' . $uniqueidforuser; + } else { + $info->username = fullname($userfrom, true); + } $info->assignment = format_string($assignmentname,true, array('context'=>$context)); $info->url = $CFG->wwwroot.'/mod/assign/view.php?id='.$coursemodule->id; $info->timeupdated = strftime('%c',$updatetime); @@ -2471,37 +3031,43 @@ class assign { * @return void */ public function send_notification($userfrom, $userto, $messagetype, $eventtype, $updatetime) { - self::send_assignment_notification($userfrom, $userto, $messagetype, $eventtype, $updatetime, $this->get_course_module(), $this->get_context(), $this->get_course(), $this->get_module_name(), $this->get_instance()->name); + self::send_assignment_notification($userfrom, $userto, $messagetype, $eventtype, + $updatetime, $this->get_course_module(), $this->get_context(), + $this->get_course(), $this->get_module_name(), + $this->get_instance()->name, $this->is_blind_marking(), + $this->get_uniqueid_for_user($userfrom->id)); } /** * Notify student upon successful submission * - * @global moodle_database $DB * @param stdClass $submission * @return void */ private function notify_student_submission_receipt(stdClass $submission) { - global $DB; + global $DB, $USER; $adminconfig = $this->get_admin_config(); if (empty($adminconfig->submissionreceipts)) { // No need to do anything return; } - $user = $DB->get_record('user', array('id'=>$submission->userid), '*', MUST_EXIST); + if ($submission->userid) { + $user = $DB->get_record('user', array('id'=>$submission->userid), '*', MUST_EXIST); + } else { + $user = $USER; + } $this->send_notification($user, $user, 'submissionreceipt', 'assign_notification', $submission->timemodified); } /** * Send notifications to graders upon student submissions * - * @global moodle_database $DB * @param stdClass $submission * @return void */ private function notify_graders(stdClass $submission) { - global $DB; + global $DB, $USER; $late = $this->get_instance()->duedate && ($this->get_instance()->duedate < time()); @@ -2509,7 +3075,11 @@ class assign { return; } - $user = $DB->get_record('user', array('id'=>$submission->userid), '*', MUST_EXIST); + if ($submission->userid) { + $user = $DB->get_record('user', array('id'=>$submission->userid), '*', MUST_EXIST); + } else { + $user = $USER; + } if ($teachers = $this->get_graders($user->id)) { foreach ($teachers as $teacher) { $this->send_notification($user, $teacher, 'gradersubmissionupdated', 'assign_notification', $submission->timemodified); @@ -2553,7 +3123,12 @@ class assign { if ($mform->get_data() == false) { return false; } - $submission = $this->get_user_submission($USER->id,true); + if ($this->get_instance()->teamsubmission) { + $submission = $this->get_group_submission($USER->id, 0, true); + } else { + $submission = $this->get_user_submission($USER->id, true); + } + if ($submission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED) { // Give each submission plugin a chance to process the submission $plugins = $this->get_submission_plugins(); @@ -2562,7 +3137,7 @@ class assign { } $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED; - $this->update_submission($submission); + $this->update_submission($submission, $USER->id, true, $this->get_instance()->teamsubmission); $completion = new completion_info($this->get_course()); if ($completion->is_enabled($this->get_course_module()) && $this->get_instance()->completionsubmit) { $completion->update_state($this->get_course_module(), COMPLETION_COMPLETE, $USER->id); @@ -2579,10 +3154,78 @@ class assign { return true; } + /** + * save the extension date for a single user + * + * @param int $userid The user id + * @param mixed $extensionduedate Either an integer date or null + * @return boolean + */ + private function save_user_extension($userid, $extensionduedate) { + global $DB; + + $grade = $this->get_user_grade($userid, true); + $grade->extensionduedate = $extensionduedate; + $grade->timemodified = time(); + + $result = $DB->update_record('assign_grades', $grade); + + if ($result) { + $this->add_to_log('grant extension', $this->format_grade_for_log($grade)); + } + return $result; + } + + /** + * save extension date + * + * @param moodleform $mform The submitted form + * @return boolean + */ + private function process_save_extension(& $mform) { + global $DB, $CFG; + + // Include extension form. + require_once($CFG->dirroot . '/mod/assign/extensionform.php'); + + // Need submit permission to submit an assignment. + require_capability('mod/assign:grantextension', $this->context); + + $batchusers = optional_param('selectedusers', '', PARAM_TEXT); + $userid = 0; + if (!$batchusers) { + $userid = required_param('userid', PARAM_INT); + $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST); + } + $mform = new mod_assign_extension_form(null, array($this->get_course_module()->id, + $userid, + $batchusers, + $this->get_instance(), + null)); + + if ($mform->is_cancelled()) { + return true; + } + + if ($formdata = $mform->get_data()) { + if ($batchusers) { + $users = explode(',', $batchusers); + $result = true; + foreach ($users as $userid) { + $result = $this->save_user_extension($userid, $formdata->extensionduedate) && $result; + } + return $result; + } else { + return $this->save_user_extension($userid, $formdata->extensionduedate); + } + } + return false; + } + + /** * save quick grades * - * @global moodle_database $DB * @return string The result of the save operation */ private function process_save_quick_grades() { @@ -2724,6 +3367,53 @@ class assign { return get_string('quickgradingchangessaved', 'assign'); } + /** + * Reveal student identities to markers (and the gradebook) + * + * @return void + */ + private function process_reveal_identities() { + global $DB, $CFG; + + require_capability('mod/assign:revealidentities', $this->context); + if (!confirm_sesskey()) { + return false; + } + + // Update the assignment record. + $update = new stdClass(); + $update->id = $this->get_instance()->id; + $update->revealidentities = 1; + $DB->update_record('assign', $update); + + // Refresh the instance data. + $this->instance = null; + + // Release the grades to the gradebook. + // First create the column in the gradebook. + $this->update_gradebook(false, $this->get_course_module()->id); + + // Now release all grades. + + $adminconfig = $this->get_admin_config(); + $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook; + $grades = $DB->get_records('assign_grades', array('assignment'=>$this->get_instance()->id)); + + $plugin = $this->get_feedback_plugin_by_type($gradebookplugin); + + foreach ($grades as $grade) { + // Fetch any comments for this student. + if ($plugin && $plugin->is_enabled() && $plugin->is_visible()) { + $grade->feedbacktext = $plugin->text_for_gradebook($grade); + $grade->feedbackformat = $plugin->format_for_gradebook($grade); + } + $this->gradebook_item_update(NULL, $grade); + } + + $this->add_to_log('reveal identities', get_string('revealidentities', 'assign')); + } + + /** * save grading options * @@ -2771,6 +3461,9 @@ class assign { if ($grade->locked) { $info .= get_string('submissionslocked', 'assign') . '. '; } + if ($grade->extensionduedate) { + $info .= get_string('userextensiondate', 'assign', userdate($grade->extensionduedate)); + } return $info; } @@ -2821,7 +3514,17 @@ class assign { return true; } if ($data = $mform->get_data()) { - $submission = $this->get_user_submission($USER->id, true); //create the submission if needed & its id + if ($this->get_instance()->teamsubmission) { + $submission = $this->get_group_submission($USER->id, 0, true); + } else { + $submission = $this->get_user_submission($USER->id, true); + } + if ($this->get_instance()->submissiondrafts) { + $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT; + } else { + $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED; + } + $grade = $this->get_user_grade($USER->id, false); // get the grade to check if it is locked if ($grade && $grade->locked) { print_error('submissionslocked', 'assign'); @@ -2837,7 +3540,7 @@ class assign { } } - $this->update_submission($submission); + $this->update_submission($submission, $USER->id, true, $this->get_instance()->teamsubmission); // Logging if (isset($data->submissionstatement)) { @@ -3023,13 +3726,18 @@ class assign { $mform->addElement('hidden', 'ajax', optional_param('ajax', 0, PARAM_INT)); $mform->setType('ajax', PARAM_INT); + if ($this->get_instance()->teamsubmission) { + $mform->addElement('selectyesno', 'applytoall', get_string('applytoteam', 'assign')); + $mform->setDefault('applytoall', 1); + } + $mform->addElement('hidden', 'action', 'submitgrade'); $mform->setType('action', PARAM_ALPHA); $buttonarray=array(); $buttonarray[] = $mform->createElement('submit', 'savegrade', get_string('savechanges', 'assign')); - if (!$last){ + if (!$last) { $buttonarray[] = $mform->createElement('submit', 'saveandshownext', get_string('savenext','assign')); } $buttonarray[] = $mform->createElement('cancel', 'cancelbutton', get_string('cancel')); @@ -3041,7 +3749,7 @@ class assign { $buttonarray[] = $mform->createElement('submit', 'nosaveandprevious', get_string('previous','assign')); } - if (!$last){ + if (!$last) { $buttonarray[] = $mform->createElement('submit', 'nosaveandnext', get_string('nosavebutnext', 'assign')); } $mform->addGroup($buttonarray, 'navar', '', array(' '), false); @@ -3116,9 +3824,14 @@ class assign { public function add_submission_form_elements(MoodleQuickForm $mform, stdClass $data) { global $USER; - $submission = $this->get_user_submission($USER->id, false); + // Team submissions. + if ($this->get_instance()->teamsubmission) { + $submission = $this->get_group_submission($USER->id, 0, false); + } else { + $submission = $this->get_user_submission($USER->id, false); + } - // submission statement + // Submission statement. $adminconfig = $this->get_admin_config(); $requiresubmissionstatement = !empty($adminconfig->requiresubmissionstatement) || @@ -3167,14 +3880,16 @@ class assign { } $submission = $this->get_user_submission($userid, false); + if (!$submission) { return; } $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT; - $this->update_submission($submission, false); + $this->update_submission($submission, $USER->id, true, $this->get_instance()->teamsubmission); - // update the modified time on the grade (grader modified) + // Update the modified time on the grade (grader modified). $grade = $this->get_user_grade($userid, true); + $grade->grader = $USER->id; $this->update_grade($grade); $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST); @@ -3241,6 +3956,55 @@ class assign { $this->add_to_log('unlock submission', get_string('unlocksubmissionforstudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user)))); } + /** + * Apply a grade from a grading form to a user (may be called multiple times for a group submission) + * + * @param stdClass $formdata - the data from the form + * @param int $userid - the user to apply the grade to + * @return void + */ + private function apply_grade_to_user($formdata, $userid) { + global $USER, $CFG, $DB; + + $grade = $this->get_user_grade($userid, true); + $gradingdisabled = $this->grading_disabled($userid); + $gradinginstance = $this->get_grading_instance($userid, $gradingdisabled); + if (!$gradingdisabled) { + if ($gradinginstance) { + $grade->grade = $gradinginstance->submit_and_get_grade($formdata->advancedgrading, $grade->id); + } else { + // Handle the case when grade is set to No Grade. + if (isset($formdata->grade)) { + $grade->grade= grade_floatval(unformat_float($formdata->grade)); + } + } + } + $grade->grader= $USER->id; + + $adminconfig = $this->get_admin_config(); + $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook; + + // Call save in plugins. + foreach ($this->feedbackplugins as $plugin) { + if ($plugin->is_enabled() && $plugin->is_visible()) { + if (!$plugin->save($grade, $formdata)) { + $result = false; + print_error($plugin->get_error()); + } + if (('assignfeedback_' . $plugin->get_type()) == $gradebookplugin) { + // This is the feedback plugin chose to push comments to the gradebook. + $grade->feedbacktext = $plugin->text_for_gradebook($grade); + $grade->feedbackformat = $plugin->format_for_gradebook($grade); + } + } + } + $this->update_grade($grade); + $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST); + + $this->add_to_log('grade submission', $this->format_grade_for_log($grade)); + } + + /** * save outcomes submitted from grading form * @@ -3288,7 +4052,7 @@ class assign { * @return bool - was the grade saved */ private function process_save_grade(&$mform) { - global $USER, $DB, $CFG; + global $CFG; // Include grade form require_once($CFG->dirroot . '/mod/assign/gradeform.php'); @@ -3313,49 +4077,22 @@ class assign { $mform = new mod_assign_grade_form(null, array($this, $data, array('rownum'=>$rownum, 'useridlist'=>$useridlist, 'last'=>false)), 'post', '', array('class'=>'gradeform')); if ($formdata = $mform->get_data()) { - $grade = $this->get_user_grade($userid, true); - $gradingdisabled = $this->grading_disabled($userid); - $gradinginstance = $this->get_grading_instance($userid, $gradingdisabled); - if (!$gradingdisabled) { - if ($gradinginstance) { - $grade->grade = $gradinginstance->submit_and_get_grade($formdata->advancedgrading, $grade->id); - } else { - // handle the case when grade is set to No Grade - if (isset($formdata->grade)) { - $grade->grade = grade_floatval(unformat_float($formdata->grade)); + if ($this->get_instance()->teamsubmission && $formdata->applytoall) { + $groupid = 0; + if ($this->get_submission_group($userid)) { + $group = $this->get_submission_group($userid); + if ($group) { + $groupid = $group->id; } } - } - $grade->grader= $USER->id; - - $adminconfig = $this->get_admin_config(); - $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook; - - // call save in plugins - foreach ($this->feedbackplugins as $plugin) { - if ($plugin->is_enabled() && $plugin->is_visible()) { - if (!$plugin->save($grade, $formdata)) { - $result = false; - print_error($plugin->get_error()); - } - if (('assignfeedback_' . $plugin->get_type()) == $gradebookplugin) { - // this is the feedback plugin chose to push comments to the gradebook - $grade->feedbacktext = $plugin->text_for_gradebook($grade); - $grade->feedbackformat = $plugin->format_for_gradebook($grade); - } + $members = $this->get_submission_group_members($groupid, true); + foreach ($members as $member) { + // User may exist in multple groups (which should put them in the default group). + $this->apply_grade_to_user($formdata, $member->id); } + } else { + $this->apply_grade_to_user($formdata, $userid); } - $this->process_outcomes($userid, $formdata); - - $grade->mailed = 0; - - $this->update_grade($grade); - - $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST); - - $this->add_to_log('grade submission', $this->format_grade_for_log($grade)); - - } else { return false; } @@ -3493,5 +4230,72 @@ class assign { return $grades; } + /** + * Call the static version of this function + * + * @param int $userid The userid to lookup + * @return int The unique id + */ + public function get_uniqueid_for_user($userid) { + return self::get_uniqueid_for_user_static($this->get_instance()->id, $userid); + } + + /** + * Foreach participant in the course - assign them a random id + * + * @param int $assignid The assignid to lookup + */ + public static function allocate_unique_ids($assignid) { + global $DB; + + $cm = get_coursemodule_from_instance('assign', $assignid, 0, false, MUST_EXIST); + $context = context_module::instance($cm->id); + + $currentgroup = groups_get_activity_group($cm, true); + $users = get_enrolled_users($context, "mod/assign:submit", $currentgroup, 'u.id'); + + // shuffle the users + shuffle($users); + + $record = new stdClass(); + $record->assignment = $assignid; + foreach ($users as $user) { + if (!$DB->get_record('assign_user_mapping', array('assignment'=>$assignid, 'userid'=>$user->id), 'id')) { + $record->userid = $user->id; + $DB->insert_record('assign_user_mapping', $record); + } + } + } + + /** + * Lookup this user id and return the unique id for this assignment + * + * @param int $userid The userid to lookup + * @return int The unique id + */ + public static function get_uniqueid_for_user_static($assignid, $userid) { + global $DB; + + // Search for a record. + if ($record = $DB->get_record('assign_user_mapping', array('assignment'=>$assignid, 'userid'=>$userid), 'id')) { + return $record->id; + } + + // Be a little smart about this - there is no record for the current user. + // We should ensure any unallocated ids for the current participant list are distrubited randomly + self::allocate_unique_ids($assignid); + + // Retry the search for a record. + if ($record = $DB->get_record('assign_user_mapping', array('assignment'=>$assignid, 'userid'=>$userid), 'id')) { + return $record->id; + } + + // The requested user must not be a participant. Add a record anyway. + $record = new stdClass(); + $record->assignment = $assignid; + $record->userid = $userid; + + return $DB->insert_record('assign_user_mapping', $record); + } } diff --git a/mod/assign/mod_form.php b/mod/assign/mod_form.php index c2cde375520..a1f177d1fb9 100644 --- a/mod/assign/mod_form.php +++ b/mod/assign/mod_form.php @@ -82,12 +82,12 @@ class mod_assign_mod_form extends moodleform_mod { $mform->addElement('date_time_selector', 'duedate', get_string('duedate', 'assign'), array('optional'=>true)); $mform->addHelpButton('duedate', 'duedate', 'assign'); $mform->setDefault('duedate', time()+7*24*3600); + $mform->addElement('date_time_selector', 'cutoffdate', get_string('cutoffdate', 'assign'), array('optional'=>true)); + $mform->addHelpButton('cutoffdate', 'cutoffdate', 'assign'); + $mform->setDefault('cutoffdate', time()+7*24*3600); $mform->addElement('selectyesno', 'alwaysshowdescription', get_string('alwaysshowdescription', 'assign')); $mform->addHelpButton('alwaysshowdescription', 'alwaysshowdescription', 'assign'); $mform->setDefault('alwaysshowdescription', 1); - $mform->addElement('selectyesno', 'preventlatesubmissions', get_string('preventlatesubmissions', 'assign')); - $mform->addHelpButton('preventlatesubmissions', 'preventlatesubmissions', 'assign'); - $mform->setDefault('preventlatesubmissions', 0); $mform->addElement('selectyesno', 'submissiondrafts', get_string('submissiondrafts', 'assign')); $mform->addHelpButton('submissiondrafts', 'submissiondrafts', 'assign'); $mform->setDefault('submissiondrafts', 0); @@ -107,6 +107,33 @@ class mod_assign_mod_form extends moodleform_mod { $mform->addHelpButton('sendlatenotifications', 'sendlatenotifications', 'assign'); $mform->setDefault('sendlatenotifications', 1); $mform->disabledIf('sendlatenotifications', 'sendnotifications', 'eq', 1); + $mform->addElement('selectyesno', 'teamsubmission', get_string('teamsubmission', 'assign')); + $mform->addHelpButton('teamsubmission', 'teamsubmission', 'assign'); + $mform->setDefault('teamsubmission', 0); + $mform->addElement('selectyesno', 'requireallteammemberssubmit', get_string('requireallteammemberssubmit', 'assign')); + $mform->addHelpButton('requireallteammemberssubmit', 'requireallteammemberssubmit', 'assign'); + $mform->setDefault('requireallteammemberssubmit', 0); + $mform->disabledIf('requireallteammemberssubmit', 'teamsubmission', 'eq', 0); + $mform->disabledIf('requireallteammemberssubmit', 'submissiondrafts', 'eq', 0); + + $groupings = groups_get_all_groupings($assignment->get_course()->id); + $options = array(); + $options[0] = get_string('none'); + foreach ($groupings as $grouping) { + $options[$grouping->id] = $grouping->name; + } + $mform->addElement('select', 'teamsubmissiongroupingid', get_string('teamsubmissiongroupingid', 'assign'), $options); + $mform->addHelpButton('teamsubmissiongroupingid', 'teamsubmissiongroupingid', 'assign'); + $mform->setDefault('teamsubmissiongroupingid', 0); + $mform->disabledIf('teamsubmissiongroupingid', 'teamsubmission', 'eq', 0); + + $mform->addElement('selectyesno', 'blindmarking', get_string('blindmarking', 'assign')); + $mform->addHelpButton('blindmarking', 'blindmarking', 'assign'); + $mform->setDefault('blindmarking', 0); + if ($assignment->has_submissions_or_grades() ) { + $mform->freeze('blindmarking'); + } + // plagiarism enabling form if (!empty($CFG->enableplagiarism)) { @@ -150,6 +177,17 @@ class mod_assign_mod_form extends moodleform_mod { $errors['duedate'] = get_string('duedatevalidation', 'assign'); } } + if ($data['duedate'] && $data['cutoffdate']) { + if ($data['duedate'] > $data['cutoffdate']) { + $errors['cutoffdate'] = get_string('cutoffdatevalidation', 'assign'); + } + } + if ($data['allowsubmissionsfromdate'] && $data['cutoffdate']) { + if ($data['allowsubmissionsfromdate'] > $data['cutoffdate']) { + $errors['cutoffdate'] = get_string('cutoffdatefromdatevalidation', 'assign'); + } + } + return $errors; } diff --git a/mod/assign/renderable.php b/mod/assign/renderable.php index 7624e6e6bb5..82dd79cd2a2 100644 --- a/mod/assign/renderable.php +++ b/mod/assign/renderable.php @@ -116,6 +116,10 @@ class assign_user_summary implements renderable { public $courseid; /** @var bool $viewfullnames */ public $viewfullnames = false; + /** @var bool $blindmarking */ + public $blindmarking = false; + /** @var int $uniqueidforuser */ + public $uniqueidforuser; /** * Constructor @@ -123,10 +127,12 @@ class assign_user_summary implements renderable { * @param int $courseid * @param bool $viewfullnames */ - public function __construct(stdClass $user, $courseid, $viewfullnames) { + public function __construct(stdClass $user, $courseid, $viewfullnames, $blindmarking, $uniqueidforuser) { $this->user = $user; $this->courseid = $courseid; $this->viewfullnames = $viewfullnames; + $this->blindmarking = $blindmarking; + $this->uniqueidforuser = $uniqueidforuser; } } @@ -289,6 +295,14 @@ class assign_submission_status implements renderable { var $alwaysshowdescription = false; /** @var stdClass the submission info (may be null) */ var $submission = null; + /** @var boolean teamsubmissionenabled - true or false */ + public $teamsubmissionenabled = false; + /** @var stdClass teamsubmission the team submission info (may be null) */ + public $teamsubmission = null; + /** @var stdClass submissiongroup the submission group info (may be null) */ + public $submissiongroup = null; + /** @var array submissiongroupmemberswhoneedtosubmit list of users who still need to submit */ + public $submissiongroupmemberswhoneedtosubmit = array(); /** @var bool submissionsenabled */ var $submissionsenabled = false; /** @var bool locked */ @@ -297,20 +311,32 @@ class assign_submission_status implements renderable { var $graded = false; /** @var int duedate */ var $duedate = 0; + /** @var int cutoffdate */ + public $cutoffdate = 0; /** @var array submissionplugins - the list of submission plugins */ var $submissionplugins = array(); /** @var string returnaction */ var $returnaction = ''; /** @var string returnparams */ var $returnparams = array(); + /** @var int courseid */ + public $courseid = 0; /** @var int coursemoduleid */ var $coursemoduleid = 0; /** @var int the view (assign_submission_status::STUDENT_VIEW OR assign_submission_status::GRADER_VIEW) */ var $view = self::STUDENT_VIEW; + /** @var bool canviewfullnames */ + public $canviewfullnames = false; /** @var bool canedit */ var $canedit = false; /** @var bool cansubmit */ var $cansubmit = false; + /** @var int extensionduedate */ + public $extensionduedate = 0; + /** @var context context */ + public $context = 0; + /** @var bool blindmarking - Should we hide student identities from graders? */ + public $blindmarking = false; /** * constructor @@ -318,35 +344,58 @@ class assign_submission_status implements renderable { * @param int $allowsubmissionsfromdate * @param bool $alwaysshowdescription * @param stdClass $submission + * @param bool $teamsubmissionenabled + * @param stdClass $teamsubmission + * @param int $submissiongroup + * @param array $submissiongroupmemberswhoneedtosubmit * @param bool $submissionsenabled * @param bool $locked * @param bool $graded * @param int $duedate + * @param int $cutoffdate * @param array $submissionplugins * @param string $returnaction * @param array $returnparams * @param int $coursemoduleid + * @param int $courseid * @param string $view * @param bool $canedit * @param bool $cansubmit + * @param bool $canviewfullnames + * @param int $extensionduedate - Any extension to the due date granted for this user + * @param context $context - Any extension to the due date granted for this user + * @param blindmarking $blindmarking - Should we hide student identities from graders? */ - public function __construct($allowsubmissionsfromdate, $alwaysshowdescription, $submission, $submissionsenabled, - $locked, $graded, $duedate, $submissionplugins, $returnaction, $returnparams, - $coursemoduleid, $view, $canedit, $cansubmit) { + public function __construct($allowsubmissionsfromdate, $alwaysshowdescription, $submission, + $teamsubmissionenabled, $teamsubmission, $submissiongroup, + $submissiongroupmemberswhoneedtosubmit, $submissionsenabled, + $locked, $graded, $duedate, $cutoffdate, $submissionplugins, $returnaction, $returnparams, + $coursemoduleid, $courseid, $view, $canedit, $cansubmit, $canviewfullnames, $extensionduedate, + $context, $blindmarking) { $this->allowsubmissionsfromdate = $allowsubmissionsfromdate; $this->alwaysshowdescription = $alwaysshowdescription; $this->submission = $submission; + $this->teamsubmissionenabled = $teamsubmissionenabled; + $this->teamsubmission = $teamsubmission; + $this->submissiongroup = $submissiongroup; + $this->submissiongroupmemberswhoneedtosubmit = $submissiongroupmemberswhoneedtosubmit; $this->submissionsenabled = $submissionsenabled; $this->locked = $locked; $this->graded = $graded; $this->duedate = $duedate; + $this->cutoffdate = $cutoffdate; $this->submissionplugins = $submissionplugins; $this->returnaction = $returnaction; $this->returnparams = $returnparams; $this->coursemoduleid = $coursemoduleid; + $this->courseid = $courseid; $this->view = $view; $this->canedit = $canedit; $this->cansubmit = $cansubmit; + $this->canviewfullnames = $canviewfullnames; + $this->extensionduedate = $extensionduedate; + $this->context = $context; + $this->blindmarking = $blindmarking; } } @@ -412,8 +461,12 @@ class assign_grading_summary implements renderable { var $submissionsneedgradingcount = 0; /** @var int duedate - The assignment due date (if one is set) */ var $duedate = 0; + /** @var int cutoffdate - The assignment cut off date (if one is set) */ + var $cutoffdate = 0; /** @var int coursemoduleid - The assignment course module id */ var $coursemoduleid = 0; + /** @var boolean teamsubmission - Are team submissions enabled for this assignment */ + public $teamsubmission = false; /** * constructor @@ -423,23 +476,28 @@ class assign_grading_summary implements renderable { * @param int $submissiondraftscount * @param bool $submissionsenabled * @param int $submissionssubmittedcount + * @param int $cutoffdate * @param int $duedate * @param int $coursemoduleid + * @param int $submissionsneedgradingcount + * @param bool $teamsubmission */ - public function __construct($participantcount, $submissiondraftsenabled, $submissiondraftscount, - $submissionsenabled, $submissionssubmittedcount, - $duedate, $coursemoduleid, $submissionsneedgradingcount) { + public function __construct($participantcount, $submissiondraftsenabled, + $submissiondraftscount, $submissionsenabled, + $submissionssubmittedcount, $cutoffdate, $duedate, + $coursemoduleid, $submissionsneedgradingcount, $teamsubmission) { $this->participantcount = $participantcount; $this->submissiondraftsenabled = $submissiondraftsenabled; $this->submissiondraftscount = $submissiondraftscount; $this->submissionsenabled = $submissionsenabled; $this->submissionssubmittedcount = $submissionssubmittedcount; $this->duedate = $duedate; + $this->cutoffdate = $cutoffdate; $this->coursemoduleid = $coursemoduleid; $this->submissionsneedgradingcount = $submissionsneedgradingcount; + $this->teamsubmission = $teamsubmission; } - } /** diff --git a/mod/assign/renderer.php b/mod/assign/renderer.php index c44f451e036..0a0d4fe7ca3 100644 --- a/mod/assign/renderer.php +++ b/mod/assign/renderer.php @@ -131,12 +131,16 @@ class mod_assign_renderer extends plugin_renderer_base { } $o .= $this->output->container_start('usersummary'); $o .= $this->output->box_start('boxaligncenter usersummarysection'); - $o .= $this->output->user_picture($summary->user); - $o .= $this->output->spacer(array('width'=>30)); - $o .= $this->output->action_link(new moodle_url('/user/view.php', - array('id' => $summary->user->id, - 'course'=>$summary->courseid)), - fullname($summary->user, $summary->viewfullnames)); + if ($summary->blindmarking) { + $o .= get_string('hiddenuser', 'assign', $summary->uniqueidforuser); + } else { + $o .= $this->output->user_picture($summary->user); + $o .= $this->output->spacer(array('width'=>30)); + $o .= $this->output->action_link(new moodle_url('/user/view.php', + array('id' => $summary->user->id, + 'course'=>$summary->courseid)), + fullname($summary->user, $summary->viewfullnames)); + } $o .= $this->output->box_end(); $o .= $this->output->container_end(); @@ -232,8 +236,13 @@ class mod_assign_renderer extends plugin_renderer_base { $t = new html_table(); // status - $this->add_table_row_tuple($t, get_string('numberofparticipants', 'assign'), - $summary->participantcount); + if ($summary->teamsubmission) { + $this->add_table_row_tuple($t, get_string('numberofteams', 'assign'), + $summary->participantcount); + } else { + $this->add_table_row_tuple($t, get_string('numberofparticipants', 'assign'), + $summary->participantcount); + } // drafts if ($summary->submissiondraftsenabled) { @@ -265,6 +274,19 @@ class mod_assign_renderer extends plugin_renderer_base { $due = format_time($duedate - $time); } $this->add_table_row_tuple($t, get_string('timeremaining', 'assign'), $due); + + if ($duedate < $time) { + $cutoffdate = $summary->cutoffdate; + if ($cutoffdate) { + if ($cutoffdate > $time) { + $late = get_string('latesubmissionsaccepted', 'assign'); + } else { + $late = get_string('nomoresubmissionsaccepted', 'assign'); + } + $this->add_table_row_tuple($t, get_string('latesubmissions', 'assign'), $late); + } + } + } // all done - write the table @@ -365,20 +387,72 @@ class mod_assign_renderer extends plugin_renderer_base { $t = new html_table(); + if ($status->teamsubmissionenabled) { + $row = new html_table_row(); + $cell1 = new html_table_cell(get_string('submissionteam', 'assign')); + $group = $status->submissiongroup; + if ($group) { + $cell2 = new html_table_cell(format_string($group->name, false, $status->context)); + } else { + $cell2 = new html_table_cell(get_string('defaultteam', 'assign')); + } + $row->cells = array($cell1, $cell2); + $t->data[] = $row; + } + $row = new html_table_row(); $cell1 = new html_table_cell(get_string('submissionstatus', 'assign')); - if ($status->submission) { - $cell2 = new html_table_cell(get_string('submissionstatus_' . $status->submission->status, 'assign')); - $cell2->attributes = array('class'=>'submissionstatus' . $status->submission->status); + if (!$status->teamsubmissionenabled) { + if ($status->submission) { + $cell2 = new html_table_cell(get_string('submissionstatus_' . $status->submission->status, 'assign')); + $cell2->attributes = array('class'=>'submissionstatus' . $status->submission->status); + } else { + if (!$status->submissionsenabled) { + $cell2 = new html_table_cell(get_string('noonlinesubmissions', 'assign')); + } else { + $cell2 = new html_table_cell(get_string('nosubmission', 'assign')); + } + } + $row->cells = array($cell1, $cell2); + $t->data[] = $row; } else { - if (!$status->submissionsenabled) { - $cell2 = new html_table_cell(get_string('noonlinesubmissions', 'assign')); + $row = new html_table_row(); + $cell1 = new html_table_cell(get_string('submissionstatus', 'assign')); + if ($status->teamsubmission) { + $submissionsummary = get_string('submissionstatus_' . $status->teamsubmission->status, 'assign'); + $groupid = 0; + if ($status->submissiongroup) { + $groupid = $status->submissiongroup->id; + } + + $members = $status->submissiongroupmemberswhoneedtosubmit; + $userslist = array(); + foreach ($members as $member) { + $url = new moodle_url('/user/view.php', array('id' => $member->id, 'course'=>$status->courseid)); + if ($status->view == assign_submission_status::GRADER_VIEW && $status->blindmarking) { + $userslist[] = $member->alias; + } else { + $userslist[] = $this->output->action_link($url, fullname($member, $status->canviewfullnames)); + } + } + if (count($userslist) > 0) { + $userstr = join(', ', $userslist); + $submissionsummary .= $this->output->container(get_string('userswhoneedtosubmit', 'assign', $userstr)); + } + + $cell2 = new html_table_cell($submissionsummary); + $cell2->attributes = array('class'=>'submissionstatus' . $status->teamsubmission->status); } else { $cell2 = new html_table_cell(get_string('nosubmission', 'assign')); + if (!$status->submissionsenabled) { + $cell2 = new html_table_cell(get_string('noonlinesubmissions', 'assign')); + } else { + $cell2 = new html_table_cell(get_string('nosubmission', 'assign')); + } } + $row->cells = array($cell1, $cell2); + $t->data[] = $row; } - $row->cells = array($cell1, $cell2); - $t->data[] = $row; // status if ($status->locked) { @@ -406,14 +480,33 @@ class mod_assign_renderer extends plugin_renderer_base { $duedate = $status->duedate; - if ($duedate >= 1) { + if ($duedate > 0) { $row = new html_table_row(); $cell1 = new html_table_cell(get_string('duedate', 'assign')); $cell2 = new html_table_cell(userdate($duedate)); $row->cells = array($cell1, $cell2); $t->data[] = $row; - // time remaining + if ($status->view == assign_submission_status::GRADER_VIEW) { + if ($status->cutoffdate) { + $row = new html_table_row(); + $cell1 = new html_table_cell(get_string('cutoffdate', 'assign')); + $cell2 = new html_table_cell(userdate($status->cutoffdate)); + $row->cells = array($cell1, $cell2); + $t->data[] = $row; + } + } + + if ($status->extensionduedate) { + $row = new html_table_row(); + $cell1 = new html_table_cell(get_string('extensionduedate', 'assign')); + $cell2 = new html_table_cell(userdate($status->extensionduedate)); + $row->cells = array($cell1, $cell2); + $t->data[] = $row; + $duedate = $status->extensionduedate; + } + + // Time remaining. $row = new html_table_row(); $cell1 = new html_table_cell(get_string('timeremaining', 'assign')); if ($duedate - $time <= 0) { @@ -440,19 +533,40 @@ class mod_assign_renderer extends plugin_renderer_base { $t->data[] = $row; } - // last modified - if ($status->submission) { + // Show graders whether this submission is editable by students. + if ($status->view == assign_submission_status::GRADER_VIEW) { + $row = new html_table_row(); + $cell1 = new html_table_cell(get_string('open', 'assign')); + if ($status->canedit) { + $cell2 = new html_table_cell(get_string('submissioneditable', 'assign')); + $cell2->attributes = array('class'=>'submissioneditable'); + } else { + $cell2 = new html_table_cell(get_string('submissionnoteditable', 'assign')); + $cell2->attributes = array('class'=>'submissionnoteditable'); + } + $row->cells = array($cell1, $cell2); + $t->data[] = $row; + } + + // Last modified. + $submission = $status->teamsubmission ? $status->teamsubmission : $status->submission; + if ($submission) { $row = new html_table_row(); $cell1 = new html_table_cell(get_string('timemodified', 'assign')); - $cell2 = new html_table_cell(userdate($status->submission->timemodified)); + $cell2 = new html_table_cell(userdate($submission->timemodified)); $row->cells = array($cell1, $cell2); $t->data[] = $row; foreach ($status->submissionplugins as $plugin) { - if ($plugin->is_enabled() && $plugin->is_visible() && !$plugin->is_empty($status->submission)) { + if ($plugin->is_enabled() && $plugin->is_visible() && !$plugin->is_empty($submission)) { $row = new html_table_row(); $cell1 = new html_table_cell($plugin->get_name()); - $pluginsubmission = new assign_submission_plugin_submission($plugin, $status->submission, assign_submission_plugin_submission::SUMMARY, $status->coursemoduleid, $status->returnaction, $status->returnparams); + $pluginsubmission = new assign_submission_plugin_submission($plugin, + $submission, + assign_submission_plugin_submission::SUMMARY, + $status->coursemoduleid, + $status->returnaction, + $status->returnparams); $cell2 = new html_table_cell($this->render($pluginsubmission)); $row->cells = array($cell1, $cell2); $t->data[] = $row; @@ -464,24 +578,28 @@ class mod_assign_renderer extends plugin_renderer_base { $o .= html_writer::table($t); $o .= $this->output->box_end(); - // links - if ($status->canedit) { - if (!$status->submission) { - $o .= $this->output->single_button(new moodle_url('/mod/assign/view.php', - array('id' => $status->coursemoduleid, 'action' => 'editsubmission')), get_string('addsubmission', 'assign'), 'get'); - } else { - $o .= $this->output->single_button(new moodle_url('/mod/assign/view.php', - array('id' => $status->coursemoduleid, 'action' => 'editsubmission')), get_string('editsubmission', 'assign'), 'get'); + // Links. + if ($status->view == assign_submission_status::STUDENT_VIEW) { + if ($status->canedit) { + if (!$submission) { + $urlparams = array('id' => $status->coursemoduleid, 'action' => 'editsubmission'); + $o .= $this->output->single_button(new moodle_url('/mod/assign/view.php', $urlparams), + get_string('addsubmission', 'assign'), 'get'); + } else { + $urlparams = array('id' => $status->coursemoduleid, 'action' => 'editsubmission'); + $o .= $this->output->single_button(new moodle_url('/mod/assign/view.php', $urlparams), + get_string('editsubmission', 'assign'), 'get'); + } } - } - if ($status->cansubmit) { - // submission.php - $o .= $this->output->single_button(new moodle_url('/mod/assign/view.php', - array('id' => $status->coursemoduleid, 'action'=>'submit')), get_string('submitassignment', 'assign'), 'get'); - $o .= $this->output->box_start('boxaligncenter submithelp'); - $o .= get_string('submitassignment_help', 'assign'); - $o .= $this->output->box_end(); + if ($status->cansubmit) { + $urlparams = array('id' => $status->coursemoduleid, 'action'=>'submit'); + $o .= $this->output->single_button(new moodle_url('/mod/assign/view.php', $urlparams), + get_string('submitassignment', 'assign'), 'get'); + $o .= $this->output->box_start('boxaligncenter submithelp'); + $o .= get_string('submitassignment_help', 'assign'); + $o .= $this->output->box_end(); + } } $o .= $this->output->container_end(); @@ -537,9 +655,10 @@ class mod_assign_renderer extends plugin_renderer_base { $o .= $this->output->box_start('boxaligncenter gradingtable'); $this->page->requires->js_init_call('M.mod_assign.init_grading_table', array()); $this->page->requires->string_for_js('nousersselected', 'assign'); + $this->page->requires->string_for_js('batchoperationconfirmgrantextension', 'assign'); $this->page->requires->string_for_js('batchoperationconfirmlock', 'assign'); - $this->page->requires->string_for_js('batchoperationconfirmunlock', 'assign'); $this->page->requires->string_for_js('batchoperationconfirmreverttodraft', 'assign'); + $this->page->requires->string_for_js('batchoperationconfirmunlock', 'assign'); $this->page->requires->string_for_js('editaction', 'assign'); // need to get from prefs $o .= $this->flexible_table($table, $table->get_rows_per_page(), true); diff --git a/mod/assign/styles.css b/mod/assign/styles.css index 8b1b67bc688..65963bb718a 100644 --- a/mod/assign/styles.css +++ b/mod/assign/styles.css @@ -130,3 +130,7 @@ div.earlysubmission { #page-mod-assign-view div.gradingtable tr .quickgrademodified { background-color: #FFCC99; } + +td.submissioneditable { + color: red; +} diff --git a/mod/assign/submission/comments/locallib.php b/mod/assign/submission/comments/locallib.php index eb879388757..8cdfebbc83d 100644 --- a/mod/assign/submission/comments/locallib.php +++ b/mod/assign/submission/comments/locallib.php @@ -164,4 +164,17 @@ class assign_submission_comments extends assign_submission_plugin { return false; } + /** + * If blind marking is enabled then disable this plugin (it shows names) + * + * @return bool + */ + public function is_enabled() { + if ($this->assignment->has_instance() && $this->assignment->is_blind_marking()) { + return false; + } + return parent::is_enabled(); + } + + } diff --git a/mod/assign/submission/onlinetext/locallib.php b/mod/assign/submission/onlinetext/locallib.php index 729fa2d9ba3..1c5b757c52e 100644 --- a/mod/assign/submission/onlinetext/locallib.php +++ b/mod/assign/submission/onlinetext/locallib.php @@ -250,7 +250,18 @@ class assign_submission_onlinetext extends assign_submission_plugin { if ($onlinetextsubmission) { $user = $DB->get_record("user", array("id"=>$submission->userid),'id,username,firstname,lastname', MUST_EXIST); - $prefix = clean_filename(fullname($user) . "_" .$submission->userid . "_"); + if (!$this->assignment->is_blind_marking()) { + $filename = str_replace('_', '', fullname($user)) . '_' . + $this->assignment->get_uniqueid_for_user($userid) . '_' . + $this->get_name() . '_'; + $prefix = clean_filename($filename); + } else { + $filename = get_string('participant', 'assign') . '_' . + $this->assignment->get_uniqueid_for_user($userid) . '_' . + $this->get_name() . '_'; + $prefix = clean_filename($filename); + } + $finaltext = str_replace('@@PLUGINFILE@@/', $prefix, $onlinetextsubmission->onlinetext); $submissioncontent = "". format_text($finaltext, $onlinetextsubmission->onlineformat, array('context'=>$this->assignment->get_context())). ""; //fetched from database diff --git a/mod/assign/upgradelib.php b/mod/assign/upgradelib.php index ff701c57b0f..0002412334d 100644 --- a/mod/assign/upgradelib.php +++ b/mod/assign/upgradelib.php @@ -89,8 +89,12 @@ class assign_upgrade_manager { $data->allowsubmissionsfromdate = $oldassignment->timeavailable; $data->grade = $oldassignment->grade; $data->submissiondrafts = $oldassignment->resubmit; - $data->preventlatesubmissions = $oldassignment->preventlate; $data->requiresubmissionstatement = 0; + $data->cutoffdate = 0; + // New way to specify no late submissions. + if ($oldassignment->preventlate) { + $data->cutoffdate = $data->duedate; + } $newassignment = new assign(null, null, null); diff --git a/mod/assign/version.php b/mod/assign/version.php index b9a29a3d16c..b8821825e3c 100644 --- a/mod/assign/version.php +++ b/mod/assign/version.php @@ -25,7 +25,7 @@ defined('MOODLE_INTERNAL') || die(); $module->component = 'mod_assign'; // Full name of the plugin (used for diagnostics) -$module->version = 2012081600; // The current module version (Date: YYYYMMDDXX) +$module->version = 2012082400; // The current module version (Date: YYYYMMDDXX) $module->requires = 2012061700; // Requires this Moodle version $module->cron = 60; diff --git a/mod/data/field/checkbox/mod.html b/mod/data/field/checkbox/mod.html index 6d8bef38234..a8812926b95 100644 --- a/mod/data/field/checkbox/mod.html +++ b/mod/data/field/checkbox/mod.html @@ -9,6 +9,6 @@ - + diff --git a/mod/data/field/latlong/field.class.php b/mod/data/field/latlong/field.class.php index a6f688961fb..e94169996bc 100644 --- a/mod/data/field/latlong/field.class.php +++ b/mod/data/field/latlong/field.class.php @@ -130,7 +130,6 @@ class data_field_latlong extends data_field_base { } else { $compasslong = sprintf('%01.4f', $long) . '°E'; } - $str = '
    '; // Now let's create the jump-to-services link $servicesshown = explode(',', $this->field->param1); @@ -148,10 +147,11 @@ class data_field_latlong extends data_field_base { ); if(sizeof($servicesshown)==1 && $servicesshown[0]) { - $str .= " linkoutservices[$servicesshown[0]]) ."' title='$servicesshown[0]'>$compasslat, $compasslong"; } elseif (sizeof($servicesshown)>1) { + $str = ''; $str .= "$compasslat, $compasslong\n"; $str .= ""; $str .= ""; + $str .= '
    '; } else { - $str.= "$compasslat, $compasslong"; + $str = "$compasslat, $compasslong"; } - $str.= ''; + return $str; } return false; diff --git a/mod/data/field/menu/mod.html b/mod/data/field/menu/mod.html index 58fa709c2bc..f92fb136b23 100644 --- a/mod/data/field/menu/mod.html +++ b/mod/data/field/menu/mod.html @@ -9,6 +9,6 @@ - + diff --git a/mod/data/field/multimenu/mod.html b/mod/data/field/multimenu/mod.html index 6d8bef38234..a8812926b95 100644 --- a/mod/data/field/multimenu/mod.html +++ b/mod/data/field/multimenu/mod.html @@ -9,6 +9,6 @@ - + diff --git a/mod/data/field/picture/field.class.php b/mod/data/field/picture/field.class.php index 6b3935ef4e8..37b320d8a9d 100644 --- a/mod/data/field/picture/field.class.php +++ b/mod/data/field/picture/field.class.php @@ -152,13 +152,13 @@ class data_field_picture extends data_field_base { 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 = ''.s($alt).''; + $str = ''.s($alt).''; } 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 = ''.s($alt).''; + $str = ''.s($alt).''; } return $str; diff --git a/mod/data/field/picture/mod.html b/mod/data/field/picture/mod.html index 4b32474bbe7..2ea07789ab3 100644 --- a/mod/data/field/picture/mod.html +++ b/mod/data/field/picture/mod.html @@ -16,27 +16,27 @@ - + - + - + - + diff --git a/mod/data/field/radiobutton/mod.html b/mod/data/field/radiobutton/mod.html index 6d8bef38234..a8812926b95 100644 --- a/mod/data/field/radiobutton/mod.html +++ b/mod/data/field/radiobutton/mod.html @@ -9,6 +9,6 @@ - + diff --git a/mod/data/field/textarea/mod.html b/mod/data/field/textarea/mod.html index fb684245bfa..e75365abf9f 100644 --- a/mod/data/field/textarea/mod.html +++ b/mod/data/field/textarea/mod.html @@ -14,7 +14,7 @@ - field->param2)) { echo('"60"'); @@ -28,7 +28,7 @@ - field->param3)) { echo('"35"'); diff --git a/mod/data/lib.php b/mod/data/lib.php index 0cc49b31235..e73490cb89c 100644 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -244,7 +244,7 @@ class data_field_base { // Base class for Database Field Types (see field/*/ $str = '
    '; $str .= ''; - $str .= ''; + $str .= ''; $str .= '
    '; return $str; @@ -1480,14 +1480,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 ''; + echo ''; echo '    '; // foreach field, print the option echo ''; echo '
    '; - echo '
    '; + echo '
    '; echo ''; // print ASC or DESC @@ -1620,7 +1614,7 @@ function data_print_preference_form($data, $perpage, $search, $sort='', $order=' echo format_text($newtext, FORMAT_HTML, $options); echo ''; - echo ''; + echo ''; echo '


    '; echo '
    '; echo '
    '; diff --git a/mod/data/styles.css b/mod/data/styles.css index 4f6846540c8..600c79d4d8b 100644 --- a/mod/data/styles.css +++ b/mod/data/styles.css @@ -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;} diff --git a/mod/data/templates.php b/mod/data/templates.php index 4751732f8ec..c551e03a10c 100644 --- a/mod/data/templates.php +++ b/mod/data/templates.php @@ -141,7 +141,7 @@ if (($mytemplate = data_submitted()) && confirm_sesskey()) { } } } else { - echo '
    '.get_string('header'.$mode,'data').'
    '; + echo '
    '.get_string('header'.$mode,'data').'
    '; } /// If everything is empty then generate some defaults @@ -198,7 +198,7 @@ if ($mode == 'listtemplate'){ echo ''; echo ' '; echo ''; - echo '
    '; + echo '
    '; $field = 'listtemplateheader'; $editor->use_editor($field, $options); @@ -290,9 +290,9 @@ echo ''; echo ''; if ($mode == 'listtemplate'){ - echo '
    '; + echo '
    '; } else { - echo '
    '; + echo '
    '; } $field = 'template'; @@ -305,7 +305,7 @@ if ($mode == 'listtemplate'){ echo ''; echo ' '; echo ''; - echo '
    '; + echo '
    '; $field = 'listtemplatefooter'; $editor->use_editor($field, $options); @@ -316,7 +316,7 @@ if ($mode == 'listtemplate'){ echo ''; echo ' '; echo ''; - echo '
    '; + echo '
    '; $field = 'rsstitletemplate'; $editor->use_editor($field, $options); @@ -325,7 +325,7 @@ if ($mode == 'listtemplate'){ echo ''; } -echo ''; +echo ''; echo ' '; echo ''; diff --git a/mod/lesson/format.php b/mod/lesson/format.php index 95bb645efd9..a7c7307624b 100644 --- a/mod/lesson/format.php +++ b/mod/lesson/format.php @@ -29,27 +29,6 @@ defined('MOODLE_INTERNAL') || die(); -/**#@+ - * The core question types. - * - * These used to be in lib/questionlib.php, but are being deprecated. Copying them - * here to keep this code working for now. - */ -if (!defined('SHORTANSWER')) { - define("SHORTANSWER", "shortanswer"); - define("TRUEFALSE", "truefalse"); - define("MULTICHOICE", "multichoice"); - define("RANDOM", "random"); - define("MATCH", "match"); - define("RANDOMSAMATCH", "randomsamatch"); - define("DESCRIPTION", "description"); - define("NUMERICAL", "numerical"); - define("MULTIANSWER", "multianswer"); - define("CALCULATED", "calculated"); - define("ESSAY", "essay"); -} -/**#@-*/ - /** * Given some question info and some data about the the answers * this function parses, organises and saves the question @@ -59,10 +38,10 @@ if (!defined('SHORTANSWER')) { * Lifted from mod/quiz/lib.php - * 1. all reference to oldanswers removed * 2. all reference to quiz_multichoice table removed - * 3. In SHORTANSWER questions usecase is store in the qoption field - * 4. In NUMERIC questions store the range as two answers - * 5. TRUEFALSE options are ignored - * 6. For MULTICHOICE questions with more than one answer the qoption field is true + * 3. In shortanswer questions usecase is store in the qoption field + * 4. In numeric questions store the range as two answers + * 5. truefalse options are ignored + * 6. For multichoice questions with more than one answer the qoption field is true * * @param opject $question Contains question data like question, type and answers. * @return object Returns $result->error or $result->notice. @@ -116,7 +95,7 @@ function lesson_save_question_options($question, $lesson) { } break; - case LESSON_PAGE_NUMERICAL: // Note similarities to SHORTANSWER + case LESSON_PAGE_NUMERICAL: // Note similarities to shortanswer. $answers = array(); $maxfraction = -1; @@ -305,11 +284,11 @@ class qformat_default { var $displayerrors = true; var $category = NULL; var $questionids = array(); - var $qtypeconvert = array(NUMERICAL => LESSON_PAGE_NUMERICAL, - MULTICHOICE => LESSON_PAGE_MULTICHOICE, - TRUEFALSE => LESSON_PAGE_TRUEFALSE, - SHORTANSWER => LESSON_PAGE_SHORTANSWER, - MATCH => LESSON_PAGE_MATCHING + var $qtypeconvert = array('numerical' => LESSON_PAGE_NUMERICAL, + 'multichoice' => LESSON_PAGE_MULTICHOICE, + 'truefalse' => LESSON_PAGE_TRUEFALSE, + 'shortanswer' => LESSON_PAGE_SHORTANSWER, + 'match' => LESSON_PAGE_MATCHING ); // Importing functions @@ -352,11 +331,11 @@ class qformat_default { case 'category': break; // the good ones - case SHORTANSWER : - case NUMERICAL : - case TRUEFALSE : - case MULTICHOICE : - case MATCH : + case 'shortanswer' : + case 'numerical' : + case 'truefalse' : + case 'multichoice' : + case 'match' : $count++; //Show nice formated question in one line. @@ -366,12 +345,12 @@ class qformat_default { $newpage->lessonid = $lesson->id; $newpage->qtype = $this->qtypeconvert[$question->qtype]; switch ($question->qtype) { - case SHORTANSWER : + case 'shortanswer' : if (isset($question->usecase)) { $newpage->qoption = $question->usecase; } break; - case MULTICHOICE : + case 'multichoice' : if (isset($question->single)) { $newpage->qoption = !$question->single; } diff --git a/mod/lesson/pagetypes/multichoice.php b/mod/lesson/pagetypes/multichoice.php index f9307560885..089f621a970 100644 --- a/mod/lesson/pagetypes/multichoice.php +++ b/mod/lesson/pagetypes/multichoice.php @@ -126,7 +126,7 @@ class lesson_page_type_multichoice extends lesson_page { } if ($this->properties->qoption) { - // MULTIANSWER allowed, user's answer is an array + // Multianswer allowed, user's answer is an array if (empty($data->answer) || !is_array($data->answer)) { $result->noanswer = true; diff --git a/mod/page/lib.php b/mod/page/lib.php index ea2133f2e08..0f03e2abcfb 100644 --- a/mod/page/lib.php +++ b/mod/page/lib.php @@ -515,7 +515,3 @@ function page_dndupload_handle($uploadinfo) { return page_add_instance($data, null); } - -function mod_page_allow_group_member_remove($itemid, $groupid, $userid) { - return true; -} diff --git a/mod/quiz/accessrule/safebrowser/rule.php b/mod/quiz/accessrule/safebrowser/rule.php index e73c5b83412..763acdc0286 100644 --- a/mod/quiz/accessrule/safebrowser/rule.php +++ b/mod/quiz/accessrule/safebrowser/rule.php @@ -61,6 +61,9 @@ class quizaccess_safebrowser extends quiz_access_rule_base { public function setup_attempt_page($page) { $page->set_title($this->quizobj->get_course()->shortname . ': ' . $page->title); $page->set_cacheable(false); + $page->set_popup_notification_allowed(false); // Prevent message notifications. + $page->set_heading($page->title); + $page->set_pagelayout('secure'); } /** diff --git a/mod/quiz/accessrule/securewindow/rule.php b/mod/quiz/accessrule/securewindow/rule.php index ede3f934de2..bbeda5449c9 100644 --- a/mod/quiz/accessrule/securewindow/rule.php +++ b/mod/quiz/accessrule/securewindow/rule.php @@ -73,7 +73,7 @@ class quizaccess_securewindow extends quiz_access_rule_base { $page->set_popup_notification_allowed(false); // Prevent message notifications. $page->set_title($this->quizobj->get_course()->shortname . ': ' . $page->title); $page->set_cacheable(false); - $page->set_pagelayout('popup'); + $page->set_pagelayout('secure'); if ($this->quizobj->is_preview_user()) { return; diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index 06a2e513b92..fc2aa97b47e 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -1150,8 +1150,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; @@ -1341,8 +1347,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'), diff --git a/mod/quiz/styles.css b/mod/quiz/styles.css index 63610241324..d07442b9980 100644 --- a/mod/quiz/styles.css +++ b/mod/quiz/styles.css @@ -178,6 +178,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;} diff --git a/mod/workshop/form/comments/backup/moodle1/lib.php b/mod/workshop/form/comments/backup/moodle1/lib.php index 5d557a443c6..2e9a0c01e43 100644 --- a/mod/workshop/form/comments/backup/moodle1/lib.php +++ b/mod/workshop/form/comments/backup/moodle1/lib.php @@ -32,8 +32,13 @@ class moodle1_workshopform_comments_handler extends moodle1_workshopform_handler /** * Converts into + * + * @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); diff --git a/mod/workshop/form/numerrors/backup/moodle1/lib.php b/mod/workshop/form/numerrors/backup/moodle1/lib.php index d2c63103811..e18a2f26701 100644 --- a/mod/workshop/form/numerrors/backup/moodle1/lib.php +++ b/mod/workshop/form/numerrors/backup/moodle1/lib.php @@ -49,9 +49,12 @@ class moodle1_workshopform_numerrors_handler extends moodle1_workshopform_handle /** * Converts into 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(); diff --git a/mod/workshop/form/rubric/backup/moodle1/lib.php b/mod/workshop/form/rubric/backup/moodle1/lib.php index be0a6c6b11a..fe604f807d2 100644 --- a/mod/workshop/form/rubric/backup/moodle1/lib.php +++ b/mod/workshop/form/rubric/backup/moodle1/lib.php @@ -46,8 +46,11 @@ class moodle1_workshopform_rubric_handler extends moodle1_workshopform_handler { /** * Processes one + * + * @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(); } diff --git a/question/category_class.php b/question/category_class.php index 79339f29e1c..34ff811f3fd 100644 --- a/question/category_class.php +++ b/question/category_class.php @@ -102,9 +102,12 @@ class question_category_list_item extends list_item { /// 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 = "edit}\" href=\"$catediturl\">".$category->name ." ".'('.$category->questioncount.')'; + $item = "edit}\" href=\"$catediturl\">" . + format_string($category->name, true, array('context' => $this->parentlist->context)) . + " ".'('.$category->questioncount.')'; - $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) { diff --git a/question/format.php b/question/format.php index 7748b6f04de..980f20973b7 100644 --- a/question/format.php +++ b/question/format.php @@ -27,29 +27,6 @@ defined('MOODLE_INTERNAL') || die(); -/**#@+ - * The core question types. - * - * These used to be in lib/questionlib.php, but are being deprecated. Copying - * them here to keep the import/export code working for now (there are 135 - * references to these constants which I don't want to try to fix at the moment.) - */ -if (!defined('SHORTANSWER')) { - define("SHORTANSWER", "shortanswer"); - define("TRUEFALSE", "truefalse"); - define("MULTICHOICE", "multichoice"); - define("RANDOM", "random"); - define("MATCH", "match"); - define("RANDOMSAMATCH", "randomsamatch"); - define("DESCRIPTION", "description"); - define("NUMERICAL", "numerical"); - define("MULTIANSWER", "multianswer"); - define("CALCULATED", "calculated"); - define("ESSAY", "essay"); -} -/**#@-*/ - - /** * Base class for question import and export formats. * diff --git a/question/format/aiken/format.php b/question/format/aiken/format.php index 4e64c2f6a93..8c3cbc99883 100644 --- a/question/format/aiken/format.php +++ b/question/format/aiken/format.php @@ -94,7 +94,7 @@ class qformat_aiken extends qformat_default { continue; } else { // Must be the first line of a new question, since no recognised prefix. - $question->qtype = MULTICHOICE; + $question->qtype = 'multichoice'; $question->name = shorten_text(s($nowline), 50); $question->questiontext = htmlspecialchars(trim($nowline), ENT_NOQUOTES); $question->questiontextformat = FORMAT_HTML; diff --git a/question/format/examview/format.php b/question/format/examview/format.php index 9cb9bb5c553..d13126619f6 100644 --- a/question/format/examview/format.php +++ b/question/format/examview/format.php @@ -37,18 +37,18 @@ require_once($CFG->libdir . '/xmlize.php'); class qformat_examview extends qformat_based_on_xml { public $qtypes = array( - 'tf' => TRUEFALSE, - 'mc' => MULTICHOICE, - 'yn' => TRUEFALSE, - 'co' => SHORTANSWER, - 'ma' => MATCH, + 'tf' => 'truefalse', + 'mc' => 'multichoice', + 'yn' => 'truefalse', + 'co' => 'shortanswer', + 'ma' => 'match', 'mtf' => 99, - 'nr' => NUMERICAL, + 'nr' => 'numerical', 'pr' => 99, - 'es' => ESSAY, + 'es' => 'essay', 'ca' => 99, 'ot' => 99, - 'sa' => SHORTANSWER, + 'sa' => 'shortanswer', ); public $matching_questions = array(); @@ -132,7 +132,7 @@ class qformat_examview extends qformat_based_on_xml { $question->questiontextformat = FORMAT_HTML; $question->questiontextfiles = array(); $question->name = shorten_text( $question->questiontext, 250 ); - $question->qtype = MATCH; + $question->qtype = 'match'; $question = $this->add_blank_combined_feedback($question); $question->subquestions = array(); $question->subanswers = array(); @@ -203,23 +203,23 @@ class qformat_examview extends qformat_based_on_xml { $question->name = shorten_text( $question->questiontext, 250 ); switch ($question->qtype) { - case MULTICHOICE: + case 'multichoice': $question = $this->parse_mc($qrec['#'], $question); break; - case MATCH: + case 'match': $groupname = trim($qrec['@']['group']); $question = $this->parse_ma($qrec['#'], $groupname); break; - case TRUEFALSE: + case 'truefalse': $question = $this->parse_tf_yn($qrec['#'], $question); break; - case SHORTANSWER: + case 'shortanswer': $question = $this->parse_co($qrec['#'], $question); break; - case ESSAY: + case 'essay': $question = $this->parse_es($qrec['#'], $question); break; - case NUMERICAL: + case 'numerical': $question = $this->parse_nr($qrec['#'], $question); break; break; diff --git a/question/format/gift/examples.txt b/question/format/gift/examples.txt index c0c1e0e2b7c..e65d4f0db64 100644 --- a/question/format/gift/examples.txt +++ b/question/format/gift/examples.txt @@ -2,7 +2,7 @@ // by Paul Tsuchido Shew, January 2004. //-----------------------------------------// -// EXAMPLES FROM DESCRIPTION +// Examples from the class description. //-----------------------------------------// Who's buried in Grant's tomb?{~Grant ~Jefferson =no one} @@ -17,7 +17,7 @@ When was Ulysses S. Grant born?{#1822:1} //-----------------------------------------// -// EXAMPLES FROM DOCUMENTATION +// Examples from the documentation. //-----------------------------------------// // ===Multiple Choice=== @@ -161,7 +161,7 @@ Which of the following is NOT a control character for the GIFT import format? { //-----------------------------------------// -// EXAMPLES FROM gift/format.php +// Examples from gift/format.php. //-----------------------------------------// Who's buried in Grant's tomb?{~Grant ~Jefferson =no one} @@ -178,7 +178,7 @@ Match the following countries with their corresponding capitals.{=Canada->Ottawa =Italy->Rome =Japan->Tokyo} //-----------------------------------------// -// MORE COMPLICATED EXAMPLES +// More complicated examples. //-----------------------------------------// ::Grant's Tomb::Grant is { diff --git a/question/format/gift/format.php b/question/format/gift/format.php index 48171a4d3c2..eb608393b1a 100644 --- a/question/format/gift/format.php +++ b/question/format/gift/format.php @@ -283,26 +283,26 @@ class qformat_gift extends qformat_default { } if ($description) { - $question->qtype = DESCRIPTION; + $question->qtype = 'description'; } else if ($answertext == '') { - $question->qtype = ESSAY; + $question->qtype = 'essay'; } else if ($answertext{0} == '#') { - $question->qtype = NUMERICAL; + $question->qtype = 'numerical'; } else if (strpos($answertext, '~') !== false) { // only Multiplechoice questions contain tilde ~ - $question->qtype = MULTICHOICE; + $question->qtype = 'multichoice'; } else if (strpos($answertext, '=') !== false && strpos($answertext, '->') !== false) { // only Matching contains both = and -> - $question->qtype = MATCH; + $question->qtype = 'match'; - } else { // either TRUEFALSE or SHORTANSWER + } else { // either truefalse or shortanswer - // TRUEFALSE question check + // truefalse question check $truefalse_check = $answertext; if (strpos($answertext, '#') > 0) { // strip comments to check for TrueFalse question @@ -311,10 +311,10 @@ class qformat_gift extends qformat_default { $valid_tf_answers = array('T', 'TRUE', 'F', 'FALSE'); if (in_array($truefalse_check, $valid_tf_answers)) { - $question->qtype = TRUEFALSE; + $question->qtype = 'truefalse'; - } else { // Must be SHORTANSWER - $question->qtype = SHORTANSWER; + } else { // Must be shortanswer + $question->qtype = 'shortanswer'; } } @@ -325,12 +325,12 @@ class qformat_gift extends qformat_default { } switch ($question->qtype) { - case DESCRIPTION: + case 'description': $question->defaultmark = 0; $question->length = 0; return $question; - case ESSAY: + case 'essay': $question->responseformat = 'editor'; $question->responsefieldlines = 15; $question->attachments = 0; @@ -338,7 +338,7 @@ class qformat_gift extends qformat_default { 'text' => '', 'format' => FORMAT_HTML, 'files' => array()); return $question; - case MULTICHOICE: + case 'multichoice': if (strpos($answertext,"=") === false) { $question->single = 0; // multiple answers are enabled if no single answer is 100% correct } else { @@ -382,7 +382,7 @@ class qformat_gift extends qformat_default { return $question; - case MATCH: + case 'match': $question = $this->add_blank_combined_feedback($question); $answers = explode('=', $answertext); @@ -413,7 +413,7 @@ class qformat_gift extends qformat_default { return $question; - case TRUEFALSE: + case 'truefalse': list($answer, $wrongfeedback, $rightfeedback) = $this->split_truefalse_comment($answertext, $question->questiontextformat); @@ -431,8 +431,8 @@ class qformat_gift extends qformat_default { return $question; - case SHORTANSWER: - // SHORTANSWER Question + case 'shortanswer': + // Shortanswer question. $answers = explode("=", $answertext); if (isset($answers[0])) { $answers[0] = trim($answers[0]); @@ -464,7 +464,7 @@ class qformat_gift extends qformat_default { return $question; - case NUMERICAL: + case 'numerical': // Note similarities to ShortAnswer $answertext = substr($answertext, 1); // remove leading "#" @@ -645,12 +645,12 @@ class qformat_gift extends qformat_default { $expout .= "\$CATEGORY: $question->category\n"; break; - case DESCRIPTION: + case 'description': $expout .= $this->write_name($question->name); $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); break; - case ESSAY: + case 'essay': $expout .= $this->write_name($question->name); $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); $expout .= "{"; @@ -658,7 +658,7 @@ class qformat_gift extends qformat_default { $expout .= "}\n"; break; - case TRUEFALSE: + case 'truefalse': $trueanswer = $question->options->answers[$question->options->trueanswer]; $falseanswer = $question->options->answers[$question->options->falseanswer]; if ($trueanswer->fraction == 1) { @@ -690,7 +690,7 @@ class qformat_gift extends qformat_default { $expout .= "}\n"; break; - case MULTICHOICE: + case 'multichoice': $expout .= $this->write_name($question->name); $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); $expout .= "{\n"; @@ -715,7 +715,7 @@ class qformat_gift extends qformat_default { $expout .= "}\n"; break; - case SHORTANSWER: + case 'shortanswer': $expout .= $this->write_name($question->name); $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); $expout .= "{\n"; @@ -729,7 +729,7 @@ class qformat_gift extends qformat_default { $expout .= "}\n"; break; - case NUMERICAL: + case 'numerical': $expout .= $this->write_name($question->name); $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); $expout .= "{#\n"; @@ -748,7 +748,7 @@ class qformat_gift extends qformat_default { $expout .= "}\n"; break; - case MATCH: + case 'match': $expout .= $this->write_name($question->name); $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); $expout .= "{\n"; diff --git a/question/format/learnwise/format.php b/question/format/learnwise/format.php index fa804756af6..3bd31b418e2 100644 --- a/question/format/learnwise/format.php +++ b/question/format/learnwise/format.php @@ -125,7 +125,7 @@ class qformat_learnwise extends qformat_default { } $question = $this->defaultquestion(); - $question->qtype = MULTICHOICE; + $question->qtype = 'multichoice'; $question->name = substr($questiontext, 0, 30); if (strlen($questiontext) > 30) { $question->name .= '...'; diff --git a/question/format/missingword/format.php b/question/format/missingword/format.php index 15e1d711045..a1b3cea580d 100644 --- a/question/format/missingword/format.php +++ b/question/format/missingword/format.php @@ -112,7 +112,7 @@ class qformat_missingword extends qformat_default { return false; case 1: - $question->qtype = SHORTANSWER; + $question->qtype = 'shortanswer'; $answer = trim($answers[0]); if ($answer[0] == "=") { @@ -125,7 +125,7 @@ class qformat_missingword extends qformat_default { return $question; default: - $question->qtype = MULTICHOICE; + $question->qtype = 'multichoice'; foreach ($answers as $key => $answer) { $answer = trim($answer); diff --git a/question/format/webct/format.php b/question/format/webct/format.php index c599ca8bbf9..6facb80fce8 100644 --- a/question/format/webct/format.php +++ b/question/format/webct/format.php @@ -314,7 +314,7 @@ class qformat_webct extends qformat_default { } } switch ($question->qtype) { - case SHORTANSWER: + case 'shortanswer': if ($maxfraction != 1) { $maxfraction = $maxfraction * 100; $errors[] = "'$question->name': ".get_string("wronggrade", "qformat_webct", $nLineCounter).' '.get_string("fractionsnomax", "question", $maxfraction); @@ -322,7 +322,7 @@ class qformat_webct extends qformat_default { } break; - case MULTICHOICE: + case 'multichoice': if ($question->single) { if ($maxfraction != 1) { $maxfraction = $maxfraction * 100; @@ -339,7 +339,7 @@ class qformat_webct extends qformat_default { } break; - case CALCULATED: + case 'calculated': foreach ($question->answers as $answer) { if ($formulaerror = qtype_calculated_find_formula_errors($answer)) { $warnings[] = "'$question->name': ". $formulaerror; @@ -352,7 +352,7 @@ class qformat_webct extends qformat_default { $question->import_process=TRUE ; unset($question->answer); //not used in calculated question break; - case MATCH: + case 'match': // MDL-10680: // switch subquestions and subanswers foreach ($question->subquestions as $id=>$subquestion) { @@ -391,7 +391,7 @@ class qformat_webct extends qformat_default { // Multiple Choice Question with only one good answer $question = $this->defaultquestion(); $question->feedback = array(); - $question->qtype = MULTICHOICE; + $question->qtype = 'multichoice'; $question->single = 1; // Only one answer is allowed $ignore_rest_of_question = FALSE; continue; @@ -401,7 +401,7 @@ class qformat_webct extends qformat_default { // Multiple Choice Question with several good answers $question = $this->defaultquestion(); $question->feedback = array(); - $question->qtype = MULTICHOICE; + $question->qtype = 'multichoice'; $question->single = 0; // Many answers allowed $ignore_rest_of_question = FALSE; continue; @@ -411,7 +411,7 @@ class qformat_webct extends qformat_default { // Short Answer Question $question = $this->defaultquestion(); $question->feedback = array(); - $question->qtype = SHORTANSWER; + $question->qtype = 'shortanswer'; $question->usecase = 0; // Ignore case $ignore_rest_of_question = FALSE; continue; @@ -420,7 +420,7 @@ class qformat_webct extends qformat_default { if (preg_match("~^:TYPE:C~i",$line)) { // Calculated Question $question = $this->defaultquestion(); - $question->qtype = CALCULATED; + $question->qtype = 'calculated'; $question->answers = array(); // No problem as they go as :FORMULA: from webct $question->units = array(); $question->dataset = array(); @@ -438,7 +438,7 @@ class qformat_webct extends qformat_default { if (preg_match("~^:TYPE:M~i",$line)) { // Match Question $question = $this->defaultquestion(); - $question->qtype = MATCH; + $question->qtype = 'match'; $question->feedback = array(); $ignore_rest_of_question = FALSE; // match question processing is not debugged continue; @@ -487,7 +487,7 @@ class qformat_webct extends qformat_default { if (!isset($question)) { continue; } - if (isset($question->qtype ) && CALCULATED == $question->qtype && preg_match( + if (isset($question->qtype ) && 'calculated' == $question->qtype && preg_match( "~^:([[:lower:]].*|::.*)-(MIN|MAX|DEC|VAL([0-9]+))::?:?($webctnumberregex)~", $line, $webct_options)) { $datasetname = preg_replace('/^::/', '', $webct_options[1]); $datasetvalue = qformat_webct_convert_formula($webct_options[4]); @@ -519,7 +519,7 @@ class qformat_webct extends qformat_default { continue; } - if (preg_match("~^:ANSWER([0-9]+):([^:]+):([0-9\.\-]+):(.*)~i",$line,$webct_options)) { /// SHORTANSWER + if (preg_match("~^:ANSWER([0-9]+):([^:]+):([0-9\.\-]+):(.*)~i",$line,$webct_options)) { // Shortanswer. $currentchoice=$webct_options[1]; $answertext=$webct_options[2]; // Start gathering next lines $question->fraction[$currentchoice]=($webct_options[3]/100); @@ -534,7 +534,7 @@ class qformat_webct extends qformat_default { } if (preg_match('~^:FORMULA:(.*)~i', $line, $webct_options)) { - // Answer for a CALCULATED question + // Answer for a calculated question ++$currentchoice; $question->answers[$currentchoice] = qformat_webct_convert_formula($webct_options[1]); @@ -591,20 +591,20 @@ class qformat_webct extends qformat_default { continue; } - if (isset($question->qtype ) && CALCULATED == $question->qtype && preg_match('~^:ANS-DEC:([1-9][0-9]*)~i', $line, $webct_options)) { + if (isset($question->qtype ) && 'calculated' == $question->qtype && preg_match('~^:ANS-DEC:([1-9][0-9]*)~i', $line, $webct_options)) { // We can but hope that this always appear before the ANSTYPE property $question->correctanswerlength[$currentchoice] = $webct_options[1]; continue; } - if (isset($question->qtype )&& CALCULATED == $question->qtype && preg_match("~^:TOL:($webctnumberregex)~i", $line, $webct_options)) { + if (isset($question->qtype )&& 'calculated' == $question->qtype && preg_match("~^:TOL:($webctnumberregex)~i", $line, $webct_options)) { // We can but hope that this always appear before the TOL property $question->tolerance[$currentchoice] = qformat_webct_convert_formula($webct_options[1]); continue; } - if (isset($question->qtype )&& CALCULATED == $question->qtype && preg_match('~^:TOLTYPE:percent~i', $line)) { + if (isset($question->qtype )&& 'calculated' == $question->qtype && preg_match('~^:TOLTYPE:percent~i', $line)) { // Percentage case is handled as relative in Moodle: $question->tolerance[$currentchoice] /= 100; $question->tolerancetype[$currentchoice] = 1; // Relative @@ -639,11 +639,11 @@ class qformat_webct extends qformat_default { continue; } - if (isset($question->qtype )&& CALCULATED == $question->qtype && preg_match('~^:ANSTYPE:dec~i', $line)) { + if (isset($question->qtype )&& 'calculated' == $question->qtype && preg_match('~^:ANSTYPE:dec~i', $line)) { $question->correctanswerformat[$currentchoice]='1'; continue; } - if (isset($question->qtype )&& CALCULATED == $question->qtype && preg_match('~^:ANSTYPE:sig~i', $line)) { + if (isset($question->qtype )&& 'calculated' == $question->qtype && preg_match('~^:ANSTYPE:sig~i', $line)) { $question->correctanswerformat[$currentchoice]='2'; continue; } diff --git a/question/format/xhtml/format.php b/question/format/xhtml/format.php index 0b70c45010d..6110d276182 100644 --- a/question/format/xhtml/format.php +++ b/question/format/xhtml/format.php @@ -72,7 +72,7 @@ class qformat_xhtml extends qformat_default { // selection depends on question type switch($question->qtype) { - case TRUEFALSE: + case 'truefalse': $st_true = get_string('true', 'qtype_truefalse'); $st_false = get_string('false', 'qtype_truefalse'); $expout .= "
      \n"; @@ -80,7 +80,7 @@ class qformat_xhtml extends qformat_default { $expout .= "
    • $st_false
    • \n"; $expout .= "
    \n"; break; - case MULTICHOICE: + case 'multichoice': $expout .= "
      \n"; foreach($question->options->answers as $answer) { $ans_text = $this->repchar( $answer->answer ); @@ -93,17 +93,17 @@ class qformat_xhtml extends qformat_default { } $expout .= "
    \n"; break; - case SHORTANSWER: + case 'shortanswer': $expout .= "
      \n"; $expout .= "
    • \n"; $expout .= "
    \n"; break; - case NUMERICAL: + case 'numerical': $expout .= "
      \n"; $expout .= "
    • \n"; $expout .= "
    \n"; break; - case MATCH: + case 'match': $expout .= "
      \n"; // build answer list @@ -128,9 +128,9 @@ class qformat_xhtml extends qformat_default { } $expout .= "
    \n"; break; - case DESCRIPTION: + case 'description': break; - case MULTIANSWER: + case 'multichoice': $expout .= "\n"; break; default: diff --git a/question/format/xml/format.php b/question/format/xml/format.php index 740279f057f..7cdb23c5a8e 100644 --- a/question/format/xml/format.php +++ b/question/format/xml/format.php @@ -387,7 +387,7 @@ class qformat_xml extends qformat_default { $qo = $this->import_headers($question); // 'header' parts particular to multichoice - $qo->qtype = MULTICHOICE; + $qo->qtype = 'multichoice'; $single = $this->getpath($question, array('#', 'single', 0, '#'), 'true'); $qo->single = $this->trans_single($single); $shuffleanswers = $this->getpath($question, @@ -473,7 +473,7 @@ class qformat_xml extends qformat_default { $qo = $this->import_headers($question); // 'header' parts particular to true/false - $qo->qtype = TRUEFALSE; + $qo->qtype = 'truefalse'; // In the past, it used to be assumed that the two answers were in the file // true first, then false. Howevever that was not always true. Now, we @@ -548,7 +548,7 @@ class qformat_xml extends qformat_default { $qo = $this->import_headers($question); // header parts particular to shortanswer - $qo->qtype = SHORTANSWER; + $qo->qtype = 'shortanswer'; // get usecase $qo->usecase = $this->getpath($question, array('#', 'usecase', 0, '#'), $qo->usecase); @@ -578,7 +578,7 @@ class qformat_xml extends qformat_default { // get common parts $qo = $this->import_headers($question); // header parts particular to shortanswer - $qo->qtype = DESCRIPTION; + $qo->qtype = 'description'; $qo->defaultmark = 0; $qo->length = 0; return $qo; @@ -594,7 +594,7 @@ class qformat_xml extends qformat_default { $qo = $this->import_headers($question); // header parts particular to numerical - $qo->qtype = NUMERICAL; + $qo->qtype = 'numerical'; // get answers array $answers = $question['#']['answer']; @@ -706,7 +706,7 @@ class qformat_xml extends qformat_default { $qo = $this->import_headers($question); // header parts particular to essay - $qo->qtype = ESSAY; + $qo->qtype = 'essay'; $qo->responseformat = $this->getpath($question, array('#', 'responseformat', 0, '#'), 'editor'); @@ -734,7 +734,7 @@ class qformat_xml extends qformat_default { $qo = $this->import_headers($question); // header parts particular to calculated - $qo->qtype = CALCULATED; + $qo->qtype = 'calculated'; $qo->synchronize = $this->getpath($question, array('#', 'synchronize', 0, '#'), 0); $single = $this->getpath($question, array('#', 'single', 0, '#'), 'true'); $qo->single = $this->trans_single($single); @@ -1363,7 +1363,7 @@ class qformat_xml extends qformat_default { $expout .= "\n"; $expout .= " ".$this->writetext($def->status)."\n"; $expout .= " ".$this->writetext($def->name)."\n"; - if ($question->qtype == CALCULATED) { + if ($question->qtype == 'calculated') { $expout .= " calculated\n"; } else { $expout .= " calculatedsimple\n"; diff --git a/question/type/multichoice/styles.css b/question/type/multichoice/styles.css index 3cadf621f90..4aa77fd2a9f 100644 --- a/question/type/multichoice/styles.css +++ b/question/type/multichoice/styles.css @@ -10,3 +10,6 @@ .que.multichoice .answer div.r1 { padding: 0.3em; } +.que.multichoice .feedback .rightanswer * { + display: inline; +} diff --git a/report/backups/index.php b/report/backups/index.php index 9dd18315137..1deaa2dca3d 100644 --- a/report/backups/index.php +++ b/report/backups/index.php @@ -27,6 +27,9 @@ require_once('../../config.php'); 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; @@ -45,6 +48,7 @@ $strerror = get_string("error"); $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 @@ -58,15 +62,18 @@ foreach ($rs as $backuprow) { 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 diff --git a/report/stats/lib.php b/report/stats/lib.php index c185fb3a2c5..1c191a453c1 100644 --- a/report/stats/lib.php +++ b/report/stats/lib.php @@ -36,7 +36,7 @@ defined('MOODLE_INTERNAL') || die; */ 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)) { @@ -54,7 +54,7 @@ function report_stats_extend_navigation_course($navigation, $course, $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)) { diff --git a/report/stats/settings.php b/report/stats/settings.php index c92ec339bba..064453835bb 100644 --- a/report/stats/settings.php +++ b/report/stats/settings.php @@ -26,7 +26,7 @@ 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; diff --git a/repository/flickr/lib.php b/repository/flickr/lib.php index 478e53b0b0d..84bac4bb284 100644 --- a/repository/flickr/lib.php +++ b/repository/flickr/lib.php @@ -37,6 +37,11 @@ class repository_flickr extends repository { private $flickr; public $photos; + /** + * Stores sizes of images to prevent multiple API call + */ + static private $sizes = array(); + /** * * @param int $repositoryid @@ -228,16 +233,35 @@ class repository_flickr extends repository { * @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 $url; + 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 $bestsize; } public function get_link($photoid) { diff --git a/repository/flickr_public/lib.php b/repository/flickr_public/lib.php index b31adaf82d0..17cdb57a94b 100644 --- a/repository/flickr_public/lib.php +++ b/repository/flickr_public/lib.php @@ -41,6 +41,11 @@ class repository_flickr_public extends repository { private $flickr; public $photos; + /** + * Stores sizes of images to prevent multiple API call + */ + static private $sizes = array(); + /** * constructor method * @@ -213,13 +218,14 @@ class repository_flickr_public extends repository { 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]; } @@ -402,16 +408,35 @@ class repository_flickr_public extends repository { * @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 $url; + 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 $bestsize; } public function get_link($photoid) { @@ -434,29 +459,23 @@ class repository_flickr_public extends repository { $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'])); } /** diff --git a/repository/googledocs/lib.php b/repository/googledocs/lib.php index 38299decb73..72ca7bbab68 100644 --- a/repository/googledocs/lib.php +++ b/repository/googledocs/lib.php @@ -92,8 +92,10 @@ class repository_googledocs extends repository { } public function get_file($url, $file = '') { + if (empty($url)) { + throw new repository_exception('cannotdownload', 'repository'); + } $gdocs = new google_docs($this->googleoauth); - $path = $this->prepare_file($file); return $gdocs->download_file($url, $path, self::GETFILE_TIMEOUT); } diff --git a/repository/lib.php b/repository/lib.php index 9bb538800af..5b34c56ac46 100644 --- a/repository/lib.php +++ b/repository/lib.php @@ -1515,6 +1515,12 @@ abstract class repository { $types = repository::get_editable_types($context); foreach ($types as $type) { if (!empty($type) && $type->get_visible()) { + // If the user does not have the permission to view the repository, it won't be displayed in + // the list of instances. Hiding the link to create new instances will prevent the + // user from creating them without being able to find them afterwards, which looks like a bug. + if (!has_capability('repository/'.$type->get_typename().':view', $context)) { + continue; + } $instanceoptionnames = repository::static_function($type->get_typename(), 'get_instance_option_names'); if (!empty($instanceoptionnames)) { $baseurl->param('new', $type->get_typename()); diff --git a/repository/manage_instances.php b/repository/manage_instances.php index 43efe2efb53..7c719d9c893 100644 --- a/repository/manage_instances.php +++ b/repository/manage_instances.php @@ -106,12 +106,16 @@ if (!empty($new)){ $type = repository::get_type_by_id($instance->options['typeid']); } -if (isset($type) && !$type->get_visible()) { - print_error('typenotvisible', 'repository', $baseurl); -} - -if (isset($type) && !$type->get_contextvisibility($context)) { - print_error('usercontextrepositorydisabled', 'repository', $baseurl); +if (isset($type)) { + if (!$type->get_visible()) { + print_error('typenotvisible', 'repository', $baseurl); + } + // Prevents the user from creating/editing an instance if the repository is not visible in + // this context OR if the user does not have the capability to view this repository in this context. + $canviewrepository = has_capability('repository/'.$type->get_typename().':view', $context); + if (!$type->get_contextvisibility($context) || !$canviewrepository) { + print_error('usercontextrepositorydisabled', 'repository', $baseurl); + } } /// Create navigation links diff --git a/theme/afterburner/config.php b/theme/afterburner/config.php index da3742da0fb..e720e8d578b 100644 --- a/theme/afterburner/config.php +++ b/theme/afterburner/config.php @@ -123,16 +123,23 @@ $THEME->layouts = array( 'file' => 'embedded.php', 'regions' => array() ), - // The pagelayout used for reports + // The pagelayout used for reports. 'report' => array( 'file' => 'default.php', 'regions' => array('side-pre'), 'defaultregion' => 'side-pre', ), + // The pagelayout used for safebrowser and securewindow. + 'secure' => array( + 'file' => 'default.php', + 'regions' => array('side-pre', 'side-post'), + 'defaultregion' => 'side-pre', + 'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nologinlinks'=>true), + ), ); $THEME->enable_dock = true; $THEME->rendererfactory = 'theme_overridden_renderer_factory'; -$THEME->csspostprocess = 'afterburner_process_css'; \ No newline at end of file +$THEME->csspostprocess = 'afterburner_process_css'; diff --git a/theme/afterburner/style/afterburner_styles.css b/theme/afterburner/style/afterburner_styles.css index 326e7d542f7..990fe49ecf3 100644 --- a/theme/afterburner/style/afterburner_styles.css +++ b/theme/afterburner/style/afterburner_styles.css @@ -209,6 +209,10 @@ select, input, button { background-color: #34637f; color: #fff; } +.ie7 select { /* fixes compatibility view */ + background-color: #eee; + color: #036; +} #loginbtn, input, button, select { cursor: pointer; margin-left: 5px; diff --git a/theme/anomaly/style/general.css b/theme/anomaly/style/general.css index d420e1e5220..d6951869be8 100644 --- a/theme/anomaly/style/general.css +++ b/theme/anomaly/style/general.css @@ -491,8 +491,7 @@ h1.headermain { padding-top: 10px; } -.forumpost .content .shortenedpost a, -.forumpost .content p { +.forumpost .content .shortenedpost a { margin: 0 10px; padding: 0; } diff --git a/theme/base/config.php b/theme/base/config.php index 7c9aa2d3799..cd85c660b47 100644 --- a/theme/base/config.php +++ b/theme/base/config.php @@ -153,12 +153,19 @@ $THEME->layouts = array( 'regions' => array(), 'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true), ), - // The pagelayout used for reports + // The pagelayout used for reports. 'report' => array( 'file' => 'report.php', 'regions' => array('side-pre'), 'defaultregion' => 'side-pre', ), + // The pagelayout used for safebrowser and securewindow. + 'secure' => array( + 'file' => 'general.php', + 'regions' => array('side-pre', 'side-post'), + 'defaultregion' => 'side-pre', + 'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nologinlinks'=>true), + ), ); // We don't want the base theme to be shown on the theme selection screen, by setting diff --git a/theme/base/style/admin.css b/theme/base/style/admin.css index 8c0ce47fc5c..df0e64ff2f5 100644 --- a/theme/base/style/admin.css +++ b/theme/base/style/admin.css @@ -42,6 +42,7 @@ #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;} diff --git a/theme/canvas/config.php b/theme/canvas/config.php index 70d1790a21d..4f4b7e85203 100644 --- a/theme/canvas/config.php +++ b/theme/canvas/config.php @@ -166,7 +166,14 @@ $THEME->layouts = array( 'file' => 'report.php', 'regions' => array('side-pre'), 'defaultregion' => 'side-pre', - ) + ), + // The pagelayout used for safebrowser and securewindow. + 'secure' => array( + 'file' => 'general.php', + 'regions' => array('side-pre', 'side-post'), + 'defaultregion' => 'side-pre', + 'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nologinlinks'=>true), + ), ); ///////////////////////////////////////////////////////// diff --git a/theme/mymobile/renderers.php b/theme/mymobile/renderers.php index b6b49cf273a..98bc7e62718 100644 --- a/theme/mymobile/renderers.php +++ b/theme/mymobile/renderers.php @@ -303,7 +303,7 @@ class theme_mymobile_core_renderer extends core_renderer { * * @return string */ - public function login_info() { + public function login_info($withlinks = null) { global $USER, $CFG, $DB, $SESSION; if (during_initial_install()) { diff --git a/theme/overlay/layout/general.php b/theme/overlay/layout/general.php index 9e3297770f3..8fc6d8b8e5d 100644 --- a/theme/overlay/layout/general.php +++ b/theme/overlay/layout/general.php @@ -125,7 +125,7 @@ echo $OUTPUT->doctype() ?> - + - + standard_end_of_body_html() ?> - \ No newline at end of file + diff --git a/theme/upgrade.txt b/theme/upgrade.txt index 4d18a33411a..4158d23f2e3 100644 --- a/theme/upgrade.txt +++ b/theme/upgrade.txt @@ -1,6 +1,11 @@ This files describes API changes in /theme/* themes, information provided here is intended especially for theme designer. +=== 2.4 === + +optional changes: +* new optional boolean parameter $withlinks for public function login_info() in lib/outputrenderers.php (MDL-31365) +* new layout option "nologinlinks" and new page layout "secure" e.g. for safebrowser and securewindow (MDL-31365) === 2.3 === @@ -12,4 +17,4 @@ optional changes: required changes: * use new page content placeholder "echo $OUTPUT->main_content()" instead of "echo core_renderer::MAIN_CONTENT_TOKEN" see git commit: 3b3f302855d7621405a8b93e49bd399d67a998d7 -* upgrade report selectors: search for "-course-report-" and replace with "-report-" \ No newline at end of file +* upgrade report selectors: search for "-course-report-" and replace with "-report-" diff --git a/theme/yui_combo.php b/theme/yui_combo.php index 2fc010815d8..bd19b5039cc 100644 --- a/theme/yui_combo.php +++ b/theme/yui_combo.php @@ -115,7 +115,8 @@ foreach ($parts as $part) { $contentfile = "$CFG->libdir/yuilib/$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); diff --git a/user/profile.php b/user/profile.php index e06e502d335..92f55cf4515 100644 --- a/user/profile.php +++ b/user/profile.php @@ -118,6 +118,12 @@ if (has_capability('moodle/user:viewhiddendetails', $context)) { $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'); @@ -247,23 +253,34 @@ if (! isset($hiddenfields['city']) && $user->city) { 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 ($currentuser +if (isset($identityfields['phone1']) && $user->phone1) { + print_row(get_string("phone").":", "$user->phone1"); +} + +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, '')); } diff --git a/user/selector/module.js b/user/selector/module.js index 80e37521c75..26a017ddf3f 100644 --- a/user/selector/module.js +++ b/user/selector/module.js @@ -77,8 +77,9 @@ M.core_user.init_user_selector = function (Y, name, hash, extrafields, lastsearc var clearbtn = Y.one('#'+this.name + '_clearbutton'); this.clearbutton = Y.Node.create(''); 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); }, diff --git a/version.php b/version.php index 45ef1b66fb1..0043e10a19a 100644 --- a/version.php +++ b/version.php @@ -30,11 +30,11 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2012083100.00; // YYYYMMDD = weekly release date of this DEV branch +$version = 2012090700.00; // YYYYMMDD = weekly release date of this DEV branch // RR = release increments - 00 in DEV branches // .XX = incremental changes -$release = '2.4dev (Build: 20120831)'; // Human-friendly version name +$release = '2.4dev (Build: 20120907)'; // Human-friendly version name $branch = '24'; // this version's branch $maturity = MATURITY_ALPHA; // this version's maturity level