Merge branch 'MDL-43004-master' of git://github.com/FMCorz/moodle
authorDan Poltawski <dan@moodle.com>
Mon, 22 Dec 2014 15:09:53 +0000 (15:09 +0000)
committerDan Poltawski <dan@moodle.com>
Mon, 22 Dec 2014 15:09:53 +0000 (15:09 +0000)
153 files changed:
admin/roles/usersroles.php
admin/tool/filetypes/classes/utils.php [new file with mode: 0644]
admin/tool/filetypes/delete.php [new file with mode: 0644]
admin/tool/filetypes/edit.php [new file with mode: 0644]
admin/tool/filetypes/edit_form.php [new file with mode: 0644]
admin/tool/filetypes/index.php [new file with mode: 0644]
admin/tool/filetypes/lang/en/tool_filetypes.php [new file with mode: 0644]
admin/tool/filetypes/renderer.php [new file with mode: 0644]
admin/tool/filetypes/revert.php [new file with mode: 0644]
admin/tool/filetypes/settings.php [new file with mode: 0644]
admin/tool/filetypes/styles.css [new file with mode: 0644]
admin/tool/filetypes/tests/behat/add_filetypes.feature [new file with mode: 0644]
admin/tool/filetypes/tests/fixtures/frog-24.png [new file with mode: 0644]
admin/tool/filetypes/tests/fixtures/frog-48.png [new file with mode: 0644]
admin/tool/filetypes/tests/fixtures/test.frog [new file with mode: 0644]
admin/tool/filetypes/tests/fixtures/zombie.gif [new file with mode: 0644]
admin/tool/filetypes/tests/tool_filetypes_test.php [new file with mode: 0644]
admin/tool/filetypes/version.php [new file with mode: 0644]
admin/tool/messageinbound/classes/manager.php
admin/tool/profiling/styles.css
admin/tool/task/cli/schedule_task.php
backup/moodle2/restore_stepslib.php
backup/util/helper/backup_cron_helper.class.php
badges/renderer.php
blocks/calendar_month/block_calendar_month.php
blocks/course_summary/block_course_summary.php
blocks/moodleblock.class.php
blocks/rss_client/block_rss_client.php
blocks/tag_flickr/block_tag_flickr.php
blocks/tag_youtube/block_tag_youtube.php
blocks/upgrade.txt
calendar/lib.php
cohort/tests/behat/upload_cohorts.feature
config-dist.php
course/editsection.php
course/lib.php
course/modedit.php
course/modlib.php
course/reset_form.php
course/tests/behat/activities_edit_completion.feature [new file with mode: 0644]
course/tests/courselib_test.php
course/view.php
course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-debug.js
course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-min.js
course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes.js
course/yui/src/toolboxes/js/resource.js
files/renderer.php
grade/edit/tree/lib.php
grade/grading/form/guide/edit.php
grade/report/grader/index.php
grade/report/singleview/classes/local/ui/finalgrade.php
grade/report/singleview/tests/behat/singleview.feature
grade/tests/behat/grade_UI_settings.feature
install/lang/mh/langconfig.php [moved from lib/pluginlib.php with 54% similarity]
lang/en/admin.php
lang/en/grades.php
lang/en/repository.php
lib/adminlib.php
lib/classes/filetypes.php [new file with mode: 0644]
lib/classes/plugininfo/theme.php
lib/classes/session/manager.php
lib/classes/task/legacy_plugin_cron_task.php
lib/classes/useragent.php
lib/conditionlib.php
lib/cronlib.php
lib/deprecatedlib.php
lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
lib/editor/atto/plugins/image/yui/src/button/js/button.js
lib/filelib.php
lib/formslib.php
lib/modinfolib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputcomponents.php
lib/outputrenderers.php
lib/phpunit/classes/util.php
lib/sessionkeepalive_ajax.php [new file with mode: 0644]
lib/tablelib.php
lib/tests/accesslib_test.php
lib/tests/behat/behat_deprecated.php
lib/tests/behat/behat_general.php
lib/tests/conditionlib_test.php [deleted file]
lib/tests/filelib_test.php
lib/tests/filetypes_test.php [new file with mode: 0644]
lib/tests/html_writer_test.php
lib/tests/modinfolib_test.php
lib/tests/tablelib_test.php
lib/tests/useragent_test.php
lib/thirdpartylibs.xml
lib/upgrade.txt
lib/uploadlib.php
lib/yui/build/moodle-core-checknet/moodle-core-checknet-debug.js
lib/yui/build/moodle-core-checknet/moodle-core-checknet-min.js
lib/yui/build/moodle-core-checknet/moodle-core-checknet.js
lib/yui/src/checknet/js/checknet.js
message/lib.php
mod/assign/batchsetmarkingworkflowstateform.php
mod/assign/feedback/editpdf/ajax.php
mod/assign/feedback/editpdf/classes/document_services.php
mod/assign/feedback/editpdf/classes/page_editor.php
mod/assign/feedback/editpdf/classes/renderer.php
mod/assign/feedback/editpdf/styles.css
mod/assign/feedback/editpdf/tests/behat/group_annotations.feature [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js
mod/assign/feedback/editpdf/yui/src/editor/js/annotationstamp.js
mod/assign/feedback/editpdf/yui/src/editor/js/comment.js
mod/assign/feedback/editpdf/yui/src/editor/js/drawable.js
mod/assign/feedback/editpdf/yui/src/editor/js/editor.js
mod/assign/gradeform.php
mod/assign/lang/en/assign.php
mod/assign/lib.php
mod/assign/locallib.php
mod/assign/tests/behat/quickgrading.feature
mod/assign/tests/locallib_test.php
mod/forum/lib.php
mod/lesson/backup/moodle2/backup_lesson_stepslib.php
mod/lesson/backup/moodle2/restore_lesson_stepslib.php
mod/lesson/db/install.xml
mod/lesson/db/upgrade.php
mod/lesson/lang/en/lesson.php
mod/lesson/lib.php
mod/lesson/locallib.php
mod/lesson/mod_form.php
mod/lesson/pagetypes/shortanswer.php
mod/lesson/renderer.php
mod/lesson/tests/behat/completion_condition_end_reached.feature [new file with mode: 0644]
mod/lesson/tests/behat/import_fillintheblank_question.feature [new file with mode: 0644]
mod/lesson/tests/fixtures/sample_blackboard_fib_qti.dat [new file with mode: 0644]
mod/lesson/upgrade.txt [new file with mode: 0644]
mod/lesson/version.php
mod/lesson/view.php
mod/quiz/renderer.php
mod/workshop/allocation/manual/styles.css
question/type/match/backup/moodle2/restore_qtype_match_plugin.class.php
report/completion/index.php
report/log/classes/renderable.php
report/log/classes/renderer.php
report/outline/tests/behat/outline.feature
report/progress/index.php
theme/base/style/core.css
theme/base/style/filemanager.css
theme/bootstrapbase/less/moodle/blocks.less
theme/bootstrapbase/less/moodle/filemanager.less
theme/bootstrapbase/less/moodle/forms.less
theme/bootstrapbase/style/moodle.css
theme/upgrade.txt
user/tests/behat/view_full_profile.feature [new file with mode: 0644]
user/view.php
version.php

index 99ded8f..4a301b1 100644 (file)
@@ -132,7 +132,7 @@ if ($courseid == SITEID) {
     $PAGE->set_heading($course->fullname.': '.$fullname);
 }
 echo $OUTPUT->header();
-echo $OUTPUT->heading($title, 3);
+echo $OUTPUT->heading($title);
 echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthnormal');
 
 // Display them.
diff --git a/admin/tool/filetypes/classes/utils.php b/admin/tool/filetypes/classes/utils.php
new file mode 100644 (file)
index 0000000..45a1c6b
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Class with static back-end methods used by the file type tool.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_filetypes;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Class with static back-end methods used by the file type tool.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class utils {
+    /**
+     * Checks if the given file type extension is invalid.
+     * The added file type extension must be unique and must not begin with a dot.
+     *
+     * @param string $extension Extension of the file type to add
+     * @param string $oldextension Extension prior to update (empty string if adding new type)
+     * @return bool True if it the file type trying to add already exists
+     */
+    public static function is_extension_invalid($extension, $oldextension = '') {
+        $extension = trim($extension);
+        if ($extension === '' || $extension[0] === '.') {
+            return true;
+        }
+
+        $mimeinfo = get_mimetypes_array();
+        if ($oldextension !== '') {
+            unset($mimeinfo[$oldextension]);
+        }
+
+        return array_key_exists($extension, $mimeinfo);
+    }
+
+    /**
+     * Checks if we are allowed to turn on the 'default icon' option. You can
+     * only have one of these for a given MIME type.
+     *
+     * @param string $mimetype MIME type
+     * @param string $oldextension File extension name (before any change)
+     */
+    public static function is_defaulticon_allowed($mimetype, $oldextension = '') {
+        $mimeinfo = get_mimetypes_array();
+        if ($oldextension !== '') {
+            unset($mimeinfo[$oldextension]);
+        }
+        foreach ($mimeinfo as $extension => $values) {
+            if ($values['type'] !== $mimetype) {
+                continue;
+            }
+            if (!empty($values['defaulticon'])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Gets all unique file type icons from a specific path, not including
+     * sub-directories.
+     *
+     * Icon files such as pdf.png, pdf-24.png and pdf-36.png etc. are counted as
+     * the same icon type.
+     *
+     * The resultant array has both key and value set to the icon name prefix,
+     * such as 'pdf' => 'pdf'.
+     *
+     * @param string $path The path of the icon path
+     * @return array An array of unique file icons within the given path
+     */
+    public static function get_icons_from_path($path) {
+        $icons = array();
+        if ($handle = @opendir($path)) {
+            while (($file = readdir($handle)) !== false) {
+                $matches = array();
+                if (preg_match('~(.+?)(?:-24|-32|-48|-64|-72|-80|-96|-128|-256)?\.(?:gif|png)$~',
+                        $file, $matches)) {
+                    $key = $matches[1];
+                    $icons[$key] = $key;
+                }
+            }
+            closedir($handle);
+        }
+        ksort($icons);
+        return $icons;
+    }
+
+    /**
+     * Gets unique file type icons from pix/f folder.
+     *
+     * @return array An array of unique file type icons e.g. 'pdf' => 'pdf'
+     */
+    public static function get_file_icons() {
+        global $CFG;
+        $path = $CFG->dirroot . '/pix/f';
+        return self::get_icons_from_path($path);
+    }
+}
diff --git a/admin/tool/filetypes/delete.php b/admin/tool/filetypes/delete.php
new file mode 100644 (file)
index 0000000..8b73f2b
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Delete a file type with a confirmation box.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require(__DIR__ . '/../../../config.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+admin_externalpage_setup('tool_filetypes');
+
+$extension = required_param('extension', PARAM_ALPHANUMEXT);
+$redirecturl = new \moodle_url('/admin/tool/filetypes/index.php');
+
+if (optional_param('delete', 0, PARAM_INT)) {
+    require_sesskey();
+
+    // Delete the file type from the config.
+    core_filetypes::delete_type($extension);
+    redirect($redirecturl);
+}
+
+// Page settings.
+$title = get_string('deletefiletypes', 'tool_filetypes');
+
+$context = context_system::instance();
+$PAGE->set_url(new \moodle_url('/admin/tool/filetypes/delete.php', array('extension' => $extension)));
+$PAGE->navbar->add($title);
+$PAGE->set_context($context);
+$PAGE->set_pagelayout('admin');
+$PAGE->set_title($SITE->fullname. ': ' . $title);
+
+// Display the page.
+echo $OUTPUT->header();
+
+$message = get_string('delete_confirmation', 'tool_filetypes', $extension);
+$deleteurl = new \moodle_url('delete.php', array('extension' => $extension, 'delete' => 1));
+$yesbutton = new single_button($deleteurl, get_string('yes'));
+$nobutton = new single_button($redirecturl, get_string('no'), 'get');
+echo $OUTPUT->confirm($message, $yesbutton, $nobutton);
+
+echo $OUTPUT->footer();
diff --git a/admin/tool/filetypes/edit.php b/admin/tool/filetypes/edit.php
new file mode 100644 (file)
index 0000000..33d934b
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Display the file type updating page.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require(__DIR__ . '/../../../config.php');
+require_once($CFG->libdir . '/adminlib.php');
+require_once('edit_form.php');
+
+admin_externalpage_setup('tool_filetypes');
+
+$oldextension = optional_param('oldextension', '', PARAM_ALPHANUMEXT);
+$mform = new tool_filetypes_form('edit.php', array('oldextension' => $oldextension));
+$title = get_string('addfiletypes', 'tool_filetypes');
+
+if ($oldextension) {
+    // This is editing an existing filetype, load data to the form.
+    $mimetypes = get_mimetypes_array();
+    if (!array_key_exists($oldextension, $mimetypes)) {
+        throw new moodle_exception('error_notfound', 'tool_filetypes');
+    }
+    $typeinfo = $mimetypes[$oldextension];
+    $formdata = array(
+        'extension' => $oldextension,
+        'mimetype' => $typeinfo['type'],
+        'icon' => $typeinfo['icon'],
+        'oldextension' => $oldextension,
+        'description' => '',
+        'groups' => '',
+        'corestring' => '',
+        'defaulticon' => 0
+    );
+    if (!empty($typeinfo['customdescription'])) {
+        $formdata['description'] = $typeinfo['customdescription'];
+    }
+    if (!empty($typeinfo['groups'])) {
+        $formdata['groups'] = implode(', ', $typeinfo['groups']);
+    }
+    if (!empty($typeinfo['string'])) {
+        $formdata['corestring'] = $typeinfo['string'];
+    }
+    if (!empty($typeinfo['defaulticon'])) {
+        $formdata['defaulticon'] = 1;
+    }
+
+    $mform->set_data($formdata);
+    $title = get_string('editfiletypes', 'tool_filetypes');
+}
+
+$backurl = new \moodle_url('/admin/tool/filetypes/index.php');
+if ($mform->is_cancelled()) {
+    redirect($backurl);
+} else if ($data = $mform->get_data()) {
+    // Convert the groups value back into an array.
+    $data->groups = trim($data->groups);
+    if ($data->groups) {
+        $data->groups = preg_split('~,\s*~', $data->groups);
+    } else {
+        $data->groups = array();
+    }
+    if (empty($data->defaulticon)) {
+        $data->defaulticon = 0;
+    }
+    if (empty($data->corestring)) {
+        $data->corestring = '';
+    }
+    if (empty($data->description)) {
+        $data->description = '';
+    }
+    if ($data->oldextension) {
+        // Update an existing file type.
+        core_filetypes::update_type($data->oldextension, $data->extension, $data->mimetype, $data->icon,
+            $data->groups, $data->corestring, $data->description, (bool)$data->defaulticon);
+    } else {
+        // Add a new file type entry.
+        core_filetypes::add_type($data->extension, $data->mimetype, $data->icon,
+            $data->groups, $data->corestring, $data->description, (bool)$data->defaulticon);
+    }
+    redirect($backurl);
+}
+
+// Page settings.
+$context = context_system::instance();
+$PAGE->set_url(new \moodle_url('/admin/tool/filetypes/edit.php', array('oldextension' => $oldextension)));
+$PAGE->navbar->add($oldextension ? s($oldextension) : $title);
+$PAGE->set_context($context);
+$PAGE->set_pagelayout('admin');
+$PAGE->set_title($SITE->fullname. ': ' . $title);
+
+// Display the page.
+echo $OUTPUT->header();
+$mform->display();
+echo $OUTPUT->footer();
diff --git a/admin/tool/filetypes/edit_form.php b/admin/tool/filetypes/edit_form.php
new file mode 100644 (file)
index 0000000..5d16177
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Customised file types editing form.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once($CFG->dirroot . '/lib/formslib.php');
+
+/**
+ * Form for adding a new custom file type or updating an existing custom file type.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_filetypes_form extends moodleform {
+
+    public function definition() {
+        global $CFG;
+        $mform = $this->_form;
+        $oldextension = $this->_customdata['oldextension'];
+
+        $mform->addElement('text', 'extension', get_string('extension', 'tool_filetypes'));
+        $mform->setType('extension', PARAM_ALPHANUMEXT);
+        $mform->addRule('extension', null, 'required', null, 'client');
+        $mform->addHelpButton('extension', 'extension', 'tool_filetypes');
+
+        $mform->addElement('text', 'mimetype',  get_string('mimetype', 'tool_filetypes'));
+        $mform->setType('mimetype', PARAM_RAW);
+        $mform->addRule('mimetype', null, 'required', null, 'client');
+        $mform->addHelpButton('mimetype', 'mimetype', 'tool_filetypes');
+
+        $fileicons = \tool_filetypes\utils::get_file_icons();
+        $mform->addElement('select', 'icon',
+                get_string('icon', 'tool_filetypes'), $fileicons);
+        $mform->addHelpButton('icon', 'icon', 'tool_filetypes');
+
+        $mform->addElement('text', 'groups',  get_string('groups', 'tool_filetypes'));
+        $mform->setType('groups', PARAM_RAW);
+        $mform->addHelpButton('groups', 'groups', 'tool_filetypes');
+
+        $mform->addElement('select', 'descriptiontype', get_string('descriptiontype', 'tool_filetypes'),
+                array('' => get_string('descriptiontype_default', 'tool_filetypes'),
+                'custom' => get_string('descriptiontype_custom', 'tool_filetypes'),
+                'lang' => get_string('descriptiontype_lang', 'tool_filetypes')));
+
+        $mform->addElement('text', 'description',  get_string('description', 'tool_filetypes'));
+        $mform->setType('description', PARAM_TEXT);
+        $mform->addHelpButton('description', 'description', 'tool_filetypes');
+        $mform->disabledIf('description', 'descriptiontype', 'ne', 'custom');
+
+        $mform->addElement('text', 'corestring',  get_string('corestring', 'tool_filetypes'));
+        $mform->setType('corestring', PARAM_ALPHANUMEXT);
+        $mform->addHelpButton('corestring', 'corestring', 'tool_filetypes');
+        $mform->disabledIf('corestring', 'descriptiontype', 'ne', 'lang');
+
+        $mform->addElement('checkbox', 'defaulticon',  get_string('defaulticon', 'tool_filetypes'));
+        $mform->addHelpButton('defaulticon', 'defaulticon', 'tool_filetypes');
+
+        $mform->addElement('hidden', 'oldextension', $oldextension);
+        $mform->setType('oldextension', PARAM_RAW);
+        $this->add_action_buttons(true, get_string('savechanges'));
+    }
+
+    public function set_data($data) {
+        // Set up the description type.
+        if (!empty($data['corestring'])) {
+            $data['descriptiontype'] = 'lang';
+        } else if (!empty($data['description'])) {
+            $data['descriptiontype'] = 'custom';
+        } else {
+            $data['descriptiontype'] = '';
+        }
+
+        // Call parent.
+        parent::set_data($data);
+    }
+
+    public function validation($data, $files) {
+        $errors = parent::validation($data, $files);
+
+        // Check the extension isn't already in use.
+        $oldextension = $data['oldextension'];
+        $extension = trim($data['extension']);
+        if (\tool_filetypes\utils::is_extension_invalid($extension, $oldextension)) {
+            $errors['extension'] = get_string('error_extension', 'tool_filetypes', $extension);
+        }
+
+        // Check the 'default icon' setting doesn't conflict with an existing one.
+        if (!empty($data['defaulticon']) && !\tool_filetypes\utils::is_defaulticon_allowed(
+                $data['mimetype'], $oldextension)) {
+            $errors['defaulticon'] = get_string('error_defaulticon', 'tool_filetypes', $extension);
+        }
+
+        return $errors;
+    }
+}
diff --git a/admin/tool/filetypes/index.php b/admin/tool/filetypes/index.php
new file mode 100644 (file)
index 0000000..6ac20dc
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Display the custom file type settings page.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require(__DIR__ . '/../../../config.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+admin_externalpage_setup('tool_filetypes');
+
+// Page settings.
+$title = get_string('pluginname', 'tool_filetypes');
+
+$context = context_system::instance();
+$PAGE->set_url(new \moodle_url('/admin/tool/filetypes/index.php'));
+$PAGE->set_context($context);
+$PAGE->set_pagelayout('admin');
+$PAGE->set_title($SITE->fullname. ': ' . $title);
+
+$renderer = $PAGE->get_renderer('tool_filetypes');
+
+// Is it restricted because set in config.php?
+$restricted = array_key_exists('customfiletypes', $CFG->config_php_settings);
+
+// Display the page.
+echo $renderer->header();
+echo $renderer->edit_table(get_mimetypes_array(), core_filetypes::get_deleted_types(),
+        $restricted);
+echo $renderer->footer();
diff --git a/admin/tool/filetypes/lang/en/tool_filetypes.php b/admin/tool/filetypes/lang/en/tool_filetypes.php
new file mode 100644 (file)
index 0000000..2640b44
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Strings for custom file types.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['addfiletypes'] = 'Add a new file type';
+$string['corestring'] = 'Alternative language string';
+$string['corestring_help'] = 'This setting can be used to select a different language string from the core mimetypes.php language file. Generally it should be left blank. For custom types, use the description field.';
+$string['defaulticon'] = 'Default icon for MIME type';
+$string['defaulticon_help'] = 'If there are multiple file extensions with the same MIME type, select this option for one of the extensions so that its icon will be used when determining an icon from the MIME type.';
+$string['delete_confirmation'] = 'Are you absolutely sure you want to remove <strong>.{$a}</strong>?';
+$string['deletea'] = 'Delete {$a}';
+$string['deletefiletypes'] = 'Delete a file type';
+$string['description'] = 'Custom description';
+$string['description_help'] = 'Simple file type description, e.g. &lsquo;Kindle ebook&rsquo;. If your site supports multiple languages and uses the multi-language filter, you can enter multi-language tags in this field to supply a description in different languages.';
+$string['descriptiontype'] = 'Description type';
+$string['descriptiontype_help'] = 'There are three possible ways to specify a description.
+
+* Default behaviour uses the MIME type. If there is a language string in mimetypes.php corresponding to that MIME type, it will be used; otherwise the MIME type itself will be displayed to users.
+* You can specify a custom description on this form.
+* You can specify the name of a languge string in mimetypes.php to use instead of the MIME type.';
+$string['descriptiontype_default'] = 'Default (MIME type or corresponding language string if available)';
+$string['descriptiontype_custom'] = 'Custom description specified in this form';
+$string['descriptiontype_lang'] = 'Alternative language string (from mimetypes.php)';
+$string['displaydescription'] = 'Description';
+$string['editfiletypes'] = 'Edit an existing file type';
+$string['emptylist'] = 'There are no file types defined.';
+$string['error_addentry'] = 'The file type extension, description,  MIME type, and icon must not contain line feed and semicolon characters.';
+$string['error_defaulticon'] = 'Another file extension with the same MIME type is already marked as the default icon.';
+$string['error_extension'] = 'The file type extension <strong>{$a}</strong> already exists or is invalid. File extensions must be unique and must not contain special characters.';
+$string['error_notfound'] = 'The file type with extension {$a} cannot be found.';
+$string['extension'] = 'Extension';
+$string['extension_help'] = 'File name extension without the dot, e.g. &lsquo;mobi&rsquo;';
+$string['groups'] = 'Type groups';
+$string['groups_help'] = 'Optional list of file type groups that this type belongs to. These are generic categories such as &lsquo;document&rsquo; and &lsquo;image&rsquo;.';
+$string['icon'] = 'File icon';
+$string['icon_help'] = 'Icon filename.
+
+The list of icons is taken from the /pix/f directory inside your Moodle installation. You can add custom icons to this folder if required.';
+$string['mimetype'] = 'MIME type';
+$string['mimetype_help'] = 'MIME type associated with this file type, e.g. &lsquo;application/x-mobipocket-ebook&rsquo;';
+$string['pluginname'] = 'File types';
+$string['revert'] = 'Restore {$a} to Moodle defaults';
+$string['revert_confirmation'] = 'Are you sure you want to restore <strong>.{$a}</strong> to Moodle defaults, discarding your changes?';
+$string['revertfiletype'] = 'Restore a file type';
+$string['source'] = 'Type';
+$string['source_custom'] = 'Custom';
+$string['source_deleted'] = 'Deleted';
+$string['source_modified'] = 'Modified';
+$string['source_standard'] = 'Standard';
diff --git a/admin/tool/filetypes/renderer.php b/admin/tool/filetypes/renderer.php
new file mode 100644 (file)
index 0000000..dee5281
--- /dev/null
@@ -0,0 +1,179 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Renderer.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Class containing the renderer functions for displaying file types.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_filetypes_renderer extends plugin_renderer_base {
+
+    /**
+     * Renderer for displaying the file type edit table.
+     *
+     * @param array $filetypes An array of file type objects (from get_mimetypes_array)
+     * @param array $deleted An array of deleted file types
+     * @param bool $restricted If true, cannot be edited because set in config.php.
+     * @return string HTML code
+     */
+    public function edit_table(array $filetypes, array $deleted, $restricted) {
+        // Get combined array of all types, with deleted marker.
+        $combined = array_merge($filetypes, $deleted);
+        foreach ($deleted as $ext => $value) {
+            $combined[$ext]['deleted'] = true;
+        }
+        ksort($combined);
+
+        $out = $this->heading(get_string('pluginname', 'tool_filetypes'));
+        if ($restricted) {
+            $out .= html_writer::div(
+                    html_writer::div(get_string('configoverride', 'admin'), 'form-overridden'),
+                    '', array('id' => 'adminsettings'));
+        }
+        if (count($combined) > 1) {
+            // Display the file type table if any file types exist (other than 'xxx').
+            $table = new html_table();
+            $headings = new html_table_row();
+            $headings->cells = array();
+            $headings->cells[] = new html_table_cell(get_string('extension', 'tool_filetypes'));
+            if (!$restricted) {
+                $headings->cells[] =
+                        new html_table_cell(html_writer::span(get_string('edit'), 'accesshide'));
+            }
+            $headings->cells[] = new html_table_cell(get_string('source', 'tool_filetypes'));
+            $headings->cells[] = new html_table_cell(get_string('mimetype', 'tool_filetypes'));
+            $headings->cells[] = new html_table_cell(get_string('groups', 'tool_filetypes'));
+            $headings->cells[] = new html_table_cell(get_string('displaydescription', 'tool_filetypes'));
+            foreach ($headings->cells as $cell) {
+                $cell->header = true;
+            }
+            $table->data = array($headings);
+            foreach ($combined as $extension => $filetype) {
+                if ($extension === 'xxx') {
+                    continue;
+                }
+                $row = new html_table_row();
+                $row->cells = array();
+
+                // First cell has icon and extension.
+                $icon = $this->pix_icon('f/' . $filetype['icon'], '');
+                $row->cells[] = new html_table_cell($icon . ' ' . html_writer::span(s($extension)));
+
+                // Reset URL and button if needed.
+                $reverturl = new \moodle_url('/admin/tool/filetypes/revert.php',
+                        array('extension' => $extension));
+                $revertbutton = html_writer::link($reverturl, $this->pix_icon('t/restore',
+                        get_string('revert', 'tool_filetypes', s($extension))));
+                if ($restricted) {
+                    $revertbutton = '';
+                }
+
+                // Rest is different for deleted items.
+                if (!empty($filetype['deleted'])) {
+                    // Show deleted standard types differently.
+                    if (!$restricted) {
+                        $row->cells[] = new html_table_cell('');
+                    }
+                    $source = new html_table_cell(get_string('source_deleted', 'tool_filetypes') .
+                            ' ' . $revertbutton);
+                    $source->attributes = array('class' => 'nonstandard');
+                    $row->cells[] = $source;
+
+                    // Other cells are blank.
+                    $row->cells[] = new html_table_cell('');
+                    $row->cells[] = new html_table_cell('');
+                    $row->cells[] = new html_table_cell('');
+                    $row->attributes = array('class' => 'deleted');
+                } else {
+                    if (!$restricted) {
+                        // Edit icons. For accessibility, the name of these links should
+                        // be different for each row, so we have to include the extension.
+                        $editurl = new \moodle_url('/admin/tool/filetypes/edit.php',
+                                array('oldextension' => $extension));
+                        $editbutton = html_writer::link($editurl, $this->pix_icon('t/edit',
+                                get_string('edita', '', s($extension))));
+                        $deleteurl = new \moodle_url('/admin/tool/filetypes/delete.php',
+                                array('extension' => $extension));
+                        $deletebutton = html_writer::link($deleteurl, $this->pix_icon('t/delete',
+                                get_string('deletea', 'tool_filetypes', s($extension))));
+                        $row->cells[] = new html_table_cell($editbutton . '&nbsp;' . $deletebutton);
+                    }
+
+                    // Source.
+                    $sourcestring = 'source_';
+                    if (!empty($filetype['custom'])) {
+                        $sourcestring .= 'custom';
+                    } else if (!empty($filetype['modified'])) {
+                        $sourcestring .= 'modified';
+                    } else {
+                        $sourcestring .= 'standard';
+                    }
+                    $source = new html_table_cell(get_string($sourcestring, 'tool_filetypes') .
+                            ($sourcestring === 'source_modified' ? ' ' . $revertbutton : ''));
+                    if ($sourcestring !== 'source_standard') {
+                        $source->attributes = array('class' => 'nonstandard');
+                    }
+                    $row->cells[] = $source;
+
+                    // MIME type.
+                    $mimetype = html_writer::div(s($filetype['type']), 'mimetype');
+                    if (!empty($filetype['defaulticon'])) {
+                        // Include the 'default for MIME type' info in the MIME type cell.
+                        $mimetype .= html_writer::div(html_writer::tag('i',
+                                get_string('defaulticon', 'tool_filetypes')));
+                    }
+                    $row->cells[] = new html_table_cell($mimetype);
+
+                    // Groups.
+                    $groups = !empty($filetype['groups']) ? implode(', ', $filetype['groups']) : '';
+                    $row->cells[] = new html_table_cell(s($groups));
+
+                    // Description.
+                    $description = get_mimetype_description(array('filename' => 'a.' . $extension));
+                    // Don't show the description if it's just a copy of the MIME type,
+                    // it makes the table ugly with the long duplicate text; leave blank instead.
+                    if ($description === $filetype['type']) {
+                        $description = '';
+                    }
+                    $row->cells[] = new html_table_cell($description);
+                }
+
+                $table->data[] = $row;
+            }
+            $out .= html_writer::table($table);
+        } else {
+            $out .= html_writer::tag('div', get_string('emptylist', 'tool_filetypes'));
+        }
+        // Displaying the 'Add' button.
+        if (!$restricted) {
+            $out .= $this->single_button(new moodle_url('/admin/tool/filetypes/edit.php',
+                    array('name' => 'add')), get_string('addfiletypes', 'tool_filetypes'), 'get');
+        }
+        return $out;
+    }
+}
diff --git a/admin/tool/filetypes/revert.php b/admin/tool/filetypes/revert.php
new file mode 100644 (file)
index 0000000..549f460
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Resets a file type to the default Moodle values.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require(__DIR__ . '/../../../config.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+admin_externalpage_setup('tool_filetypes');
+
+$extension = required_param('extension', PARAM_RAW);
+$redirecturl = new \moodle_url('/admin/tool/filetypes/index.php');
+
+if (optional_param('revert', 0, PARAM_INT)) {
+    require_sesskey();
+
+    // Reset the file type in config.
+    core_filetypes::revert_type_to_default($extension);
+    redirect($redirecturl);
+}
+
+// Page settings.
+$title = get_string('revertfiletype', 'tool_filetypes');
+
+$context = context_system::instance();
+$PAGE->set_url(new \moodle_url('/admin/tool/filetypes/revert.php', array('extension' => $extension)));
+$PAGE->navbar->add($title);
+$PAGE->set_context($context);
+$PAGE->set_pagelayout('admin');
+$PAGE->set_title($SITE->fullname. ': ' . $title);
+
+// Display the page.
+echo $OUTPUT->header();
+
+$message = get_string('revert_confirmation', 'tool_filetypes', $extension);
+$reverturl = new \moodle_url('revert.php', array('extension' => $extension, 'revert' => 1));
+$yesbutton = new single_button($reverturl, get_string('yes'));
+$nobutton = new single_button($redirecturl, get_string('no'), 'get');
+echo $OUTPUT->confirm($message, $yesbutton, $nobutton);
+
+echo $OUTPUT->footer();
diff --git a/admin/tool/filetypes/settings.php b/admin/tool/filetypes/settings.php
new file mode 100644 (file)
index 0000000..2028b0d
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Configure the setting page of the custom file type as an external page.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($hassiteconfig) {
+    $ADMIN->add('server', new admin_externalpage('tool_filetypes',
+            new lang_string('pluginname', 'tool_filetypes'),
+            $CFG->wwwroot . '/admin/tool/filetypes/index.php'));
+}
diff --git a/admin/tool/filetypes/styles.css b/admin/tool/filetypes/styles.css
new file mode 100644 (file)
index 0000000..ed2353a
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * Styles for admin tool page.
+ */
+
+/* Fix bad default table wrap (caused by the long mimetype field). */
+.path-admin-tool-filetypes .generaltable .c0,
+.path-admin-tool-filetypes .generaltable .c1,
+.path-admin-tool-filetypes .generaltable .c2,
+.path-admin-tool-filetypes .generaltable th {
+    white-space: nowrap;
+}
+
+/* Formatting for deleted lines. */
+.path-admin-tool-filetypes .generaltable .deleted .c0 img {
+    opacity: 0.2;
+}
+
+.path-admin-tool-filetypes .generaltable .deleted .c0 span {
+    text-decoration: line-through;
+}
+
+/* Custom types show bold */
+.path-admin-tool-filetypes .generaltable .nonstandard {
+    font-weight: bold;
+}
+
+/* Spacing around the 'Defined in config.php' stripe */
+.path-admin-tool-filetypes .form-overridden {
+    display: inline-block;
+    margin-bottom: 1em;
+    padding: 4px 6px;
+}
diff --git a/admin/tool/filetypes/tests/behat/add_filetypes.feature b/admin/tool/filetypes/tests/behat/add_filetypes.feature
new file mode 100644 (file)
index 0000000..18d043e
--- /dev/null
@@ -0,0 +1,107 @@
+@tool @tool_filetypes
+Feature: Add customised file types
+  In order to support a file mime type which doesn't exist in Moodle
+  As an administrator
+  I need to add a new customised file type
+
+  Scenario: Add a new file type
+    Given I log in as "admin"
+    And I navigate to "File types" node in "Site administration > Server"
+    And I press "Add"
+    # Try setting all the form fields, not just the optional ones.
+    And I set the following fields to these values:
+      | Extension                  | frog                                      |
+      | MIME type                  | application/x-frog                        |
+      | File icon                  | archive                                   |
+      | Type groups                | document                                  |
+      | Description type           | Custom description specified in this form |
+      | Custom description         | Froggy file                               |
+      | Default icon for MIME type | 1                                         |
+    When I press "Save changes"
+    Then I should see "Froggy file" in the "application/x-frog" "table_row"
+    And I should see "document" in the "application/x-frog" "table_row"
+    And I should see "frog" in the "application/x-frog" "table_row"
+    And "//img[contains(@src, 'archive')]" "xpath_element" should exist in the "application/x-frog" "table_row"
+
+  Scenario: Update an existing file type
+    Given I log in as "admin"
+    And I navigate to "File types" node in "Site administration > Server"
+    When I click on "Edit 7z" "link"
+    And I set the following fields to these values:
+      | Extension | doc |
+    And I press "Save changes"
+    Then I should see "File extensions must be unique"
+    And I set the following fields to these values:
+      | Extension | frog |
+    And I press "Save changes"
+    And I should see "frog" in the "application/x-7z-compressed" "table_row"
+
+  Scenario: Delete an existing file type
+    Given I log in as "admin"
+    And I navigate to "File types" node in "Site administration > Server"
+    When I click on "Delete 7z" "link"
+    Then I should see "Are you absolutely sure you want to remove .7z?"
+    And I press "Yes"
+    And I should see "Deleted" in the "7z" "table_row"
+
+  Scenario: Delete a custom file type
+    Given I log in as "admin"
+    And I navigate to "File types" node in "Site administration > Server"
+    And I press "Add"
+    And I set the following fields to these values:
+      | Extension                  | frog                                      |
+      | MIME type                  | application/x-frog                        |
+    And I press "Save changes"
+    When I click on "Delete frog" "link"
+    And I press "Yes"
+    Then I should not see "frog"
+
+  Scenario: Revert changes to deleted file type
+    Given I log in as "admin"
+    And I navigate to "File types" node in "Site administration > Server"
+    When I click on "Delete 7z" "link"
+    And I press "Yes"
+    And I follow "Restore 7z to Moodle defaults"
+    And I press "Yes"
+    Then I should not see "Deleted" in the "7z" "table_row"
+
+  Scenario: Revert changes to updated file type
+    Given I log in as "admin"
+    And I navigate to "File types" node in "Site administration > Server"
+    And I click on "Edit 7z" "link"
+    And I set the following fields to these values:
+      | File icon | document |
+    And I press "Save changes"
+    When I follow "Restore 7z to Moodle defaults"
+    And I press "Yes"
+    Then "//img[contains(@src, 'archive')]" "xpath_element" should exist in the "7z" "table_row"
+
+  @javascript
+  Scenario: Create a resource activity which contains a customised file type
+    Given the following "courses" exist:
+      | fullname | shortname |
+      | Course 1 | C1        |
+    And I log in as "admin"
+    And I navigate to "File types" node in "Site administration > Server"
+    And I press "Add"
+    And I set the following fields to these values:
+      | Extension          | frog                                      |
+      | MIME type          | application/x-frog                        |
+      | File icon          | archive                                   |
+      | Description type   | Custom description specified in this form |
+      | Custom description | Froggy file                               |
+    And I press "Save changes"
+    # Create a resource activity and add it to a course
+    And I am on homepage
+    And I follow "Course 1"
+    And I turn editing mode on
+    When I add a "File" to section "1"
+    And I set the following fields to these values:
+      | Name        | An example of customised file type |
+      | Description | File description                   |
+    And I upload "admin/tool/filetypes/tests/fixtures/test.frog" file to "Select files" filemanager
+    And I expand all fieldsets
+    And I set the field "Show type" to "1"
+    And I press "Save and return to course"
+    Then I should see "Froggy file"
+    And the "src" attribute of ".modtype_resource a img" "css_element" should contain "archive"
diff --git a/admin/tool/filetypes/tests/fixtures/frog-24.png b/admin/tool/filetypes/tests/fixtures/frog-24.png
new file mode 100644 (file)
index 0000000..91a99b9
Binary files /dev/null and b/admin/tool/filetypes/tests/fixtures/frog-24.png differ
diff --git a/admin/tool/filetypes/tests/fixtures/frog-48.png b/admin/tool/filetypes/tests/fixtures/frog-48.png
new file mode 100644 (file)
index 0000000..91a99b9
Binary files /dev/null and b/admin/tool/filetypes/tests/fixtures/frog-48.png differ
diff --git a/admin/tool/filetypes/tests/fixtures/test.frog b/admin/tool/filetypes/tests/fixtures/test.frog
new file mode 100644 (file)
index 0000000..f9819bb
--- /dev/null
@@ -0,0 +1 @@
+This is not a real file format.
\ No newline at end of file
diff --git a/admin/tool/filetypes/tests/fixtures/zombie.gif b/admin/tool/filetypes/tests/fixtures/zombie.gif
new file mode 100644 (file)
index 0000000..9884f47
Binary files /dev/null and b/admin/tool/filetypes/tests/fixtures/zombie.gif differ
diff --git a/admin/tool/filetypes/tests/tool_filetypes_test.php b/admin/tool/filetypes/tests/tool_filetypes_test.php
new file mode 100644 (file)
index 0000000..6d53683
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Unit tests for the custom file types.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+use tool_filetypes\utils;
+
+/**
+ * Unit tests for the custom file types.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_filetypes_test extends advanced_testcase {
+    /**
+     * Tests is_extension_invalid() function.
+     */
+    public function test_is_extension_invalid() {
+        // The pdf file extension already exists in default moodle minetypes.
+        $this->assertTrue(utils::is_extension_invalid('pdf'));
+
+        // The frog extension does not.
+        $this->assertFalse(utils::is_extension_invalid('frog'));
+
+        // However you could use the pdf extension when editing the pdf extension.
+        $this->assertFalse(utils::is_extension_invalid('pdf', 'pdf'));
+
+        // Blank extension is invalid.
+        $this->assertTrue(utils::is_extension_invalid(''));
+
+        // Extensions with dot are invalid.
+        $this->assertTrue(utils::is_extension_invalid('.frog'));
+    }
+
+    /**
+     * Tests is_defaulticon_allowed() function.
+     */
+    public function test_is_defaulticon_allowed() {
+        // You ARE allowed to set a default icon for a MIME type that hasn't
+        // been used yet.
+        $this->assertTrue(utils::is_defaulticon_allowed('application/x-frog'));
+
+        // You AREN'T allowed to set default icon for text/plain as there is
+        // already a type that has that set.
+        $this->assertFalse(utils::is_defaulticon_allowed('text/plain'));
+
+        // But you ARE still allowed to set it when actually editing txt, which
+        // is the default.
+        $this->assertTrue(utils::is_defaulticon_allowed('text/plain', 'txt'));
+    }
+
+    /**
+     * Tests get_icons_from_path() function.
+     */
+    public function test_get_icons_from_path() {
+        // Get icons from the fixtures folder.
+        $icons = utils::get_icons_from_path(__DIR__ . '/fixtures');
+
+        // The icons are returned alphabetically and with keys === values.
+        // For the icon with numbers after the name, only the base name is
+        // returned and only one of it.
+        $this->assertEquals(array('frog' => 'frog', 'zombie' => 'zombie'), $icons);
+    }
+}
diff --git a/admin/tool/filetypes/version.php b/admin/tool/filetypes/version.php
new file mode 100644 (file)
index 0000000..f4cefec
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Version details.
+ *
+ * @package tool_filetypes
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014112700;
+$plugin->requires = 2014112700;
+$plugin->component = 'tool_filetypes';
index dcb4e7b..7e12367 100644 (file)
@@ -295,6 +295,11 @@ class manager {
         // First flag this message to prevent another running hitting this message while we look at the headers.
         $this->add_flag_to_message($messageid, self::MESSAGE_FLAGGED);
 
+        if ($this->is_bulk_message($message, $messageid)) {
+            mtrace("- The message has a bulk header set. This is likely an auto-generated reply - discarding.");
+            return;
+        }
+
         // Record the user that this script is currently being run as.  This is important when re-processing existing
         // messages, as cron_setup_user is called multiple times.
         $originaluser = $USER;
@@ -741,6 +746,43 @@ class manager {
         return in_array($flag, $flags);
     }
 
+    /**
+     * Attempt to determine whether this message is a bulk message (e.g. automated reply).
+     *
+     * @param \Horde_Imap_Client_Data_Fetch $message The message to process
+     * @param string|\Horde_Imap_Client_Ids $messageid The Hore message Uid
+     * @return boolean
+     */
+    private function is_bulk_message(
+            \Horde_Imap_Client_Data_Fetch $message,
+            $messageid) {
+        $query = new \Horde_Imap_Client_Fetch_Query();
+        $query->headerText(array('peek' => true));
+
+        $messagedata = $this->client->fetch($this->get_mailbox(), $query, array('ids' => $messageid))->first();
+
+        // Assume that this message is not bulk to begin with.
+        $isbulk = false;
+
+        // An auto-reply may itself include the Bulk Precedence.
+        $precedence = $messagedata->getHeaderText(0, \Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->getValue('Precedence');
+        $isbulk = $isbulk || strtolower($precedence) == 'bulk';
+
+        // If the X-Autoreply header is set, and not 'no', then this is an automatic reply.
+        $autoreply = $messagedata->getHeaderText(0, \Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->getValue('X-Autoreply');
+        $isbulk = $isbulk || ($autoreply && $autoreply != 'no');
+
+        // If the X-Autorespond header is set, and not 'no', then this is an automatic response.
+        $autorespond = $messagedata->getHeaderText(0, \Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->getValue('X-Autorespond');
+        $isbulk = $isbulk || ($autorespond && $autorespond != 'no');
+
+        // If the Auto-Submitted header is set, and not 'no', then this is a non-human response.
+        $autosubmitted = $messagedata->getHeaderText(0, \Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->getValue('Auto-Submitted');
+        $isbulk = $isbulk || ($autosubmitted && $autosubmitted != 'no');
+
+        return $isbulk;
+    }
+
     /**
      * Send the message to the appropriate handler.
      *
index 03ec2af..4025312 100644 (file)
@@ -16,9 +16,6 @@
 .path-admin-tool-profiling .flexible .referencerun {
     font-weight: bold;
 }
-.path-admin-tool-profiling .flexible .r1 .cell {
-    background-color: whitesmoke;
-}
 .path-admin-tool-profiling .flexible {
     margin-left:auto;
     margin-right:auto
index 0660eff..62b0ad9 100644 (file)
@@ -123,6 +123,7 @@ if ($execute = $options['execute']) {
         } else {
             $task->set_cron_lock($cronlock);
         }
+        get_mailer('buffer');
         $task->execute();
         if (isset($predbqueries)) {
             mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
@@ -130,6 +131,7 @@ if ($execute = $options['execute']) {
         }
         mtrace("Task completed.");
         \core\task\manager::scheduled_task_complete($task);
+        get_mailer('close');
         exit(0);
     } catch (Exception $e) {
         if ($DB->is_transaction_started()) {
@@ -139,6 +141,7 @@ if ($execute = $options['execute']) {
         mtrace("... used " . (microtime(true) - $pretime) . " seconds");
         mtrace("Task failed: " . $e->getMessage());
         \core\task\manager::scheduled_task_failed($task);
+        get_mailer('close');
         exit(1);
     }
 }
index bd0b08f..1380f20 100644 (file)
@@ -2630,7 +2630,21 @@ class restore_course_completion_structure_step extends restore_structure_step {
                 'timecompleted' => $this->apply_date_offset($data->timecompleted),
                 'reaggregate' => $data->reaggregate
             );
-            $DB->insert_record('course_completions', $params);
+
+            $existing = $DB->get_record('course_completions', array(
+                'userid' => $data->userid,
+                'course' => $data->course
+            ));
+
+            // MDL-46651 - If cron writes out a new record before we get to it
+            // then we should replace it with the Truth data from the backup.
+            // This may be obsolete after MDL-48518 is resolved
+            if ($existing) {
+                $params['id'] = $existing->id;
+                $DB->update_record('course_completions', $params);
+            } else {
+                $DB->insert_record('course_completions', $params);
+            }
         }
     }
 
index 5adb973..671a8b5 100644 (file)
@@ -403,7 +403,7 @@ abstract class backup_cron_automated_helper {
                 $bc->log('No directory specified for automated backups',
                         backup::LOG_WARNING);
                 $outcome = self::BACKUP_STATUS_WARNING;
-            } else if (!file_exists($dir) || !is_dir($dir) || !is_writable($dir) && $storage !== 0) {
+            } else if ($storage !== 0 && (!file_exists($dir) || !is_dir($dir) || !is_writable($dir))) {
                 // If we need to copy the backup file to an external dir and it is not writable, change status to error.
                 $bc->log('Specified backup directory is not writable - ',
                         backup::LOG_ERROR, $dir);
index 99c099e..3632182 100644 (file)
@@ -140,7 +140,7 @@ class core_badges_renderer extends plugin_renderer_base {
         $dl = array();
         $dl[get_string('name')] = $badge->name;
         $dl[get_string('description', 'badges')] = $badge->description;
-        $dl[get_string('createdon', 'search')] = $badge->timecreated;
+        $dl[get_string('createdon', 'search')] = userdate($badge->timecreated);
         $dl[get_string('badgeimage', 'badges')] = print_badge_image($badge, $context, 'large');
         $display .= $this->definition_list($dl);
 
index 41cd98e..ace839e 100644 (file)
@@ -30,15 +30,6 @@ class block_calendar_month extends block_base {
         $this->title = get_string('pluginname', 'block_calendar_month');
     }
 
-    /**
-     * Return preferred_width.
-     *
-     * @return int
-     */
-    public function preferred_width() {
-        return 210;
-    }
-
     /**
      * Return the content of this block.
      *
index 281aba3..c2163fc 100644 (file)
@@ -74,10 +74,6 @@ class block_course_summary extends block_base {
         return true;
     }
 
-    function preferred_width() {
-        return 210;
-    }
-
 }
 
 
index bbbd72b..0061a9b 100644 (file)
@@ -326,11 +326,6 @@ class block_base {
             $correct = false;
         }
 
-        $width = $this->preferred_width();
-        if (!is_int($width) || $width <= 0) {
-            $errors[] = 'invalid_width';
-            $correct = false;
-        }
         return $correct;
     }
 
@@ -594,17 +589,6 @@ class block_base {
         return array('moodle/block:view', 'moodle/block:edit');
     }
 
-    // Methods deprecated in Moodle 2.0 ========================================
-
-    /**
-     * Default case: the block wants to be 180 pixels wide
-     * @deprecated since Moodle 2.0.
-     * @return int
-     */
-    function preferred_width() {
-        return 180;
-    }
-
     /**
      * Can be overridden by the block to prevent the block from being dockable.
      *
index 27b771d..2838651 100644 (file)
         $this->title = get_string('pluginname', 'block_rss_client');
     }
 
-    function preferred_width() {
-        return 210;
-    }
-
     function applicable_formats() {
         return array('all' => true, 'tag' => false);   // Needs work to make it work on tags MDL-11960
     }
index 46f8c19..06f691d 100644 (file)
@@ -43,10 +43,6 @@ class block_tag_flickr extends block_base {
         return true;
     }
 
-    function preferred_width() {
-        return 170;
-    }
-
     function get_content() {
         global $CFG, $USER;
 
index 3779d42..a06588e 100644 (file)
@@ -45,10 +45,6 @@ class block_tag_youtube extends block_base {
         return true;
     }
 
-    function preferred_width() {
-        return 140;
-    }
-
     function get_content() {
         global $CFG;
 
index abd99d2..288eff3 100644 (file)
@@ -1,6 +1,10 @@
 This files describes API changes in /blocks/* - activity modules,
 information provided here is intended especially for developers.
 
+=== 2.9 ===
+
+* The obsolete method preferred_width() was removed (it was not doing anything)
+
 === 2.8 ===
 
 * The instance_config_print() function was removed. It was deprecated in
index 96aa0ce..8658afd 100644 (file)
@@ -249,7 +249,7 @@ function calendar_get_mini($courses, $groups, $users, $calmonth = false, $calyea
 
     // These are used for DB queries, so we want unixtime, so we need to use Gregorian dates.
     $display->tstart = make_timestamp($gy, $gm, $gd, $gh, $gmin, 0);
-    $display->tend = $display->tstart + ($display->maxdays * DAYSECS) - 1;
+    $display->tend = strtotime("+{$display->maxdays} days", $display->tstart) - 1;
 
     // Align the starting weekday to fall in our display range
     // This is simple, not foolproof.
@@ -1931,6 +1931,9 @@ function calendar_normalize_tz($tz) {
         case('Japan Standard Time'):
             $tz = 'Asia/Tokyo';
             break;
+        case('Romance Standard Time'):
+            $tz = 'Europe/Brussels';
+            break;
     }
     return $tz;
 }
@@ -2971,6 +2974,7 @@ function calendar_add_icalendar_event($event, $courseid, $subscriptionid, $timez
     } else {
         $endtz = isset($event->properties['DTEND'][0]->parameters['TZID']) ? $event->properties['DTEND'][0]->parameters['TZID'] :
                 $timezone;
+        $endtz = calendar_normalize_tz($endtz);
         $eventrecord->timeduration = strtotime($event->properties['DTEND'][0]->value . ' ' . $endtz) - $eventrecord->timestart;
     }
 
index 9a70da3..4836a10 100644 (file)
@@ -42,12 +42,13 @@ Feature: A privileged user can create cohorts using a CSV file
       | Cat 1         | cohort name 4 | cohortid4 |                   | 0           | Created manually |
       | Cat 2         | cohort name 5 | cohortid5 |                   | 0           | Created manually |
       | Cat 3         | cohort name 6 | cohortid6 |                   | 0           | Created manually |
-    And the "class" attribute of "cohort name 1" "table_row" should not contain "dimmed_text"
-    And the "class" attribute of "cohort name 2" "table_row" should not contain "dimmed_text"
+    And ".dimmed_text" "css_element" should not exist in the "cohort name 1" "table_row"
+    And ".dimmed_text" "css_element" should not exist in the "cohort name 2" "table_row"
+    And ".dimmed_text" "css_element" should exist in the "cohort name 3" "table_row"
     And the "class" attribute of "cohort name 3" "table_row" should contain "dimmed_text"
-    And the "class" attribute of "cohort name 4" "table_row" should not contain "dimmed_text"
+    And ".dimmed_text" "css_element" should not exist in the "cohort name 4" "table_row"
     And the "class" attribute of "cohort name 5" "table_row" should contain "dimmed_text"
-    And the "class" attribute of "cohort name 6" "table_row" should not contain "dimmed_text"
+    And ".dimmed_text" "css_element" should not exist in the "cohort name 6" "table_row"
 
   @javascript
   Scenario: Upload cohorts with default category context as admin
index 513b437..4338d7e 100644 (file)
@@ -517,6 +517,23 @@ $CFG->admin = 'admin';
 // on a shared file system that supports locking.
 //      $CFG->lock_file_root = $CFG->dataroot . '/lock';
 //
+// Moodle 2.9 allows administrators to customise the list of supported file types.
+// To add a new filetype or override the definition of an existing one, set the
+// customfiletypes variable like this:
+//
+// $CFG->customfiletypes = array(
+//     (object)array(
+//         'extension' => 'frog',
+//         'icon' => 'archive',
+//         'type' => 'application/frog',
+//         'customdescription' => 'Amphibian-related file archive'
+//     )
+// );
+//
+// The extension, icon, and type fields are required. The icon field can refer to
+// any icon inside the pix/f folder. You can also set the customdescription field
+// (shown above) and (for advanced use) the groups, string, and defaulticon fields.
+//
 //=========================================================================
 // 7. SETTINGS FOR DEVELOPMENT SERVERS - not intended for production use!!!
 //=========================================================================
index f3765a4..6b8ce0a 100644 (file)
@@ -26,7 +26,6 @@
 require_once("../config.php");
 require_once("lib.php");
 require_once($CFG->libdir . '/formslib.php');
-require_once($CFG->libdir . '/conditionlib.php');
 
 $id = required_param('id', PARAM_INT);    // course_sections.id
 $sectionreturn = optional_param('sr', 0, PARAM_INT);
index 07cd6ed..b8212d5 100644 (file)
@@ -998,9 +998,6 @@ function get_array_of_activities($courseid) {
 //  groupingid - grouping id
 //  extra - contains extra string to include in any link
     global $CFG, $DB;
-    if(!empty($CFG->enableavailability)) {
-        require_once($CFG->libdir.'/conditionlib.php');
-    }
 
     $course = $DB->get_record('course', array('id'=>$courseid));
 
index b2d8ede..234e3ad 100644 (file)
@@ -28,7 +28,6 @@ require_once("lib.php");
 require_once($CFG->libdir.'/filelib.php');
 require_once($CFG->libdir.'/gradelib.php');
 require_once($CFG->libdir.'/completionlib.php');
-require_once($CFG->libdir.'/conditionlib.php');
 require_once($CFG->libdir.'/plagiarismlib.php');
 require_once($CFG->dirroot . '/course/modlib.php');
 
index fa1ac35..9a44714 100644 (file)
@@ -483,12 +483,17 @@ function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) {
     }
 
     $completion = new completion_info($course);
-    if ($completion->is_enabled() && !empty($moduleinfo->completionunlocked)) {
-        // Update completion settings.
-        $cm->completion                = $moduleinfo->completion;
-        $cm->completiongradeitemnumber = $moduleinfo->completiongradeitemnumber;
-        $cm->completionview            = $moduleinfo->completionview;
-        $cm->completionexpected        = $moduleinfo->completionexpected;
+    if ($completion->is_enabled()) {
+        // Completion settings that would affect users who have already completed
+        // the activity may be locked; if so, these should not be updated.
+        if (!empty($moduleinfo->completionunlocked)) {
+            $cm->completion = $moduleinfo->completion;
+            $cm->completiongradeitemnumber = $moduleinfo->completiongradeitemnumber;
+            $cm->completionview = $moduleinfo->completionview;
+        }
+        // The expected date does not affect users who have completed the activity,
+        // so it is safe to update it regardless of the lock status.
+        $cm->completionexpected = $moduleinfo->completionexpected;
     }
     if (!empty($CFG->enableavailability)) {
         // This code is used both when submitting the form, which uses a long
index 52ba8fa..1662307 100644 (file)
@@ -65,7 +65,9 @@ class course_reset_form extends moodleform {
         $mform->addElement('header', 'gradebookheader', get_string('gradebook', 'grades'));
 
         $mform->addElement('checkbox', 'reset_gradebook_items', get_string('removeallcourseitems', 'grades'));
+        $mform->addHelpButton('reset_gradebook_items', 'removeallcourseitems', 'grades');
         $mform->addElement('checkbox', 'reset_gradebook_grades', get_string('removeallcoursegrades', 'grades'));
+        $mform->addHelpButton('reset_gradebook_grades', 'removeallcoursegrades', 'grades');
         $mform->disabledIf('reset_gradebook_grades', 'reset_gradebook_items', 'checked');
 
 
diff --git a/course/tests/behat/activities_edit_completion.feature b/course/tests/behat/activities_edit_completion.feature
new file mode 100644 (file)
index 0000000..b1d2b6c
--- /dev/null
@@ -0,0 +1,60 @@
+@core @core_course
+Feature: Edit completion settings of an activity
+  In order to edit completion settings without accidentally breaking user data
+  As a teacher
+  I need to edit the activity and use the unlock button if required
+
+  Background:
+    Given the following "courses" exist:
+      | fullname | shortname | enablecompletion |
+      | Course 1 | C1        | 1                |
+    And I log in as "admin"
+    And I set the following administration settings values:
+      | Enable completion tracking | 1 |
+    And I am on homepage
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I add a "Page" to section "1" and I fill the form with:
+      | Name                | TestPage |
+      | Description         | x        |
+      | Page content        | x        |
+      | Completion tracking | 2        |
+      | Require view        | 1        |
+    And I follow "Course 1"
+
+  Scenario: Completion is not locked when the activity has not yet been viewed
+    Given I click on "Edit settings" "link" in the "TestPage" activity
+    When I expand all fieldsets
+    Then I should see "Completion tracking"
+    And I should not see "Completion options locked"
+
+  Scenario: Completion is locked after the activity has been viewed
+    Given I follow "TestPage"
+    When I follow "Edit settings"
+    And I expand all fieldsets
+    Then I should see "Completion options locked"
+
+  @javascript
+  Scenario: Pressing the unlock button allows the user to edit completion settings
+    Given I follow "TestPage"
+    When I follow "Edit settings"
+    And I expand all fieldsets
+    And I press "Unlock completion options"
+    Then I should see "Completion options unlocked"
+    And I set the field "Completion tracking" to "Students can manually mark the activity as completed"
+    And I press "Save and display"
+    And I follow "Edit settings"
+    And I expand all fieldsets
+    Then the field "Completion tracking" matches value "Students can manually mark the activity as completed"
+
+  @javascript
+  Scenario: Even when completion is locked, the user can still set the date
+    Given I follow "TestPage"
+    And I follow "Edit settings"
+    And I expand all fieldsets
+    When I click on "id_completionexpected_enabled" "checkbox"
+    And I set the field "id_completionexpected_year" to "2013"
+    And I press "Save and display"
+    And I follow "Edit settings"
+    And I expand all fieldsets
+    Then the field "id_completionexpected_year" matches value "2013"
index 77ef9e0..9c38afe 100644 (file)
@@ -233,7 +233,7 @@ class core_course_courselib_testcase extends advanced_testcase {
                 ']}';
         $coursegradeitem = grade_item::fetch_course_item($moduleinfo->course); //the activity will become available only when the user reach some grade into the course itself.
         $moduleinfo->conditiongradegroup = array(array('conditiongradeitemid' => $coursegradeitem->id, 'conditiongrademin' => 10, 'conditiongrademax' => 80));
-        $moduleinfo->conditionfieldgroup = array(array('conditionfield' => 'email', 'conditionfieldoperator' => OP_CONTAINS, 'conditionfieldvalue' => '@'));
+        $moduleinfo->conditionfieldgroup = array(array('conditionfield' => 'email', 'conditionfieldoperator' => \availability_profile\condition::OP_CONTAINS, 'conditionfieldvalue' => '@'));
         $moduleinfo->conditioncompletiongroup = array(array('conditionsourcecmid' => $assigncm->id, 'conditionrequiredcompletion' => COMPLETION_COMPLETE)); // "conditionsourcecmid == 0" => none
 
         // Grading and Advanced grading.
index 3cc2d0c..f406a06 100644 (file)
@@ -4,7 +4,6 @@
 
     require_once('../config.php');
     require_once('lib.php');
-    require_once($CFG->libdir.'/conditionlib.php');
     require_once($CFG->libdir.'/completionlib.php');
 
     $id          = optional_param('id', 0, PARAM_INT);
index 6eebc68..9fb6c3a 100644 (file)
Binary files a/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-debug.js and b/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-debug.js differ
index e258f95..9be9edb 100644 (file)
Binary files a/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-min.js and b/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-min.js differ
index 6eebc68..9fb6c3a 100644 (file)
Binary files a/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes.js and b/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes.js differ
index 83139a3..228cff2 100644 (file)
@@ -423,10 +423,17 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, {
 
         // If activity is conditionally hidden, then don't toggle.
         if (!dimarea.hasClass(CSS.CONDITIONALHIDDEN)) {
-            // Change the UI.
-            dimarea.toggleClass(toggleclass);
-            // We need to toggle dimming on the description too.
-            activity.all(SELECTOR.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);
+            if (action === 'hide') {
+                // Change the UI.
+                dimarea.addClass(toggleclass);
+                // We need to toggle dimming on the description too.
+                activity.all(SELECTOR.CONTENTAFTERLINK).addClass(CSS.DIMMEDTEXT);
+            } else {
+                // Change the UI.
+                dimarea.removeClass(toggleclass);
+                // We need to toggle dimming on the description too.
+                activity.all(SELECTOR.CONTENTAFTERLINK).removeClass(CSS.DIMMEDTEXT);
+            }
         }
         // Toggle availablity info for conditional activities.
         if (availabilityinfo) {
index 49e272e..e878fa6 100644 (file)
@@ -189,6 +189,9 @@ class core_files_renderer extends plugin_renderer_base {
         $strdndnotsupported = get_string('dndnotsupported_insentence', 'moodle').$OUTPUT->help_icon('dndnotsupported');
         $strdndenabledinbox = get_string('dndenabled_inbox', 'moodle');
         $loading = get_string('loading', 'repository');
+        $straddfiletext = get_string('addfiletext', 'repository');
+        $strcreatefolder = get_string('createfolder', 'repository');
+        $strdownloadallfiles = get_string('downloadallfiles', 'repository');
 
         $html = '
 <div id="filemanager-'.$client_id.'" class="filemanager fm-loading">
@@ -200,25 +203,31 @@ class core_files_renderer extends plugin_renderer_base {
         <div class="filemanager-toolbar">
             <div class="fp-toolbar">
                 <div class="fp-btn-add">
-                    <a role="button" title="'.$straddfile.'" href="#"><img src="'.$this->pix_url('a/add_file').'" alt="" /></a>
+                    <a role="button" title="' . $straddfile . '" href="#">
+                        <img src="' . $this->pix_url('a/add_file') . '" alt="' . $straddfiletext . '" />
+                    </a>
                 </div>
                 <div class="fp-btn-mkdir">
-                    <a role="button" title="'.$strmakedir.'" href="#"><img src="'.$this->pix_url('a/create_folder').'" alt="" /></a>
+                    <a role="button" title="' . $strmakedir . '" href="#">
+                        <img src="' . $this->pix_url('a/create_folder') . '" alt="' . $strcreatefolder . '" />
+                    </a>
                 </div>
                 <div class="fp-btn-download">
-                    <a role="button" title="'.$strdownload.'" href="#"><img src="'.$this->pix_url('a/download_all').'" alt="" /></a>
+                    <a role="button" title="' . $strdownload . '" href="#">
+                        <img src="' . $this->pix_url('a/download_all').'" alt="' . $strdownloadallfiles . '" />
+                    </a>
                 </div>
                 <img class="fp-img-downloading" src="'.$this->pix_url('i/loading_small').'" alt="" />
             </div>
             <div class="fp-viewbar">
                 <a title="'. get_string('displayicons', 'repository') .'" class="fp-vb-icons" href="#">
-                    <img alt="" src="'. $this->pix_url('fp/view_icon_active', 'theme') .'" />
+                    <img alt="'. get_string('displayasicons', 'repository') .'" src="'. $this->pix_url('fp/view_icon_active', 'theme') .'" />
                 </a>
                 <a title="'. get_string('displaydetails', 'repository') .'" class="fp-vb-details" href="#">
-                    <img alt="" src="'. $this->pix_url('fp/view_list_active', 'theme') .'" />
+                    <img alt="'. get_string('displayasdetails', 'repository') .'" src="'. $this->pix_url('fp/view_list_active', 'theme') .'" />
                 </a>
                 <a title="'. get_string('displaytree', 'repository') .'" class="fp-vb-tree" href="#">
-                    <img alt="" src="'. $this->pix_url('fp/view_tree_active', 'theme') .'" />
+                    <img alt="'. get_string('displayastree', 'repository') .'" src="'. $this->pix_url('fp/view_tree_active', 'theme') .'" />
                 </a>
             </div>
         </div>
index fe90e41..67b5fd7 100644 (file)
@@ -427,11 +427,6 @@ class grade_edit_tree {
             );
 
             $str .= $checkboxlbl . $checkbox . $hiddenlabel . $input;
-
-            if ($item->aggregationcoef > 0) {
-                $str .= ' ' . html_writer::tag('abbr', get_string('aggregationcoefextrasumabbr', 'grades'),
-                        array('title' => get_string('aggregationcoefextrasum', 'grades')));
-            }
         }
 
         return $str;
@@ -771,6 +766,11 @@ class grade_edit_tree_column_range extends grade_edit_tree_column {
             $grademax = format_float($item->grademax, $item->get_decimals());
         }
 
+        if ($item->aggregationcoef > 0 && $parent_cat->is_extracredit_used()) {
+            $grademax .= ' ' . html_writer::tag('abbr', get_string('aggregationcoefextrasumabbr', 'grades'),
+                array('title' => get_string('aggregationcoefextrasum', 'grades')));
+        }
+
         $itemcell = parent::get_item_cell($item, $params);
         $itemcell->text = $grademax;
         return $itemcell;
index c4d4810..4d3b831 100644 (file)
@@ -57,6 +57,10 @@ if ($mform->is_cancelled()) {
     redirect($returnurl);
 }
 
+// Try to keep the session alive on this page as it may take some time
+// before significant interaction happens with the server.
+\core\session\manager::keepalive();
+
 echo $OUTPUT->header();
 $mform->display();
-echo $OUTPUT->footer();
\ No newline at end of file
+echo $OUTPUT->footer();
index 822a758..f15efe2 100644 (file)
@@ -40,8 +40,8 @@ $target        = optional_param('target', 0, PARAM_ALPHANUM);
 $toggle        = optional_param('toggle', null, PARAM_INT);
 $toggle_type   = optional_param('toggle_type', 0, PARAM_ALPHANUM);
 
-$graderreportsifirst  = optional_param('sifirst', null, PARAM_ALPHA);
-$graderreportsilast   = optional_param('silast', null, PARAM_ALPHA);
+$graderreportsifirst  = optional_param('sifirst', null, PARAM_NOTAGS);
+$graderreportsilast   = optional_param('silast', null, PARAM_NOTAGS);
 
 // The report object is recreated each time, save search information to SESSION object for future use.
 if (isset($graderreportsifirst)) {
index dd5348c..d57083f 100644 (file)
@@ -48,7 +48,8 @@ class finalgrade extends grade_attribute_format implements unique_value, be_disa
         $this->label = $this->grade->grade_item->itemname;
 
         $isoverridden = $this->grade->is_overridden();
-        if (!empty($isoverridden)) {
+        // If the grade is overridden or the grade type is not an activity then use finalgrade.
+        if (!empty($isoverridden) || $this->grade->grade_item->itemtype != 'mod') {
             $val = $this->grade->finalgrade;
         } else {
             $val = $this->grade->rawgrade;
index d33a7e0..7e4197a 100644 (file)
@@ -66,6 +66,13 @@ Feature: We can use Single view
     And the following should exist in the "generaltable" table:
         | Test assignment one |
         | 10.00 |
+    And I set the following fields to these values:
+        | Test grade item | 45 |
+    And I press "Update"
+    Then I should see "Grades were set for 1 items"
+    And I press "Continue"
+    And the field "Grade for Test grade item" matches value "45.00"
+    And the field "Grade for Course total" matches value "55.00"
     And I click on "Show grades for Test assignment three" "link"
     And I click on "Override for james (Student) 1" "checkbox"
     And I set the following fields to these values:
index ee6ec76..33ce5b3 100644 (file)
@@ -48,11 +48,11 @@ Feature: Site settings can be used to hide parts of the gradebook UI
 
   @javascript
   Scenario: Disable category overriding
-    And ".r1 .course input[type='text']" "css_element" should exist
+    And "tr .course input[type='text']" "css_element" should exist
     Then I navigate to "Grade category settings" node in "Site administration > Grades"
     And I click on "Allow category grades to be manually overridden" "checkbox"
     And I press "Save changes"
     And I follow "Home"
     And I follow "Course 1"
     And I follow "Grades"
-    And ".r0 .course input[type='text']" "css_element" should not exist
+    And "tr .course input[type='text']" "css_element" should not exist
similarity index 54%
rename from lib/pluginlib.php
rename to install/lang/mh/langconfig.php
index 4cf3de6..1990612 100644 (file)
@@ -1,4 +1,5 @@
 <?php
+
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Defines classes used for plugins management
+ * Automatically generated strings for Moodle installer
  *
- * This library provides a unified interface to various plugin types in
- * Moodle. It is mainly used by the plugins management admin page and the
- * plugins check page during the upgrade.
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
  *
- * @todo MDL-46122 This file will be removed in 2.9
- * @package    core
- * @copyright  2011 David Mudrak <david@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 defined('MOODLE_INTERNAL') || die();
 
-debugging('lib/pluginlib.php has been deprecated, the classes that used to exist are now autoloaded. Please removed ' .
-          'any calls to include or require this file.', DEBUG_DEVELOPER);
\ No newline at end of file
+$string['thislanguage'] = 'Ebon';
index 7cfb317..8089a7f 100644 (file)
@@ -439,6 +439,7 @@ $string['doclang'] = 'Language for docs';
 $string['docroot'] = 'Moodle Docs document root';
 $string['doctonewwindow'] = 'Open in new window';
 $string['download'] = 'Download';
+$string['durationunits'] = 'duration units';
 $string['edithelpdocs'] = 'Edit help documents';
 $string['editlang'] = '<b>Edit</b>';
 $string['editorbackgroundcolor'] = 'Background colour';
index 0e9438c..1ec79b8 100644 (file)
@@ -570,7 +570,9 @@ $string['recovergradesdefault_help'] = 'By default recover old grades when re-en
 $string['refreshpreview'] = 'Refresh preview';
 $string['regradeanyway'] = 'Regrade anyway';
 $string['removeallcoursegrades'] = 'Delete all grades';
+$string['removeallcoursegrades_help'] = 'If ticked, all grade items which were manually added to the gradebook will be deleted, together with grades and data on overridden, excluded, hidden and locked grades. Only grade items associated with activities will remain.';
 $string['removeallcourseitems'] = 'Delete all items and categories';
+$string['removeallcourseitems_help'] = 'If ticked, all categories and grade items which were manually added to the gradebook will be deleted, together with grades and data on overridden, excluded, hidden and locked grades. Only grade items associated with activities will remain.';
 $string['report'] = 'Report';
 $string['reportdefault'] = 'Report default ({$a})';
 $string['reportplugins'] = 'Report plugins';
index 082fa47..6e7352b 100644 (file)
@@ -27,6 +27,7 @@ $string['activaterep'] = 'Active repositories';
 $string['activerepository'] = 'Available repository plugins';
 $string['add'] = 'Add';
 $string['addfile'] = 'Add...';
+$string['addfiletext'] = 'Add file';
 $string['addplugin'] = 'Add a repository plugin';
 $string['allowexternallinks'] = 'Allow external links';
 $string['areamainfile'] = 'Main file';
@@ -87,10 +88,14 @@ $string['deleterepository'] = 'Delete this repository';
 $string['detailview'] = 'View details';
 $string['dimensions'] = 'Dimensions';
 $string['disabled'] = 'Disabled';
+$string['displayasdetails'] = 'Display as file details';
+$string['displayasicons'] = 'Display as file icons';
+$string['displayastree'] = 'Display as file tree';
 $string['displaydetails'] = 'Display folder with file details';
 $string['displayicons'] = 'Display folder with file icons';
 $string['displaytree'] = 'Display folder as file tree';
 $string['download'] = 'Download';
+$string['downloadallfiles'] = 'Download all files';
 $string['downloadfolder'] = 'Download all';
 $string['downloadsucc'] = 'The file has been downloaded successfully';
 $string['draftareanofiles'] = 'Cannot be downloaded because there is no files attached';
@@ -128,6 +133,7 @@ $string['getfiletimeout'] = 'Get file timeout';
 $string['help'] = 'Help';
 $string['choosealink'] = 'Choose a link...';
 $string['chooselicense'] = 'Choose license';
+$string['createfolder'] = 'Create folder';
 $string['iconview'] = 'View as icons';
 $string['imagesize'] = '{$a->width} x {$a->height} px';
 $string['instance'] = 'instance';
index 0c6aafe..afc13a1 100644 (file)
@@ -3389,8 +3389,13 @@ class admin_setting_configduration extends admin_setting {
 
         $units = self::get_units();
 
+        $inputid = $this->get_id() . 'v';
+
         $return = '<div class="form-duration defaultsnext">';
-        $return .= '<input type="text" size="5" id="'.$this->get_id().'v" name="'.$this->get_full_name().'[v]" value="'.s($data['v']).'" />';
+        $return .= '<input type="text" size="5" id="' . $inputid . '" name="' . $this->get_full_name() .
+            '[v]" value="' . s($data['v']) . '" />';
+        $return .= '<label for="' . $this->get_id() . 'u" class="accesshide">' .
+            get_string('durationunits', 'admin') . '</label>';
         $return .= '<select id="'.$this->get_id().'u" name="'.$this->get_full_name().'[u]">';
         foreach ($units as $val => $text) {
             $selected = '';
@@ -3404,7 +3409,7 @@ class admin_setting_configduration extends admin_setting {
             $return .= '<option value="'.$val.'"'.$selected.'>'.$text.'</option>';
         }
         $return .= '</select></div>';
-        return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
+        return format_admin_setting($this, $this->visiblename, $return, $this->description, $inputid, '', $defaultinfo, $query);
     }
 }
 
@@ -6907,7 +6912,7 @@ function admin_output_new_settings_by_page($node) {
  * @param string $title label element
  * @param string $form form fragment, html code - not highlighted automatically
  * @param string $description
- * @param bool $label link label to id, true by default
+ * @param mixed $label link label to id, true by default or string being the label to connect it to
  * @param string $warning warning text
  * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
  * @param string $query search query to be highlighted
@@ -6920,10 +6925,12 @@ function format_admin_setting($setting, $title='', $form='', $description='', $l
     $fullname = $setting->get_full_name();
 
     // sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate
-    if ($label) {
+    if ($label === true) {
         $labelfor = 'for = "'.$setting->get_id().'"';
-    } else {
+    } else if ($label === false) {
         $labelfor = '';
+    } else {
+        $labelfor = 'for="' . $label . '"';
     }
     $form .= $setting->output_setting_flags();
 
@@ -8431,7 +8438,7 @@ class admin_setting_configcolourpicker extends admin_setting {
             $content .= html_writer::empty_tag('input', array('type'=>'button','id'=>$this->get_id().'_preview', 'value'=>get_string('preview'), 'class'=>'admin_colourpicker_preview'));
         }
         $content .= html_writer::end_tag('div');
-        return format_admin_setting($this, $this->visiblename, $content, $this->description, false, '', $this->get_defaultsetting(), $query);
+        return format_admin_setting($this, $this->visiblename, $content, $this->description, true, '', $this->get_defaultsetting(), $query);
     }
 }
 
diff --git a/lib/classes/filetypes.php b/lib/classes/filetypes.php
new file mode 100644 (file)
index 0000000..13ee04b
--- /dev/null
@@ -0,0 +1,682 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Class to manage the custom filetypes list that is stored in a config variable.
+ *
+ * @package core
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir . '/filelib.php');
+
+/**
+ * Class to manage the custom filetypes list that is stored in a config variable.
+ *
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class core_filetypes {
+    /** @var array Cached MIME types for current request */
+    protected static $cachedtypes;
+
+    /**
+     * Gets default MIME types that are included as standard.
+     *
+     * Note: Use the function get_mimetypes_array to access this data including
+     * any customisations the user might have made.
+     *
+     * @return array Default (pre-installed) MIME type information
+     */
+    protected static function get_default_types() {
+        return array(
+            'xxx' => array('type' => 'document/unknown', 'icon' => 'unknown'),
+            '3gp' => array('type' => 'video/quicktime', 'icon' => 'quicktime', 'groups' => array('video'), 'string' => 'video'),
+            '7z' => array('type' => 'application/x-7z-compressed', 'icon' => 'archive',
+                    'groups' => array('archive'), 'string' => 'archive'),
+            'aac' => array('type' => 'audio/aac', 'icon' => 'audio', 'groups' => array('audio'), 'string' => 'audio'),
+            'accdb' => array('type' => 'application/msaccess', 'icon' => 'base'),
+            'ai' => array('type' => 'application/postscript', 'icon' => 'eps', 'groups' => array('image'), 'string' => 'image'),
+            'aif' => array('type' => 'audio/x-aiff', 'icon' => 'audio', 'groups' => array('audio'), 'string' => 'audio'),
+            'aiff' => array('type' => 'audio/x-aiff', 'icon' => 'audio', 'groups' => array('audio'), 'string' => 'audio'),
+            'aifc' => array('type' => 'audio/x-aiff', 'icon' => 'audio', 'groups' => array('audio'), 'string' => 'audio'),
+            'applescript' => array('type' => 'text/plain', 'icon' => 'text'),
+            'asc' => array('type' => 'text/plain', 'icon' => 'sourcecode'),
+            'asm' => array('type' => 'text/plain', 'icon' => 'sourcecode'),
+            'au' => array('type' => 'audio/au', 'icon' => 'audio', 'groups' => array('audio'), 'string' => 'audio'),
+            'avi' => array('type' => 'video/x-ms-wm', 'icon' => 'avi',
+                    'groups' => array('video', 'web_video'), 'string' => 'video'),
+            'bmp' => array('type' => 'image/bmp', 'icon' => 'bmp', 'groups' => array('image'), 'string' => 'image'),
+            'c' => array('type' => 'text/plain', 'icon' => 'sourcecode'),
+            'cct' => array('type' => 'shockwave/director', 'icon' => 'flash'),
+            'cpp' => array('type' => 'text/plain', 'icon' => 'sourcecode'),
+            'cs' => array('type' => 'application/x-csh', 'icon' => 'sourcecode'),
+            'css' => array('type' => 'text/css', 'icon' => 'text', 'groups' => array('web_file')),
+            'csv' => array('type' => 'text/csv', 'icon' => 'spreadsheet', 'groups' => array('spreadsheet')),
+            'dv' => array('type' => 'video/x-dv', 'icon' => 'quicktime', 'groups' => array('video'), 'string' => 'video'),
+            'dmg' => array('type' => 'application/octet-stream', 'icon' => 'unknown'),
+
+            'doc' => array('type' => 'application/msword', 'icon' => 'document', 'groups' => array('document')),
+            'bdoc' => array('type' => 'application/x-digidoc', 'icon' => 'document', 'groups' => array('archive')),
+            'cdoc' => array('type' => 'application/x-digidoc', 'icon' => 'document', 'groups' => array('archive')),
+            'ddoc' => array('type' => 'application/x-digidoc', 'icon' => 'document', 'groups' => array('archive')),
+            'docx' => array('type' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+                    'icon' => 'document', 'groups' => array('document')),
+            'docm' => array('type' => 'application/vnd.ms-word.document.macroEnabled.12', 'icon' => 'document'),
+            'dotx' => array('type' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+                    'icon' => 'document'),
+            'dotm' => array('type' => 'application/vnd.ms-word.template.macroEnabled.12', 'icon' => 'document'),
+
+            'dcr' => array('type' => 'application/x-director', 'icon' => 'flash'),
+            'dif' => array('type' => 'video/x-dv', 'icon' => 'quicktime', 'groups' => array('video'), 'string' => 'video'),
+            'dir' => array('type' => 'application/x-director', 'icon' => 'flash'),
+            'dxr' => array('type' => 'application/x-director', 'icon' => 'flash'),
+            'eps' => array('type' => 'application/postscript', 'icon' => 'eps'),
+            'epub' => array('type' => 'application/epub+zip', 'icon' => 'epub', 'groups' => array('document')),
+            'fdf' => array('type' => 'application/pdf', 'icon' => 'pdf'),
+            'flv' => array('type' => 'video/x-flv', 'icon' => 'flash',
+                    'groups' => array('video', 'web_video'), 'string' => 'video'),
+            'f4v' => array('type' => 'video/mp4', 'icon' => 'flash', 'groups' => array('video', 'web_video'), 'string' => 'video'),
+
+            'gallery' => array('type' => 'application/x-smarttech-notebook', 'icon' => 'archive'),
+            'galleryitem' => array('type' => 'application/x-smarttech-notebook', 'icon' => 'archive'),
+            'gallerycollection' => array('type' => 'application/x-smarttech-notebook', 'icon' => 'archive'),
+            'gif' => array('type' => 'image/gif', 'icon' => 'gif', 'groups' => array('image', 'web_image'), 'string' => 'image'),
+            'gtar' => array('type' => 'application/x-gtar', 'icon' => 'archive',
+                    'groups' => array('archive'), 'string' => 'archive'),
+            'tgz' => array('type' => 'application/g-zip', 'icon' => 'archive', 'groups' => array('archive'), 'string' => 'archive'),
+            'gz' => array('type' => 'application/g-zip', 'icon' => 'archive', 'groups' => array('archive'), 'string' => 'archive'),
+            'gzip' => array('type' => 'application/g-zip', 'icon' => 'archive',
+                    'groups' => array('archive'), 'string' => 'archive'),
+            'h' => array('type' => 'text/plain', 'icon' => 'sourcecode'),
+            'hpp' => array('type' => 'text/plain', 'icon' => 'sourcecode'),
+            'hqx' => array('type' => 'application/mac-binhex40', 'icon' => 'archive',
+                    'groups' => array('archive'), 'string' => 'archive'),
+            'htc' => array('type' => 'text/x-component', 'icon' => 'markup'),
+            'html' => array('type' => 'text/html', 'icon' => 'html', 'groups' => array('web_file')),
+            'xhtml' => array('type' => 'application/xhtml+xml', 'icon' => 'html', 'groups' => array('web_file')),
+            'htm' => array('type' => 'text/html', 'icon' => 'html', 'groups' => array('web_file')),
+            'ico' => array('type' => 'image/vnd.microsoft.icon', 'icon' => 'image',
+                    'groups' => array('image'), 'string' => 'image'),
+            'ics' => array('type' => 'text/calendar', 'icon' => 'text'),
+            'isf' => array('type' => 'application/inspiration', 'icon' => 'isf'),
+            'ist' => array('type' => 'application/inspiration.template', 'icon' => 'isf'),
+            'java' => array('type' => 'text/plain', 'icon' => 'sourcecode'),
+            'jar' => array('type' => 'application/java-archive', 'icon' => 'archive'),
+            'jcb' => array('type' => 'text/xml', 'icon' => 'markup'),
+            'jcl' => array('type' => 'text/xml', 'icon' => 'markup'),
+            'jcw' => array('type' => 'text/xml', 'icon' => 'markup'),
+            'jmt' => array('type' => 'text/xml', 'icon' => 'markup'),
+            'jmx' => array('type' => 'text/xml', 'icon' => 'markup'),
+            'jnlp' => array('type' => 'application/x-java-jnlp-file', 'icon' => 'markup'),
+            'jpe' => array('type' => 'image/jpeg', 'icon' => 'jpeg', 'groups' => array('image', 'web_image'), 'string' => 'image'),
+            'jpeg' => array('type' => 'image/jpeg', 'icon' => 'jpeg', 'groups' => array('image', 'web_image'), 'string' => 'image'),
+            'jpg' => array('type' => 'image/jpeg', 'icon' => 'jpeg', 'groups' => array('image', 'web_image'), 'string' => 'image'),
+            'jqz' => array('type' => 'text/xml', 'icon' => 'markup'),
+            'js' => array('type' => 'application/x-javascript', 'icon' => 'text', 'groups' => array('web_file')),
+            'latex' => array('type' => 'application/x-latex', 'icon' => 'text'),
+            'm' => array('type' => 'text/plain', 'icon' => 'sourcecode'),
+            'mbz' => array('type' => 'application/vnd.moodle.backup', 'icon' => 'moodle'),
+            'mdb' => array('type' => 'application/x-msaccess', 'icon' => 'base'),
+            'mht' => array('type' => 'message/rfc822', 'icon' => 'archive'),
+            'mhtml' => array('type' => 'message/rfc822', 'icon' => 'archive'),
+            'mov' => array('type' => 'video/quicktime', 'icon' => 'quicktime',
+                    'groups' => array('video', 'web_video'), 'string' => 'video'),
+            'movie' => array('type' => 'video/x-sgi-movie', 'icon' => 'quicktime', 'groups' => array('video'), 'string' => 'video'),
+            'mw' => array('type' => 'application/maple', 'icon' => 'math'),
+            'mws' => array('type' => 'application/maple', 'icon' => 'math'),
+            'm3u' => array('type' => 'audio/x-mpegurl', 'icon' => 'mp3', 'groups' => array('audio'), 'string' => 'audio'),
+            'mp3' => array('type' => 'audio/mp3', 'icon' => 'mp3', 'groups' => array('audio', 'web_audio'), 'string' => 'audio'),
+            'mp4' => array('type' => 'video/mp4', 'icon' => 'mpeg', 'groups' => array('video', 'web_video'), 'string' => 'video'),
+            'm4v' => array('type' => 'video/mp4', 'icon' => 'mpeg', 'groups' => array('video', 'web_video'), 'string' => 'video'),
+            'm4a' => array('type' => 'audio/mp4', 'icon' => 'mp3', 'groups' => array('audio'), 'string' => 'audio'),
+            'mpeg' => array('type' => 'video/mpeg', 'icon' => 'mpeg', 'groups' => array('video', 'web_video'), 'string' => 'video'),
+            'mpe' => array('type' => 'video/mpeg', 'icon' => 'mpeg', 'groups' => array('video', 'web_video'), 'string' => 'video'),
+            'mpg' => array('type' => 'video/mpeg', 'icon' => 'mpeg', 'groups' => array('video', 'web_video'), 'string' => 'video'),
+            'mpr' => array('type' => 'application/vnd.moodle.profiling', 'icon' => 'moodle'),
+
+            'nbk' => array('type' => 'application/x-smarttech-notebook', 'icon' => 'archive'),
+            'notebook' => array('type' => 'application/x-smarttech-notebook', 'icon' => 'archive'),
+
+            'odt' => array('type' => 'application/vnd.oasis.opendocument.text', 'icon' => 'writer', 'groups' => array('document')),
+            'ott' => array('type' => 'application/vnd.oasis.opendocument.text-template',
+                    'icon' => 'writer', 'groups' => array('document')),
+            'oth' => array('type' => 'application/vnd.oasis.opendocument.text-web', 'icon' => 'oth', 'groups' => array('document')),
+            'odm' => array('type' => 'application/vnd.oasis.opendocument.text-master', 'icon' => 'writer'),
+            'odg' => array('type' => 'application/vnd.oasis.opendocument.graphics', 'icon' => 'draw'),
+            'otg' => array('type' => 'application/vnd.oasis.opendocument.graphics-template', 'icon' => 'draw'),
+            'odp' => array('type' => 'application/vnd.oasis.opendocument.presentation', 'icon' => 'impress'),
+            'otp' => array('type' => 'application/vnd.oasis.opendocument.presentation-template', 'icon' => 'impress'),
+            'ods' => array('type' => 'application/vnd.oasis.opendocument.spreadsheet',
+                    'icon' => 'calc', 'groups' => array('spreadsheet')),
+            'ots' => array('type' => 'application/vnd.oasis.opendocument.spreadsheet-template',
+                    'icon' => 'calc', 'groups' => array('spreadsheet')),
+            'odc' => array('type' => 'application/vnd.oasis.opendocument.chart', 'icon' => 'chart'),
+            'odf' => array('type' => 'application/vnd.oasis.opendocument.formula', 'icon' => 'math'),
+            'odb' => array('type' => 'application/vnd.oasis.opendocument.database', 'icon' => 'base'),
+            'odi' => array('type' => 'application/vnd.oasis.opendocument.image', 'icon' => 'draw'),
+            'oga' => array('type' => 'audio/ogg', 'icon' => 'audio', 'groups' => array('audio'), 'string' => 'audio'),
+            'ogg' => array('type' => 'audio/ogg', 'icon' => 'audio', 'groups' => array('audio'), 'string' => 'audio'),
+            'ogv' => array('type' => 'video/ogg', 'icon' => 'video', 'groups' => array('video'), 'string' => 'video'),
+
+            'pct' => array('type' => 'image/pict', 'icon' => 'image', 'groups' => array('image'), 'string' => 'image'),
+            'pdf' => array('type' => 'application/pdf', 'icon' => 'pdf'),
+            'php' => array('type' => 'text/plain', 'icon' => 'sourcecode'),
+            'pic' => array('type' => 'image/pict', 'icon' => 'image', 'groups' => array('image'), 'string' => 'image'),
+            'pict' => array('type' => 'image/pict', 'icon' => 'image', 'groups' => array('image'), 'string' => 'image'),
+            'png' => array('type' => 'image/png', 'icon' => 'png', 'groups' => array('image', 'web_image'), 'string' => 'image'),
+            'pps' => array('type' => 'application/vnd.ms-powerpoint', 'icon' => 'powerpoint', 'groups' => array('presentation')),
+            'ppt' => array('type' => 'application/vnd.ms-powerpoint', 'icon' => 'powerpoint', 'groups' => array('presentation')),
+            'pptx' => array('type' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+                    'icon' => 'powerpoint'),
+            'pptm' => array('type' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'icon' => 'powerpoint'),
+            'potx' => array('type' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
+                    'icon' => 'powerpoint'),
+            'potm' => array('type' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', 'icon' => 'powerpoint'),
+            'ppam' => array('type' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', 'icon' => 'powerpoint'),
+            'ppsx' => array('type' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+                    'icon' => 'powerpoint'),
+            'ppsm' => array('type' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', 'icon' => 'powerpoint'),
+            'ps' => array('type' => 'application/postscript', 'icon' => 'pdf'),
+            'pub' => array('type' => 'application/x-mspublisher', 'icon' => 'publisher', 'groups' => array('presentation')),
+
+            'qt' => array('type' => 'video/quicktime', 'icon' => 'quicktime',
+                    'groups' => array('video', 'web_video'), 'string' => 'video'),
+            'ra' => array('type' => 'audio/x-realaudio-plugin', 'icon' => 'audio',
+                    'groups' => array('audio', 'web_audio'), 'string' => 'audio'),
+            'ram' => array('type' => 'audio/x-pn-realaudio-plugin', 'icon' => 'audio',
+                    'groups' => array('audio'), 'string' => 'audio'),
+            'rar' => array('type' => 'application/x-rar-compressed', 'icon' => 'archive',
+                    'groups' => array('archive'), 'string' => 'archive'),
+            'rhb' => array('type' => 'text/xml', 'icon' => 'markup'),
+            'rm' => array('type' => 'audio/x-pn-realaudio-plugin', 'icon' => 'audio',
+                    'groups' => array('audio'), 'string' => 'audio'),
+            'rmvb' => array('type' => 'application/vnd.rn-realmedia-vbr', 'icon' => 'video',
+                    'groups' => array('video'), 'string' => 'video'),
+            'rtf' => array('type' => 'text/rtf', 'icon' => 'text', 'groups' => array('document')),
+            'rtx' => array('type' => 'text/richtext', 'icon' => 'text'),
+            'rv' => array('type' => 'audio/x-pn-realaudio-plugin', 'icon' => 'audio',
+                    'groups' => array('video'), 'string' => 'video'),
+            'sh' => array('type' => 'application/x-sh', 'icon' => 'sourcecode'),
+            'sit' => array('type' => 'application/x-stuffit', 'icon' => 'archive',
+                    'groups' => array('archive'), 'string' => 'archive'),
+            'smi' => array('type' => 'application/smil', 'icon' => 'text'),
+            'smil' => array('type' => 'application/smil', 'icon' => 'text'),
+            'sqt' => array('type' => 'text/xml', 'icon' => 'markup'),
+            'svg' => array('type' => 'image/svg+xml', 'icon' => 'image',
+                    'groups' => array('image', 'web_image'), 'string' => 'image'),
+            'svgz' => array('type' => 'image/svg+xml', 'icon' => 'image',
+                    'groups' => array('image', 'web_image'), 'string' => 'image'),
+            'swa' => array('type' => 'application/x-director', 'icon' => 'flash'),
+            'swf' => array('type' => 'application/x-shockwave-flash', 'icon' => 'flash', 'groups' => array('video', 'web_video')),
+            'swfl' => array('type' => 'application/x-shockwave-flash', 'icon' => 'flash', 'groups' => array('video', 'web_video')),
+
+            'sxw' => array('type' => 'application/vnd.sun.xml.writer', 'icon' => 'writer'),
+            'stw' => array('type' => 'application/vnd.sun.xml.writer.template', 'icon' => 'writer'),
+            'sxc' => array('type' => 'application/vnd.sun.xml.calc', 'icon' => 'calc'),
+            'stc' => array('type' => 'application/vnd.sun.xml.calc.template', 'icon' => 'calc'),
+            'sxd' => array('type' => 'application/vnd.sun.xml.draw', 'icon' => 'draw'),
+            'std' => array('type' => 'application/vnd.sun.xml.draw.template', 'icon' => 'draw'),
+            'sxi' => array('type' => 'application/vnd.sun.xml.impress', 'icon' => 'impress'),
+            'sti' => array('type' => 'application/vnd.sun.xml.impress.template', 'icon' => 'impress'),
+            'sxg' => array('type' => 'application/vnd.sun.xml.writer.global', 'icon' => 'writer'),
+            'sxm' => array('type' => 'application/vnd.sun.xml.math', 'icon' => 'math'),
+
+            'tar' => array('type' => 'application/x-tar', 'icon' => 'archive', 'groups' => array('archive'), 'string' => 'archive'),
+            'tif' => array('type' => 'image/tiff', 'icon' => 'tiff', 'groups' => array('image'), 'string' => 'image'),
+            'tiff' => array('type' => 'image/tiff', 'icon' => 'tiff', 'groups' => array('image'), 'string' => 'image'),
+            'tex' => array('type' => 'application/x-tex', 'icon' => 'text'),
+            'texi' => array('type' => 'application/x-texinfo', 'icon' => 'text'),
+            'texinfo' => array('type' => 'application/x-texinfo', 'icon' => 'text'),
+            'tsv' => array('type' => 'text/tab-separated-values', 'icon' => 'text'),
+            'txt' => array('type' => 'text/plain', 'icon' => 'text', 'defaulticon' => true),
+            'wav' => array('type' => 'audio/wav', 'icon' => 'wav', 'groups' => array('audio'), 'string' => 'audio'),
+            'webm' => array('type' => 'video/webm', 'icon' => 'video', 'groups' => array('video'), 'string' => 'video'),
+            'wmv' => array('type' => 'video/x-ms-wmv', 'icon' => 'wmv', 'groups' => array('video'), 'string' => 'video'),
+            'asf' => array('type' => 'video/x-ms-asf', 'icon' => 'wmv', 'groups' => array('video'), 'string' => 'video'),
+            'wma' => array('type' => 'audio/x-ms-wma', 'icon' => 'audio', 'groups' => array('audio'), 'string' => 'audio'),
+
+            'xbk' => array('type' => 'application/x-smarttech-notebook', 'icon' => 'archive'),
+            'xdp' => array('type' => 'application/pdf', 'icon' => 'pdf'),
+            'xfd' => array('type' => 'application/pdf', 'icon' => 'pdf'),
+            'xfdf' => array('type' => 'application/pdf', 'icon' => 'pdf'),
+
+            'xls' => array('type' => 'application/vnd.ms-excel', 'icon' => 'spreadsheet', 'groups' => array('spreadsheet')),
+            'xlsx' => array('type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'icon' => 'spreadsheet'),
+            'xlsm' => array('type' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
+                    'icon' => 'spreadsheet', 'groups' => array('spreadsheet')),
+            'xltx' => array('type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+                    'icon' => 'spreadsheet'),
+            'xltm' => array('type' => 'application/vnd.ms-excel.template.macroEnabled.12', 'icon' => 'spreadsheet'),
+            'xlsb' => array('type' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'icon' => 'spreadsheet'),
+            'xlam' => array('type' => 'application/vnd.ms-excel.addin.macroEnabled.12', 'icon' => 'spreadsheet'),
+
+            'xml' => array('type' => 'application/xml', 'icon' => 'markup'),
+            'xsl' => array('type' => 'text/xml', 'icon' => 'markup'),
+
+            'zip' => array('type' => 'application/zip', 'icon' => 'archive', 'groups' => array('archive'), 'string' => 'archive')
+        );
+    }
+
+    /**
+     * Gets all the current types.
+     *
+     * @return array Associative array from extension to array of data about type
+     */
+    public static function &get_types() {
+        // If it was already done in this request, use cache.
+        if (self::$cachedtypes) {
+            return self::$cachedtypes;
+        }
+
+        // Get defaults.
+        $mimetypes = self::get_default_types();
+
+        // If there are no custom types, just return.
+        $custom = self::get_custom_types();
+        if (empty($custom)) {
+            return $mimetypes;
+        }
+
+        // Check value is an array.
+        if (!is_array($custom)) {
+            debugging('Invalid $CFG->customfiletypes (not array)', DEBUG_DEVELOPER);
+            return $mimetypes;
+        }
+
+        foreach ($custom as $customentry) {
+            // Each entry is a stdClass object similar to the array values above.
+            if (empty($customentry->extension)) {
+                debugging('Invalid $CFG->customfiletypes entry (extension field required)',
+                        DEBUG_DEVELOPER);
+                continue;
+            }
+
+            // To delete a standard entry, set 'deleted' to true.
+            if (!empty($customentry->deleted)) {
+                unset($mimetypes[$customentry->extension]);
+                continue;
+            }
+
+            // Check required fields.
+            if (empty($customentry->type) || empty($customentry->icon)) {
+                debugging('Invalid $CFG->customfiletypes entry ' . $customentry->extension .
+                        ' (type and icon fields required)', DEBUG_DEVELOPER);
+                continue;
+            }
+
+            // Build result array.
+            $result = array('type' => $customentry->type, 'icon' => $customentry->icon);
+            if (!empty($customentry->groups)) {
+                if (!is_array($customentry->groups)) {
+                    debugging('Invalid $CFG->customfiletypes entry ' . $customentry->extension .
+                            ' (groups field not array)', DEBUG_DEVELOPER);
+                    continue;
+                }
+                $result['groups'] = $customentry->groups;
+            }
+            if (!empty($customentry->string)) {
+                if (!is_string($customentry->string)) {
+                    debugging('Invalid $CFG->customfiletypes entry ' . $customentry->extension .
+                            ' (string field not string)', DEBUG_DEVELOPER);
+                    continue;
+                }
+                $result['string'] = $customentry->string;
+            }
+            if (!empty($customentry->defaulticon)) {
+                if (!is_bool($customentry->defaulticon)) {
+                    debugging('Invalid $CFG->customfiletypes entry ' . $customentry->extension .
+                            ' (defaulticon field not bool)', DEBUG_DEVELOPER);
+                    continue;
+                }
+                $result['defaulticon'] = $customentry->defaulticon;
+            }
+            if (!empty($customentry->customdescription)) {
+                if (!is_string($customentry->customdescription)) {
+                    debugging('Invalid $CFG->customfiletypes entry ' . $customentry->extension .
+                            ' (customdescription field not string)', DEBUG_DEVELOPER);
+                    continue;
+                }
+                // As the name suggests, this field is used only for custom entries.
+                $result['customdescription'] = $customentry->customdescription;
+            }
+
+            // Track whether it is a custom filetype or a modified existing
+            // filetype.
+            if (array_key_exists($customentry->extension, $mimetypes)) {
+                $result['modified'] = true;
+            } else {
+                $result['custom'] = true;
+            }
+
+            // Add result array to list.
+            $mimetypes[$customentry->extension] = $result;
+        }
+
+        self::$cachedtypes = $mimetypes;
+        return self::$cachedtypes;
+    }
+
+    /**
+     * Gets custom types from config variable, after decoding the JSON if required.
+     *
+     * @return array Array of custom types (empty array if none)
+     */
+    protected static function get_custom_types() {
+        global $CFG;
+        if (!empty($CFG->customfiletypes)) {
+            if (is_array($CFG->customfiletypes)) {
+                // You can define this as an array in config.php...
+                return $CFG->customfiletypes;
+            } else {
+                // Or as a JSON string in the config table.
+                return json_decode($CFG->customfiletypes);
+            }
+        } else {
+            return array();
+        }
+    }
+
+    /**
+     * Sets the custom types into config variable, encoding into JSON.
+     *
+     * @param array $types Array of custom types
+     * @throws coding_exception If the custom types are fixed in config.php.
+     */
+    protected static function set_custom_types(array $types) {
+        global $CFG;
+        // Check the setting hasn't been forced.
+        if (array_key_exists('customfiletypes', $CFG->config_php_settings)) {
+            throw new coding_exception('Cannot set custom filetypes because they ' .
+                    'are defined in config.php');
+        }
+        if (empty($types)) {
+            unset_config('customfiletypes');
+        } else {
+            set_config('customfiletypes', json_encode(array_values($types)));
+        }
+
+        // Clear the cached type list.
+        self::reset_caches();
+    }
+
+    /**
+     * Clears the type cache. This is not needed in normal use as the
+     * set_custom_types function automatically clears the cache. Intended for
+     * use in unit tests.
+     */
+    public static function reset_caches() {
+        self::$cachedtypes = null;
+    }
+
+    /**
+     * Gets the default types that have been deleted. Returns an array containing
+     * the defaults of all those types.
+     *
+     * @return array Array (same format as get_mimetypes_array)
+     */
+    public static function get_deleted_types() {
+        $defaults = self::get_default_types();
+        $deleted = array();
+        foreach (self::get_custom_types() as $customentry) {
+            if (!empty($customentry->deleted)) {
+                $deleted[$customentry->extension] = $defaults[$customentry->extension];
+            }
+        }
+        return $deleted;
+    }
+
+    /**
+     * Adds a new entry to the list of custom filetypes.
+     *
+     * @param string $extension File extension without dot, e.g. 'doc'
+     * @param string $mimetype MIME type e.g. 'application/msword'
+     * @param string $coreicon Core icon to use e.g. 'document'
+     * @param array $groups Array of group strings that this type belongs to
+     * @param string $corestring Custom lang string name in mimetypes.php
+     * @param string $customdescription Custom description (plain text/multilang)
+     * @param bool $defaulticon True if this should be the default icon for the type
+     * @throws coding_exception If the extension already exists, or otherwise invalid
+     */
+    public static function add_type($extension, $mimetype, $coreicon,
+            array $groups = array(), $corestring = '', $customdescription = '',
+            $defaulticon = false) {
+        // Check for blank extensions or incorrectly including the dot.
+        $extension = (string)$extension;
+        if ($extension === '' || $extension[0] === '.') {
+            throw new coding_exception('Invalid extension .' . $extension);
+        }
+
+        // Check extension not already used.
+        $mimetypes = get_mimetypes_array();
+        if (array_key_exists($extension, $mimetypes)) {
+            throw new coding_exception('Extension ' . $extension . ' already exists');
+        }
+
+        // For default icon, check there isn't already something with default icon
+        // set for that MIME type.
+        if ($defaulticon) {
+            foreach ($mimetypes as $type) {
+                if ($type['type'] === $mimetype && !empty($type['defaulticon'])) {
+                    throw new coding_exception('MIME type ' . $mimetype .
+                            ' already has a default icon set');
+                }
+            }
+        }
+
+        // Get existing custom filetype list.
+        $customs = self::get_custom_types();
+
+        // Check if there's a 'deleted' entry for the extension, if so then get
+        // rid of it.
+        foreach ($customs as $key => $custom) {
+            if ($custom->extension === $extension) {
+                unset($customs[$key]);
+            }
+        }
+
+        // Set up config record for new type.
+        $newtype = self::create_config_record($extension, $mimetype, $coreicon, $groups,
+                $corestring, $customdescription, $defaulticon);
+
+        // See if there's a default value with this extension.
+        $needsadding = true;
+        $defaults = self::get_default_types();
+        if (array_key_exists($extension, $defaults)) {
+            // If it has the same values, we don't need to add it.
+            $defaultvalue = $defaults[$extension];
+            $modified = (array)$newtype;
+            unset($modified['extension']);
+            ksort($defaultvalue);
+            ksort($modified);
+            if ($modified === $defaultvalue) {
+                $needsadding = false;
+            }
+        }
+
+        // Add to array and set in config.
+        if ($needsadding) {
+            $customs[] = $newtype;
+        }
+        self::set_custom_types($customs);
+    }
+
+    /**
+     * Updates an entry in the list of filetypes in config.
+     *
+     * @param string $extension File extension without dot, e.g. 'doc'
+     * @param string $newextension New file extension (same if not changing)
+     * @param string $mimetype MIME type e.g. 'application/msword'
+     * @param string $coreicon Core icon to use e.g. 'document'
+     * @param array $groups Array of group strings that this type belongs to
+     * @param string $corestring Custom lang string name in mimetypes.php
+     * @param string $customdescription Custom description (plain text/multilang)
+     * @param bool $defaulticon True if this should be the default icon for the type
+     * @throws coding_exception If the new extension already exists, or otherwise invalid
+     */
+    public static function update_type($extension, $newextension, $mimetype, $coreicon,
+            array $groups = array(), $corestring = '', $customdescription = '',
+            $defaulticon = false) {
+
+        // Extension must exist.
+        $extension = (string)$extension;
+        $mimetypes = get_mimetypes_array();
+        if (!array_key_exists($extension, $mimetypes)) {
+            throw new coding_exception('Extension ' . $extension . ' not found');
+        }
+
+        // If there's a new extension then this must not exist.
+        $newextension = (string)$newextension;
+        if ($newextension !== $extension) {
+            if ($newextension === '' || $newextension[0] === '.') {
+                throw new coding_exception('Invalid extension .' . $newextension);
+            }
+            if (array_key_exists($newextension, $mimetypes)) {
+                throw new coding_exception('Extension ' . $newextension . ' already exists');
+            }
+        }
+
+        // For default icon, check there isn't already something with default icon
+        // set for that MIME type (unless it's this).
+        if ($defaulticon) {
+            foreach ($mimetypes as $ext => $type) {
+                if ($ext !== $extension && $type['type'] === $mimetype &&
+                        !empty($type['defaulticon'])) {
+                    throw new coding_exception('MIME type ' . $mimetype .
+                            ' already has a default icon set');
+                }
+            }
+        }
+
+        // Delete the old extension and then add the new one (may be same). This
+        // will correctly handle cases when a default type is involved.
+        self::delete_type($extension);
+        self::add_type($newextension, $mimetype, $coreicon, $groups, $corestring,
+                $customdescription, $defaulticon);
+    }
+
+    /**
+     * Deletes a file type from the config list (or, for a standard one, marks it
+     * as deleted).
+     *
+     * @param string $extension File extension without dot, e.g. 'doc'
+     * @throws coding_exception If the extension does not exist, or otherwise invalid
+     */
+    public static function delete_type($extension) {
+        // Extension must exist.
+        $mimetypes = get_mimetypes_array();
+        if (!array_key_exists($extension, $mimetypes)) {
+            throw new coding_exception('Extension ' . $extension . ' not found');
+        }
+
+        // Get existing custom filetype list.
+        $customs = self::get_custom_types();
+
+        // Remove any entries for this extension.
+        foreach ($customs as $key => $custom) {
+            if ($custom->extension === $extension && empty($custom->deleted)) {
+                unset($customs[$key]);
+            }
+        }
+
+        // If it was a standard entry (doesn't have 'custom' set) then add a
+        // deleted marker.
+        if (empty($mimetypes[$extension]['custom'])) {
+            $customs[] = (object)array('extension' => $extension, 'deleted' => true);
+        }
+
+        // Save and reset cache.
+        self::set_custom_types($customs);
+    }
+
+    /**
+     * Reverts a file type to the default. May only be called on types that have
+     * default values. This will undelete the type if necessary or set its values.
+     * If the type is already at default values, does nothing.
+     *
+     * @param string $extension File extension without dot, e.g. 'doc'
+     * @return bool True if anything was changed, false if it was already default
+     * @throws coding_exception If the extension is not a default type.
+     */
+    public static function revert_type_to_default($extension) {
+        $extension = (string)$extension;
+
+        // Check it actually is a default type.
+        $defaults = self::get_default_types();
+        if (!array_key_exists($extension, $defaults)) {
+            throw new coding_exception('Extension ' . $extension . ' is not a default type');
+        }
+
+        // Loop through all the custom settings.
+        $changed = false;
+        $customs = self::get_custom_types();
+        foreach ($customs as $key => $customentry) {
+            if ($customentry->extension === $extension) {
+                unset($customs[$key]);
+                $changed = true;
+            }
+        }
+
+        // Save changes if any.
+        if ($changed) {
+            self::set_custom_types($customs);
+        }
+        return $changed;
+    }
+
+    /**
+     * Converts function parameters into a record for storing in the JSON value.
+     *
+     * @param string $extension File extension without dot, e.g. 'doc'
+     * @param string $mimetype MIME type e.g. 'application/msword'
+     * @param string $coreicon Core icon to use e.g. 'document'
+     * @param array $groups Array of group strings that this type belongs to
+     * @param string $corestring Custom lang string name in mimetypes.php
+     * @param string $customdescription Custom description (plain text/multilang)
+     * @param bool $defaulticon True if this should be the default icon for the type
+     * @return stdClass Record matching the parameters
+     */
+    protected static function create_config_record($extension, $mimetype,
+            $coreicon, array $groups, $corestring, $customdescription, $defaulticon) {
+        // Construct new entry.
+        $newentry = (object)array('extension' => (string)$extension, 'type' => (string)$mimetype,
+                'icon' => (string)$coreicon);
+        if ($groups) {
+            if (!is_array($groups)) {
+                throw new coding_exception('Groups must be an array');
+            }
+            foreach ($groups as $group) {
+                if (!is_string($group)) {
+                    throw new coding_exception('Groups must be an array of strings');
+                }
+            }
+            $newentry->groups = $groups;
+        }
+        if ($corestring) {
+            $newentry->string = (string)$corestring;
+        }
+        if ($customdescription) {
+            $newentry->customdescription = (string)$customdescription;
+        }
+        if ($defaulticon) {
+            $newentry->defaulticon = true;
+        }
+        return $newentry;
+    }
+}
index f7a5868..2c6a5c0 100644 (file)
@@ -34,7 +34,7 @@ class theme extends base {
     public function is_uninstall_allowed() {
         global $CFG;
 
-        if ($this->name === 'standard' or $this->name === 'base' or $this->name === 'bootstrapbase') {
+        if ($this->name === 'base' or $this->name === 'bootstrapbase') {
             // All of these are protected for now.
             return false;
         }
index 0d461c2..8dfc9ce 100644 (file)
@@ -847,4 +847,41 @@ class manager {
         \core\session\manager::set_user($user);
         $event->trigger();
     }
+
+    /**
+     * Add a JS session keepalive to the page.
+     *
+     * A JS session keepalive script will be called to update the session modification time every $frequency seconds.
+     *
+     * Upon failure, the specified error message will be shown to the user.
+     *
+     * @param string $identifier The string identifier for the message to show on failure.
+     * @param string $component The string component for the message to show on failure.
+     * @param int $frequency The update frequency in seconds.
+     * @throws coding_exception IF the frequency is longer than the session lifetime.
+     */
+    public static function keepalive($identifier = 'sessionerroruser', $component = 'error', $frequency = null) {
+        global $CFG, $PAGE;
+
+        if ($frequency) {
+            if ($frequency > $CFG->sessiontimeout) {
+                // Sanity check the frequency.
+                throw new \coding_exception('Keepalive frequency is longer than the session lifespan.');
+            }
+        } else {
+            // A frequency of sessiontimeout / 3 allows for one missed request whilst still preserving the session.
+            $frequency = $CFG->sessiontimeout / 3;
+        }
+
+        // Add the session keepalive script to the list of page output requirements.
+        $sessionkeepaliveurl = new \moodle_url('/lib/sessionkeepalive_ajax.php');
+        $PAGE->requires->string_for_js($identifier, $component);
+        $PAGE->requires->yui_module('moodle-core-checknet', 'M.core.checknet.init', array(array(
+            // The JS config takes this is milliseconds rather than seconds.
+            'frequency' => $frequency * 1000,
+            'message' => array($identifier, $component),
+            'uri' => $sessionkeepaliveurl->out(),
+        )));
+    }
+
 }
index b2d0873..4aa1f45 100644 (file)
@@ -79,7 +79,6 @@ class legacy_plugin_cron_task extends scheduled_task {
 
         // Run all cron jobs for each module.
         mtrace("Starting activity modules");
-        get_mailer('buffer');
         if ($mods = $DB->get_records_select("modules", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
             foreach ($mods as $mod) {
                 $libfile = "$CFG->dirroot/mod/$mod->name/lib.php";
@@ -105,7 +104,6 @@ class legacy_plugin_cron_task extends scheduled_task {
                 }
             }
         }
-        get_mailer('close');
         mtrace("Finished activity modules");
 
         mtrace("Starting blocks");
index 81094ba..a2c4dba 100644 (file)
@@ -756,7 +756,7 @@ class core_useragent {
         if ($useragent === false) {
             return false;
         }
-        if (strpos($useragent, 'Linux; U; Android') === false) {
+        if (strpos($useragent, 'Android') === false) {
             return false;
         }
         if (empty($version)) {
index 499cfee..ebd3b84 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Used to be used for tracking conditions that apply before activities are
- * displayed to students ('conditional availability').
- *
- * Now replaced by the availability API. This library is a stub; some functions
- * still work while others throw exceptions. New code should not rely on the
- * classes, functions, or constants defined here.
+ * DO NOT INCLUDE THIS FILE.
  *
  * @package core_availability
  * @copyright 2014 The Open University
 
 defined('MOODLE_INTERNAL') || die();
 
-/**
- * CONDITION_STUDENTVIEW_HIDE - The activity is not displayed to students at all when conditions aren't met.
- */
-define('CONDITION_STUDENTVIEW_HIDE', 0);
-/**
- * CONDITION_STUDENTVIEW_SHOW - The activity is displayed to students as a greyed-out name, with
- * informational text that explains the conditions under which it will be available.
- */
-define('CONDITION_STUDENTVIEW_SHOW', 1);
-
-/**
- * CONDITION_MISSING_NOTHING - The $item variable is expected to contain all completion-related data
- */
-define('CONDITION_MISSING_NOTHING', 0);
-/**
- * CONDITION_MISSING_EXTRATABLE - The $item variable is expected to contain the fields from
- * the relevant table (course_modules or course_sections) but not the _availability data
- */
-define('CONDITION_MISSING_EXTRATABLE', 1);
-/**
- * CONDITION_MISSING_EVERYTHING - The $item variable is expected to contain nothing except the ID
- */
-define('CONDITION_MISSING_EVERYTHING', 2);
-
-/**
- * OP_CONTAINS - comparison operator that determines whether a specified user field contains
- * a provided variable
- */
-define('OP_CONTAINS', 'contains');
-/**
- * OP_DOES_NOT_CONTAIN - comparison operator that determines whether a specified user field does not
- * contain a provided variable
- */
-define('OP_DOES_NOT_CONTAIN', 'doesnotcontain');
-/**
- * OP_IS_EQUAL_TO - comparison operator that determines whether a specified user field is equal to
- * a provided variable
- */
-define('OP_IS_EQUAL_TO', 'isequalto');
-/**
- * OP_STARTS_WITH - comparison operator that determines whether a specified user field starts with
- * a provided variable
- */
-define('OP_STARTS_WITH', 'startswith');
-/**
- * OP_ENDS_WITH - comparison operator that determines whether a specified user field ends with
- * a provided variable
- */
-define('OP_ENDS_WITH', 'endswith');
-/**
- * OP_IS_EMPTY - comparison operator that determines whether a specified user field is empty
- */
-define('OP_IS_EMPTY', 'isempty');
-/**
- * OP_IS_NOT_EMPTY - comparison operator that determines whether a specified user field is not empty
- */
-define('OP_IS_NOT_EMPTY', 'isnotempty');
-
-require_once($CFG->libdir.'/completionlib.php');
-
-/**
- * Core class to handle conditional activities.
- *
- * This class is now deprecated and partially functional. Public functions either
- * work and output deprecated messages or (in the case of the more obscure ones
- * which weren't really for public use, or those which can't be implemented in
- * the new API) throw exceptions.
- *
- * @copyright 2014 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @deprecated Since Moodle 2.7
- */
-class condition_info extends condition_info_base {
-    /**
-     * Constructs with course-module details.
-     *
-     * @global moodle_database $DB
-     * @uses CONDITION_MISSING_NOTHING
-     * @param object $cm Moodle course-module object. Required ->id, ->course
-     *   will save time, using a full cm_info will save more time
-     * @param int $expectingmissing Used to control whether or not a developer
-     *   debugging message (performance warning) will be displayed if some of
-     *   the above data is missing and needs to be retrieved; a
-     *   CONDITION_MISSING_xx constant
-     * @param bool $loaddata If you need a 'write-only' object, set this value
-     *   to false to prevent database access from constructor
-     * @deprecated Since Moodle 2.7
-     */
-    public function __construct($cm, $expectingmissing = CONDITION_MISSING_NOTHING,
-            $loaddata=true) {
-        global $DB;
-        debugging('The condition_info class is deprecated; change to \core_availability\info_module',
-                DEBUG_DEVELOPER);
-
-        // Check ID as otherwise we can't do the other queries.
-        if (empty($cm->id)) {
-            throw new coding_exception('Invalid parameters; item ID not included');
-        }
-
-        // Load cm_info object.
-        if (!($cm instanceof cm_info)) {
-            // Get modinfo.
-            if (empty($cm->course)) {
-                $modinfo = get_fast_modinfo(
-                        $DB->get_field('course_modules', 'course', array('id' => $cm->id), MUST_EXIST));
-            } else {
-                $modinfo = get_fast_modinfo($cm->course);
-            }
-
-            // Get $cm object.
-            $cm = $modinfo->get_cm($cm->id);
-        }
-
-        $this->item = $cm;
-    }
-
-    /**
-     * Adds the extra availability conditions (if any) into the given
-     * course-module (or section) object.
-     *
-     * This function may be called statically (for the editing form) or
-     * dynamically.
-     *
-     * @param object $cm Moodle course-module data object
-     * @deprecated Since Moodle 2.7 (does nothing)
-     */
-    public static function fill_availability_conditions($cm) {
-        debugging('Calls to condition_info::fill_availability_conditions should be removed',
-                DEBUG_DEVELOPER);
-    }
-
-    /**
-     * Gets the course-module object with full necessary data to determine availability.
-     *
-     * @return object Course-module object with full data
-     * @deprecated Since Moodle 2.7
-     */
-    public function get_full_course_module() {
-        debugging('Calls to condition_info::get_full_course_module should be removed',
-                DEBUG_DEVELOPER);
-        return $this->item;
-    }
-
-    /**
-     * Used to update a table (which no longer exists) based on form data
-     * (which is no longer used).
-     *
-     * Should only have been called from core code. Now removed (throws exception).
-     *
-     * @param object $cm Course-module with as much data as necessary, min id
-     * @param object $fromform Data from form
-     * @param bool $wipefirst If true, wipes existing conditions
-     * @deprecated Since Moodle 2.7 (not available)
-     * @throws Always throws a coding_exception
-     */
-    public static function update_cm_from_form($cm, $fromform, $wipefirst=true) {
-        throw new coding_exception('Function no longer available');
-    }
-
-    /**
-     * Used to be used in course/lib.php because we needed to disable the
-     * completion JS if a completion value affects a conditional activity.
-     *
-     * Should only have been called from core code. Now removed (throws exception).
-     *
-     * @global stdClass $CONDITIONLIB_PRIVATE
-     * @param object $course Moodle course object
-     * @param object $item Moodle course-module
-     * @deprecated Since Moodle 2.7 (not available)
-     * @throws Always throws a coding_exception
-     */
-    public static function completion_value_used_as_condition($course, $cm) {
-        throw new coding_exception('Function no longer available');
-    }
-}
-
-
-/**
- * Handles conditional access to sections.
- *
- * This class is now deprecated and partially functional. Public functions either
- * work and output deprecated messages or (in the case of the more obscure ones
- * which weren't really for public use, or those which can't be implemented in
- * the new API) throw exceptions.
- *
- * @copyright 2014 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @deprecated Since Moodle 2.7
- */
-class condition_info_section extends condition_info_base {
-    /**
-     * Constructs with course-module details.
-     *
-     * @global moodle_database $DB
-     * @uses CONDITION_MISSING_NOTHING
-     * @param object $section Moodle section object. Required ->id, ->course
-     *   will save time, using a full section_info will save more time
-     * @param int $expectingmissing Used to control whether or not a developer
-     *   debugging message (performance warning) will be displayed if some of
-     *   the above data is missing and needs to be retrieved; a
-     *   CONDITION_MISSING_xx constant
-     * @param bool $loaddata If you need a 'write-only' object, set this value
-     *   to false to prevent database access from constructor
-     * @deprecated Since Moodle 2.7
-     */
-    public function __construct($section, $expectingmissing = CONDITION_MISSING_NOTHING,
-            $loaddata=true) {
-        global $DB;
-        debugging('The condition_info_section class is deprecated; change to \core_availability\info_section',
-                DEBUG_DEVELOPER);
-
-        // Check ID as otherwise we can't do the other queries.
-        if (empty($section->id)) {
-            throw new coding_exception('Invalid parameters; item ID not included');
-        }
-
-        // Load cm_info object.
-        if (!($section instanceof section_info)) {
-            // Get modinfo.
-            if (empty($section->course)) {
-                $modinfo = get_fast_modinfo(
-                        $DB->get_field('course_sections', 'course', array('id' => $section->id), MUST_EXIST));
-            } else {
-                $modinfo = get_fast_modinfo($section->course);
-            }
-
-            // Get $cm object.
-            foreach ($modinfo->get_section_info_all() as $possible) {
-                if ($possible->id === $section->id) {
-                    $section = $possible;
-                    break;
-                }
-            }
-        }
-
-        $this->item = $section;
-    }
-
-    /**
-     * Adds the extra availability conditions (if any) into the given
-     * course-module (or section) object.
-     *
-     * This function may be called statically (for the editing form) or
-     * dynamically.
-     *
-     * @param object $section Moodle section data object
-     * @deprecated Since Moodle 2.7 (does nothing)
-     */
-    public static function fill_availability_conditions($section) {
-        debugging('Calls to condition_info_section::fill_availability_conditions should be removed',
-                DEBUG_DEVELOPER);
-    }
-
-    /**
-     * Gets the section object with full necessary data to determine availability.
-     *
-     * @return section_info Section object with full data
-     * @deprecated Since Moodle 2.7
-     */
-    public function get_full_section() {
-        debugging('Calls to condition_info_section::get_full_section should be removed',
-                DEBUG_DEVELOPER);
-        return $this->item;
-    }
-
-    /**
-     * Utility function that used to be called by modedit.php; updated a
-     * table (that no longer exists) based on the module form data.
-     *
-     * Should only have been called from core code. Now removed (throws exception).
-     *
-     * @param object $section Section object, must at minimum contain id
-     * @param object $fromform Data from form
-     * @param bool $wipefirst If true, wipes existing conditions
-     * @deprecated Since Moodle 2.7 (not available)
-     * @throws Always throws a coding_exception
-     */
-    public static function update_section_from_form($section, $fromform, $wipefirst=true) {
-        throw new coding_exception('Function no longer available');
-    }
-}
-
-
-/**
- * Base class to handle conditional items (course_modules or sections).
- *
- * This class is now deprecated and partially functional. Public functions either
- * work and output deprecated messages or (in the case of the more obscure ones
- * which weren't really for public use, or those which can't be implemented in
- * the new API) throw exceptions.
- *
- * @copyright 2012 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @deprecated Since Moodle 2.7
- */
-abstract class condition_info_base {
-    /** @var cm_info|section_info Item with availability data */
-    protected $item;
-
-    /**
-     * The operators that provide the relationship
-     * between a field and a value.
-     *
-     * @deprecated Since Moodle 2.7 (not available)
-     * @throws Always throws a coding_exception
-     */
-    public static function get_condition_user_field_operators() {
-        throw new coding_exception('Function no longer available');
-    }
-
-    /**
-     * Returns list of user fields that can be compared.
-     *
-     * If you specify $formatoptions, then format_string will be called on the
-     * custom field names. This is necessary for multilang support to work so
-     * you should include this parameter unless you are going to format the
-     * text later.
-     *
-     * @param array $formatoptions Passed to format_string if provided
-     * @deprecated Since Moodle 2.7 (not available)
-     * @throws Always throws a coding_exception
-     */
-    public static function get_condition_user_fields($formatoptions = null) {
-        throw new coding_exception('Function no longer available');
-    }
-
-    /**
-     * Adds to the database a condition based on completion of another module.
-     *
-     * Should only have been called from core and test code. Now removed
-     * (throws exception).
-     *
-     * @global moodle_database $DB
-     * @param int $cmid ID of other module
-     * @param int $requiredcompletion COMPLETION_xx constant
-     * @deprecated Since Moodle 2.7 (not available)
-     * @throws Always throws a coding_exception
-     */
-    public function add_completion_condition($cmid, $requiredcompletion) {
-        throw new coding_exception('Function no longer available');
-    }
-
-    /**
-     * Adds user fields condition
-     *
-     * Should only have been called from core and test code. Now removed
-     * (throws exception).
-     *
-     * @param mixed $field numeric if it is a user profile field, character
-     *                     if it is a column in the user table
-     * @param int $operator specifies the relationship between field and value
-     * @param char $value the value of the field
-     * @deprecated Since Moodle 2.7 (not available)
-     * @throws Always throws a coding_exception
-     */
-    public function add_user_field_condition($field, $operator, $value) {
-        throw new coding_exception('Function no longer available');
-    }
-
-    /**
-     * Adds to the database a condition based on the value of a grade item.
-     *
-     * Should only have been called from core and test code. Now removed
-     * (throws exception).
-     *
-     * @global moodle_database $DB
-     * @param int $gradeitemid ID of grade item
-     * @param float $min Minimum grade (>=), up to 5 decimal points, or null if none
-     * @param float $max Maximum grade (<), up to 5 decimal points, or null if none
-     * @param bool $updateinmemory If true, updates data in memory; otherwise,
-     *   memory version may be out of date (this has performance consequences,
-     *   so don't do it unless it really needs updating)
-     * @deprecated Since Moodle 2.7 (not available)
-     * @throws Always throws a coding_exception
-     */
-    public function add_grade_condition($gradeitemid, $min, $max, $updateinmemory=false) {
-        throw new coding_exception('Function no longer available');
-    }
-
-    /**
-     * Erases from the database all conditions for this activity.
-     *
-     * Should only have been called from core and test code. Now removed
-     * (throws exception).
-     *
-     * @deprecated Since Moodle 2.7 (not available)
-     * @throws Always throws a coding_exception
-     */
-    public function wipe_conditions() {
-        throw new coding_exception('Function no longer available');
-    }
-
-    /**
-     * Integration point with new API; obtains the new class for this item.
-     *
-     * @return \core_availability\info Availability info for item
-     */
-    protected function get_availability_info() {
-        if ($this->item instanceof section_info) {
-            return new \core_availability\info_section($this->item);
-        } else {
-            return new \core_availability\info_module($this->item);
-        }
-    }
-
-    /**
-     * Obtains a string describing all availability restrictions (even if
-     * they do not apply any more).
-     *
-     * @param course_modinfo|null $modinfo Usually leave as null for default. Specify when
-     *   calling recursively from inside get_fast_modinfo()
-     * @return string Information string (for admin) about all restrictions on
-     *   this item
-     * @deprecated Since Moodle 2.7
-     */
-    public function get_full_information($modinfo=null) {
-        debugging('condition_info*::get_full_information() is deprecated, replace ' .
-                'with new \core_availability\info_module($cm)->get_full_information()',
-                DEBUG_DEVELOPER);
-        return $this->get_availability_info()->get_full_information($modinfo);
-    }
-
-    /**
-     * Determines whether this particular item is currently available
-     * according to these criteria.
-     *
-     * - This does not include the 'visible' setting (i.e. this might return
-     *   true even if visible is false); visible is handled independently.
-     * - This does not take account of the viewhiddenactivities capability.
-     *   That should apply later.
-     *
-     * @uses COMPLETION_COMPLETE
-     * @uses COMPLETION_COMPLETE_FAIL
-     * @uses COMPLETION_COMPLETE_PASS
-     * @param string $information If the item has availability restrictions,
-     *   a string that describes the conditions will be stored in this variable;
-     *   if this variable is set blank, that means don't display anything
-     * @param bool $grabthelot Performance hint: if true, caches information
-     *   required for all course-modules, to make the front page and similar
-     *   pages work more quickly (works only for current user)
-     * @param int $userid If set, specifies a different user ID to check availability for
-     * @param course_modinfo|null $modinfo Usually leave as null for default. Specify when
-     *   calling recursively from inside get_fast_modinfo()
-     * @return bool True if this item is available to the user, false otherwise
-     * @deprecated Since Moodle 2.7
-     */
-    public function is_available(&$information, $grabthelot=false, $userid=0, $modinfo=null) {
-        debugging('condition_info*::is_available() is deprecated, replace ' .
-                'with new \core_availability\info_module($cm)->is_available()',
-                DEBUG_DEVELOPER);
-        return $this->get_availability_info()->is_available(
-                $information, $grabthelot, $userid, $modinfo);
-    }
-
-    /**
-     * Checks whether availability information should be shown to normal users.
-     *
-     * This information no longer makes sense with the new system because there
-     * are multiple show options. (I doubt anyone much used this function anyhow!)
-     *
-     * @return bool True if information about availability should be shown to
-     *   normal users
-     * @deprecated Since Moodle 2.7
-     */
-    public function show_availability() {
-        debugging('condition_info*::show_availability() is deprecated and there ' .
-                'is no direct replacement (this is no longer a boolean value), ' .
-                'please refactor code',
-                DEBUG_DEVELOPER);
-        return false;
-    }
-
-    /**
-     * Used to be called by grade code to inform the completion system when a
-     * grade was changed.
-     *
-     * This function should not have ever been used outside the grade API, so
-     * it now just throws an exception.
-     *
-     * @param grade_grade $grade
-     * @param bool $deleted
-     * @deprecated Since Moodle 2.7 (not available)
-     * @throws Always throws a coding_exception
-     */
-    public static function inform_grade_changed($grade, $deleted) {
-        throw new coding_exception('Function no longer available');
-    }
-
-    /**
-     * Used to be used for testing.
-     *
-     * @deprecated since 2.6
-     */
-    public static function wipe_session_cache() {
-        debugging('Calls to completion_info::wipe_session_cache should be removed', DEBUG_DEVELOPER);
-    }
-
-    /**
-     * Initialises the global cache
-     *
-     * @deprecated Since Moodle 2.7
-     */
-    public static function init_global_cache() {
-        debugging('Calls to completion_info::init_globa_cache should be removed', DEBUG_DEVELOPER);
-    }
-}
+throw new coding_exception('condition_info,condition_info_section,condition_info_base classes can not be used any more,
+        please use respective classes from \core_availability namespace');
\ No newline at end of file
index 4312ba6..cb8bb98 100644 (file)
@@ -70,6 +70,7 @@ function cron_run() {
         $predbqueries = $DB->perf_get_queries();
         $pretime      = microtime(1);
         try {
+            get_mailer('buffer');
             $task->execute();
             if (isset($predbqueries)) {
                 mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
@@ -89,6 +90,7 @@ function cron_run() {
             mtrace("Scheduled task failed: " . $task->get_name() . "," . $e->getMessage());
             \core\task\manager::scheduled_task_failed($task);
         }
+        get_mailer('close');
         unset($task);
     }
 
@@ -101,6 +103,7 @@ function cron_run() {
         $predbqueries = $DB->perf_get_queries();
         $pretime      = microtime(1);
         try {
+            get_mailer('buffer');
             $task->execute();
             if (isset($predbqueries)) {
                 mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
@@ -120,6 +123,7 @@ function cron_run() {
             mtrace("Adhoc task failed: " . get_class($task) . "," . $e->getMessage());
             \core\task\manager::adhoc_task_failed($task);
         }
+        get_mailer('close');
         unset($task);
     }
 
index 987e5c2..83f548a 100644 (file)
@@ -60,12 +60,9 @@ function add_to_log($courseid, $module, $action, $url='', $info='', $cm=0, $user
  *
  * @deprecated since 2.7 - use new file picker instead
  *
- * @param string $newfilepath
- * @param stdClass $course
- * @param bool $nourl
  */
 function clam_log_upload($newfilepath, $course=null, $nourl=false) {
-    debugging('clam_log_upload() is not supposed to be used any more, use new file picker instead', DEBUG_DEVELOPER);
+    throw new coding_exception('clam_log_upload() can not be used any more, please use file picker instead');
 }
 
 /**
@@ -73,12 +70,9 @@ function clam_log_upload($newfilepath, $course=null, $nourl=false) {
  *
  * @deprecated since 2.7 - use new file picker instead
  *
- * @param string $oldfilepath
- * @param string $newfilepath
- * @param int $userid The user
  */
 function clam_log_infected($oldfilepath='', $newfilepath='', $userid=0) {
-    debugging('clam_log_infected() is not supposed to be used any more, use new file picker instead', DEBUG_DEVELOPER);
+    throw new coding_exception('clam_log_infected() can not be used any more, please use file picker instead');
 }
 
 /**
@@ -86,12 +80,9 @@ function clam_log_infected($oldfilepath='', $newfilepath='', $userid=0) {
  *
  * @deprecated since 2.7 - use new file picker instead
  *
- * @param string $oldpath
- * @param string $newpath
- * @param boolean $update
  */
 function clam_change_log($oldpath, $newpath, $update=true) {
-    debugging('clam_change_log() is not supposed to be used any more, use new file picker instead', DEBUG_DEVELOPER);
+    throw new coding_exception('clam_change_log() can not be used any more, please use file picker instead');
 }
 
 /**
@@ -99,14 +90,33 @@ function clam_change_log($oldpath, $newpath, $update=true) {
  *
  * @deprecated since 2.7 - infected files are now deleted in file picker
  *
- * @param string $file
- * @return boolean
  */
 function clam_replace_infected_file($file) {
-    debugging('clam_change_log() is not supposed to be used any more', DEBUG_DEVELOPER);
-    return false;
+    throw new coding_exception('clam_replace_infected_file() can not be used any more, please use file picker instead');
+}
+
+/**
+ * Deals with an infected file - either moves it to a quarantinedir
+ * (specified in CFG->quarantinedir) or deletes it.
+ *
+ * If moving it fails, it deletes it.
+ *
+ * @deprecated since 2.7
+ */
+function clam_handle_infected_file($file, $userid=0, $basiconly=false) {
+    throw new coding_exception('clam_handle_infected_file() can not be used any more, please use file picker instead');
 }
 
+/**
+ * If $CFG->runclamonupload is set, we scan a given file. (called from {@link preprocess_files()})
+ *
+ * @deprecated since 2.7
+ */
+function clam_scan_moodle_file(&$file, $course) {
+    throw new coding_exception('clam_scan_moodle_file() can not be used any more, please use file picker instead');
+}
+
+
 /**
  * Checks whether the password compatibility library will work with the current
  * version of PHP. This cannot be done using PHP version numbers since the fix
@@ -3250,16 +3260,10 @@ function get_context_instance($contextlevel, $instance = 0, $strictness = IGNORE
  * Get a context instance as an object, from a given context id.
  *
  * @deprecated since Moodle 2.2 MDL-35009 - please do not use this function any more.
- * @todo MDL-34550 This will be deleted in Moodle 2.8
  * @see context::instance_by_id($id)
- * @param int $id context id
- * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found;
- *                        MUST_EXIST means throw exception if no record or multiple records found
- * @return context|bool the context object or false if not found.
  */
 function get_context_instance_by_id($id, $strictness = IGNORE_MISSING) {
-    debugging('get_context_instance_by_id() is deprecated, please use context::instance_by_id($id) instead.', DEBUG_DEVELOPER);
-    return context::instance_by_id($id, $strictness);
+    throw new coding_exception('get_context_instance_by_id() is now removed, please use context::instance_by_id($id) instead.');
 }
 
 /**
@@ -3891,75 +3895,31 @@ function can_use_html_editor() {
 
 
 /**
- * Returns an object with counts of failed login attempts
- *
- * Returns information about failed login attempts.  If the current user is
- * an admin, then two numbers are returned:  the number of attempts and the
- * number of accounts.  For non-admins, only the attempts on the given user
- * are shown.
+ * Returns an object with counts of failed login attempts.
  *
- * @deprecate since Moodle 2.7, use {@link user_count_login_failures()} instead.
- * @global moodle_database $DB
- * @uses CONTEXT_SYSTEM
- * @param string $mode Either 'admin' or 'everybody'
- * @param string $username The username we are searching for
- * @param string $lastlogin The date from which we are searching
- * @return int
+ * @deprecated since Moodle 2.7, use {@link user_count_login_failures()} instead.
  */
 function count_login_failures($mode, $username, $lastlogin) {
-    global $DB;
-
-    debugging('This method has been deprecated. Please use user_count_login_failures() instead.', DEBUG_DEVELOPER);
-
-    $params = array('mode'=>$mode, 'username'=>$username, 'lastlogin'=>$lastlogin);
-    $select = "module='login' AND action='error' AND time > :lastlogin";
-
-    $count = new stdClass();
-
-    if (is_siteadmin()) {
-        if ($count->attempts = $DB->count_records_select('log', $select, $params)) {
-            $count->accounts = $DB->count_records_select('log', $select, $params, 'COUNT(DISTINCT info)');
-            return $count;
-        }
-    } else if ($mode == 'everybody') {
-        if ($count->attempts = $DB->count_records_select('log', "$select AND info = :username", $params)) {
-            return $count;
-        }
-    }
-    return NULL;
+    throw new coding_exception('count_login_failures() can not be used any more, please use user_count_login_failures().');
 }
 
 /**
- * Returns whether ajax is enabled/allowed or not.
- * This function is deprecated and always returns true.
+ * It should no longer be required to work without JavaScript enabled.
  *
- * @param array $unused - not used any more.
- * @return bool
- * @deprecated since 2.7 MDL-33099 - please do not use this function any more.
- * @todo MDL-44088 This will be removed in Moodle 2.9.
+ * @deprecated since 2.7 MDL-33099/MDL-44088 - please do not use this function any more.
  */
 function ajaxenabled(array $browsers = null) {
-    debugging('ajaxenabled() is deprecated - please update your code to assume it returns true.', DEBUG_DEVELOPER);
-    return true;
+    throw new coding_exception('ajaxenabled() can not be used anymore. Update your code to work with JS at all times.');
 }
 
 /**
- * Determine whether a course module is visible within a course,
- * this is different from instance_is_visible() - faster and visibility for user
+ * Determine whether a course module is visible within a course.
  *
- * @global object
- * @global object
- * @uses DEBUG_DEVELOPER
- * @uses CONTEXT_MODULE
- * @param object $cm object
- * @param int $userid empty means current user
- * @return bool Success
- * @deprecated Since Moodle 2.7
+ * @deprecated Since Moodle 2.7 MDL-44070
  */
 function coursemodule_visible_for_user($cm, $userid=0) {
-    debugging('coursemodule_visible_for_user() deprecated since Moodle 2.7. ' .
-            'Replace with \core_availability\info_module::is_user_visible().');
-    return \core_availability\info_module::is_user_visible($cm, $userid, false);
+    throw new coding_exception('coursemodule_visible_for_user() can not be used any more,
+            please use \core_availability\info_module::is_user_visible()');
 }
 
 /**
index 511c6a8..dc3a025 100644 (file)
Binary files a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js and b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js differ
index 8cf00ac..0c40a56 100644 (file)
Binary files a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js and b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js differ
index 511c6a8..dc3a025 100644 (file)
Binary files a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js and b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js differ
index 3071112..3f0b620 100644 (file)
@@ -212,7 +212,8 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
             tags: 'img',
             tagMatchRequiresAll: false
         });
-        this.editor.delegate('dblclick', this._handleDoubleClick, 'img', this);
+        this.editor.delegate('dblclick', this._displayDialogue, 'img', this);
+        this.editor.delegate('click', this._handleClick, 'img', this);
         this.editor.on('drop', this._handleDragDrop, this);
     },
 
@@ -328,21 +329,22 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
         }
         return false;
 
-    },
+},
 
     /**
-     * Handle a double click on an image.
+     * Handle a click on an image.
      *
-     * @method _handleDoubleClick
+     * @method _handleClick
      * @param {EventFacade} e
      * @private
      */
-    _handleDoubleClick: function(e) {
+    _handleClick: function(e) {
         var image = e.target;
 
         var selection = this.get('host').getSelectionFromNode(image);
-        this.get('host').setSelection(selection);
-        this._displayDialogue();
+        if (this.get('host').getSelection() !== selection) {
+            this.get('host').setSelection(selection);
+        }
     },
 
     /**
index 7553ff6..6e52e04 100644 (file)
@@ -1386,206 +1386,8 @@ function download_file_content($url, $headers=null, $postdata=null, $fullrespons
  *   Unknown types should use the 'xxx' entry which includes defaults.
  */
 function &get_mimetypes_array() {
-    static $mimearray = array (
-        'xxx'  => array ('type'=>'document/unknown', 'icon'=>'unknown'),
-        '3gp'  => array ('type'=>'video/quicktime', 'icon'=>'quicktime', 'groups'=>array('video'), 'string'=>'video'),
-        '7z'  => array ('type'=>'application/x-7z-compressed', 'icon'=>'archive', 'groups'=>array('archive'), 'string'=>'archive'),
-        'aac'  => array ('type'=>'audio/aac', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
-        'accdb'  => array ('type'=>'application/msaccess', 'icon'=>'base'),
-        'ai'   => array ('type'=>'application/postscript', 'icon'=>'eps', 'groups'=>array('image'), 'string'=>'image'),
-        'aif'  => array ('type'=>'audio/x-aiff', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
-        'aiff' => array ('type'=>'audio/x-aiff', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
-        'aifc' => array ('type'=>'audio/x-aiff', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
-        'applescript'  => array ('type'=>'text/plain', 'icon'=>'text'),
-        'asc'  => array ('type'=>'text/plain', 'icon'=>'sourcecode'),
-        'asm'  => array ('type'=>'text/plain', 'icon'=>'sourcecode'),
-        'au'   => array ('type'=>'audio/au', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
-        'avi'  => array ('type'=>'video/x-ms-wm', 'icon'=>'avi', 'groups'=>array('video','web_video'), 'string'=>'video'),
-        'bmp'  => array ('type'=>'image/bmp', 'icon'=>'bmp', 'groups'=>array('image'), 'string'=>'image'),
-        'c'    => array ('type'=>'text/plain', 'icon'=>'sourcecode'),
-        'cct'  => array ('type'=>'shockwave/director', 'icon'=>'flash'),
-        'cpp'  => array ('type'=>'text/plain', 'icon'=>'sourcecode'),
-        'cs'   => array ('type'=>'application/x-csh', 'icon'=>'sourcecode'),
-        'css'  => array ('type'=>'text/css', 'icon'=>'text', 'groups'=>array('web_file')),
-        'csv'  => array ('type'=>'text/csv', 'icon'=>'spreadsheet', 'groups'=>array('spreadsheet')),
-        'dv'   => array ('type'=>'video/x-dv', 'icon'=>'quicktime', 'groups'=>array('video'), 'string'=>'video'),
-        'dmg'  => array ('type'=>'application/octet-stream', 'icon'=>'unknown'),
-
-        'doc'  => array ('type'=>'application/msword', 'icon'=>'document', 'groups'=>array('document')),
-        'bdoc' => array ('type'=>'application/x-digidoc', 'icon'=>'document', 'groups'=>array('archive')),
-        'cdoc' => array ('type'=>'application/x-digidoc', 'icon'=>'document', 'groups'=>array('archive')),
-        'ddoc' => array ('type'=>'application/x-digidoc', 'icon'=>'document', 'groups'=>array('archive')),
-        'docx' => array ('type'=>'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'icon'=>'document', 'groups'=>array('document')),
-        'docm' => array ('type'=>'application/vnd.ms-word.document.macroEnabled.12', 'icon'=>'document'),
-        'dotx' => array ('type'=>'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'icon'=>'document'),
-        'dotm' => array ('type'=>'application/vnd.ms-word.template.macroEnabled.12', 'icon'=>'document'),
-
-        'dcr'  => array ('type'=>'application/x-director', 'icon'=>'flash'),
-        'dif'  => array ('type'=>'video/x-dv', 'icon'=>'quicktime', 'groups'=>array('video'), 'string'=>'video'),
-        'dir'  => array ('type'=>'application/x-director', 'icon'=>'flash'),
-        'dxr'  => array ('type'=>'application/x-director', 'icon'=>'flash'),
-        'eps'  => array ('type'=>'application/postscript', 'icon'=>'eps'),
-        'epub' => array ('type'=>'application/epub+zip', 'icon'=>'epub', 'groups'=>array('document')),
-        'fdf'  => array ('type'=>'application/pdf', 'icon'=>'pdf'),
-        'flv'  => array ('type'=>'video/x-flv', 'icon'=>'flash', 'groups'=>array('video','web_video'), 'string'=>'video'),
-        'f4v'  => array ('type'=>'video/mp4', 'icon'=>'flash', 'groups'=>array('video','web_video'), 'string'=>'video'),
-
-        'gallery'           => array ('type'=>'application/x-smarttech-notebook', 'icon'=>'archive'),
-        'galleryitem'       => array ('type'=>'application/x-smarttech-notebook', 'icon'=>'archive'),
-        'gallerycollection' => array ('type'=>'application/x-smarttech-notebook', 'icon'=>'archive'),
-        'gif'  => array ('type'=>'image/gif', 'icon'=>'gif', 'groups'=>array('image', 'web_image'), 'string'=>'image'),
-        'gtar' => array ('type'=>'application/x-gtar', 'icon'=>'archive', 'groups'=>array('archive'), 'string'=>'archive'),
-        'tgz'  => array ('type'=>'application/g-zip', 'icon'=>'archive', 'groups'=>array('archive'), 'string'=>'archive'),
-        'gz'   => array ('type'=>'application/g-zip', 'icon'=>'archive', 'groups'=>array('archive'), 'string'=>'archive'),
-        'gzip' => array ('type'=>'application/g-zip', 'icon'=>'archive', 'groups'=>array('archive'), 'string'=>'archive'),
-        'h'    => array ('type'=>'text/plain', 'icon'=>'sourcecode'),
-        'hpp'  => array ('type'=>'text/plain', 'icon'=>'sourcecode'),
-        'hqx'  => array ('type'=>'application/mac-binhex40', 'icon'=>'archive', 'groups'=>array('archive'), 'string'=>'archive'),
-        'htc'  => array ('type'=>'text/x-component', 'icon'=>'markup'),
-        'html' => array ('type'=>'text/html', 'icon'=>'html', 'groups'=>array('web_file')),
-        'xhtml'=> array ('type'=>'application/xhtml+xml', 'icon'=>'html', 'groups'=>array('web_file')),
-        'htm'  => array ('type'=>'text/html', 'icon'=>'html', 'groups'=>array('web_file')),
-        'ico'  => array ('type'=>'image/vnd.microsoft.icon', 'icon'=>'image', 'groups'=>array('image'), 'string'=>'image'),
-        'ics'  => array ('type'=>'text/calendar', 'icon'=>'text'),
-        'isf'  => array ('type'=>'application/inspiration', 'icon'=>'isf'),
-        'ist'  => array ('type'=>'application/inspiration.template', 'icon'=>'isf'),
-        'java' => array ('type'=>'text/plain', 'icon'=>'sourcecode'),
-        'jar'  => array ('type'=>'application/java-archive', 'icon' => 'archive'),
-        'jcb'  => array ('type'=>'text/xml', 'icon'=>'markup'),
-        'jcl'  => array ('type'=>'text/xml', 'icon'=>'markup'),
-        'jcw'  => array ('type'=>'text/xml', 'icon'=>'markup'),
-        'jmt'  => array ('type'=>'text/xml', 'icon'=>'markup'),
-        'jmx'  => array ('type'=>'text/xml', 'icon'=>'markup'),
-        'jnlp' => array ('type'=>'application/x-java-jnlp-file', 'icon'=>'markup'),
-        'jpe'  => array ('type'=>'image/jpeg', 'icon'=>'jpeg', 'groups'=>array('image', 'web_image'), 'string'=>'image'),
-        'jpeg' => array ('type'=>'image/jpeg', 'icon'=>'jpeg', 'groups'=>array('image', 'web_image'), 'string'=>'image'),
-        'jpg'  => array ('type'=>'image/jpeg', 'icon'=>'jpeg', 'groups'=>array('image', 'web_image'), 'string'=>'image'),
-        'jqz'  => array ('type'=>'text/xml', 'icon'=>'markup'),
-        'js'   => array ('type'=>'application/x-javascript', 'icon'=>'text', 'groups'=>array('web_file')),
-        'latex'=> array ('type'=>'application/x-latex', 'icon'=>'text'),
-        'm'    => array ('type'=>'text/plain', 'icon'=>'sourcecode'),
-        'mbz'  => array ('type'=>'application/vnd.moodle.backup', 'icon'=>'moodle'),
-        'mdb'  => array ('type'=>'application/x-msaccess', 'icon'=>'base'),
-        'mht'  => array ('type'=>'message/rfc822', 'icon'=>'archive'),
-        'mhtml'=> array ('type'=>'message/rfc822', 'icon'=>'archive'),
-        'mov'  => array ('type'=>'video/quicktime', 'icon'=>'quicktime', 'groups'=>array('video','web_video'), 'string'=>'video'),
-        'movie'=> array ('type'=>'video/x-sgi-movie', 'icon'=>'quicktime', 'groups'=>array('video'), 'string'=>'video'),
-        'mw'   => array ('type'=>'application/maple', 'icon'=>'math'),
-        'mws'  => array ('type'=>'application/maple', 'icon'=>'math'),
-        'm3u'  => array ('type'=>'audio/x-mpegurl', 'icon'=>'mp3', 'groups'=>array('audio'), 'string'=>'audio'),
-        'mp3'  => array ('type'=>'audio/mp3', 'icon'=>'mp3', 'groups'=>array('audio','web_audio'), 'string'=>'audio'),
-        'mp4'  => array ('type'=>'video/mp4', 'icon'=>'mpeg', 'groups'=>array('video','web_video'), 'string'=>'video'),
-        'm4v'  => array ('type'=>'video/mp4', 'icon'=>'mpeg', 'groups'=>array('video','web_video'), 'string'=>'video'),
-        'm4a'  => array ('type'=>'audio/mp4', 'icon'=>'mp3', 'groups'=>array('audio'), 'string'=>'audio'),
-        'mpeg' => array ('type'=>'video/mpeg', 'icon'=>'mpeg', 'groups'=>array('video','web_video'), 'string'=>'video'),
-        'mpe'  => array ('type'=>'video/mpeg', 'icon'=>'mpeg', 'groups'=>array('video','web_video'), 'string'=>'video'),
-        'mpg'  => array ('type'=>'video/mpeg', 'icon'=>'mpeg', 'groups'=>array('video','web_video'), 'string'=>'video'),
-        'mpr'  => array ('type'=>'application/vnd.moodle.profiling', 'icon'=>'moodle'),
-
-        'nbk'       => array ('type'=>'application/x-smarttech-notebook', 'icon'=>'archive'),
-        'notebook'  => array ('type'=>'application/x-smarttech-notebook', 'icon'=>'archive'),
-
-        'odt'  => array ('type'=>'application/vnd.oasis.opendocument.text', 'icon'=>'writer', 'groups'=>array('document')),
-        'ott'  => array ('type'=>'application/vnd.oasis.opendocument.text-template', 'icon'=>'writer', 'groups'=>array('document')),
-        'oth'  => array ('type'=>'application/vnd.oasis.opendocument.text-web', 'icon'=>'oth', 'groups'=>array('document')),
-        'odm'  => array ('type'=>'application/vnd.oasis.opendocument.text-master', 'icon'=>'writer'),
-        'odg'  => array ('type'=>'application/vnd.oasis.opendocument.graphics', 'icon'=>'draw'),
-        'otg'  => array ('type'=>'application/vnd.oasis.opendocument.graphics-template', 'icon'=>'draw'),
-        'odp'  => array ('type'=>'application/vnd.oasis.opendocument.presentation', 'icon'=>'impress'),
-        'otp'  => array ('type'=>'application/vnd.oasis.opendocument.presentation-template', 'icon'=>'impress'),
-        'ods'  => array ('type'=>'application/vnd.oasis.opendocument.spreadsheet', 'icon'=>'calc', 'groups'=>array('spreadsheet')),
-        'ots'  => array ('type'=>'application/vnd.oasis.opendocument.spreadsheet-template', 'icon'=>'calc', 'groups'=>array('spreadsheet')),
-        'odc'  => array ('type'=>'application/vnd.oasis.opendocument.chart', 'icon'=>'chart'),
-        'odf'  => array ('type'=>'application/vnd.oasis.opendocument.formula', 'icon'=>'math'),
-        'odb'  => array ('type'=>'application/vnd.oasis.opendocument.database', 'icon'=>'base'),
-        'odi'  => array ('type'=>'application/vnd.oasis.opendocument.image', 'icon'=>'draw'),
-        'oga'  => array ('type'=>'audio/ogg', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
-        'ogg'  => array ('type'=>'audio/ogg', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
-        'ogv'  => array ('type'=>'video/ogg', 'icon'=>'video', 'groups'=>array('video'), 'string'=>'video'),
-
-        'pct'  => array ('type'=>'image/pict', 'icon'=>'image', 'groups'=>array('image'), 'string'=>'image'),
-        'pdf'  => array ('type'=>'application/pdf', 'icon'=>'pdf'),
-        'php'  => array ('type'=>'text/plain', 'icon'=>'sourcecode'),
-        'pic'  => array ('type'=>'image/pict', 'icon'=>'image', 'groups'=>array('image'), 'string'=>'image'),
-        'pict' => array ('type'=>'image/pict', 'icon'=>'image', 'groups'=>array('image'), 'string'=>'image'),
-        'png'  => array ('type'=>'image/png', 'icon'=>'png', 'groups'=>array('image', 'web_image'), 'string'=>'image'),
-        'pps'  => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'powerpoint', 'groups'=>array('presentation')),
-        'ppt'  => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'powerpoint', 'groups'=>array('presentation')),
-        'pptx' => array ('type'=>'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'icon'=>'powerpoint'),
-        'pptm' => array ('type'=>'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'icon'=>'powerpoint'),
-        'potx' => array ('type'=>'application/vnd.openxmlformats-officedocument.presentationml.template', 'icon'=>'powerpoint'),
-        'potm' => array ('type'=>'application/vnd.ms-powerpoint.template.macroEnabled.12', 'icon'=>'powerpoint'),
-        'ppam' => array ('type'=>'application/vnd.ms-powerpoint.addin.macroEnabled.12', 'icon'=>'powerpoint'),
-        'ppsx' => array ('type'=>'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'icon'=>'powerpoint'),
-        'ppsm' => array ('type'=>'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', 'icon'=>'powerpoint'),
-        'ps'   => array ('type'=>'application/postscript', 'icon'=>'pdf'),
-        'pub'  => array ('type'=>'application/x-mspublisher', 'icon'=>'publisher', 'groups'=>array('presentation')),
-
-        'qt'   => array ('type'=>'video/quicktime', 'icon'=>'quicktime', 'groups'=>array('video','web_video'), 'string'=>'video'),
-        'ra'   => array ('type'=>'audio/x-realaudio-plugin', 'icon'=>'audio', 'groups'=>array('audio','web_audio'), 'string'=>'audio'),
-        'ram'  => array ('type'=>'audio/x-pn-realaudio-plugin', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
-        'rar'  => array ('type'=>'application/x-rar-compressed', 'icon'=>'archive', 'groups'=>array('archive'), 'string'=>'archive'),
-        'rhb'  => array ('type'=>'text/xml', 'icon'=>'markup'),
-        'rm'   => array ('type'=>'audio/x-pn-realaudio-plugin', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
-        'rmvb' => array ('type'=>'application/vnd.rn-realmedia-vbr', 'icon'=>'video', 'groups'=>array('video'), 'string'=>'video'),
-        'rtf'  => array ('type'=>'text/rtf', 'icon'=>'text', 'groups'=>array('document')),
-        'rtx'  => array ('type'=>'text/richtext', 'icon'=>'text'),
-        'rv'   => array ('type'=>'audio/x-pn-realaudio-plugin', 'icon'=>'audio', 'groups'=>array('video'), 'string'=>'video'),
-        'sh'   => array ('type'=>'application/x-sh', 'icon'=>'sourcecode'),
-        'sit'  => array ('type'=>'application/x-stuffit', 'icon'=>'archive', 'groups'=>array('archive'), 'string'=>'archive'),
-        'smi'  => array ('type'=>'application/smil', 'icon'=>'text'),
-        'smil' => array ('type'=>'application/smil', 'icon'=>'text'),
-        'sqt'  => array ('type'=>'text/xml', 'icon'=>'markup'),
-        'svg'  => array ('type'=>'image/svg+xml', 'icon'=>'image', 'groups'=>array('image','web_image'), 'string'=>'image'),
-        'svgz' => array ('type'=>'image/svg+xml', 'icon'=>'image', 'groups'=>array('image','web_image'), 'string'=>'image'),
-        'swa'  => array ('type'=>'application/x-director', 'icon'=>'flash'),
-        'swf'  => array ('type'=>'application/x-shockwave-flash', 'icon'=>'flash', 'groups'=>array('video','web_video')),
-        'swfl' => array ('type'=>'application/x-shockwave-flash', 'icon'=>'flash', 'groups'=>array('video','web_video')),
-
-        'sxw'  => array ('type'=>'application/vnd.sun.xml.writer', 'icon'=>'writer'),
-        'stw'  => array ('type'=>'application/vnd.sun.xml.writer.template', 'icon'=>'writer'),
-        'sxc'  => array ('type'=>'application/vnd.sun.xml.calc', 'icon'=>'calc'),
-        'stc'  => array ('type'=>'application/vnd.sun.xml.calc.template', 'icon'=>'calc'),
-        'sxd'  => array ('type'=>'application/vnd.sun.xml.draw', 'icon'=>'draw'),
-        'std'  => array ('type'=>'application/vnd.sun.xml.draw.template', 'icon'=>'draw'),
-        'sxi'  => array ('type'=>'application/vnd.sun.xml.impress', 'icon'=>'impress'),
-        'sti'  => array ('type'=>'application/vnd.sun.xml.impress.template', 'icon'=>'impress'),
-        'sxg'  => array ('type'=>'application/vnd.sun.xml.writer.global', 'icon'=>'writer'),
-        'sxm'  => array ('type'=>'application/vnd.sun.xml.math', 'icon'=>'math'),
-
-        'tar'  => array ('type'=>'application/x-tar', 'icon'=>'archive', 'groups'=>array('archive'), 'string'=>'archive'),
-        'tif'  => array ('type'=>'image/tiff', 'icon'=>'tiff', 'groups'=>array('image'), 'string'=>'image'),
-        'tiff' => array ('type'=>'image/tiff', 'icon'=>'tiff', 'groups'=>array('image'), 'string'=>'image'),
-        'tex'  => array ('type'=>'application/x-tex', 'icon'=>'text'),
-        'texi' => array ('type'=>'application/x-texinfo', 'icon'=>'text'),
-        'texinfo'  => array ('type'=>'application/x-texinfo', 'icon'=>'text'),
-        'tsv'  => array ('type'=>'text/tab-separated-values', 'icon'=>'text'),
-        'txt'  => array ('type'=>'text/plain', 'icon'=>'text', 'defaulticon'=>true),
-        'wav'  => array ('type'=>'audio/wav', 'icon'=>'wav', 'groups'=>array('audio'), 'string'=>'audio'),
-        'webm'  => array ('type'=>'video/webm', 'icon'=>'video', 'groups'=>array('video'), 'string'=>'video'),
-        'wmv'  => array ('type'=>'video/x-ms-wmv', 'icon'=>'wmv', 'groups'=>array('video'), 'string'=>'video'),
-        'asf'  => array ('type'=>'video/x-ms-asf', 'icon'=>'wmv', 'groups'=>array('video'), 'string'=>'video'),
-        'wma'  => array ('type'=>'audio/x-ms-wma', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
-
-        'xbk'  => array ('type'=>'application/x-smarttech-notebook', 'icon'=>'archive'),
-        'xdp'  => array ('type'=>'application/pdf', 'icon'=>'pdf'),
-        'xfd'  => array ('type'=>'application/pdf', 'icon'=>'pdf'),
-        'xfdf' => array ('type'=>'application/pdf', 'icon'=>'pdf'),
-
-        'xls'  => array ('type'=>'application/vnd.ms-excel', 'icon'=>'spreadsheet', 'groups'=>array('spreadsheet')),
-        'xlsx' => array ('type'=>'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'icon'=>'spreadsheet'),
-        'xlsm' => array ('type'=>'application/vnd.ms-excel.sheet.macroEnabled.12', 'icon'=>'spreadsheet', 'groups'=>array('spreadsheet')),
-        'xltx' => array ('type'=>'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'icon'=>'spreadsheet'),
-        'xltm' => array ('type'=>'application/vnd.ms-excel.template.macroEnabled.12', 'icon'=>'spreadsheet'),
-        'xlsb' => array ('type'=>'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'icon'=>'spreadsheet'),
-        'xlam' => array ('type'=>'application/vnd.ms-excel.addin.macroEnabled.12', 'icon'=>'spreadsheet'),
-
-        'xml'  => array ('type'=>'application/xml', 'icon'=>'markup'),
-        'xsl'  => array ('type'=>'text/xml', 'icon'=>'markup'),
-
-        'zip'  => array ('type'=>'application/zip', 'icon'=>'archive', 'groups'=>array('archive'), 'string'=>'archive')
-    );
-    return $mimearray;
+    // Get types from the core_filetypes function, which includes caching.
+    return core_filetypes::get_types();
 }
 
 /**
@@ -1862,7 +1664,12 @@ function get_mimetype_description($obj, $capitalise=false) {
     // MIME types may include + symbol but this is not permitted in string ids.
     $safemimetype = str_replace('+', '_', $mimetype);
     $safemimetypestr = str_replace('+', '_', $mimetypestr);
-    if (get_string_manager()->string_exists($safemimetype, 'mimetypes')) {
+    $customdescription = mimeinfo('customdescription', $filename);
+    if ($customdescription) {
+        // Call format_string on the custom description so that multilang
+        // filter can be used (if enabled).
+        $result = format_string($customdescription);
+    } else if (get_string_manager()->string_exists($safemimetype, 'mimetypes')) {
         $result = get_string($safemimetype, 'mimetypes', (object)$a);
     } else if (get_string_manager()->string_exists($safemimetypestr, 'mimetypes')) {
         $result = get_string($safemimetypestr, 'mimetypes', (object)$a);
@@ -3166,14 +2973,6 @@ class curl {
      * @return resource The curl handle
      */
     private function apply_opt($curl, $options) {
-        // Some more security first.
-        if (defined('CURLOPT_PROTOCOLS')) {
-            $this->options['CURLOPT_PROTOCOLS'] = (CURLPROTO_HTTP | CURLPROTO_HTTPS);
-        }
-        if (defined('CURLOPT_REDIR_PROTOCOLS')) {
-            $this->options['CURLOPT_REDIR_PROTOCOLS'] = (CURLPROTO_HTTP | CURLPROTO_HTTPS);
-        }
-
         // Clean up
         $this->cleanopt();
         // set cookie
@@ -3235,12 +3034,14 @@ class curl {
             $this->options['CURLOPT_FOLLOWLOCATION'] = 0;
         }
 
+        // Limit the protocols to HTTP and HTTPS.
+        if (defined('CURLOPT_PROTOCOLS')) {
+            $this->options['CURLOPT_PROTOCOLS'] = (CURLPROTO_HTTP | CURLPROTO_HTTPS);
+            $this->options['CURLOPT_REDIR_PROTOCOLS'] = (CURLPROTO_HTTP | CURLPROTO_HTTPS);
+        }
+
         // Set options.
         foreach($this->options as $name => $val) {
-            if ($name === 'CURLOPT_PROTOCOLS' or $name === 'CURLOPT_REDIR_PROTOCOLS') {
-                // These can not be changed, sorry.
-                continue;
-            }
             if ($name === 'CURLOPT_FOLLOWLOCATION' and $this->emulateredirects) {
                 // The redirects are emulated elsewhere.
                 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 0);
index 1291d31..b6f0c17 100644 (file)
@@ -2595,7 +2595,7 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
 
         'fieldset'=>"\n\t\t".'<div id="{id}" class="fitem {advanced}<!-- BEGIN required --> required<!-- END required --> fitem_{type} {emptylabel}"><div class="fitemtitle"><div class="fgrouplabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg} </label>{help}</div></div><fieldset class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</fieldset></div>',
 
-        'static'=>"\n\t\t".'<div class="fitem {advanced} {emptylabel}"><div class="fitemtitle"><div class="fstaticlabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg} </label>{help}</div></div><div class="felement fstatic <!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</div></div>',
+        'static'=>"\n\t\t".'<div class="fitem {advanced} {emptylabel}"><div class="fitemtitle"><div class="fstaticlabel">{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg} {help}</div></div><div class="felement fstatic <!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</div></div>',
 
         'warning'=>"\n\t\t".'<div class="fitem {advanced} {emptylabel}">{element}</div>',
 
index e8a3b9f..2621a3f 100644 (file)
@@ -1065,9 +1065,7 @@ class cm_info implements IteratorAggregate {
         'added' => false,
         'availability' => false,
         'available' => 'get_available',
-        'availablefrom' => 'get_deprecated_available_date',
         'availableinfo' => 'get_available_info',
-        'availableuntil' => 'get_deprecated_available_date',
         'completion' => false,
         'completionexpected' => false,
         'completiongradeitemnumber' => false,
@@ -1095,7 +1093,6 @@ class cm_info implements IteratorAggregate {
         'score' => false,
         'section' => false,
         'sectionnum' => false,
-        'showavailability' => 'get_show_availability',
         'showdescription' => false,
         'uservisible' => 'get_user_visible',
         'visible' => false,
@@ -1185,9 +1182,6 @@ class cm_info implements IteratorAggregate {
 
         // Do not iterate over deprecated properties.
         $props = self::$standardproperties;
-        unset($props['showavailability']);
-        unset($props['availablefrom']);
-        unset($props['availableuntil']);
         unset($props['groupmembersonly']);
 
         foreach ($props as $key => $unused) {
@@ -1798,7 +1792,6 @@ class cm_info implements IteratorAggregate {
         $this->state = self::STATE_BUILDING_DYNAMIC;
 
         if (!empty($CFG->enableavailability)) {
-            require_once($CFG->libdir. '/conditionlib.php');
             // Get availability information.
             $ci = new \core_availability\info_module($this);
 
@@ -1847,35 +1840,6 @@ class cm_info implements IteratorAggregate {
         return $this->available;
     }
 
-    /**
-     * Getter method for property $showavailability. Works by checking the
-     * availableinfo property to see if it's empty or not.
-     *
-     * @return int
-     * @deprecated Since Moodle 2.7
-     */
-    private function get_show_availability() {
-        debugging('$cm->showavailability property has been deprecated. You ' .
-                'can replace it by checking if $cm->availableinfo has content.',
-                DEBUG_DEVELOPER);
-        return ($this->get_available_info() !== '') ? 1 : 0;
-    }
-
-    /**
-     * Getter method for $availablefrom and $availableuntil. Just returns zero
-     * as these are no longer supported.
-     *
-     * @return int Zero
-     * @deprecated Since Moodle 2.7
-     */
-    private function get_deprecated_available_date() {
-        debugging('$cm->availablefrom and $cm->availableuntil have been deprecated. This ' .
-                'information is no longer available as the system provides more complex ' .
-                'options (for example, there might be different dates for different users).',
-                DEBUG_DEVELOPER);
-        return 0;
-    }
-
     /**
      * Getter method for $availablefrom and $availableuntil. Just returns zero
      * as these are no longer supported.
@@ -1906,7 +1870,6 @@ class cm_info implements IteratorAggregate {
      *
      * If the activity is unavailable, additional checks are required to determine if its hidden or greyed out
      *
-     * @see is_user_access_restricted_by_conditional_access()
      * @return void
      */
     private function update_user_visible() {
@@ -1975,33 +1938,13 @@ class cm_info implements IteratorAggregate {
      * Checks whether the module's conditional access settings mean that the
      * user cannot see the activity at all
      *
-     * This is deprecated because it is confusing (name sounds like it's about
-     * access restriction but it is actually about display), is not used
-     * anywhere, and is not necessary. Nobody (outside conditional libraries)
-     * should care what it is that restricted something.
-     *
-     * @return bool True if the user cannot see the module. False if the activity is either available or should be greyed out.
-     * @deprecated since 2.7
+     * @deprecated since 2.7 MDL-44070
      */
     public function is_user_access_restricted_by_conditional_access() {
-        global $CFG;
-        debugging('cm_info::is_user_access_restricted_by_conditional_access() ' .
-                'is deprecated; this function is not needed (use $cm->uservisible ' .
+        throw new coding_exception('cm_info::is_user_access_restricted_by_conditional_access() ' .
+                'can not be used any more; this function is not needed (use $cm->uservisible ' .
                 'and $cm->availableinfo to decide whether it should be available ' .
-                'or appear)', DEBUG_DEVELOPER);
-
-        if (empty($CFG->enableavailability)) {
-            return false;
-        }
-
-        $userid = $this->modinfo->get_user_id();
-        if ($userid == -1) {
-            return null;
-        }
-
-        // Return false if user can access the activity, or if its availability
-        // info is set (= should be displayed even though not accessible).
-        return !$this->get_user_visible() && !$this->get_available_info();
+                'or appear)');
     }
 
     /**
@@ -2698,7 +2641,6 @@ class section_info implements IteratorAggregate {
         $this->_available = true;
         $this->_availableinfo = '';
         if (!empty($CFG->enableavailability)) {
-            require_once($CFG->libdir. '/conditionlib.php');
             // Get availability information.
             $ci = new \core_availability\info_section($this);
             $this->_available = $ci->is_available($this->_availableinfo, true,
@@ -2772,62 +2714,6 @@ class section_info implements IteratorAggregate {
         return $this->_uservisible;
     }
 
-    /**
-     * Getter method for property $showavailability. Works by checking the
-     * availableinfo property to see if it's empty or not.
-     *
-     * @return int
-     * @deprecated Since Moodle 2.7
-     */
-    private function get_showavailability() {
-        debugging('$section->showavailability property has been deprecated. You ' .
-                'can replace it by checking if $section->availableinfo has content.',
-                DEBUG_DEVELOPER);
-        return ($this->get_availableinfo() !== '') ? 1 : 0;
-    }
-
-    /**
-     * Getter method for $availablefrom. Just returns zero as no longer supported.
-     *
-     * @return int Zero
-     * @deprecated Since Moodle 2.7
-     */
-    private function get_availablefrom() {
-        debugging('$section->availablefrom has been deprecated. This ' .
-                'information is no longer available as the system provides more complex ' .
-                'options (for example, there might be different dates for different users).',
-                DEBUG_DEVELOPER);
-        return 0;
-    }
-
-    /**
-     * Getter method for $availablefrom. Just returns zero as no longer supported.
-     *
-     * @return int Zero
-     * @deprecated Since Moodle 2.7
-     */
-    private function get_availableuntil() {
-        debugging('$section->availableuntil has been deprecated. This ' .
-                'information is no longer available as the system provides more complex ' .
-                'options (for example, there might be different dates for different users).',
-                DEBUG_DEVELOPER);
-        return 0;
-    }
-
-    /**
-     * Getter method for $groupingid. Just returns zero as no longer supported.
-     *
-     * @return int Zero
-     * @deprecated Since Moodle 2.7
-     */
-    private function get_groupingid() {
-        debugging('$section->groupingid has been deprecated. This ' .
-                'information is no longer available as the system provides more complex ' .
-                'options (for example, combining multiple groupings).',
-                DEBUG_DEVELOPER);
-        return 0;
-    }
-
     /**
      * Restores the course_sections.sequence value
      *
index 6dced9e..25e51ea 100644 (file)
@@ -8664,7 +8664,8 @@ function getremoteaddr($default='0.0.0.0') {
             } else {
                 // Remove port from IPv4.
                 if (substr_count($address, ":") == 1) {
-                    $address = explode(":", $address)[0];
+                    $parts = explode(":", $address);
+                    $address = $parts[0];
                 }
             }
 
index 4bbdc67..6db3cd2 100644 (file)
@@ -1297,17 +1297,8 @@ class global_navigation extends navigation_node {
         }
 
         // Give the local plugins a chance to include some navigation if they want.
-        foreach (core_component::get_plugin_list_with_file('local', 'lib.php', true) as $plugin => $file) {
-            $function = "local_{$plugin}_extends_navigation";
-            $oldfunction = "{$plugin}_extends_navigation";
-            if (function_exists($function)) {
-                // This is the preferred function name as there is less chance of conflicts
-                $function($this);
-            } else if (function_exists($oldfunction)) {
-                // We continue to support the old function name to ensure backwards compatibility
-                debugging("Deprecated local plugin navigation callback: Please rename '{$oldfunction}' to '{$function}'. Support for the old callback will be dropped after the release of 2.4", DEBUG_DEVELOPER);
-                $oldfunction($this);
-            }
+        foreach (get_plugin_list_with_function('local', 'extends_navigation') as $function) {
+            $function($this);
         }
 
         // Remove any empty root nodes
index 4752d7f..04aeba4 100644 (file)
@@ -1447,9 +1447,6 @@ class html_writer {
      * method. In most cases this is not an issue at all so we do not clone by default for performance
      * and memory consumption reasons.
      *
-     * Please do not use .r0/.r1 for css, as they will be removed in Moodle 2.9.
-     * @todo MDL-43902 , remove r0 and r1 from tr classes.
-     *
      * @param html_table $table data to be rendered
      * @return string HTML code
      */
@@ -1579,7 +1576,6 @@ class html_writer {
         }
 
         if (!empty($table->data)) {
-            $oddeven    = 1;
             $keys       = array_keys($table->data);
             $lastrowkey = end($keys);
             $output .= html_writer::start_tag('tbody', array());
@@ -1601,12 +1597,10 @@ class html_writer {
                         $row = $newrow;
                     }
 
-                    $oddeven = $oddeven ? 0 : 1;
                     if (isset($table->rowclasses[$key])) {
                         $row->attributes['class'] .= ' ' . $table->rowclasses[$key];
                     }
 
-                    $row->attributes['class'] .= ' r' . $oddeven;
                     if ($key == $lastrowkey) {
                         $row->attributes['class'] .= ' lastrow';
                     }
@@ -2077,9 +2071,7 @@ class html_table {
 
     /**
      * @var array Array of classes to add to particular rows, space-separated string.
-     * Classes 'r0' or 'r1' are added automatically for every odd or even row,
-     * respectively. Class 'lastrow' is added automatically for the last row
-     * in the table.
+     * Class 'lastrow' is added automatically for the last row in the table.
      *
      * Example of usage:
      * $t->rowclasses[9] = 'tenth'
index 78e48e5..e67564e 100644 (file)
@@ -624,7 +624,6 @@ class core_renderer extends renderer_base {
             $withlinks = empty($this->page->layout_options['nologinlinks']);
         }
 
-        $loginpage = ((string)$this->page->url === get_login_url());
         $course = $this->page->course;
         if (\core\session\manager::is_loggedinas()) {
             $realuser = \core\session\manager::get_realuser();
@@ -640,6 +639,7 @@ class core_renderer extends renderer_base {
             $realuserinfo = '';
         }
 
+        $loginpage = $this->is_login_page();
         $loginurl = get_login_url();
 
         if (empty($course->id)) {
@@ -717,6 +717,25 @@ class core_renderer extends renderer_base {
         return $loggedinas;
     }
 
+    /**
+     * Check whether the current page is a login page.
+     *
+     * @since Moodle 2.9
+     * @return bool
+     */
+    protected function is_login_page() {
+        // This is a real bit of a hack, but its a rarety that we need to do something like this.
+        // In fact the login pages should be only these two pages and as exposing this as an option for all pages
+        // could lead to abuse (or at least unneedingly complex code) the hack is the way to go.
+        return in_array(
+            $this->page->url->out_as_local_url(false, array()),
+            array(
+                '/login/index.php',
+                '/login/forgot_password.php',
+            )
+        );
+    }
+
     /**
      * Return the 'back' link that normally appears in the footer.
      *
@@ -2939,7 +2958,7 @@ EOD;
             return $returnstr;
         }
 
-        $loginpage = ((string)$this->page->url === get_login_url());
+        $loginpage = $this->is_login_page();
         $loginurl = get_login_url();
         // If not logged in, show the typical not-logged-in string.
         if (!isloggedin()) {
index 1c1928e..d127691 100644 (file)
@@ -205,6 +205,8 @@ class phpunit_util extends testing_util {
         core_text::reset_caches();
         get_message_processors(false, true);
         filter_manager::reset_caches();
+        core_filetypes::reset_caches();
+
         // Reset internal users.
         core_user::reset_internal_users();
 
diff --git a/lib/sessionkeepalive_ajax.php b/lib/sessionkeepalive_ajax.php
new file mode 100644 (file)
index 0000000..7a930ff
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Ensure that session is kept alive.
+ *
+ * @copyright 2014 Andrew Nicols
+ * @package   core
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('AJAX_SCRIPT', true);
+require_once(dirname(__DIR__) . '/config.php');
+
+// Require the session key - want to make sure that this isn't called
+// maliciously to keep a session alive longer than intended.
+if (!confirm_sesskey()) {
+    header('HTTP/1.1 403 Forbidden');
+    print_error('invalidsesskey');
+}
+
+// Update the session.
+\core\session\manager::touch_session(session_id());
index 4267315..2e6960f 100644 (file)
@@ -1016,9 +1016,6 @@ class flexible_table {
 
     /**
      * This function is not part of the public api.
-     *
-     * Please do not use .r0/.r1 for css, as they will be removed in Moodle 2.9.
-     * @todo MDL-43902 , remove r0 and r1 from tr classes.
      */
     function print_row($row, $classname = '') {
         echo $this->get_row_html($row, $classname);
@@ -1034,8 +1031,7 @@ class flexible_table {
      */
     public function get_row_html($row, $classname = '') {
         static $suppress_lastrow = NULL;
-        $oddeven = $this->currentrow % 2;
-        $rowclasses = array('r' . $oddeven);
+        $rowclasses = array();
 
         if ($classname) {
             $rowclasses[] = $classname;
index 035e0f8..d616c77 100644 (file)
@@ -2579,7 +2579,6 @@ class core_accesslib_testcase extends advanced_testcase {
 
         foreach ($DB->get_records('context') as $contextid => $record) {
             $context = context::instance_by_id($contextid);
-            $this->assertEquals($context, get_context_instance_by_id($contextid, IGNORE_MISSING));
             $this->assertEquals($context, get_context_instance($record->contextlevel, $record->instanceid));
             $this->assertEquals($context->get_parent_context_ids(), get_parent_contexts($context));
             if ($context->id == SYSCONTEXTID) {
@@ -2599,8 +2598,6 @@ class core_accesslib_testcase extends advanced_testcase {
         // Make sure a debugging is thrown.
         get_context_instance($record->contextlevel, $record->instanceid);
         $this->assertDebuggingCalled('get_context_instance() is deprecated, please use context_xxxx::instance() instead.', DEBUG_DEVELOPER);
-        get_context_instance_by_id($record->id);
-        $this->assertDebuggingCalled('get_context_instance_by_id() is deprecated, please use context::instance_by_id($id) instead.', DEBUG_DEVELOPER);
         get_system_context();
         $this->assertDebuggingCalled('get_system_context() is deprecated, please use context_system::instance() instead.', DEBUG_DEVELOPER);
         get_parent_contexts($context);
index bed16a9..495a486 100644 (file)
@@ -43,491 +43,248 @@ use Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
 class behat_deprecated extends behat_base {
 
     /**
-     * Click on the specified element inside a table row containing the specified text.
-     *
-     * @deprecated since Moodle 2.7 MDL-42627
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_general::i_click_on_in_the()
-     *
      * @Given /^I click on "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>(?:[^"]|\\")*)" in the "(?P<row_text_string>(?:[^"]|\\")*)" table row$/
-     * @throws ElementNotFoundException
-     * @param string $element Element we look for
-     * @param string $selectortype The type of what we look for
-     * @param string $tablerowtext The table row text
+     * @deprecated since Moodle 2.7 MDL-42627 - please do not use this step any more.
      */
     public function i_click_on_in_the_table_row($element, $selectortype, $tablerowtext) {
-
-        // Throw an exception if deprecated methods are not allowed otherwise allow it's execution.
         $alternative = 'I click on "' . $this->escape($element) . '" "' . $this->escape($selectortype) .
             '" in the "' . $this->escape($tablerowtext) . '" "table_row"';
-        $this->deprecated_message($alternative);
-
-        return new Given($alternative);
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Goes to notification page ensuring site admin navigation is loaded.
-     *
-     * Step [I expand "Site administration" node] will ensure that administration menu
-     * is opened in both javascript and non-javascript modes.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     *
      * @Given /^I go to notifications page$/
-     * @return Given[]
+     * @deprecated since Moodle 2.7 MDL-42731 - please do not use this step any more.
      */
     public function i_go_to_notifications_page() {
         $alternative = array(
             'I expand "' . get_string('administrationsite') . '" node',
             'I click on "' . get_string('notifications') . '" "link" in the "'.get_string('administration').'" "block"'
         );
-        $this->deprecated_message($alternative);
-        return array(
-            new Given($alternative[0]),
-            new Given($alternative[1]),
-        );
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Adds the specified file from the 'Recent files' repository to the specified filepicker of the current page.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_filepicker::i_add_file_from_repository_to_filemanager()
-     *
      * @When /^I add "(?P<filename_string>(?:[^"]|\\")*)" file from recent files to "(?P<filepicker_field_string>(?:[^"]|\\")*)" filepicker$/
-     * @param string $filename
-     * @param string $filepickerelement
+     * @deprecated since Moodle 2.7 MDL-42174 - please do not use this step any more.
      */
     public function i_add_file_from_recent_files_to_filepicker($filename, $filepickerelement) {
         $reponame = get_string('pluginname', 'repository_recent');
         $alternative = 'I add "' . $this->escape($filename) . '" file from "' .
                 $reponame . '" to "' . $this->escape($filepickerelement) . '" filemanager';
-        $this->deprecated_message($alternative);
-        return array(
-            new Given($alternative)
-        );
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Uploads a file to the specified filemanager leaving other fields in upload form default. The paths should be relative to moodle codebase.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_repository_upload::i_upload_file_to_filemanager()
-     *
      * @When /^I upload "(?P<filepath_string>(?:[^"]|\\")*)" file to "(?P<filepicker_field_string>(?:[^"]|\\")*)" filepicker$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $filepath
-     * @param string $filepickerelement
+     * @deprecated since Moodle 2.7 MDL-42174 - please do not use this step any more.
      */
     public function i_upload_file_to_filepicker($filepath, $filepickerelement) {
         $alternative = 'I upload "' . $this->escape($filepath) . '" file to "' .
                 $this->escape($filepickerelement) . '" filemanager';
-        $this->deprecated_message($alternative);
-        return array(
-            new Given($alternative)
-        );
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Creates a folder with specified name in the current folder and in the specified filepicker field.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_filepicker::i_create_folder_in_filemanager()
-     *
      * @Given /^I create "(?P<foldername_string>(?:[^"]|\\")*)" folder in "(?P<filepicker_field_string>(?:[^"]|\\")*)" filepicker$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $foldername
-     * @param string $filepickerelement
+     * @deprecated since Moodle 2.7 MDL-42174 - please do not use this step any more.
      */
     public function i_create_folder_in_filepicker($foldername, $filepickerelement) {
         $alternative = 'I create "' . $this->escape($foldername) .
                 '" folder in "' . $this->escape($filepickerelement) . '" filemanager';
-        $this->deprecated_message($alternative);
-        return array(new Given($alternative));
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Opens the contents of a filepicker folder. It looks for the folder in the current folder and in the path bar.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_filepicker::i_open_folder_from_filemanager()
-     *
      * @Given /^I open "(?P<foldername_string>(?:[^"]|\\")*)" folder from "(?P<filepicker_field_string>(?:[^"]|\\")*)" filepicker$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $foldername
-     * @param string $filepickerelement
+     * @deprecated since Moodle 2.7 MDL-42174 - please do not use this step any more.
      */
     public function i_open_folder_from_filepicker($foldername, $filepickerelement) {
         $alternative = 'I open "' . $this->escape($foldername) . '" folder from "' .
                 $this->escape($filepickerelement) . '" filemanager';
-        $this->deprecated_message($alternative);
-        return array(new Given($alternative));
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Unzips the specified file from the specified filepicker field. The zip file has to be visible in the current folder.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_filepicker::i_unzip_file_from_filemanager()
-     *
      * @Given /^I unzip "(?P<filename_string>(?:[^"]|\\")*)" file from "(?P<filepicker_field_string>(?:[^"]|\\")*)" filepicker$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $filename
-     * @param string $filepickerelement
+     * @deprecated since Moodle 2.7 MDL-42174 - please do not use this step any more.
      */
     public function i_unzip_file_from_filepicker($filename, $filepickerelement) {
         $alternative = 'I unzip "' . $this->escape($filename) . '" file from "' .
                 $this->escape($filepickerelement) . '" filemanager';
-        $this->deprecated_message($alternative);
-        return array(new Given($alternative));
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Zips the specified folder from the specified filepicker field. The folder has to be in the current folder.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_filepicker::i_zip_folder_from_filemanager()
-     *
      * @Given /^I zip "(?P<filename_string>(?:[^"]|\\")*)" folder from "(?P<filepicker_field_string>(?:[^"]|\\")*)" filepicker$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $foldername
-     * @param string $filepickerelement
+     * @deprecated since Moodle 2.7 MDL-42174 - please do not use this step any more.
      */
     public function i_zip_folder_from_filepicker($foldername, $filepickerelement) {
         $alternative = 'I zip "' . $this->escape($foldername) . '" folder from "' .
                 $this->escape($filepickerelement) . '" filemanager';
-        $this->deprecated_message($alternative);
-        return array(new Given($alternative));
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Deletes the specified file or folder from the specified filepicker field.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_filepicker::i_delete_file_from_filemanager()
-     *
      * @Given /^I delete "(?P<file_or_folder_name_string>(?:[^"]|\\")*)" from "(?P<filepicker_field_string>(?:[^"]|\\")*)" filepicker$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $name
-     * @param string $filepickerelement
+     * @deprecated since Moodle 2.7 MDL-42174 - please do not use this step any more.
      */
     public function i_delete_file_from_filepicker($name, $filepickerelement) {
         $alternative = 'I delete "' . $this->escape($name) . '" from "' .
                 $this->escape($filepickerelement) . '" filemanager';
-        $this->deprecated_message($alternative);
-        return array(new Given($alternative));
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Sends a message to the specified user from the logged user.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_message::i_send_message_to_user()
-     *
      * @Given /^I send "(?P<message_contents_string>(?:[^"]|\\")*)" message to "(?P<username_string>(?:[^"]|\\")*)"$/
-     * @throws ElementNotFoundException
-     * @param string $messagecontent
-     * @param string $tousername
+     * @deprecated since Moodle 2.7 MDL-43584 - please do not use this step any more.
      */
     public function i_send_message_to_user($messagecontent, $tousername) {
-
-        global $DB;
-
-        // Runs by CLI, same PHP process that created the user.
-        $touser = $DB->get_record('user', array('username' => $tousername));
-        if (!$touser) {
-            throw new ElementNotFoundException($this->getSession(), '"' . $tousername . '" ');
-        }
-        $tofullname = fullname($touser);
-
-        $alternative = 'I send "' . $this->escape($messagecontent) . '" message to "' . $tofullname . '" user';
-        $this->deprecated_message($alternative);
-        return new Given($alternative);
+        $alternative = 'I send "' . $this->escape($messagecontent) . '" message to "USER_FULL_NAME" user';
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Adds the user to the specified cohort.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_cohort::i_add_user_to_cohort_members()
-     *
      * @Given /^I add "(?P<user_username_string>(?:[^"]|\\")*)" user to "(?P<cohort_idnumber_string>(?:[^"]|\\")*)" cohort$/
-     * @param string $username
-     * @param string $cohortidnumber
+     * @deprecated since Moodle 2.7 MDL-43584 - please do not use this step any more.
      */
     public function i_add_user_to_cohort($username, $cohortidnumber) {
-        global $DB;
-
-        // The user was created by the data generator, executed by the same PHP process that is
-        // running this step, not by any Selenium action.
-        $user = $DB->get_record('user', array('username' => $username));
-        $userlocator = $user->firstname . ' ' . $user->lastname . ' (' . $user->email . ')';
-
-        $alternative = 'I add "' . $this->escape($userlocator) .
-            '" user to "' . $this->escape($cohortidnumber) . '" cohort members';
-        $this->deprecated_message($alternative);
-
-        return new Given($alternative);
+        $alternative = 'I add "USER_FIRST_NAME USER_LAST_NAME (USER_EMAIL)" user to "'
+                . $this->escape($cohortidnumber) . '" cohort members';
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Add the specified user to the group. You should be in the groups page when running this step.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_groups::i_add_user_to_group_members()
-     *
      * @Given /^I add "(?P<username_string>(?:[^"]|\\")*)" user to "(?P<group_name_string>(?:[^"]|\\")*)" group$/
-     * @param string $username
-     * @param string $groupname
+     * @deprecated since Moodle 2.7 MDL-43584 - please do not use this step any more.
      */
     public function i_add_user_to_group($username, $groupname) {
-        global $DB;
-
-        $user = $DB->get_record('user', array('username' => $username));
-        $userfullname = fullname($user);
-
-        $alternative = 'I add "' . $this->escape($userfullname) .
-            '" user to "' . $this->escape($groupname) . '" group members';
-        $this->deprecated_message($alternative);
-
-        return new Given($alternative);
+        $alternative = 'I add "USER_FULL_NAME" user to "' . $this->escape($groupname) . '" group members';
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Fills in form text field with specified id|name|label|value. It works with text-based fields.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_forms::i_set_the_field_to()
-     *
      * @When /^I fill in "(?P<field_string>(?:[^"]|\\")*)" with "(?P<value_string>(?:[^"]|\\")*)"$/
-     * @throws ElementNotFoundException Thrown by behat_base::find
-     * @param string $field
-     * @param string $value
+     * @deprecated since Moodle 2.7 MDL-43738 - please do not use this step any more.
      */
     public function fill_field($field, $value) {
         $alternative = 'I set the field "' . $this->escape($field) . '" to "' . $this->escape($value) . '"';
-        $this->deprecated_message($alternative);
-
-        return new Given($alternative);
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Selects option in select field with specified id|name|label|value.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_forms::i_set_the_field_to()
-     *
      * @When /^I select "(?P<option_string>(?:[^"]|\\")*)" from "(?P<select_string>(?:[^"]|\\")*)"$/
-     * @throws ElementNotFoundException Thrown by behat_base::find
-     * @param string $option
-     * @param string $select
+     * @deprecated since Moodle 2.7 MDL-43738 - please do not use this step any more.
      */
     public function select_option($option, $select) {
         $alternative = 'I set the field "' . $this->escape($select) . '" to "' . $this->escape($option) . '"';
-        $this->deprecated_message($alternative);
-
-        return new Given($alternative);
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Selects the specified id|name|label from the specified radio button.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_forms::i_set_the_field_to()
-     *
      * @When /^I select "(?P<radio_button_string>(?:[^"]|\\")*)" radio button$/
-     * @throws ElementNotFoundException Thrown by behat_base::find
-     * @param string $radio The radio button id, name or label value
+     * @deprecated since Moodle 2.7 MDL-43738 - please do not use this step any more.
      */
     public function select_radio($radio) {
         $alternative = 'I set the field "' . $this->escape($radio) . '" to "1"';
-        $this->deprecated_message($alternative);
-
-        return new Given($alternative);
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Checks checkbox with specified id|name|label|value.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_forms::i_set_the_field_to()
-     *
      * @When /^I check "(?P<option_string>(?:[^"]|\\")*)"$/
-     * @throws ElementNotFoundException Thrown by behat_base::find
-     * @param string $option
+     * @deprecated since Moodle 2.7 MDL-43738 - please do not use this step any more.
      */
     public function check_option($option) {
         $alternative = 'I set the field "' . $this->escape($option) . '" to "1"';
-        $this->deprecated_message($alternative);
-
-        return new Given($alternative);
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Unchecks checkbox with specified id|name|label|value.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_forms::i_set_the_field_to()
-     *
      * @When /^I uncheck "(?P<option_string>(?:[^"]|\\")*)"$/
-     * @throws ElementNotFoundException Thrown by behat_base::find
-     * @param string $option
+     * @deprecated since Moodle 2.7 MDL-43738 - please do not use this step any more.
      */
     public function uncheck_option($option) {
         $alternative = 'I set the field "' . $this->escape($option) . '" to ""';
-        $this->deprecated_message($alternative);
-
-        return new Given($alternative);
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Checks that the field matches the specified value. When using multi-select fields use commas to separate selected options.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_forms::the_field_matches_value()
-     *
      * @Then /^the "(?P<field_string>(?:[^"]|\\")*)" field should match "(?P<value_string>(?:[^"]|\\")*)" value$/
-     * @throws ExpectationException
-     * @throws ElementNotFoundException Thrown by behat_base::find
-     * @param string $locator
-     * @param string $value
+     * @deprecated since Moodle 2.7 MDL-43738 - please do not use this step any more.
      */
     public function the_field_should_match_value($locator, $value) {
         $alternative = 'the field "' . $this->escape($locator) . '" matches value "' . $this->escape($value) . '"';
-        $this->deprecated_message($alternative);
-
-        return new Then($alternative);
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Checks, that checkbox with specified in|name|label|value is checked.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_forms::the_field_matches_value()
-     *
      * @Then /^the "(?P<checkbox_string>(?:[^"]|\\")*)" checkbox should be checked$/
-     * @param string $checkbox
+     * @deprecated since Moodle 2.7 MDL-43738 - please do not use this step any more.
      */
     public function assert_checkbox_checked($checkbox) {
         $alternative = 'the field "' . $this->escape($checkbox) . '" matches value "1"';
-        $this->deprecated_message($alternative);
-
-        return new Then($alternative);
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Checks, that checkbox with specified in|name|label|value is unchecked.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_forms::the_field_matches_value()
-     *
      * @Then /^the "(?P<checkbox_string>(?:[^"]|\\")*)" checkbox should not be checked$/
-     * @param string $checkbox
+     * @deprecated since Moodle 2.7 MDL-43738 - please do not use this step any more.
      */
     public function assert_checkbox_not_checked($checkbox) {
         $alternative = 'the field "' . $this->escape($checkbox) . '" matches value ""';
-        $this->deprecated_message($alternative);
-
-        return new Then($alternative);
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Fills a moodle form with field/value data.
-     *
-     * @deprecated since 2.7
-     * @todo MDL-42862 This will be deleted in Moodle 2.9
-     * @see behat_forms::i_set_the_following_fields_to_these_values()
-     *
      * @Given /^I fill the moodle form with:$/
-     * @throws ElementNotFoundException Thrown by behat_base::find
-     * @param TableNode $data
+     * @deprecated since Moodle 2.7 MDL-43738 - please do not use this step any more.
      */
     public function i_fill_the_moodle_form_with(TableNode $data) {
         $alternative = 'I set the following fields to these values:';
-        $this->deprecated_message($alternative);
-
-        return new Given($alternative, $data);
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Checks the provided element and selector type exists in the current page.
-     *
-     * This step is for advanced users, use it if you don't find anything else suitable for what you need.
-     *
      * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should exists$/
-     * @throws ElementNotFoundException Thrown by behat_base::find
-     * @param string $element The locator of the specified selector
-     * @param string $selectortype The selector type
+     * @deprecated since Moodle 2.7 MDL-43236 - please do not use this step any more.
      */
     public function should_exists($element, $selectortype) {
         $alternative = '"' . $this->escape($element) . '" "' . $this->escape($selectortype) . '" should exist';
-        $this->deprecated_message($alternative);
-        return new Then($alternative);
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Checks that the provided element and selector type not exists in the current page.
-     *
-     * This step is for advanced users, use it if you don't find anything else suitable for what you need.
-     *
      * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should not exists$/
-     * @throws ExpectationException
-     * @param string $element The locator of the specified selector
-     * @param string $selectortype The selector type
+     * @deprecated since Moodle 2.7 MDL-43236 - please do not use this step any more.
      */
     public function should_not_exists($element, $selectortype) {
         $alternative = '"' . $this->escape($element) . '" "' . $this->escape($selectortype) . '" should not exist';
-        $this->deprecated_message($alternative);
-        return new Then($alternative);
+        $this->deprecated_message($alternative, true);
     }
 
     /**
-     * Creates the specified element. More info about available elements in http://docs.moodle.org/dev/Acceptance_testing#Fixtures.
-     *
      * @Given /^the following "(?P<element_string>(?:[^"]|\\")*)" exists:$/
-     *
-     * @throws Exception
-     * @throws PendingException
-     * @param string    $elementname The name of the entity to add
-     * @param TableNode $data
+     * @deprecated since Moodle 2.7 MDL-43236 - please do not use this step any more.
      */
     public function the_following_exists($elementname, TableNode $data) {
         $alternative = 'the following "' . $this->escape($elementname) . '" exist:';
-        $this->deprecated_message($alternative);
-        return new Given($alternative, $data);
+        $this->deprecated_message($alternative, true);
     }
 
+
     /**
      * Throws an exception if $CFG->behat_usedeprecated is not allowed.
      *
      * @throws Exception
      * @param string|array $alternatives Alternative/s to the requested step
+     * @param bool $throwexception If set to true we always throw exception, irrespective of behat_usedeprecated setting.
      * @return void
      */
-    protected function deprecated_message($alternatives) {
+    protected function deprecated_message($alternatives, $throwexception = false) {
         global $CFG;
 
         // We do nothing if it is enabled.
-        if (!empty($CFG->behat_usedeprecated)) {
+        if (!empty($CFG->behat_usedeprecated) && !$throwexception) {
             return;
         }
 
@@ -535,11 +292,23 @@ class behat_deprecated extends behat_base {
             $alternatives = array($alternatives);
         }
 
-        $message = 'Deprecated step, rather than using this step you can:';
+        // Show an appropriate message based on the throwexception flag.
+        if ($throwexception) {
+            $message = 'This step has been removed. Rather than using this step you can:';
+        } else {
+            $message = 'Deprecated step, rather than using this step you can:';
+        }
+
+        // Add all alternatives to the message.
         foreach ($alternatives as $alternative) {
             $message .= PHP_EOL . '- ' . $alternative;
         }
-        $message .= PHP_EOL . '- Set $CFG->behat_usedeprecated in config.php to allow the use of deprecated steps if you don\'t have any other option';
+
+        if (!$throwexception) {
+            $message .= PHP_EOL . '- Set $CFG->behat_usedeprecated in config.php to allow the use of deprecated steps
+                    if you don\'t have any other option';
+        }
+
         throw new Exception($message);
     }
 
index b0a02c5..904e8b0 100644 (file)
@@ -206,6 +206,14 @@ class behat_general extends behat_base {
         $this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
     }
 
+    /**
+     * Dismisses the currently displayed alert dialog. This step does not work in all the browsers, consider it experimental.
+     * @Given /^I dismiss the currently displayed dialog$/
+     */
+    public function dismiss_currently_displayed_alert_dialog() {
+        $this->getSession()->getDriver()->getWebDriverSession()->dismiss_alert();
+    }
+
     /**
      * Clicks link with specified id|title|alt|text.
      *
@@ -337,13 +345,27 @@ class behat_general extends behat_base {
      *
      * @When /^I click on "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" confirming the dialogue$/
      * @throws ElementNotFoundException Thrown by behat_base::find
-     * @param string $link
+     * @param string $element Element we look for
+     * @param string $selectortype The type of what we look for
      */
     public function i_click_on_confirming_the_dialogue($element, $selectortype) {
         $this->i_click_on($element, $selectortype);
         $this->accept_currently_displayed_alert_dialog();
     }
 
+    /**
+     * Clicks the specified element and dismissing the expected dialogue.
+     *
+     * @When /^I click on "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" dismissing the dialogue$/
+     * @throws ElementNotFoundException Thrown by behat_base::find
+     * @param string $element Element we look for
+     * @param string $selectortype The type of what we look for
+     */
+    public function i_click_on_dismissing_the_dialogue($element, $selectortype) {
+        $this->i_click_on($element, $selectortype);
+        $this->dismiss_currently_displayed_alert_dialog();
+    }
+
     /**
      * Click on the element of the specified type which is located inside the second element.
      *
diff --git a/lib/tests/conditionlib_test.php b/lib/tests/conditionlib_test.php
deleted file mode 100644 (file)
index f2b036b..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Tests for deprecated conditional activities classes.
- *
- * @package core_availability
- * @copyright 2014 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-global $CFG;
-require_once($CFG->dirroot . '/lib/conditionlib.php');
-
-
-/**
- * Tests for deprecated conditional activities classes.
- *
- * @package core_availability
- * @copyright 2014 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
- */
-class core_conditionlib_testcase extends advanced_testcase {
-
-    protected function setUp() {
-        global $CFG;
-        parent::setUp();
-
-        $this->resetAfterTest();
-
-        $CFG->enableavailability = 1;
-        $CFG->enablecompletion = 1;
-        $user = $this->getDataGenerator()->create_user();
-        $this->setUser($user);
-    }
-
-    public function test_constructor() {
-        $generator = $this->getDataGenerator();
-        $course = $generator->create_course();
-        $page = $generator->get_plugin_generator('mod_page')->create_instance(
-                array('course' => $course));
-        $modinfo = get_fast_modinfo($course);
-
-        // No ID.
-        try {
-            $test = new condition_info((object)array());
-            $this->fail();
-        } catch (coding_exception $e) {
-            // Do nothing.
-            $this->assertDebuggingCalled();
-        }
-
-        // Get actual cm_info for comparison.
-        $realcm = $modinfo->get_cm($page->cmid);
-
-        // No other data.
-        $test = new condition_info((object)array('id' => $page->cmid));
-        $this->assertDebuggingCalled();
-        $this->assertEquals($realcm, $test->get_full_course_module());
-        $this->assertDebuggingCalled();
-
-        // Course id.
-        $test = new condition_info((object)array('id' => $page->cmid, 'course' => $course->id));
-        $this->assertDebuggingCalled();
-        $this->assertEquals($realcm, $test->get_full_course_module());
-        $this->assertDebuggingCalled();
-
-        // Full cm.
-        $test = new condition_info($realcm);
-        $this->assertDebuggingCalled();
-        $this->assertEquals($realcm, $test->get_full_course_module());
-        $this->assertDebuggingCalled();
-    }
-
-    /**
-     * Same as above test but for course_sections instead of course_modules.
-     */
-    public function test_section_constructor() {
-        $generator = $this->getDataGenerator();
-        $course = $generator->create_course(
-                array('numsections' => 1), array('createsections' => true));
-        $modinfo = get_fast_modinfo($course);
-
-        // No ID.
-        try {
-            $test = new condition_info_section(((object)array()));
-            $this->fail();
-        } catch (coding_exception $e) {
-            // Do nothing.
-            $this->assertDebuggingCalled();
-        }
-
-        // Get actual cm_info for comparison.
-        $realsection = $modinfo->get_section_info(1);
-
-        // No other data.
-        $test = new condition_info_section((object)array('id' => $realsection->id));
-        $this->assertDebuggingCalled();
-        $this->assertEquals($realsection, $test->get_full_section());
-        $this->assertDebuggingCalled();
-
-        // Course id.
-        $test = new condition_info_section((object)array('id' => $realsection->id,
-                'course' => $course->id));
-        $this->assertDebuggingCalled();
-        $this->assertEquals($realsection, $test->get_full_section());
-        $this->assertDebuggingCalled();
-
-        // Full object.
-        $test = new condition_info_section($realsection);
-        $this->assertDebuggingCalled();
-        $this->assertEquals($realsection, $test->get_full_section());
-        $this->assertDebuggingCalled();
-    }
-
-    /**
-     * Tests the is_available function for modules. This does not test all the
-     * conditions and stuff, because it only needs to check that the system
-     * connects through to the real availability API. Also tests
-     * get_full_information function.
-     */
-    public function test_is_available() {
-        // Create course.
-        $generator = $this->getDataGenerator();
-        $course = $generator->create_course();
-
-        // Create activity with no restrictions and one with date restriction.
-        $page1 = $generator->get_plugin_generator('mod_page')->create_instance(
-                array('course' => $course));
-        $time = time() + 100;
-        $avail = '{"op":"|","show":true,"c":[{"type":"date","d":">=","t":' . $time . '}]}';
-        $page2 = $generator->get_plugin_generator('mod_page')->create_instance(
-                array('course' => $course, 'availability' => $avail));
-
-        // No conditions.
-        $ci = new condition_info((object)array('id' => $page1->cmid),
-                CONDITION_MISSING_EVERYTHING);
-        $this->assertDebuggingCalled();
-        $this->assertTrue($ci->is_available($text, false, 0));
-        $this->assertDebuggingCalled();
-        $this->assertEquals('', $text);
-
-        // Date condition.
-        $ci = new condition_info((object)array('id' => $page2->cmid),
-            CONDITION_MISSING_EVERYTHING);
-        $this->assertDebuggingCalled();
-        $this->assertFalse($ci->is_available($text));
-        $this->assertDebuggingCalled();
-        $expectedtime = userdate($time, get_string('strftimedate', 'langconfig'));
-        $this->assertContains($expectedtime, $text);
-
-        // Full information display.
-        $text = $ci->get_full_information();
-        $this->assertDebuggingCalled();
-        $expectedtime = userdate($time, get_string('strftimedate', 'langconfig'));
-        $this->assertContains($expectedtime, $text);
-    }
-
-    /**
-     * Tests the is_available function for sections.
-     */
-    public function test_section_is_available() {
-        global $DB;
-
-        // Create course.
-        $generator = $this->getDataGenerator();
-        $course = $generator->create_course(
-                array('numsections' => 2), array('createsections' => true));
-
-        // Set one of the sections unavailable.
-        $time = time() + 100;
-        $avail = '{"op":"|","show":true,"c":[{"type":"date","d":">=","t":' . $time . '}]}';
-        $DB->set_field('course_sections', 'availability', $avail, array(
-                'course' => $course->id, 'section' => 2));
-
-        $modinfo = get_fast_modinfo($course);
-
-        // No conditions.
-        $ci = new condition_info_section($modinfo->get_section_info(1));
-        $this->assertDebuggingCalled();
-        $this->assertTrue($ci->is_available($text, false, 0));
-        $this->assertDebuggingCalled();
-        $this->assertEquals('', $text);
-
-        // Date condition.
-        $ci = new condition_info_section($modinfo->get_section_info(2));
-        $this->assertDebuggingCalled();
-        $this->assertFalse($ci->is_available($text));
-        $this->assertDebuggingCalled();
-        $expectedtime = userdate($time, get_string('strftimedate', 'langconfig'));
-        $this->assertContains($expectedtime, $text);
-
-        // Full information display.
-        $text = $ci->get_full_information();
-        $this->assertDebuggingCalled();
-        $expectedtime = userdate($time, get_string('strftimedate', 'langconfig'));
-        $this->assertContains($expectedtime, $text);
-    }
-}
index 004dc8b..cab417d 100644 (file)
@@ -504,6 +504,45 @@ class core_filelib_testcase extends advanced_testcase {
         $this->assertSame('OK', $contents);
     }
 
+    public function test_curl_protocols() {
+
+        // HTTP and HTTPS requests were verified in previous requests. Now check
+        // that we can selectively disable some protocols.
+        $curl = new curl();
+
+        // Other protocols than HTTP(S) are disabled by default.
+        $testurl = 'file:///';
+        $curl->get($testurl);
+        $this->assertNotEmpty($curl->error);
+        $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
+
+        $testurl = 'ftp://nowhere';
+        $curl->get($testurl);
+        $this->assertNotEmpty($curl->error);
+        $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
+
+        $testurl = 'telnet://somewhere';
+        $curl->get($testurl);
+        $this->assertNotEmpty($curl->error);
+        $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
+
+        // Protocols are also disabled during redirections.
+        $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php');
+        $curl->get($testurl, array('proto' => 'file'));
+        $this->assertNotEmpty($curl->error);
+        $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
+
+        $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php');
+        $curl->get($testurl, array('proto' => 'ftp'));
+        $this->assertNotEmpty($curl->error);
+        $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
+
+        $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php');
+        $curl->get($testurl, array('proto' => 'telnet'));
+        $this->assertNotEmpty($curl->error);
+        $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
+    }
+
     /**
      * Testing prepare draft area
      *
@@ -785,4 +824,58 @@ EOF;
         // Test it does nothing to the 'plain' data.
         $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected));
     }
+
+    /**
+     * Tests the get_mimetype_description function.
+     */
+    public function test_get_mimetype_description() {
+        $this->resetAfterTest();
+
+        // Test example type (.doc).
+        $this->assertEquals(get_string('application/msword', 'mimetypes'),
+                get_mimetype_description(array('filename' => 'test.doc')));
+
+        // Test an unknown file type.
+        $this->assertEquals(get_string('document/unknown', 'mimetypes'),
+                get_mimetype_description(array('filename' => 'test.frog')));
+
+        // Test a custom filetype with no lang string specified.
+        core_filetypes::add_type('frog', 'application/x-frog', 'document');
+        $this->assertEquals('application/x-frog',
+                get_mimetype_description(array('filename' => 'test.frog')));
+
+        // Test custom description.
+        core_filetypes::update_type('frog', 'frog', 'application/x-frog', 'document',
+                array(), '', 'Froggy file');
+        $this->assertEquals('Froggy file',
+                get_mimetype_description(array('filename' => 'test.frog')));
+
+        // Test custom description using multilang filter.
+        filter_set_global_state('multilang', TEXTFILTER_ON);
+        filter_set_applies_to_strings('multilang', true);
+        core_filetypes::update_type('frog', 'frog', 'application/x-frog', 'document',
+                array(), '', '<span lang="en" class="multilang">Green amphibian</span>' .
+                '<span lang="fr" class="multilang">Amphibian vert</span>');
+        $this->assertEquals('Green amphibian',
+                get_mimetype_description(array('filename' => 'test.frog')));
+    }
+
+    /**
+     * Tests the get_mimetypes_array function.
+     */
+    public function test_get_mimetypes_array() {
+        $mimeinfo = get_mimetypes_array();
+
+        // Test example MIME type (doc).
+        $this->assertEquals('application/msword', $mimeinfo['doc']['type']);
+        $this->assertEquals('document', $mimeinfo['doc']['icon']);
+        $this->assertEquals(array('document'), $mimeinfo['doc']['groups']);
+        $this->assertFalse(isset($mimeinfo['doc']['string']));
+        $this->assertFalse(isset($mimeinfo['doc']['defaulticon']));
+        $this->assertFalse(isset($mimeinfo['doc']['customdescription']));
+
+        // Check the less common fields using other examples.
+        $this->assertEquals('image', $mimeinfo['png']['string']);
+        $this->assertEquals(true, $mimeinfo['txt']['defaulticon']);
+    }
 }
diff --git a/lib/tests/filetypes_test.php b/lib/tests/filetypes_test.php
new file mode 100644 (file)
index 0000000..5115360
--- /dev/null
@@ -0,0 +1,253 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Unit tests for /lib/classes/filetypes.php.
+ *
+ * @package core
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir . '/filelib.php');
+
+/**
+ * Unit tests for /lib/classes/filetypes.php.
+ *
+ * @package core
+ * @copyright 2014 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_filetypes_testcase extends advanced_testcase {
+
+    public function test_add_type() {
+        $this->resetAfterTest();
+
+        // Check the filetypes to be added do not exist yet (basically this
+        // ensures we're testing the cache clear).
+        $types = get_mimetypes_array();
+        $this->assertArrayNotHasKey('frog', $types);
+        $this->assertArrayNotHasKey('zombie', $types);
+
+        // Add two filetypes (minimal, then all options).
+        core_filetypes::add_type('frog', 'application/x-frog', 'document');
+        core_filetypes::add_type('zombie', 'application/x-zombie', 'document',
+            array('document', 'image'), 'image', 'A zombie', true);
+
+        // Check they now exist, and check data.
+        $types = get_mimetypes_array();
+        $this->assertEquals('application/x-frog', $types['frog']['type']);
+        $this->assertEquals('document', $types['frog']['icon']);
+        $this->assertEquals(array('document', 'image'), $types['zombie']['groups']);
+        $this->assertEquals('image', $types['zombie']['string']);
+        $this->assertEquals(true, $types['zombie']['defaulticon']);
+        $this->assertEquals('A zombie', $types['zombie']['customdescription']);
+
+        // Test adding again causes exception.
+        try {
+            core_filetypes::add_type('frog', 'application/x-frog', 'document');
+            $this->fail();
+        } catch (coding_exception $e) {
+            $this->assertContains('already exists', $e->getMessage());
+            $this->assertContains('frog', $e->getMessage());
+        }
+
+        // Test bogus extension causes exception.
+        try {
+            core_filetypes::add_type('.frog', 'application/x-frog', 'document');
+            $this->fail();
+        } catch (coding_exception $e) {
+            $this->assertContains('Invalid extension', $e->getMessage());
+            $this->assertContains('..frog', $e->getMessage());
+        }
+        try {
+            core_filetypes::add_type('', 'application/x-frog', 'document');
+            $this->fail();
+        } catch (coding_exception $e) {
+            $this->assertContains('Invalid extension', $e->getMessage());
+        }
+
+        // Test there is an exception if you add something with defaulticon when
+        // there is already a type that has it.
+        try {
+            core_filetypes::add_type('gecko', 'text/plain', 'document',
+                    array(), '', '', true);
+            $this->fail();
+        } catch (coding_exception $e) {
+            $this->assertContains('default icon set', $e->getMessage());
+            $this->assertContains('text/plain', $e->getMessage());
+        }
+    }
+
+    public function test_update_type() {
+        $this->resetAfterTest();
+
+        // Check previous value for the MIME type of Word documents.
+        $types = get_mimetypes_array();
+        $this->assertEquals('application/msword', $types['doc']['type']);
+
+        // Change it.
+        core_filetypes::update_type('doc', 'doc', 'application/x-frog', 'document');
+
+        // Check the MIME type is now set and also the other (not specified)
+        // options, like groups, were removed.
+        $types = get_mimetypes_array();
+        $this->assertEquals('application/x-frog', $types['doc']['type']);
+        $this->assertArrayNotHasKey('groups', $types['doc']);
+
+        // This time change the extension.
+        core_filetypes::update_type('doc', 'docccc', 'application/x-frog', 'document');
+        $types = get_mimetypes_array();
+        $this->assertEquals('application/x-frog', $types['docccc']['type']);
+        $this->assertArrayNotHasKey('doc', $types);
+
+        // Test unknown extension.
+        try {
+            core_filetypes::update_type('doc', 'doc', 'application/x-frog', 'document');
+            $this->fail();
+        } catch (coding_exception $e) {
+            $this->assertContains('not found', $e->getMessage());
+            $this->assertContains('doc', $e->getMessage());
+        }
+
+        // Test bogus extension causes exception.
+        try {
+            core_filetypes::update_type('docccc', '.frog', 'application/x-frog', 'document');
+            $this->fail();
+        } catch (coding_exception $e) {
+            $this->assertContains('Invalid extension', $e->getMessage());
+            $this->assertContains('.frog', $e->getMessage());
+        }
+        try {
+            core_filetypes::update_type('docccc', '', 'application/x-frog', 'document');
+            $this->fail();
+        } catch (coding_exception $e) {
+            $this->assertContains('Invalid extension', $e->getMessage());
+        }
+
+        // Test defaulticon changes.
+        try {
+            core_filetypes::update_type('docccc', 'docccc', 'text/plain', 'document',
+                    array(), '', '', true);
+            $this->fail();
+        } catch (coding_exception $e) {
+            $this->assertContains('default icon set', $e->getMessage());
+            $this->assertContains('text/plain', $e->getMessage());
+        }
+    }
+
+    public function test_delete_type() {
+        $this->resetAfterTest();
+
+        // Filetype exists.
+        $types = get_mimetypes_array();
+        $this->assertArrayHasKey('doc', $types);
+
+        // Remove it.
+        core_filetypes::delete_type('doc');
+        $types = get_mimetypes_array();
+        $this->assertArrayNotHasKey('doc', $types);
+
+        // Test removing one that doesn't exist causes exception.
+        try {
+            core_filetypes::delete_type('doc');
+            $this->fail();
+        } catch (coding_exception $e) {
+            $this->assertContains('not found', $e->getMessage());
+            $this->assertContains('doc', $e->getMessage());
+        }
+
+        // Try a custom type (slightly different).
+        core_filetypes::add_type('frog', 'application/x-frog', 'document');
+        $types = get_mimetypes_array();
+        $this->assertArrayHasKey('frog', $types);
+        core_filetypes::delete_type('frog');
+        $types = get_mimetypes_array();
+        $this->assertArrayNotHasKey('frog', $types);
+    }
+
+    public function test_revert_type_to_default() {
+        $this->resetAfterTest();
+
+        // Delete and then revert.
+        core_filetypes::delete_type('doc');
+        $this->assertArrayNotHasKey('doc', get_mimetypes_array());
+        core_filetypes::revert_type_to_default('doc');
+        $this->assertArrayHasKey('doc', get_mimetypes_array());
+
+        // Update and then revert.
+        core_filetypes::update_type('asm', 'asm', 'text/plain', 'sourcecode', array(), '', 'An asm file');
+        $types = get_mimetypes_array();
+        $this->assertEquals('An asm file', $types['asm']['customdescription']);
+        core_filetypes::revert_type_to_default('asm');
+        $types = get_mimetypes_array();
+        $this->assertArrayNotHasKey('customdescription', $types['asm']);
+
+        // Test reverting a non-default type causes exception.
+        try {
+            core_filetypes::revert_type_to_default('frog');
+            $this->fail();
+        } catch (coding_exception $e) {
+            $this->assertContains('not a default type', $e->getMessage());
+            $this->assertContains('frog', $e->getMessage());
+        }
+    }
+
+    /**
+     * Check that the logic cleans up the variable by deleting parts that are
+     * no longer needed.
+     */
+    public function test_cleanup() {
+        global $CFG;
+        $this->resetAfterTest();
+
+        // The custom filetypes setting is empty to start with.
+        $this->assertObjectNotHasAttribute('customfiletypes', $CFG);
+
+        // Add a custom filetype, then delete it.
+        core_filetypes::add_type('frog', 'application/x-frog', 'document');
+        $this->assertObjectHasAttribute('customfiletypes', $CFG);
+        core_filetypes::delete_type('frog');
+        $this->assertObjectNotHasAttribute('customfiletypes', $CFG);
+
+        // Change a standard filetype, then change it back.
+        core_filetypes::update_type('asm', 'asm', 'text/plain', 'document');
+        $this->assertObjectHasAttribute('customfiletypes', $CFG);
+        core_filetypes::update_type('asm', 'asm', 'text/plain', 'sourcecode');
+        $this->assertObjectNotHasAttribute('customfiletypes', $CFG);
+
+        // Delete a standard filetype, then add it back (the same).
+        core_filetypes::delete_type('asm');
+        $this->assertObjectHasAttribute('customfiletypes', $CFG);
+        core_filetypes::add_type('asm', 'text/plain', 'sourcecode');
+        $this->assertObjectNotHasAttribute('customfiletypes', $CFG);
+
+        // Revert a changed type.
+        core_filetypes::update_type('asm', 'asm', 'text/plain', 'document');
+        $this->assertObjectHasAttribute('customfiletypes', $CFG);
+        core_filetypes::revert_type_to_default('asm');
+        $this->assertObjectNotHasAttribute('customfiletypes', $CFG);
+
+        // Revert a deleted type.
+        core_filetypes::delete_type('asm');
+        $this->assertObjectHasAttribute('customfiletypes', $CFG);
+        core_filetypes::revert_type_to_default('asm');
+        $this->assertObjectNotHasAttribute('customfiletypes', $CFG);
+    }
+}
index bf6ecee..43f1ece 100644 (file)
@@ -206,7 +206,7 @@ class core_html_writer_testcase extends basic_testcase {
 
         $expected = <<<EOF
 <table class="generaltable" id="Jeffrey" data-name="Colin">
-<tbody><tr class="r0 lastrow" id="Bob" data-name="Fred">
+<tbody><tr class="lastrow" id="Bob" data-name="Fred">
 <td class="cell c0 lastcol" id="Jeremy" data-name="John" style=""></td>
 </tr>
 </tbody>
index be11cfd..938366d 100644 (file)
@@ -26,7 +26,6 @@ defined('MOODLE_INTERNAL') || die();
 
 global $CFG;
 require_once($CFG->libdir . '/modinfolib.php');
-require_once($CFG->libdir . '/conditionlib.php');
 
 /**
  * Unit tests for modinfolib.php
@@ -416,88 +415,6 @@ class core_modinfolib_testcase extends advanced_testcase {
         $this->assertNotEquals('Illegal overwriting', $modinfo->cms);
     }
 
-    /**
-     * Test is_user_access_restricted_by_conditional_access()
-     *
-     * The underlying conditional access system is more thoroughly tested in lib/tests/conditionlib_test.php
-     */
-    public function test_is_user_access_restricted_by_conditional_access() {
-        global $DB, $CFG;
-
-        $this->resetAfterTest();
-
-        // Enable conditional availability before creating modules, otherwise the condition data is not written in DB.
-        $CFG->enableavailability = true;
-
-        // Create a course.
-        $course = $this->getDataGenerator()->create_course();
-        // 1. Create an activity that is currently unavailable and hidden entirely (for students).
-        $assign1 = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id),
-                array('availability' => '{"op":"|","show":false,"c":[' .
-                '{"type":"date","d":">=","t":' . (time() + 10000) . '}]}'));
-        // 2. Create an activity that is currently available.
-        $assign2 = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id));
-        // 3. Create an activity that is currently unavailable and set to be greyed out.
-        $assign3 = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id),
-                array('availability' => '{"op":"|","show":true,"c":[' .
-                '{"type":"date","d":">=","t":' . (time() + 10000) . '}]}'));
-
-        // Set up a teacher.
-        $coursecontext = context_course::instance($course->id);
-        $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
-        $teacher = $this->getDataGenerator()->create_user();
-        role_assign($teacherrole->id, $teacher->id, $coursecontext);
-
-        // If conditional availability is disabled the activity will always be unrestricted.
-        $CFG->enableavailability = false;
-        $cm = get_fast_modinfo($course)->instances['assign'][$assign1->id];
-        $this->assertTrue($cm->uservisible);
-
-        // Test deprecated function.
-        $this->assertFalse($cm->is_user_access_restricted_by_conditional_access());
-        $this->assertEquals(1, count(phpunit_util::get_debugging_messages()));
-        phpunit_util::reset_debugging();
-
-        // Turn on conditional availability and reset the get_fast_modinfo cache.
-        $CFG->enableavailability = true;
-        get_fast_modinfo($course, 0, true);
-
-        // The unavailable, hidden entirely activity should now be restricted.
-        $cm = get_fast_modinfo($course)->instances['assign'][$assign1->id];
-        $this->assertFalse($cm->uservisible);
-        $this->assertFalse($cm->available);
-        $this->assertEquals('', $cm->availableinfo);
-
-        // Test deprecated function.
-        $this->assertTrue($cm->is_user_access_restricted_by_conditional_access());
-        $this->assertEquals(1, count(phpunit_util::get_debugging_messages()));
-        phpunit_util::reset_debugging();
-
-        // If the activity is available it should not be restricted.
-        $cm = get_fast_modinfo($course)->instances['assign'][$assign2->id];
-        $this->assertTrue($cm->uservisible);
-        $this->assertTrue($cm->available);
-
-        // If the activity is unavailable and set to be greyed out it should not be restricted.
-        $cm = get_fast_modinfo($course)->instances['assign'][$assign3->id];
-        $this->assertFalse($cm->uservisible);
-        $this->assertFalse($cm->available);
-        $this->assertNotEquals('', (string)$cm->availableinfo);
-
-        // Test deprecated function (weird case, it actually checks visibility).
-        $this->assertFalse($cm->is_user_access_restricted_by_conditional_access());
-        $this->assertEquals(1, count(phpunit_util::get_debugging_messages()));
-        phpunit_util::reset_debugging();
-
-        // If the activity is unavailable and set to be hidden entirely its restricted unless user has 'moodle/course:viewhiddenactivities'.
-        // Switch to a teacher and reload the context info.
-        $this->setUser($teacher);
-        $this->assertTrue(has_capability('moodle/course:viewhiddenactivities', $coursecontext));
-        $cm = get_fast_modinfo($course)->instances['assign'][$assign1->id];
-        $this->assertTrue($cm->uservisible);
-        $this->assertFalse($cm->available);
-    }
-
     public function test_is_user_access_restricted_by_capability() {
         global $DB;
 
@@ -747,75 +664,6 @@ class core_modinfolib_testcase extends advanced_testcase {
         $this->assertNull($section->availability);
     }
 
-    /**
-     * Some properties have been deprecated from both the section and module
-     * classes. This checks they still work (and show warnings).
-     */
-    public function test_availability_deprecations() {
-        global $CFG, $DB;
-        $this->resetAfterTest();
-        $CFG->enableavailability = true;
-
-        // Create a course with two modules. The modules are not available to
-        // users. One of them is set to show this information, the other is not.
-        // Same setup for sections.
-        $generator = $this->getDataGenerator();
-        $course = $this->getDataGenerator()->create_course(
-                array('format' => 'topics', 'numsections' => 2),
-                array('createsections' => true));
-        $show = '{"op":"|","show":true,"c":[{"type":"date","d":"<","t":1395857332}]}';
-        $noshow = '{"op":"|","show":false,"c":[{"type":"date","d":"<","t":1395857332}]}';
-        $forum1 = $generator->create_module('forum',
-                array('course' => $course->id, 'availability' => $show));
-        $forum2 = $generator->create_module('forum',
-                array('course' => $course->id, 'availability' => $noshow));
-        $DB->set_field('course_sections', 'availability',
-                $show, array('course' => $course->id, 'section' => 1));
-        $DB->set_field('course_sections', 'availability',
-                $noshow, array('course' => $course->id, 'section' => 2));
-
-        // Create a user without special permissions.
-        $user = $generator->create_user();
-        $generator->enrol_user($user->id, $course->id);
-
-        // Get modinfo and cm objects.
-        $modinfo = get_fast_modinfo($course, $user->id);
-        $cm1 = $modinfo->get_cm($forum1->cmid);
-        $cm2 = $modinfo->get_cm($forum2->cmid);
-
-        // Check the showavailability property.
-        $this->assertEquals(1, $cm1->showavailability);
-        $this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
-        $this->assertEquals(0, $cm2->showavailability);
-        $this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
-
-        // Check the dates (these always return 0 now).
-        $this->assertEquals(0, $cm1->availablefrom);
-        $this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
-        $this->assertEquals(0, $cm1->availableuntil);
-        $this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
-
-        // Get section objects.
-        $section1 = $modinfo->get_section_info(1);
-        $section2 = $modinfo->get_section_info(2);
-
-        // Check showavailability.
-        $this->assertEquals(1, $section1->showavailability);
-        $this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
-        $this->assertEquals(0, $section2->showavailability);
-        $this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
-
-        // Check dates (zero).
-        $this->assertEquals(0, $section1->availablefrom);
-        $this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
-                $this->assertEquals(0, $section1->availableuntil);
-        $this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
-
-        // Check groupingid (zero).
-        $this->assertEquals(0, $section1->groupingid);
-        $this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
-    }
-
     /**
      * Tests for get_groups() method.
      */
index dba5b3a..4a59e44 100644 (file)
@@ -361,7 +361,10 @@ class core_tablelib_testcase extends basic_testcase {
         $table->define_columns($columns);
         $table->define_headers($headers);
         $table->define_baseurl('/invalid.php');
+
         $row = $table->get_row_html($data);
         $this->assertRegExp('/row 0 col 0/', $row);
+        $this->assertRegExp('/<tr class=""/', $row);
+        $this->assertRegExp('/<td class="cell c0"/', $row);
     }
 }
index 6ed05b0..530f1d9 100644 (file)
@@ -139,7 +139,8 @@ class core_useragent_testcase extends basic_testcase {
                 'Nexus' => 'Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17 â€“Nexus'
             ),
             '537' => array(
-                'Samsung GT-9505' => 'Mozilla/5.0 (Linux; Android 4.3; it-it; SAMSUNG GT-I9505/I9505XXUEMJ7 Build/JSS15J) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.5 Chrome/28.0.1500.94 Mobile Safari/537.36'
+                'Samsung GT-9505' => 'Mozilla/5.0 (Linux; Android 4.3; it-it; SAMSUNG GT-I9505/I9505XXUEMJ7 Build/JSS15J) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.5 Chrome/28.0.1500.94 Mobile Safari/537.36',
+                'Nexus 5' => 'Mozilla/5.0 (Linux; Android 5.0; Nexus 5 Build/LPX13D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.102 Mobile Safari/537.36'
             )
         ),
         'Chrome' => array(
@@ -205,6 +206,26 @@ class core_useragent_testcase extends basic_testcase {
         $this->assertFalse(core_useragent::is_chrome());
         $this->assertFalse(core_useragent::check_chrome_version());
 
+        core_useragent::instance(true, $this->user_agents['WebKit Android']['537']['Samsung GT-9505']);
+        $this->assertTrue(core_useragent::is_webkit());
+        $this->assertTrue(core_useragent::check_webkit_version());
+        $this->assertTrue(core_useragent::check_webkit_android_version('527'));
+        $this->assertTrue(core_useragent::is_chrome());
+        $this->assertTrue(core_useragent::check_chrome_version());
+        $this->assertFalse(core_useragent::check_webkit_android_version(590));
+        $this->assertFalse(core_useragent::is_safari());
+        $this->assertFalse(core_useragent::check_safari_version());
+
+        core_useragent::instance(true, $this->user_agents['WebKit Android']['537']['Nexus 5']);
+        $this->assertTrue(core_useragent::is_webkit());
+        $this->assertTrue(core_useragent::check_webkit_version());
+        $this->assertTrue(core_useragent::check_webkit_android_version('527'));
+        $this->assertTrue(core_useragent::is_chrome());
+        $this->assertTrue(core_useragent::check_chrome_version());
+        $this->assertFalse(core_useragent::check_webkit_android_version(590));
+        $this->assertFalse(core_useragent::is_safari());
+        $this->assertFalse(core_useragent::check_safari_version());
+
         core_useragent::instance(true, $this->user_agents['Chrome']['8']['Mac OS X']);
         $this->assertTrue(core_useragent::is_chrome());
         $this->assertTrue(core_useragent::check_chrome_version());
index 84a93d0..843857b 100644 (file)
     <version>1.10.6</version>
     <licenseversion></licenseversion>
   </library>
-  <library>
-    <location>form</location>
-    <name>MoodleForms</name>
-    <license>GPL</license>
-    <version></version>
-    <licenseversion>2.0+</licenseversion>
-  </library>
   <library>
     <location>html2text.php</location>
     <name>HTML2Text</name>
index 7ea8021..2988b2a 100644 (file)
@@ -4,9 +4,11 @@ information provided here is intended especially for developers.
 === 2.9 ===
 
 * \core\event\course_viewed 'other' argument renamed from coursesectionid to coursesectionnumber as it contains the section number.
+* New API core_filetypes::add_type (etc.) allows custom filetypes to be added and modified.
 * PHPUnit: PHPMailer Sink is now started for all tests and is setup within the phpunit wrapper for advanced tests.
   Catching debugging messages when sending mail will no longer work. Use $sink = $this->redirectEmails(); and then check
   the message in the sink instead.
+* The file pluginlib.php was deprecated since 2.6 and has now been removed, do not include or require it.
 
 === 2.8 ===
 
index 8182c6b..4d74609 100644 (file)
@@ -37,466 +37,16 @@ defined('MOODLE_INTERNAL') || die();
  */
 class upload_manager {
 
-   /**
-    * Array to hold local copies of stuff in $_FILES
-    * @var array $files
-    */
-    var $files;
-   /**
-    * Holds all configuration stuff
-    * @var array $config
-    */
-    var $config;
-   /**
-    * Keep track of if we're ok
-    * (errors for each file are kept in $files['whatever']['uploadlog']
-    * @var boolean $status
-    */
-    var $status;
-   /**
-    * The course this file has been uploaded for. {@link $COURSE}
-    * (for logging and virus notifications)
-    * @var course $course
-    */
-    var $course;
-   /**
-    * If we're only getting one file.
-    * (for logging and virus notifications)
-    * @var string $inputname
-    */
-    var $inputname;
-   /**
-    * If we're given silent=true in the constructor, this gets built
-    * up to hold info about the process.
-    * @var string $notify
-    */
-    var $notify;
-
     /**
      * Constructor, sets up configuration stuff so we know how to act.
      *
      * Note: destination not taken as parameter as some modules want to use the insertid in the path and we need to check the other stuff first.
      *
-     * @uses $CFG
-     * @param string $inputname If this is given the upload manager will only process the file in $_FILES with this name.
-     * @param boolean $deleteothers Whether to delete other files in the destination directory (optional, defaults to false)
-     * @param boolean $handlecollisions Whether to use {@link handle_filename_collision()} or not. (optional, defaults to false)
-     * @param course $course The course the files are being uploaded for (for logging and virus notifications) {@link $COURSE}
-     * @param boolean $recoverifmultiple If we come across a virus, or if a file doesn't validate or whatever, do we continue? optional, defaults to true.
-     * @param int $modbytes Max bytes for this module - this and $course->maxbytes are used to get the maxbytes from {@link get_max_upload_file_size()}.
-     * @param boolean $silent Whether to notify errors or not.
-     * @param boolean $allownull Whether we care if there's no file when we've set the input name.
-     * @param boolean $allownullmultiple Whether we care if there's no files AT ALL  when we've got multiples. This won't complain if we have file 1 and file 3 but not file 2, only for NO FILES AT ALL.
-     */
-    function __construct($inputname='', $deleteothers=false, $handlecollisions=false, $course=null, $recoverifmultiple=false, $modbytes=0, $silent=false, $allownull=false, $allownullmultiple=true) {
-        global $CFG, $SITE;
-
-        debugging('upload_manager class is deprecated, use new file picker instead', DEBUG_DEVELOPER);
-
-        if (empty($course->id)) {
-            $course = $SITE;
-        }
-
-        $this->config = new stdClass();
-        $this->config->deleteothers = $deleteothers;
-        $this->config->handlecollisions = $handlecollisions;
-        $this->config->recoverifmultiple = $recoverifmultiple;
-        $this->config->maxbytes = get_max_upload_file_size($CFG->maxbytes, $course->maxbytes, $modbytes);
-        $this->config->silent = $silent;
-        $this->config->allownull = $allownull;
-        $this->files = array();
-        $this->status = false;
-        $this->course = $course;
-        $this->inputname = $inputname;
-        if (empty($this->inputname)) {
-            $this->config->allownull = $allownullmultiple;
-        }
-    }
-
-    /**
-     * Gets all entries out of $_FILES and stores them locally in $files and then
-     * checks each one against {@link get_max_upload_file_size()} and calls {@link cleanfilename()}
-     * and scans them for viruses etc.
-     * @uses $CFG
-     * @uses $_FILES
-     * @return boolean
-     */
-    function preprocess_files() {
-        global $CFG, $OUTPUT;
-
-        foreach ($_FILES as $name => $file) {
-            $this->status = true; // only set it to true here so that we can check if this function has been called.
-            if (empty($this->inputname) || $name == $this->inputname) { // if we have input name, only process if it matches.
-                $file['originalname'] = $file['name']; // do this first for the log.
-                $this->files[$name] = $file; // put it in first so we can get uploadlog out in print_upload_log.
-                $this->files[$name]['uploadlog'] = ''; // initialize error log
-                $this->status = $this->validate_file($this->files[$name]); // default to only allowing empty on multiple uploads.
-                if (!$this->status && ($this->files[$name]['error'] == 0 || $this->files[$name]['error'] == 4) && ($this->config->allownull || empty($this->inputname))) {
-                    // this shouldn't cause everything to stop.. modules should be responsible for knowing which if any are compulsory.
-                    continue;
-                }
-                if ($this->status && !empty($CFG->runclamonupload)) {
-                    $this->status = clam_scan_moodle_file($this->files[$name],$this->course);
-                }
-                if (!$this->status) {
-                 &n