Merge branch 'MDL-27884-master' of git://github.com/ankitagarwal/moodle
authorDan Poltawski <dan@moodle.com>
Mon, 7 Jan 2013 05:30:24 +0000 (13:30 +0800)
committerDan Poltawski <dan@moodle.com>
Mon, 7 Jan 2013 05:30:24 +0000 (13:30 +0800)
61 files changed:
admin/filters.php
admin/tool/uploaduser/index.php
backup/backupfilesedit.php
backup/moodle2/restore_stepslib.php
comment/comment_post.php
course/dnduploadlib.php
course/switchrole.php
enrol/ldap/lib.php
enrol/locallib.php
enrol/otherusers.php
enrol/renderer.php
filter/activitynames/db/install.php
filter/algebra/algebradebug.php
filter/algebra/pix.php
filter/manage.php
filter/mediaplugin/db/install.php
filter/tex/displaytex.php
filter/tex/pix.php
filter/tex/texdebug.php
filter/upgrade.txt
grade/edit/tree/lib.php
group/externallib.php
lang/en/error.php
lib/adminlib.php
lib/db/install.xml
lib/db/upgrade.php
lib/editor/tinymce/plugins/dragmath/lib.php
lib/editor/tinymce/plugins/moodleemoticon/lib.php
lib/editor/tinymce/plugins/spellchecker/classes/GoogleSpell.php
lib/editor/tinymce/plugins/spellchecker/readme_moodle.txt
lib/editor/tinymce/upgrade.txt
lib/filelib.php
lib/filterlib.php
lib/navigationlib.php
lib/pluginlib.php
lib/rsslib.php
lib/tests/filter_test.php
lib/tests/textlib_test.php
lib/textlib.class.php
lib/weblib.php
mod/assign/feedback/file/importziplib.php
mod/assign/feedback/file/locallib.php
mod/assign/feedback/offline/locallib.php
mod/assign/locallib.php
mod/assign/submission/comments/lib.php
mod/data/data.js
mod/data/edit.php
mod/data/field/file/field.class.php
mod/data/field/picture/field.class.php
mod/forum/lib.php
mod/forum/rsslib.php
mod/scorm/report/reportlib.php
mod/upgrade.txt
mod/wiki/filesedit.php
question/format/learnwise/format.php
question/format/learnwise/lang/en/qformat_learnwise.php
report/outline/index.php
report/security/locallib.php
tag/coursetags_add.php
user/files.php
version.php

index c102666..64589cf 100644 (file)
@@ -35,7 +35,7 @@
     require_once($CFG->libdir . '/adminlib.php');
 
     $action = optional_param('action', '', PARAM_ALPHANUMEXT);
-    $filterpath = optional_param('filterpath', '', PARAM_PATH);
+    $filterpath = optional_param('filterpath', '', PARAM_SAFEDIR);
 
     require_login();
     $systemcontext = context_system::instance();
 
     case 'down':
         if (isset($filters[$filterpath])) {
-            $oldpos = $filters[$filterpath]->sortorder;
-            if ($oldpos <= count($filters)) {
-                filter_set_global_state($filterpath, $filters[$filterpath]->active, $oldpos + 1);
-            }
+            filter_set_global_state($filterpath, $filters[$filterpath]->active, 1);
         }
         break;
 
     case 'up':
         if (isset($filters[$filterpath])) {
             $oldpos = $filters[$filterpath]->sortorder;
-            if ($oldpos >= 1) {
-                filter_set_global_state($filterpath, $filters[$filterpath]->active, $oldpos - 1);
-            }
+            filter_set_global_state($filterpath, $filters[$filterpath]->active, -1);
         }
         break;
 
     case 'delete':
-        if (!empty($filternames[$filterpath])) {
-            $filtername = $filternames[$filterpath];
-        } else {
-            $filtername = $filterpath;
-        }
-
-        if (substr($filterpath, 0, 4) == 'mod/') {
-            $mod = basename($filterpath);
-            $a = new stdClass;
-            $a->filter = $filtername;
-            $a->module = get_string('modulename', $mod);
-            print_error('cannotdeletemodfilter', 'admin', $returnurl, $a);
-        }
-
         // If not yet confirmed, display a confirmation message.
         if (!optional_param('confirm', '', PARAM_BOOL)) {
+            $filtername = filter_get_name($filterpath);
+
             $title = get_string('deletefilterareyousure', 'admin', $filtername);
             echo $OUTPUT->header();
             echo $OUTPUT->heading($title);
         }
 
         // Do the deletion.
-        $title = get_string('deletingfilter', 'admin', $filtername);
+        $title = get_string('deletingfilter', 'admin', $filterpath);
         echo $OUTPUT->header();
         echo $OUTPUT->heading($title);
 
         filter_delete_all_for_filter($filterpath);
 
         $a = new stdClass;
-        $a->filter = $filtername;
-        $a->directory = $filterpath;
+        $a->filter = $filterpath;
+        $a->directory = "$CFG->dirroot/filter/$filterpath";
         echo $OUTPUT->box(get_string('deletefilterfiles', 'admin', $a), 'generalbox', 'notice');
         echo $OUTPUT->continue_button($returnurl);
         echo $OUTPUT->footer();
@@ -241,7 +224,7 @@ function get_table_row($filterinfo, $isfirstrow, $islastactive, $applytostrings)
     }
 
     // Disable/off/on
-    $select = new single_select(filters_action_url($filter, 'setstate'), 'newstate', $activechoices, $filterinfo->active, null, 'active' . basename($filter));
+    $select = new single_select(filters_action_url($filter, 'setstate'), 'newstate', $activechoices, $filterinfo->active, null, 'active' . $filter);
     $select->set_label(get_string('isactive', 'filters'), array('class' => 'accesshide'));
     $row[] = $OUTPUT->render($select);
 
@@ -263,25 +246,20 @@ function get_table_row($filterinfo, $isfirstrow, $islastactive, $applytostrings)
     $row[] = $updown;
 
     // Apply to strings.
-    $select = new single_select(filters_action_url($filter, 'setapplyto'), 'stringstoo', $applytochoices, $applytostrings, null, 'applyto' . basename($filter));
+    $select = new single_select(filters_action_url($filter, 'setapplyto'), 'stringstoo', $applytochoices, $applytostrings, null, 'applyto' . $filter);
     $select->set_label(get_string('applyto', 'filters'), array('class' => 'accesshide'));
     $select->disabled = $filterinfo->active == TEXTFILTER_DISABLED;
     $row[] = $OUTPUT->render($select);
 
     // Settings link, if required
     if (filter_has_global_settings($filter)) {
-        $row[] = '<a href="' . $CFG->wwwroot . '/' . $CFG->admin . '/settings.php?section=filtersetting' .
-                str_replace('/', '',$filter) . '">' . get_string('settings') . '</a>';
+        $row[] = '<a href="' . $CFG->wwwroot . '/' . $CFG->admin . '/settings.php?section=filtersetting' . $filter . '">' . get_string('settings') . '</a>';
     } else {
         $row[] = '';
     }
 
     // Delete
-    if (substr($filter, 0, 4) != 'mod/') {
-        $row[] = '<a href="' . filters_action_url($filter, 'delete') . '">' . get_string('delete') . '</a>';
-    } else {
-        $row[] = '';
-    }
+    $row[] = '<a href="' . filters_action_url($filter, 'delete') . '">' . get_string('delete') . '</a>';
 
     return $row;
 }
index 632cfe1..9a6a104 100644 (file)
@@ -285,7 +285,11 @@ if ($formdata = $mform2->is_cancelled()) {
             $userserrors++;
             continue;
         }
-
+        if ($user->username !== clean_param($user->username, PARAM_USERNAME)) {
+            $upt->track('status', get_string('invalidusername', 'error', 'username'), 'error');
+            $upt->track('username', $errorstr, 'error');
+            $userserrors++;
+        }
         if ($existinguser = $DB->get_record('user', array('username'=>$user->username, 'mnethostid'=>$CFG->mnet_localhost_id))) {
             $upt->track('id', $existinguser->id, 'normal', false);
         }
index 6b81b7d..ac5584c 100644 (file)
@@ -33,7 +33,7 @@ $currentcontext = required_param('currentcontext', PARAM_INT);
 // file parameters
 $component  = optional_param('component', null, PARAM_COMPONENT);
 $filearea   = optional_param('filearea', null, PARAM_AREA);
-$returnurl  = optional_param('returnurl', null, PARAM_URL);
+$returnurl  = optional_param('returnurl', null, PARAM_LOCALURL);
 
 list($context, $course, $cm) = get_context_info_array($currentcontext);
 $filecontext = context::instance_by_id($contextid, IGNORE_MISSING);
index babbcd0..b7da2d3 100644 (file)
@@ -297,8 +297,13 @@ class restore_gradebook_structure_step extends restore_structure_step {
 
         $data->courseid = $this->get_courseid();
 
-        $newitemid = $DB->insert_record('grade_settings', $data);
-        //$this->set_mapping('grade_setting', $oldid, $newitemid);
+        if (!$DB->record_exists('grade_settings', array('courseid' => $data->courseid, 'name' => $data->name))) {
+            $newitemid = $DB->insert_record('grade_settings', $data);
+        } else {
+            $newitemid = $data->id;
+        }
+
+        $this->set_mapping('grade_setting', $oldid, $newitemid);
     }
 
     /**
@@ -1817,6 +1822,14 @@ class restore_filters_structure_step extends restore_structure_step {
 
         $data = (object)$data;
 
+        if (strpos($data->filter, 'filter/') === 0) {
+            $data->filter = substr($data->filter, 7);
+
+        } else if (strpos($data->filter, '/') !== false) {
+            // Unsupported old filter.
+            return;
+        }
+
         if (!filter_is_enabled($data->filter)) { // Not installed or not enabled, nothing to do
             return;
         }
@@ -1827,6 +1840,14 @@ class restore_filters_structure_step extends restore_structure_step {
 
         $data = (object)$data;
 
+        if (strpos($data->filter, 'filter/') === 0) {
+            $data->filter = substr($data->filter, 7);
+
+        } else if (strpos($data->filter, '/') !== false) {
+            // Unsupported old filter.
+            return;
+        }
+
         if (!filter_is_enabled($data->filter)) { // Not installed or not enabled, nothing to do
             return;
         }
index b7a39a8..a960293 100644 (file)
@@ -38,7 +38,7 @@ $action    = optional_param('action',    '',  PARAM_ALPHA);
 $area      = optional_param('area',      '',  PARAM_AREA);
 $content   = optional_param('content',   '',  PARAM_RAW);
 $itemid    = optional_param('itemid',    '',  PARAM_INT);
-$returnurl = optional_param('returnurl', '/', PARAM_URL);
+$returnurl = optional_param('returnurl', '/', PARAM_LOCALURL);
 $component = optional_param('component', '',  PARAM_COMPONENT);
 
 // Currently this script can only add comments
index 6fb00b0..21dc7a7 100644 (file)
@@ -555,6 +555,15 @@ class dndupload_ajax_processor {
         $this->cm->groupmode = $this->course->groupmode;
         $this->cm->groupingid = $this->course->defaultgroupingid;
 
+        // Set the correct default for completion tracking.
+        $this->cm->completion = COMPLETION_TRACKING_NONE;
+        $completion = new completion_info($this->course);
+        if ($completion->is_enabled()) {
+            if (plugin_supports('mod', $this->cm->modulename, FEATURE_MODEDIT_DEFAULT_COMPLETION, true)) {
+                $this->cm->completion = COMPLETION_TRACKING_MANUAL;
+            }
+        }
+
         if (!$this->cm->id = add_course_module($this->cm)) {
             throw new coding_exception("Unable to create the course module");
         }
index 719762e..2b756b6 100644 (file)
@@ -35,7 +35,7 @@ require_once($CFG->dirroot.'/course/lib.php');
 
 $id         = required_param('id', PARAM_INT);
 $switchrole = optional_param('switchrole',-1, PARAM_INT);
-$returnurl  = optional_param('returnurl', false, PARAM_URL);
+$returnurl  = optional_param('returnurl', false, PARAM_LOCALURL);
 
 $PAGE->set_url('/course/switchrole.php', array('id'=>$id));
 
@@ -84,4 +84,4 @@ if ($returnurl === false) {
     $returnurl = new moodle_url('/course/view.php', array('id' => $course->id));
 }
 
-redirect($returnurl);
\ No newline at end of file
+redirect($returnurl);
index 31224de..f602ffb 100644 (file)
@@ -653,7 +653,7 @@ class enrol_ldap_plugin extends enrol_plugin {
      * @param object role is a record from the mdl_role table.
      * @return array
      */
-    protected function find_ext_enrolments ($ldapconnection, $memberuid, $role) {
+    protected function find_ext_enrolments (&$ldapconnection, $memberuid, $role) {
         global $CFG;
         require_once($CFG->libdir.'/ldaplib.php');
 
@@ -718,13 +718,13 @@ class enrol_ldap_plugin extends enrol_plugin {
         // Get all contexts and look for first matching user
         $ldap_contexts = explode(';', $ldap_contexts);
         $ldap_pagedresults = ldap_paged_results_supported($this->get_config('ldap_version'));
-        $ldap_cookie = '';
         foreach ($ldap_contexts as $context) {
             $context = trim($context);
             if (empty($context)) {
                 continue;
             }
 
+            $ldap_cookie = '';
             $flat_records = array();
             do {
                 if ($ldap_pagedresults) {
index 2ee9a7b..984354a 100644 (file)
@@ -586,16 +586,29 @@ class course_enrolment_manager {
      */
     public function unassign_role_from_user($userid, $roleid) {
         global $DB;
-        require_capability('moodle/role:assign', $this->context);
-        $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
-        try {
-            role_unassign($roleid, $user->id, $this->context->id, '', NULL);
-        } catch (Exception $e) {
+        // Admins may unassign any role, others only those they could assign.
+        if (!is_siteadmin() and !array_key_exists($roleid, $this->get_assignable_roles())) {
             if (defined('AJAX_SCRIPT')) {
-                throw $e;
+                throw new moodle_exception('invalidrole');
             }
             return false;
         }
+        $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
+        $ras = $DB->get_records('role_assignments', array('contextid'=>$this->context->id, 'userid'=>$user->id, 'roleid'=>$roleid));
+        foreach ($ras as $ra) {
+            if ($ra->component) {
+                if (strpos($ra->component, 'enrol_') !== 0) {
+                    continue;
+                }
+                if (!$plugin = enrol_get_plugin(substr($ra->component, 6))) {
+                    continue;
+                }
+                if ($plugin->roles_protected()) {
+                    continue;
+                }
+            }
+            role_unassign($ra->roleid, $ra->userid, $ra->contextid, $ra->component, $ra->itemid);
+        }
         return true;
     }
 
@@ -706,6 +719,7 @@ class course_enrolment_manager {
     public function get_user_roles($userid) {
         $roles = array();
         $ras = get_user_roles($this->context, $userid, true, 'c.contextlevel DESC, r.sortorder ASC');
+        $plugins = $this->get_enrolment_plugins(false);
         foreach ($ras as $ra) {
             if ($ra->contextid != $this->context->id) {
                 if (!array_key_exists($ra->roleid, $roles)) {
@@ -717,7 +731,18 @@ class course_enrolment_manager {
             if (array_key_exists($ra->roleid, $roles) && $roles[$ra->roleid] === false) {
                 continue;
             }
-            $roles[$ra->roleid] = ($ra->itemid == 0 and $ra->component === '');
+            $changeable = true;
+            if ($ra->component) {
+                $changeable = false;
+                if (strpos($ra->component, 'enrol_') === 0) {
+                    $plugin = substr($ra->component, 6);
+                    if (isset($plugins[$plugin])) {
+                        $changeable = !$plugins[$plugin]->roles_protected();
+                    }
+                }
+            }
+
+            $roles[$ra->roleid] = $changeable;
         }
         return $roles;
     }
@@ -807,6 +832,7 @@ class course_enrolment_manager {
 
         $userroles = $this->get_other_users($sort, $direction, $page, $perpage);
         $roles = $this->get_all_roles();
+        $plugins = $this->get_enrolment_plugins(false);
 
         $context    = $this->get_context();
         $now = time();
@@ -821,8 +847,17 @@ class course_enrolment_manager {
             }
             $a = new stdClass;
             $a->role = $roles[$userrole->roleid]->localname;
-            $changeable = ($userrole->component == '');
             if ($contextid == $this->context->id) {
+                $changeable = true;
+                if ($userrole->component) {
+                    $changeable = false;
+                    if (strpos($userrole->component, 'enrol_') === 0) {
+                        $plugin = substr($userrole->component, 6);
+                        if (isset($plugins[$plugin])) {
+                            $changeable = !$plugin[$plugin]->roles_protected();
+                        }
+                    }
+                }
                 $roletext = get_string('rolefromthiscourse', 'enrol', $a);
             } else {
                 $changeable = false;
@@ -888,7 +923,11 @@ class course_enrolment_manager {
             // Roles
             $details['roles'] = array();
             foreach ($this->get_user_roles($user->id) as $rid=>$rassignable) {
-                $details['roles'][$rid] = array('text'=>$allroles[$rid]->localname, 'unchangeable'=>(!$rassignable || !isset($assignable[$rid])));
+                $unchangeable = !$rassignable;
+                if (!is_siteadmin() and !isset($assignable[$rid])) {
+                    $unchangeable = true;
+                }
+                $details['roles'][$rid] = array('text'=>$allroles[$rid]->localname, 'unchangeable'=>$unchangeable);
             }
 
             // Users
index 3fd7f52..4decf20 100644 (file)
@@ -47,6 +47,7 @@ $PAGE->set_pagelayout('admin');
 $manager = new course_enrolment_manager($PAGE, $course, $filter);
 $table = new course_enrolment_other_users_table($manager, $PAGE);
 $PAGE->set_url('/enrol/otherusers.php', $manager->get_url_params()+$table->get_url_params());
+navigation_node::override_active_url(new moodle_url('/enrol/otherusers.php', array('id' => $id)));
 
 $userdetails = array (
     'picture' => false,
index 05e43f5..de8505c 100644 (file)
@@ -189,7 +189,7 @@ class core_enrol_renderer extends plugin_renderer_base {
         // get list of roles
         $rolesoutput = '';
         foreach ($roles as $roleid=>$role) {
-            if ($canassign && !$role['unchangeable']) {
+            if ($canassign and (is_siteadmin() or isset($assignableroles[$roleid])) and !$role['unchangeable']) {
                 $strunassign = get_string('unassignarole', 'role', $role['text']);
                 $icon = html_writer::empty_tag('img', array('alt'=>$strunassign, 'src'=>$iconenrolremove));
                 $url = new moodle_url($pageurl, array('action'=>'unassign', 'role'=>$roleid, 'user'=>$userid));
index 8caef86..ea065a5 100644 (file)
@@ -26,6 +26,6 @@ function xmldb_filter_activitynames_install() {
     global $CFG;
     require_once("$CFG->libdir/filterlib.php");
 
-    filter_set_global_state('filter/activitynames', TEXTFILTER_ON);
+    filter_set_global_state('activitynames', TEXTFILTER_ON);
 }
 
index d451fbf..3939977 100644 (file)
@@ -7,7 +7,7 @@
 
     require_once("../../config.php");
 
-    if (!filter_is_enabled('filter/algebra')) {
+    if (!filter_is_enabled('algebra')) {
         print_error('filternotenabled');
     }
 
index a4f5843..c4f29d9 100644 (file)
@@ -9,7 +9,7 @@ define('NO_MOODLE_COOKIES', true); // Because it interferes with caching
 
     require_once('../../config.php');
 
-    if (!filter_is_enabled('filter/algebra')) {
+    if (!filter_is_enabled('algebra')) {
         print_error('filternotenabled');
     }
 
index 04893f1..19a1e29 100644 (file)
@@ -27,7 +27,7 @@ require_once(dirname(__FILE__) . '/../config.php');
 require_once($CFG->libdir . '/adminlib.php');
 
 $contextid = required_param('contextid',PARAM_INT);
-$forfilter = optional_param('filter', '', PARAM_SAFEPATH);
+$forfilter = optional_param('filter', '', PARAM_SAFEDIR);
 
 list($context, $course, $cm) = get_context_info_array($contextid);
 
@@ -82,8 +82,8 @@ if ($forfilter) {
         print_error('filterdoesnothavelocalconfig', 'error', $forfilter);
     }
     require_once($CFG->dirroot . '/filter/local_settings_form.php');
-    require_once($CFG->dirroot . '/' . $forfilter . '/filterlocalsettings.php');
-    $formname = basename($forfilter) . '_filter_local_settings_form';
+    require_once($CFG->dirroot . '/filter/' . $forfilter . '/filterlocalsettings.php');
+    $formname = $forfilter . '_filter_local_settings_form';
     $settingsform = new $formname($CFG->wwwroot . '/filter/manage.php', $forfilter, $context);
     if ($settingsform->is_cancelled()) {
         redirect($baseurl);
@@ -96,7 +96,7 @@ if ($forfilter) {
 /// Process any form submission.
 if ($forfilter == '' && optional_param('savechanges', false, PARAM_BOOL) && confirm_sesskey()) {
     foreach ($availablefilters as $filter => $filterinfo) {
-        $newstate = optional_param(str_replace('/', '_', $filter), false, PARAM_INT);
+        $newstate = optional_param($filter, false, PARAM_INT);
         if ($newstate !== false && $newstate != $filterinfo->localstate) {
             filter_set_local_state($filter, $context->id, $newstate);
         }
@@ -181,9 +181,8 @@ if (empty($availablefilters)) {
         } else {
             $activechoices[TEXTFILTER_INHERIT] = $strdefaultoff;
         }
-        $filtername = str_replace('/', '_', $filter);
-        $select = html_writer::label($filterinfo->localstate, 'menu'. $filtername, false, array('class' => 'accesshide'));
-        $select .= html_writer::select($activechoices, $filtername, $filterinfo->localstate, false);
+        $select = html_writer::label($filterinfo->localstate, 'menu'. $filter, false, array('class' => 'accesshide'));
+        $select .= html_writer::select($activechoices, $filter, $filterinfo->localstate, false);
         $row[] = $select;
 
         // Settings link, if required
index 64e3fb9..c128f85 100644 (file)
@@ -27,6 +27,6 @@ function xmldb_filter_mediaplugin_install() {
     global $CFG;
     require_once("$CFG->libdir/filterlib.php");
 
-    filter_set_global_state('filter/mediaplugin', TEXTFILTER_ON);
+    filter_set_global_state('mediaplugin', TEXTFILTER_ON);
 }
 
index ddc6ee6..76eb64a 100644 (file)
@@ -29,7 +29,7 @@ define('NO_MOODLE_COOKIES', true); // Because it interferes with caching
 
 require('../../config.php');
 
-if (!filter_is_enabled('filter/tex') and !filter_is_enabled('filter/algebra')) {
+if (!filter_is_enabled('tex') and !filter_is_enabled('algebra')) {
     print_error('filternotenabled');
 }
 
index deb43d5..4ae2cd0 100644 (file)
@@ -9,7 +9,7 @@ define('NO_MOODLE_COOKIES', true); // Because it interferes with caching
 
     require_once('../../config.php');
 
-    if (!filter_is_enabled('filter/tex')) {
+    if (!filter_is_enabled('tex')) {
         print_error('filternotenabled');
     }
 
index 3200449..0817d7e 100644 (file)
@@ -28,7 +28,7 @@
 
     require_once("../../config.php");
 
-    if (!filter_is_enabled('filter/tex')) {
+    if (!filter_is_enabled('tex')) {
         print_error('filternotenabled');
     }
 
index cf94ace..99cffdd 100644 (file)
@@ -1,6 +1,13 @@
 This file describes API changes in core filter API and plugins,
 information provided here is intended especially for developers.
 
+=== 2.5 ===
+
+* legacy_filter emulation was removed
+* support for 'mod/*' filters was removed
+* use short filter name instead of old path, ex.: 'filter/tex' ---> 'tex'
+  in all filter API functions and methods
+
 === 2.3 ===
 
 * new setup() method added to moodle_text_filter, invoked before
index 820148f..62c7a4f 100644 (file)
@@ -273,12 +273,6 @@ class grade_edit_tree {
                 $root = true;
             }
 
-            $row_count_offset = 0;
-
-            if (empty($category_total_item) && !$this->moving) {
-                $row_count_offset = -1;
-            }
-
             $levelclass = "level$level";
 
             $courseclass = '';
@@ -297,7 +291,7 @@ class grade_edit_tree {
             $headercell->scope = 'row';
             $headercell->attributes['title'] = $object->stripped_name;
             $headercell->attributes['class'] = 'cell rowspan ' . $levelclass;
-            $headercell->rowspan = $row_count+1+$row_count_offset;
+            $headercell->rowspan = $row_count + 1;
             $row->cells[] = $headercell;
 
             foreach ($this->columns as $column) {
index f7599d7..5963c6e 100644 (file)
@@ -579,7 +579,7 @@ class core_group_external extends external_api {
                             'courseid' => new external_value(PARAM_INT, 'id of course'),
                             'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
                             'description' => new external_value(PARAM_RAW, 'grouping description text'),
-                            'descriptionformat' => new external_format_value('descripiton', VALUE_DEFAULT)
+                            'descriptionformat' => new external_format_value('description', VALUE_DEFAULT)
                         )
                     ), 'List of grouping object. A grouping has a courseid, a name and a description.'
                 )
index b211788..0bf030b 100644 (file)
@@ -331,6 +331,7 @@ $string['invalidurl'] = 'Invalid URL';
 $string['invaliduser'] = 'Invalid user';
 $string['invaliduserid'] = 'Invalid user id';
 $string['invaliduserfield'] = 'Invalid user field: {$a}';
+$string['invalidusername'] = 'The given username contains invalid characters';
 $string['invalidxmlfile'] = '"{$a}" is not a valid XML file';
 $string['iplookupfailed'] = 'Cannot find geo information about this IP address {$a}';
 $string['iplookupprivate'] = 'Cannot display lookup of private IP address';
index ba9a666..674fdb4 100644 (file)
@@ -6092,8 +6092,7 @@ class admin_page_managefilters extends admin_externalpage {
                 $found = true;
                 break;
             }
-            list($type, $filter) = explode('/', $path);
-            if (strpos($filter, $query) !== false) {
+            if (strpos($path, $query) !== false) {
                 $found = true;
                 break;
             }
index f60af6c..bea2404 100644 (file)
     <TABLE NAME="filter_active" COMMENT="Stores information about which filters are active in which contexts. Also the filter sort order. See get_active_filters in lib/filterlib.php for how this data is used." PREVIOUS="course_format_options" NEXT="filter_config">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="filter"/>
-        <FIELD NAME="filter" TYPE="char" LENGTH="32" NOTNULL="true" SEQUENCE="false" COMMENT="The filter internal name, like 'filter/tex' or 'mod/glossary'." PREVIOUS="id" NEXT="contextid"/>
+        <FIELD NAME="filter" TYPE="char" LENGTH="32" NOTNULL="true" SEQUENCE="false" COMMENT="The filter internal name, like 'tex'." PREVIOUS="id" NEXT="contextid"/>
         <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="References context.id." PREVIOUS="filter" NEXT="active"/>
         <FIELD NAME="active" TYPE="int" LENGTH="4" NOTNULL="true" SEQUENCE="false" COMMENT="Whether this filter is active in this context. +1 = On, -1 = Off, no row with this contextid = inherit. As a special case, when contextid points to the system context, -9999 means this filter is completely disabled." PREVIOUS="contextid" NEXT="sortorder"/>
         <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Only relevant if contextid points to the system context. In other cases this field should contain 0. The order in which the filters should be applied." PREVIOUS="active"/>
     <TABLE NAME="filter_config" COMMENT="Stores per-context configuration settings for filters which have them." PREVIOUS="filter_active" NEXT="event">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="filter"/>
-        <FIELD NAME="filter" TYPE="char" LENGTH="32" NOTNULL="true" SEQUENCE="false" COMMENT="The filter internal name, like 'filter/tex' or 'mod/glossary'." PREVIOUS="id" NEXT="contextid"/>
+        <FIELD NAME="filter" TYPE="char" LENGTH="32" NOTNULL="true" SEQUENCE="false" COMMENT="The filter internal name, like 'tex'." PREVIOUS="id" NEXT="contextid"/>
         <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="References context.id." PREVIOUS="filter" NEXT="name"/>
         <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The config variable name." PREVIOUS="contextid" NEXT="value"/>
         <FIELD NAME="value" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="The correspoding config variable value." PREVIOUS="name"/>
index 48a78a8..3dcc1d1 100644 (file)
@@ -1524,5 +1524,46 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2012120300.04);
     }
 
+    if ($oldversion < 2012120300.07) {
+        // Purge removed module filters and all their settings.
+
+        $tables = array('filter_active', 'filter_config');
+        foreach ($tables as $table) {
+            $DB->delete_records_select($table, "filter LIKE 'mod/%'");
+            $filters = $DB->get_records_sql("SELECT DISTINCT filter FROM {{$table}} WHERE filter LIKE 'filter/%'");
+            foreach ($filters as $filter) {
+                $DB->set_field($table, 'filter', substr($filter->filter, 7), array('filter'=>$filter->filter));
+            }
+        }
+
+        $configs = array('stringfilters', 'filterall');
+        foreach ($configs as $config) {
+            if ($filters = get_config(null, $config)) {
+                $filters = explode(',', $filters);
+                $newfilters = array();
+                foreach($filters as $filter) {
+                    if (strpos($filter, '/') === false) {
+                        $newfilters[] = $filter;
+                    } else if (strpos($filter, 'filter/') === 0) {
+                        $newfilters[] = substr($filter, 7);
+                    }
+                }
+                $filters = implode(',', $newfilters);
+                set_config($config, $filters);
+            }
+        }
+
+        unset($tables);
+        unset($table);
+        unset($configs);
+        unset($newfilters);
+        unset($filters);
+        unset($filter);
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2012120300.07);
+    }
+
+
     return true;
 }
index 6c2eac2..d4b8ac7 100644 (file)
@@ -33,7 +33,7 @@ class tinymce_dragmath extends editor_tinymce_plugin {
         if ($this->get_config('requiretex', 1)) {
             // If TeX filter is disabled, do not add button.
             $filters = filter_get_active_in_context($context);
-            if (!array_key_exists('filter/tex', $filters)) {
+            if (!array_key_exists('tex', $filters)) {
                 return;
             }
         }
index f61c9ea..c2751a5 100644 (file)
@@ -34,7 +34,7 @@ class tinymce_moodleemoticon extends editor_tinymce_plugin {
         if ($this->get_config('requireemoticon', 1)) {
             // If emoticon filter is disabled, do not add button.
             $filters = filter_get_active_in_context($context);
-            if (!array_key_exists('filter/emoticon', $filters)) {
+            if (!array_key_exists('emoticon', $filters)) {
                 return;
             }
         }
index f96d4a9..5edf76a 100644 (file)
@@ -128,6 +128,7 @@ class GoogleSpell extends SpellChecker {
        }\r
 \r
        function _unhtmlentities($string) {\r
+        return textlib::entities_to_utf8($string); // Moodle hack\r
                $string = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string);\r
                $string = preg_replace('~&#([0-9]+);~e', 'chr(\\1)', $string);\r
 \r
index ffd1bee..deb2af9 100644 (file)
@@ -7,6 +7,4 @@ List of changes:
 * Modified config file to use moodle $CFG.
 * Moved static files to /tinymce/ subfolder.
 * MDL-25736 - French spellchecker fixes.
-
-Commits:
-https://github.com/moodle/custom-tinymce_spellchecker_php/commits/MOODLE_22_2.0.6b
+* Fix htmlentities conversion in GoogleSpell.php
index 2bdb84d..5696839 100644 (file)
@@ -2,6 +2,11 @@ This files describes API changes in /lib/editor/tinymce/* - TinyMCE editor,
 information provided here is intended especially for developers.
 
 
+=== 2.5 ===
+
+* update filter related code to use short filter names instead
+  of original paths, ex.: 'filter/tex' ---> 'tex'
+
 === 2.4 ===
 
 new features:
index ab120b3..3f6cb45 100644 (file)
@@ -2715,7 +2715,7 @@ function file_modify_html_header($text) {
     }*/
 
     $ufo = '';
-    if (filter_is_enabled('filter/mediaplugin')) {
+    if (filter_is_enabled('mediaplugin')) {
         // this script is needed by most media filter plugins.
         $attributes = array('type'=>'text/javascript', 'src'=>$CFG->httpswwwroot . '/lib/ufo.js');
         $ufo = html_writer::tag('script', '', $attributes) . "\n";
index de80835..b8760c0 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,8 +17,7 @@
 /**
  * Library functions for managing text filter plugins.
  *
- * @package    core
- * @subpackage filter
+ * @package    core_filter
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -50,8 +48,7 @@ define('TEXTFILTER_EXCL_SEPARATOR', '-%-');
  *
  * This class is a singleton.
  *
- * @package    core
- * @subpackage filter
+ * @package    core_filter
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -115,33 +112,27 @@ class filter_manager {
     }
 
     /**
-     * Factory method for creating a filter
+     * Factory method for creating a filter.
      *
-     * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
-     * @param object $context context object.
+     * @param string $filtername The filter name, for example 'tex'.
+     * @param context $context context object.
      * @param array $localconfig array of local configuration variables for this filter.
-     * @return object moodle_text_filter The filter, or null, if this type of filter is
+     * @return moodle_text_filter The filter, or null, if this type of filter is
      *      not recognised or could not be created.
      */
     protected function make_filter_object($filtername, $context, $localconfig) {
         global $CFG;
-        $path = $CFG->dirroot .'/'. $filtername .'/filter.php';
+        $path = $CFG->dirroot .'/filter/'. $filtername .'/filter.php';
         if (!is_readable($path)) {
             return null;
         }
         include_once($path);
 
-        $filterclassname = 'filter_' . basename($filtername);
+        $filterclassname = 'filter_' . $filtername;
         if (class_exists($filterclassname)) {
             return new $filterclassname($context, $localconfig);
         }
 
-        // TODO: deprecated since 2.2, will be out in 2.3, see MDL-29996
-        $legacyfunctionname = basename($filtername) . '_filter';
-        if (function_exists($legacyfunctionname)) {
-            return new legacy_filter($legacyfunctionname, $context, $localconfig);
-        }
-
         return null;
     }
 
@@ -193,7 +184,7 @@ class filter_manager {
      */
     public function filter_text($text, $context, array $options = array()) {
         $text = $this->apply_filter_chain($text, $this->get_text_filters($context), $options);
-        /// <nolink> tags removed for XHTML compatibility
+        // <nolink> tags removed for XHTML compatibility
         $text = str_replace(array('<nolink>', '</nolink>'), '', $text);
         return $text;
     }
@@ -202,7 +193,7 @@ class filter_manager {
      * Filter a piece of string
      *
      * @param string $string The text to filter
-     * @param object $context
+     * @param context $context
      * @return string resulting string
      */
     public function filter_string($string, $context) {
@@ -211,7 +202,7 @@ class filter_manager {
 
     /**
      * @todo Document this function
-     * @param object $context
+     * @param context $context
      * @return object A string filter
      */
     public function text_filtering_hash($context) {
@@ -253,8 +244,7 @@ class filter_manager {
  *
  * @todo Document this class
  *
- * @package    core
- * @subpackage filter
+ * @package    core_filter
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -286,8 +276,7 @@ class null_filter_manager {
  *
  * @todo Document this class
  *
- * @package    core
- * @subpackage filter
+ * @package    core_filter
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -351,8 +340,7 @@ class performance_measuring_filter_manager extends filter_manager {
  * Base class for text filters. You just need to override this class and
  * implement the filter method.
  *
- * @package    core
- * @subpackage filter
+ * @package    core_filter
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -364,9 +352,9 @@ abstract class moodle_text_filter {
 
     /**
      * Set any context-specific configuration for this filter.
-     * @param object $context The current course id.
-     * @param object $context The current context.
-     * @param array $config Any context-specific configuration for this filter.
+     *
+     * @param context $context The current context.
+     * @param array $localconfig Any context-specific configuration for this filter.
      */
     public function __construct($context, array $localconfig) {
         $this->context = $context;
@@ -408,50 +396,6 @@ abstract class moodle_text_filter {
     public abstract function filter($text, array $options = array());
 }
 
-/**
- * moodle_text_filter implementation that encapsulates an old-style filter that
- * only defines a function, not a class.
- *
- * @deprecated since 2.2, see MDL-29995
- * @todo will be out in 2.3, see MDL-29996
- * @package    core
- * @subpackage filter
- * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class legacy_filter extends moodle_text_filter {
-    /** @var string */
-    protected $filterfunction;
-    protected $courseid;
-
-    /**
-     * Set any context-specific configuration for this filter.
-     *
-     * @param string $filterfunction
-     * @param object $context The current context.
-     * @param array $config Any context-specific configuration for this filter.
-     */
-    public function __construct($filterfunction, $context, array $localconfig) {
-        parent::__construct($context, $localconfig);
-        $this->filterfunction = $filterfunction;
-        $this->courseid = get_courseid_from_context($this->context);
-    }
-
-    /**
-     * @param string $text
-     * @param array $options options - not supported for legacy filters
-     * @return mixed
-     */
-    public function filter($text, array $options = array()) {
-        if ($this->courseid) {
-            // old filters are called only when inside courses
-            return call_user_func($this->filterfunction, $this->courseid, $text);
-        } else {
-            return $text;
-        }
-    }
-}
-
 /**
  * This is just a little object to define a phrase and some instructions
  * for how to process it.  Filters can create an array of these to pass
@@ -509,66 +453,40 @@ class filterobject {
 }
 
 /**
- * Look up the name of this filter in the most appropriate location.
- * If $filterlocation = 'mod' then does get_string('filtername', $filter);
- * else if $filterlocation = 'filter' then does get_string('filtername', 'filter_' . $filter);
- * with a fallback to get_string('filtername', $filter) for backwards compatibility.
- * These are the only two options supported at the moment.
+ * Look up the name of this filter
  *
- * @param string $filter the folder name where the filter lives.
+ * @param string $filter the filter name
  * @return string the human-readable name for this filter.
  */
 function filter_get_name($filter) {
-    // TODO: should we be using pluginname here instead? , see MDL-29998
-    list($type, $filter) = explode('/', $filter);
-    switch ($type) {
-        case 'filter':
-            $strfiltername = get_string('filtername', 'filter_' . $filter);
-            if (substr($strfiltername, 0, 2) != '[[') {
-                // found a valid string.
-                return $strfiltername;
-            }
-            // Fall through to try the legacy location.
-
-        // TODO: deprecated since 2.2, will be out in 2.3, see MDL-29996
-        case 'mod':
-            $strfiltername = get_string('filtername', $filter);
-            if (substr($strfiltername, 0, 2) == '[[') {
-                $strfiltername .= ' (' . $type . '/' . $filter . ')';
-            }
-            return $strfiltername;
+    if (strpos($filter, 'filter/') === 0) {
+        debugging("Old '$filter'' parameter used in filter_get_name()");
+        $filter = substr($filter, 7);
+    } else if (strpos($filter, '/') !== false) {
+        throw new coding_exception('Unknown filter type ' . $filter);
+    }
 
-        default:
-            throw new coding_exception('Unknown filter type ' . $type);
+    if (get_string_manager()->string_exists('filtername', 'filter_' . $filter)) {
+        return get_string('filtername', 'filter_' . $filter);
+    } else {
+        return $filter;
     }
 }
 
 /**
  * Get the names of all the filters installed in this Moodle.
  *
- * @global object
  * @return array path => filter name from the appropriate lang file. e.g.
- * array('mod/glossary' => 'Glossary Auto-linking', 'filter/tex' => 'TeX Notation');
+ * array('tex' => 'TeX Notation');
  * sorted in alphabetical order of name.
  */
 function filter_get_all_installed() {
     global $CFG;
+
     $filternames = array();
-    // TODO: deprecated since 2.2, will be out in 2.3, see MDL-29996
-    $filterlocations = array('mod', 'filter');
-    foreach ($filterlocations as $filterlocation) {
-        // TODO: move get_list_of_plugins() to get_plugin_list()
-        $filters = get_list_of_plugins($filterlocation);
-        foreach ($filters as $filter) {
-            // MDL-29994 - Ignore mod/data and mod/glossary filters forever, this will be out in 2.3
-            if ($filterlocation == 'mod' && ($filter == 'data' || $filter == 'glossary')) {
-                continue;
-            }
-            $path = $filterlocation . '/' . $filter;
-            if (is_readable($CFG->dirroot . '/' . $path . '/filter.php')) {
-                $strfiltername = filter_get_name($path);
-                $filternames[$path] = $strfiltername;
-            }
+    foreach (get_list_of_plugins('filter') as $filter) {
+        if (is_readable("$CFG->dirroot/filter/$filter/filter.php")) {
+            $filternames[$filter] = filter_get_name($filter);
         }
     }
     collatorlib::asort($filternames);
@@ -578,17 +496,11 @@ function filter_get_all_installed() {
 /**
  * Set the global activated state for a text filter.
  *
- * @global object
- * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
- * @param integer $state One of the values TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_DISABLED.
- * @param integer $sortorder (optional) a position in the sortorder to place this filter.
- *      If not given defaults to:
- *      No change in order if we are updating an existing record, and not changing to or from TEXTFILTER_DISABLED.
- *      Just after the last currently active filter when adding an unknown filter
- *          in state TEXTFILTER_ON or TEXTFILTER_OFF, or enabling/disabling an existing filter.
- *      Just after the very last filter when adding an unknown filter in state TEXTFILTER_DISABLED
+ * @param string $filtername The filter name, for example 'tex'.
+ * @param int $state One of the values TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_DISABLED.
+ * @param int $move 1 means up, 0 means the same, -1 means down
  */
-function filter_set_global_state($filter, $state, $sortorder = false) {
+function filter_set_global_state($filtername, $state, $move = 0) {
     global $DB;
 
     // Check requested state is valid.
@@ -597,84 +509,141 @@ function filter_set_global_state($filter, $state, $sortorder = false) {
                 "Must be one of TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_DISABLED.");
     }
 
-    // Check sortorder is valid.
-    if ($sortorder !== false) {
-        if ($sortorder < 1 || $sortorder > $DB->get_field('filter_active', 'MAX(sortorder)', array()) + 1) {
-            throw new coding_exception("Invalid sort order passed to filter_set_global_state.");
-        }
+    if ($move > 0) {
+        $move = 1;
+    } else if ($move < 0) {
+        $move = -1;
+    }
+
+    if (strpos($filtername, 'filter/') === 0) {
+        //debugging("Old filtername '$filtername' parameter used in filter_set_global_state()", DEBUG_DEVELOPER);
+        $filtername = substr($filtername, 7);
+    } else if (strpos($filtername, '/') !== false) {
+        throw new coding_exception("Invalid filter name '$filtername' used in filter_set_global_state()");
     }
 
-    // See if there is an existing record.
+    $transaction = $DB->start_delegated_transaction();
+
     $syscontext = context_system::instance();
-    $rec = $DB->get_record('filter_active', array('filter' => $filter, 'contextid' => $syscontext->id));
-    if (empty($rec)) {
-        $insert = true;
-        $rec = new stdClass;
-        $rec->filter = $filter;
-        $rec->contextid = $syscontext->id;
-    } else {
-        $insert = false;
-        if ($sortorder === false && !($rec->active == TEXTFILTER_DISABLED xor $state == TEXTFILTER_DISABLED)) {
-            $sortorder = $rec->sortorder;
+    $filters = $DB->get_records('filter_active', array('contextid' => $syscontext->id), 'sortorder ASC');
+
+    $on = array();
+    $off = array();
+
+    foreach($filters as $f) {
+        if ($f->active == TEXTFILTER_DISABLED) {
+            $off[$f->filter] = $f;
+        } else {
+            $on[$f->filter] = $f;
         }
     }
 
-    // Automatic sort order.
-    if ($sortorder === false) {
-        if ($state == TEXTFILTER_DISABLED && $insert) {
-            $prevmaxsortorder = $DB->get_field('filter_active', 'MAX(sortorder)', array());
-        } else {
-            $prevmaxsortorder = $DB->get_field_select('filter_active', 'MAX(sortorder)', 'active <> ?', array(TEXTFILTER_DISABLED));
+    // Update the state or add new record.
+    if (isset($on[$filtername])) {
+        $filter = $on[$filtername];
+        if ($filter->active != $state) {
+            $filter->active = $state;
+            $DB->update_record('filter_active', $filter);
+            if ($filter->active == TEXTFILTER_DISABLED) {
+                unset($on[$filtername]);
+                $off = array($filter->filter => $filter) + $off;
+            }
         }
-        if (empty($prevmaxsortorder)) {
-            $sortorder = 1;
-        } else {
-            $sortorder = $prevmaxsortorder + 1;
-            if (!$insert && $state == TEXTFILTER_DISABLED) {
-                $sortorder = $prevmaxsortorder;
+
+    } else if (isset($off[$filtername])) {
+        $filter = $off[$filtername];
+        if ($filter->active != $state) {
+            $filter->active = $state;
+            $DB->update_record('filter_active', $filter);
+            if ($filter->active != TEXTFILTER_DISABLED) {
+                unset($off[$filtername]);
+                $on[$filter->filter] = $filter;
             }
         }
+
+    } else {
+        $filter = new stdClass();
+        $filter->filter    = $filtername;
+        $filter->contextid = $syscontext->id;
+        $filter->active    = $state;
+        $filter->sortorder = 99999;
+        $filter->id = $DB->insert_record('filter_active', $filter);
+
+        $filters[$filter->id] = $filter;
+        if ($state == TEXTFILTER_DISABLED) {
+            $off[$filter->filter] = $filter;
+        } else {
+            $on[$filter->filter] = $filter;
+        }
     }
 
-    // Move any existing records out of the way of the sortorder.
-    if ($insert) {
-        $DB->execute('UPDATE {filter_active} SET sortorder = sortorder + 1 WHERE sortorder >= ?', array($sortorder));
-    } else if ($sortorder != $rec->sortorder) {
-        $sparesortorder = $DB->get_field('filter_active', 'MIN(sortorder)', array()) - 1;
-        $DB->set_field('filter_active', 'sortorder', $sparesortorder, array('filter' => $filter, 'contextid' => $syscontext->id));
-        if ($sortorder < $rec->sortorder) {
-            $DB->execute('UPDATE {filter_active} SET sortorder = sortorder + 1 WHERE sortorder >= ? AND sortorder < ?',
-                    array($sortorder, $rec->sortorder));
-        } else if ($sortorder > $rec->sortorder) {
-            $DB->execute('UPDATE {filter_active} SET sortorder = sortorder - 1 WHERE sortorder <= ? AND sortorder > ?',
-                    array($sortorder, $rec->sortorder));
+    // Move only active.
+    if ($move != 0 and isset($on[$filter->filter])) {
+        $i = 1;
+        foreach ($on as $f) {
+            $f->newsortorder = $i;
+            $i++;
         }
+
+        $filter->newsortorder = $filter->newsortorder + $move;
+
+        foreach ($on as $f) {
+            if ($f->id == $filter->id) {
+                continue;
+            }
+            if ($f->newsortorder == $filter->newsortorder) {
+                if ($move == 1) {
+                    $f->newsortorder = $f->newsortorder - 1;
+                } else {
+                    $f->newsortorder = $f->newsortorder + 1;
+                }
+            }
+        }
+
+        collatorlib::asort_objects_by_property($on, 'newsortorder', collatorlib::SORT_NUMERIC);
     }
 
-    // Insert/update the new record.
-    $rec->active = $state;
-    $rec->sortorder = $sortorder;
-    if ($insert) {
-        $DB->insert_record('filter_active', $rec);
-    } else {
-        $DB->update_record('filter_active', $rec);
+    // Inactive are sorted by filter name.
+    collatorlib::asort_objects_by_property($off, 'filter', collatorlib::SORT_NATURAL);
+
+    // Update records if necessary.
+    $i = 1;
+    foreach ($on as $f) {
+        if ($f->sortorder != $i) {
+            $DB->set_field('filter_active', 'sortorder', $i, array('id'=>$f->id));
+        }
+        $i++;
+    }
+    foreach ($off as $f) {
+        if ($f->sortorder != $i) {
+            $DB->set_field('filter_active', 'sortorder', $i, array('id'=>$f->id));
+        }
+        $i++;
     }
+
+    $transaction->allow_commit();
 }
 
 /**
- * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @param string $filtername The filter name, for example 'tex'.
  * @return boolean is this filter allowed to be used on this site. That is, the
  *      admin has set the global 'active' setting to On, or Off, but available.
  */
-function filter_is_enabled($filter) {
-    return array_key_exists($filter, filter_get_globally_enabled());
+function filter_is_enabled($filtername) {
+    if (strpos($filtername, 'filter/') === 0) {
+        //debugging("Old filtername '$filtername' parameter used in filter_is_enabled()", DEBUG_DEVELOPER);
+        $filtername = substr($filtername, 7);
+    } else if (strpos($filtername, '/') !== false) {
+        throw new coding_exception("Invalid filter name '$filtername' used in filter_is_enabled()");
+    }
+    return array_key_exists($filtername, filter_get_globally_enabled());
 }
 
 /**
  * Return a list of all the filters that may be in use somewhere.
  *
  * @staticvar array $enabledfilters
- * @return array where the keys and values are both the filter name, like 'filter/tex'.
+ * @return array where the keys and values are both the filter name, like 'tex'.
  */
 function filter_get_globally_enabled() {
     static $enabledfilters = null;
@@ -694,8 +663,7 @@ function filter_get_globally_enabled() {
  * Return the names of the filters that should also be applied to strings
  * (when they are enabled).
  *
- * @global object
- * @return array where the keys and values are both the filter name, like 'filter/tex'.
+ * @return array where the keys and values are both the filter name, like 'tex'.
  */
 function filter_get_string_filters() {
     global $CFG;
@@ -711,7 +679,7 @@ function filter_get_string_filters() {
  * Sets whether a particular active filter should be applied to all strings by
  * format_string, or just used by format_text.
  *
- * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @param string $filter The filter name, for example 'tex'.
  * @param boolean $applytostrings if true, this filter will apply to format_string
  *      and format_text, when it is enabled.
  */
@@ -732,8 +700,7 @@ function filter_set_applies_to_strings($filter, $applytostrings) {
 /**
  * Set the local activated state for a text filter.
  *
- * @global object
- * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @param string $filter The filter name, for example 'tex'.
  * @param integer $contextid The id of the context to get the local config for.
  * @param integer $state One of the values TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_INHERIT.
  * @return void
@@ -778,8 +745,7 @@ function filter_set_local_state($filter, $contextid, $state) {
 /**
  * Set a particular local config variable for a filter in a context.
  *
- * @global object
- * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @param string $filter The filter name, for example 'tex'.
  * @param integer $contextid The id of the context to get the local config for.
  * @param string $name the setting name.
  * @param string $value the corresponding value.
@@ -808,8 +774,7 @@ function filter_set_local_config($filter, $contextid, $name, $value) {
 /**
  * Remove a particular local config variable for a filter in a context.
  *
- * @global object
- * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @param string $filter The filter name, for example 'tex'.
  * @param integer $contextid The id of the context to get the local config for.
  * @param string $name the setting name.
  */
@@ -824,8 +789,7 @@ function filter_unset_local_config($filter, $contextid, $name) {
  * for you automatically. You only need this, for example, when you are getting
  * the config so you can show the user an editing from.
  *
- * @global object
- * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @param string $filter The filter name, for example 'tex'.
  * @param integer $contextid The ID of the context to get the local config for.
  * @return array of name => value pairs.
  */
@@ -838,7 +802,6 @@ function filter_get_local_config($filter, $contextid) {
  * This function is for use by backup. Gets all the filter information specific
  * to one context.
  *
- * @global object
  * @param int $contextid
  * @return array Array with two elements. The first element is an array of objects with
  *      fields filter and active. These come from the filter_active table. The
@@ -847,7 +810,6 @@ function filter_get_local_config($filter, $contextid) {
  */
 function filter_get_all_local_settings($contextid) {
     global $DB;
-    $context = context_system::instance();
     return array(
         $DB->get_records('filter_active', array('contextid' => $contextid), 'filter', 'filter,active'),
         $DB->get_records('filter_config', array('contextid' => $contextid), 'filter,name', 'filter,name,value'),
@@ -858,14 +820,13 @@ function filter_get_all_local_settings($contextid) {
  * Get the list of active filters, in the order that they should be used
  * for a particular context, along with any local configuration variables.
  *
- * @global object
- * @param object $context a context
+ * @param context $context a context
  * @return array an array where the keys are the filter names, for example
- *      'filter/tex' or 'mod/glossary' and the values are any local
+ *      'tex' and the values are any local
  *      configuration for that filter, as an array of name => value pairs
  *      from the filter_config table. In a lot of cases, this will be an
  *      empty array. So, an example return value for this function might be
- *      array('filter/tex' => array(), 'mod/glossary' => array('glossaryid', 123))
+ *      array(tex' => array())
  */
 function filter_get_active_in_context($context) {
     global $DB, $FILTERLIB_PRIVATE;
@@ -891,15 +852,13 @@ function filter_get_active_in_context($context) {
              JOIN {context} ctx ON f.contextid = ctx.id
              WHERE ctx.id IN ($contextids)
              GROUP BY filter
-             HAVING MAX(f.active * " . $DB->sql_cast_2signed('ctx.depth') .
-                    ") > -MIN(f.active * " . $DB->sql_cast_2signed('ctx.depth') . ")
+             HAVING MAX(f.active * ctx.depth) > -MIN(f.active * ctx.depth)
          ) active
          LEFT JOIN {filter_config} fc ON fc.filter = active.filter AND fc.contextid = $context->id
          ORDER BY active.sortorder";
-    //TODO: remove sql_cast_2signed() once we do not support upgrade from Moodle 2.2
     $rs = $DB->get_recordset_sql($sql);
 
-    // Masssage the data into the specified format to return.
+    // Massage the data into the specified format to return.
     $filters = array();
     foreach ($rs as $row) {
         if (!isset($filters[$row->filter])) {
@@ -918,6 +877,7 @@ function filter_get_active_in_context($context) {
 /**
  * Preloads the list of active filters for all activities (modules) on the course
  * using two database queries.
+ *
  * @param course_modinfo $modinfo Course object from get_fast_modinfo
  */
 function filter_preload_activities(course_modinfo $modinfo) {
@@ -1000,7 +960,7 @@ function filter_preload_activities(course_modinfo $modinfo) {
         }
     }
 
-    // Chuck away the ones that aren't active
+    // Chuck away the ones that aren't active.
     foreach ($courseactive as $filter=>$score) {
         if ($score <= 0) {
             unset($courseactive[$filter]);
@@ -1010,7 +970,7 @@ function filter_preload_activities(course_modinfo $modinfo) {
     }
 
     // Loop through the contexts to reconstruct filter_active lists for each
-    // cm on the course
+    // cm on the course.
     if (!isset($FILTERLIB_PRIVATE->active)) {
         $FILTERLIB_PRIVATE->active = array();
     }
@@ -1023,18 +983,18 @@ function filter_preload_activities(course_modinfo $modinfo) {
             foreach ($remainingactives[$contextid] as $row) {
                 if ($row->active > 0 && empty($banned[$row->filter])) {
                     // If it's marked active for specific context, add entry
-                    // (doesn't matter if one exists already)
+                    // (doesn't matter if one exists already).
                     $FILTERLIB_PRIVATE->active[$contextid][$row->filter] = array();
                 } else {
                     // If it's marked inactive, remove entry (doesn't matter
-                    // if it doesn't exist)
+                    // if it doesn't exist).
                     unset($FILTERLIB_PRIVATE->active[$contextid][$row->filter]);
                 }
             }
         }
     }
 
-    // Process all config rows to add config data to these entries
+    // Process all config rows to add config data to these entries.
     foreach ($filterconfigs as $row) {
         if (isset($FILTERLIB_PRIVATE->active[$row->contextid][$row->filter])) {
             $FILTERLIB_PRIVATE->active[$row->contextid][$row->filter][$row->name] = $row->value;
@@ -1046,10 +1006,9 @@ function filter_preload_activities(course_modinfo $modinfo) {
  * List all of the filters that are available in this context, and what the
  * local and inherited states of that filter are.
  *
- * @global object
- * @param object $context a context that is not the system context.
- * @return array an array with filter names, for example 'filter/tex' or
- *      'mod/glossary' as keys. and and the values are objects with fields:
+ * @param context $context a context that is not the system context.
+ * @return array an array with filter names, for example 'tex'
+ *      as keys. and and the values are objects with fields:
  *      ->filter filter name, same as the key.
  *      ->localstate TEXTFILTER_ON/OFF/INHERIT
  *      ->inheritedstate TEXTFILTER_ON/OFF - the state that will be used if localstate is set to TEXTFILTER_INHERIT.
@@ -1072,8 +1031,7 @@ function filter_get_available_in_context($context) {
                 ELSE fa.active END AS localstate,
              parent_states.inheritedstate
          FROM (SELECT f.filter, MAX(f.sortorder) AS sortorder,
-                    CASE WHEN MAX(f.active * " . $DB->sql_cast_2signed('ctx.depth') .
-                            ") > -MIN(f.active * " . $DB->sql_cast_2signed('ctx.depth') . ") THEN " . TEXTFILTER_ON . "
+                    CASE WHEN MAX(f.active * ctx.depth) > -MIN(f.active * ctx.depth) THEN " . TEXTFILTER_ON . "
                     ELSE " . TEXTFILTER_OFF . " END AS inheritedstate
              FROM {filter_active} f
              JOIN {context} ctx ON f.contextid = ctx.id
@@ -1089,7 +1047,6 @@ function filter_get_available_in_context($context) {
 /**
  * This function is for use by the filter administration page.
  *
- * @global object
  * @return array 'filtername' => object with fields 'filter' (=filtername), 'active' and 'sortorder'
  */
 function filter_get_global_states() {
@@ -1101,14 +1058,12 @@ function filter_get_global_states() {
 /**
  * Delete all the data in the database relating to a filter, prior to deleting it.
  *
- * @global object
- * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @param string $filter The filter name, for example 'tex'.
  */
 function filter_delete_all_for_filter($filter) {
     global $DB;
-    if (substr($filter, 0, 7) == 'filter/') {
-        unset_all_config_for_plugin('filter_' . basename($filter));
-    }
+
+    unset_all_config_for_plugin('filter_' . $filter);
     $DB->delete_records('filter_active', array('filter' => $filter));
     $DB->delete_records('filter_config', array('filter' => $filter));
 }
@@ -1126,27 +1081,26 @@ function filter_delete_all_for_context($contextid) {
 
 /**
  * Does this filter have a global settings page in the admin tree?
- * (The settings page for a filter must be called, for example,
- * filtersettingfiltertex or filtersettingmodglossay.)
+ * (The settings page for a filter must be called, for example, filtersettingfiltertex.)
  *
- * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @param string $filter The filter name, for example 'tex'.
  * @return boolean Whether there should be a 'Settings' link on the config page.
  */
 function filter_has_global_settings($filter) {
     global $CFG;
-    $settingspath = $CFG->dirroot . '/' . $filter . '/filtersettings.php';
+    $settingspath = $CFG->dirroot . '/filter/' . $filter . '/filtersettings.php';
     return is_readable($settingspath);
 }
 
 /**
  * Does this filter have local (per-context) settings?
  *
- * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @param string $filter The filter name, for example 'tex'.
  * @return boolean Whether there should be a 'Settings' link on the manage filters in context page.
  */
 function filter_has_local_settings($filter) {
     global $CFG;
-    $settingspath = $CFG->dirroot . '/' . $filter . '/filterlocalsettings.php';
+    $settingspath = $CFG->dirroot . '/filter/' . $filter . '/filterlocalsettings.php';
     return is_readable($settingspath);
 }
 
@@ -1162,7 +1116,7 @@ function filter_context_may_have_filter_settings($context) {
 }
 
 /**
- * Process phrases intelligently found within a HTML text (such as adding links)
+ * Process phrases intelligently found within a HTML text (such as adding links).
  *
  * @staticvar array $usedpharses
  * @param string $text             the text that we are filtering
@@ -1179,8 +1133,8 @@ function filter_phrases($text, &$link_array, $ignoretagsopen=NULL, $ignoretagscl
 
     static $usedphrases;
 
-    $ignoretags = array();  //To store all the enclosig tags to be completely ignored
-    $tags = array();        //To store all the simple tags to be ignored
+    $ignoretags = array();  // To store all the enclosig tags to be completely ignored.
+    $tags = array();        // To store all the simple tags to be ignored.
 
     if (!$overridedefaultignore) {
         // A list of open/close tags that we should not replace within
@@ -1192,12 +1146,12 @@ function filter_phrases($text, &$link_array, $ignoretagsopen=NULL, $ignoretagscl
         $filterignoretagsclose = array('</head>', '</nolink>', '</span>',
                  '</script>', '</textarea>', '</select>','</a>');
     } else {
-        // Set an empty default list
+        // Set an empty default list.
         $filterignoretagsopen = array();
         $filterignoretagsclose = array();
     }
 
-    // Add the user defined ignore tags to the default list
+    // Add the user defined ignore tags to the default list.
     if ( is_array($ignoretagsopen) ) {
         foreach ($ignoretagsopen as $open) {
             $filterignoretagsopen[] = $open;
@@ -1207,41 +1161,41 @@ function filter_phrases($text, &$link_array, $ignoretagsopen=NULL, $ignoretagscl
         }
     }
 
-/// Invalid prefixes and suffixes for the fullmatch searches
-/// Every "word" character, but the underscore, is a invalid suffix or prefix.
-/// (nice to use this because it includes national characters (accents...) as word characters.
+    // Invalid prefixes and suffixes for the fullmatch searches
+    // Every "word" character, but the underscore, is a invalid suffix or prefix.
+    // (nice to use this because it includes national characters (accents...) as word characters.
     $filterinvalidprefixes = '([^\W_])';
     $filterinvalidsuffixes = '([^\W_])';
 
-    //// Double up some magic chars to avoid "accidental matches"
+    // Double up some magic chars to avoid "accidental matches"
     $text = preg_replace('/([#*%])/','\1\1',$text);
 
 
-////Remove everything enclosed by the ignore tags from $text
+    //Remove everything enclosed by the ignore tags from $text
     filter_save_ignore_tags($text,$filterignoretagsopen,$filterignoretagsclose,$ignoretags);
 
-/// Remove tags from $text
+    // Remove tags from $text
     filter_save_tags($text,$tags);
 
-/// Time to cycle through each phrase to be linked
+    // Time to cycle through each phrase to be linked
     $size = sizeof($link_array);
     for ($n=0; $n < $size; $n++) {
         $linkobject =& $link_array[$n];
 
-    /// Set some defaults if certain properties are missing
-    /// Properties may be missing if the filterobject class has not been used to construct the object
+        // Set some defaults if certain properties are missing
+        // Properties may be missing if the filterobject class has not been used to construct the object
         if (empty($linkobject->phrase)) {
             continue;
         }
 
-    /// Avoid integers < 1000 to be linked. See bug 1446.
+        // Avoid integers < 1000 to be linked. See bug 1446.
         $intcurrent = intval($linkobject->phrase);
         if (!empty($intcurrent) && strval($intcurrent) == $linkobject->phrase && $intcurrent < 1000) {
             continue;
         }
 
-    /// All this work has to be done ONLY it it hasn't been done before
-    if (!$linkobject->work_calculated) {
+        // All this work has to be done ONLY it it hasn't been done before
+         if (!$linkobject->work_calculated) {
             if (!isset($linkobject->hreftagbegin) or !isset($linkobject->hreftagend)) {
                 $linkobject->work_hreftagbegin = '<span class="highlight"';
                 $linkobject->work_hreftagend   = '</span>';
@@ -1250,8 +1204,8 @@ function filter_phrases($text, &$link_array, $ignoretagsopen=NULL, $ignoretagscl
                 $linkobject->work_hreftagend   = $linkobject->hreftagend;
             }
 
-        /// Double up chars to protect true duplicates
-        /// be cleared up before returning to the user.
+            // Double up chars to protect true duplicates
+            // be cleared up before returning to the user.
             $linkobject->work_hreftagbegin = preg_replace('/([#*%])/','\1\1',$linkobject->work_hreftagbegin);
 
             if (empty($linkobject->casesensitive)) {
@@ -1265,41 +1219,41 @@ function filter_phrases($text, &$link_array, $ignoretagsopen=NULL, $ignoretagscl
                 $linkobject->work_fullmatch = true;
             }
 
-        /// Strip tags out of the phrase
+            // Strip tags out of the phrase
             $linkobject->work_phrase = strip_tags($linkobject->phrase);
 
-        /// Double up chars that might cause a false match -- the duplicates will
-        /// be cleared up before returning to the user.
+            // Double up chars that might cause a false match -- the duplicates will
+            // be cleared up before returning to the user.
             $linkobject->work_phrase = preg_replace('/([#*%])/','\1\1',$linkobject->work_phrase);
 
-        /// Set the replacement phrase properly
+            // Set the replacement phrase properly
             if ($linkobject->replacementphrase) {    //We have specified a replacement phrase
-            /// Strip tags
+                // Strip tags
                 $linkobject->work_replacementphrase = strip_tags($linkobject->replacementphrase);
             } else {                                 //The replacement is the original phrase as matched below
                 $linkobject->work_replacementphrase = '$1';
             }
 
-        /// Quote any regular expression characters and the delimiter in the work phrase to be searched
+            // Quote any regular expression characters and the delimiter in the work phrase to be searched
             $linkobject->work_phrase = preg_quote($linkobject->work_phrase, '/');
 
-        /// Work calculated
+            // Work calculated
             $linkobject->work_calculated = true;
 
         }
 
-    /// If $CFG->filtermatchoneperpage, avoid previously (request) linked phrases
+        // If $CFG->filtermatchoneperpage, avoid previously (request) linked phrases
         if (!empty($CFG->filtermatchoneperpage)) {
             if (!empty($usedphrases) && in_array($linkobject->work_phrase,$usedphrases)) {
                 continue;
             }
         }
 
-    /// Regular expression modifiers
+        // Regular expression modifiers
         $modifiers = ($linkobject->work_casesensitive) ? 's' : 'isu'; // works in unicode mode!
 
-    /// Do we need to do a fullmatch?
-    /// If yes then go through and remove any non full matching entries
+        // Do we need to do a fullmatch?
+        // If yes then go through and remove any non full matching entries
         if ($linkobject->work_fullmatch) {
             $notfullmatches = array();
             $regexp = '/'.$filterinvalidprefixes.'('.$linkobject->work_phrase.')|('.$linkobject->work_phrase.')'.$filterinvalidsuffixes.'/'.$modifiers;
@@ -1316,7 +1270,7 @@ function filter_phrases($text, &$link_array, $ignoretagsopen=NULL, $ignoretagscl
             }
         }
 
-    /// Finally we do our highlighting
+        // Finally we do our highlighting
         if (!empty($CFG->filtermatchonepertext) || !empty($CFG->filtermatchoneperpage)) {
             $resulttext = preg_replace('/('.$linkobject->work_phrase.')/'.$modifiers,
                                       $linkobject->work_hreftagbegin.
@@ -1330,43 +1284,43 @@ function filter_phrases($text, &$link_array, $ignoretagsopen=NULL, $ignoretagscl
         }
 
 
-    /// If the text has changed we have to look for links again
+        // If the text has changed we have to look for links again
         if ($resulttext != $text) {
-        /// Set $text to $resulttext
+            // Set $text to $resulttext
             $text = $resulttext;
-        /// Remove everything enclosed by the ignore tags from $text
+            // Remove everything enclosed by the ignore tags from $text
             filter_save_ignore_tags($text,$filterignoretagsopen,$filterignoretagsclose,$ignoretags);
-        /// Remove tags from $text
+            // Remove tags from $text
             filter_save_tags($text,$tags);
-        /// If $CFG->filtermatchoneperpage, save linked phrases to request
+            // If $CFG->filtermatchoneperpage, save linked phrases to request
             if (!empty($CFG->filtermatchoneperpage)) {
                 $usedphrases[] = $linkobject->work_phrase;
             }
         }
 
 
-    /// Replace the not full matches before cycling to next link object
+        // Replace the not full matches before cycling to next link object
         if (!empty($notfullmatches)) {
             $text = str_replace(array_keys($notfullmatches),$notfullmatches,$text);
             unset($notfullmatches);
         }
     }
 
-/// Rebuild the text with all the excluded areas
+    // Rebuild the text with all the excluded areas
 
     if (!empty($tags)) {
         $text = str_replace(array_keys($tags), $tags, $text);
     }
 
     if (!empty($ignoretags)) {
-        $ignoretags = array_reverse($ignoretags); /// Reversed so "progressive" str_replace() will solve some nesting problems.
+        $ignoretags = array_reverse($ignoretags);     // Reversed so "progressive" str_replace() will solve some nesting problems.
         $text = str_replace(array_keys($ignoretags),$ignoretags,$text);
     }
 
-    //// Remove the protective doubleups
+    // Remove the protective doubleups
     $text =  preg_replace('/([#*%])(\1)/','\1',$text);
 
-/// Add missing javascript for popus
+    // Add missing javascript for popus
     $text = filter_add_javascript($text);
 
 
@@ -1415,10 +1369,10 @@ function filter_remove_duplicates($linkarray) {
  **/
 function filter_save_ignore_tags(&$text, $filterignoretagsopen, $filterignoretagsclose, &$ignoretags) {
 
-/// Remove everything enclosed by the ignore tags from $text
+    // Remove everything enclosed by the ignore tags from $text
     foreach ($filterignoretagsopen as $ikey=>$opentag) {
         $closetag = $filterignoretagsclose[$ikey];
-    /// form regular expression
+        // form regular expression
         $opentag  = str_replace('/','\/',$opentag); // delimit forward slashes
         $closetag = str_replace('/','\/',$closetag); // delimit forward slashes
         $pregexp = '/'.$opentag.'(.*?)'.$closetag.'/is';
@@ -1464,10 +1418,10 @@ function filter_add_javascript($text) {
     global $CFG;
 
     if (stripos($text, '</html>') === FALSE) {
-        return $text; // this is not a html file
+        return $text; // This is not a html file.
     }
     if (strpos($text, 'onclick="return openpopup') === FALSE) {
-        return $text; // no popup - no need to add javascript
+        return $text; // No popup - no need to add javascript.
     }
     $js ="
     <script type=\"text/javascript\">
@@ -1485,11 +1439,11 @@ function filter_add_javascript($text) {
     // -->
     </script>";
     if (stripos($text, '</head>') !== FALSE) {
-        //try to add it into the head element
+        // Try to add it into the head element.
         $text = str_ireplace('</head>', $js.'</head>', $text);
         return $text;
     }
 
-    //last chance - try adding head element
+    // Last chance - try adding head element.
     return preg_replace("/<html.*?>/is", "\\0<head>".$js.'</head>', $text);
 }
index a470f39..8458475 100644 (file)
@@ -1663,19 +1663,21 @@ class global_navigation extends navigation_node {
                     $categoryids[] = $category->key;
                 }
             }
-            list($categoriessql, $params) = $DB->get_in_or_equal($categoryids, SQL_PARAMS_NAMED);
-            $params['limit'] = (!empty($CFG->navcourselimit))?$CFG->navcourselimit:20;
-            $sql = "SELECT cc.id, COUNT(c.id) AS coursecount
-                      FROM {course_categories} cc
-                      JOIN {course} c ON c.category = cc.id
-                     WHERE cc.id {$categoriessql}
-                  GROUP BY cc.id
-                    HAVING COUNT(c.id) > :limit";
-            $excessivecategories = $DB->get_records_sql($sql, $params);
-            foreach ($categories as &$category) {
-                if (array_key_exists($category->key, $excessivecategories) && !$this->can_add_more_courses_to_category($category)) {
-                    $url = new moodle_url('/course/category.php', array('id'=>$category->key));
-                    $category->add(get_string('viewallcourses'), $url, self::TYPE_SETTING);
+            if ($categoryids) {
+                list($categoriessql, $params) = $DB->get_in_or_equal($categoryids, SQL_PARAMS_NAMED);
+                $params['limit'] = (!empty($CFG->navcourselimit))?$CFG->navcourselimit:20;
+                $sql = "SELECT cc.id, COUNT(c.id) AS coursecount
+                          FROM {course_categories} cc
+                          JOIN {course} c ON c.category = cc.id
+                         WHERE cc.id {$categoriessql}
+                      GROUP BY cc.id
+                        HAVING COUNT(c.id) > :limit";
+                $excessivecategories = $DB->get_records_sql($sql, $params);
+                foreach ($categories as &$category) {
+                    if (array_key_exists($category->key, $excessivecategories) && !$this->can_add_more_courses_to_category($category)) {
+                        $url = new moodle_url('/course/category.php', array('id'=>$category->key));
+                        $category->add(get_string('viewallcourses'), $url, self::TYPE_SETTING);
+                    }
                 }
             }
         }
index b740093..3be4ca9 100644 (file)
@@ -2654,12 +2654,12 @@ class plugininfo_filter extends plugininfo_base {
         // get the list of filters from both /filter and /mod location
         $installed = filter_get_all_installed();
 
-        foreach ($installed as $filterlegacyname => $displayname) {
+        foreach ($installed as $name => $displayname) {
             $plugin                 = new $typeclass();
             $plugin->type           = $type;
             $plugin->typerootdir    = $typerootdir;
-            $plugin->name           = self::normalize_legacy_name($filterlegacyname);
-            $plugin->rootdir        = $CFG->dirroot . '/' . $filterlegacyname;
+            $plugin->name           = $name;
+            $plugin->rootdir        = "$CFG->dirroot/filter/$name";
             $plugin->displayname    = $displayname;
 
             $plugin->load_disk_version();
@@ -2676,9 +2676,9 @@ class plugininfo_filter extends plugininfo_base {
             // if we're upgrading from 1.9, the table does not exist yet
             // if it does, make sure that all installed filters are registered
             $needsreload  = false;
-            foreach (array_keys($installed) as $filterlegacyname) {
-                if (!isset($globalstates[self::normalize_legacy_name($filterlegacyname)])) {
-                    filter_set_global_state($filterlegacyname, TEXTFILTER_DISABLED);
+            foreach (array_keys($installed) as $name) {
+                if (!isset($globalstates[$name]) && !isset($globalstates['filter/'.$name])) {
+                    filter_set_global_state($name, TEXTFILTER_DISABLED);
                     $needsreload = true;
                 }
             }
@@ -2695,8 +2695,8 @@ class plugininfo_filter extends plugininfo_base {
                 $plugin->type           = $type;
                 $plugin->typerootdir    = $typerootdir;
                 $plugin->name           = $name;
-                $plugin->rootdir        = $CFG->dirroot . '/' . $info->legacyname;
-                $plugin->displayname    = $info->legacyname;
+                $plugin->rootdir        = "$CFG->dirroot/filter/$name";
+                $plugin->displayname    = $name;
 
                 $plugin->load_db_version();
 
@@ -2721,11 +2721,6 @@ class plugininfo_filter extends plugininfo_base {
      * @see load_version_php()
      */
     protected function load_version_php() {
-        if (strpos($this->name, 'mod_') === 0) {
-            // filters bundled with modules do not have a version.php and so
-            // do not provide their own versioning information.
-            return new stdClass();
-        }
         return parent::load_version_php();
     }
 
@@ -2733,8 +2728,7 @@ class plugininfo_filter extends plugininfo_base {
 
         $globalstates = self::get_global_states();
 
-        foreach ($globalstates as $filterlegacyname => $info) {
-            $name = self::normalize_legacy_name($filterlegacyname);
+        foreach ($globalstates as $name => $info) {
             if ($name === $this->name) {
                 if ($info->active == TEXTFILTER_DISABLED) {
                     return false;
@@ -2753,8 +2747,7 @@ class plugininfo_filter extends plugininfo_base {
         if (!isset($globalstates[$this->name])) {
             return parent::get_settings_section_name();
         }
-        $legacyname = $globalstates[$this->name]->legacyname;
-        return 'filtersetting' . str_replace('/', '', $legacyname);
+        return 'filtersetting' . $this->name;
     }
 
     public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
@@ -2776,33 +2769,7 @@ class plugininfo_filter extends plugininfo_base {
     }
 
     public function get_uninstall_url() {
-
-        if (strpos($this->name, 'mod_') === 0) {
-            return null;
-        } else {
-            $globalstates = self::get_global_states();
-            $legacyname = $globalstates[$this->name]->legacyname;
-            return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $legacyname, 'action' => 'delete'));
-        }
-    }
-
-    /**
-     * Convert legacy filter names like 'filter/foo' or 'mod/bar' into frankenstyle
-     *
-     * @param string $legacyfiltername legacy filter name
-     * @return string frankenstyle-like name
-     */
-    protected static function normalize_legacy_name($legacyfiltername) {
-
-        $name = str_replace('/', '_', $legacyfiltername);
-        if (strpos($name, 'filter_') === 0) {
-            $name = substr($name, 7);
-            if (empty($name)) {
-                throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername);
-            }
-        }
-
-        return $name;
+        return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $this->name, 'action' => 'delete'));
     }
 
     /**
@@ -2826,10 +2793,8 @@ class plugininfo_filter extends plugininfo_base {
                 $globalstatescache = array();
 
             } else {
-                foreach (filter_get_global_states() as $legacyname => $info) {
-                    $name                       = self::normalize_legacy_name($legacyname);
+                foreach (filter_get_global_states() as $name => $info) {
                     $filterinfo                 = new stdClass();
-                    $filterinfo->legacyname     = $legacyname;
                     $filterinfo->active         = $info->active;
                     $filterinfo->sortorder      = $info->sortorder;
                     $globalstatescache[$name]   = $filterinfo;
index dda8714..6046e66 100644 (file)
@@ -207,10 +207,21 @@ function rss_get_file_full_name($componentname, $filename) {
  *
  * @param stdClass $instance the instance of the source of the RSS feed
  * @param string $sql the SQL used to produce the RSS feed
+ * @param array $params the parameters used in the SQL query
  * @return string the name of the RSS file
  */
-function rss_get_file_name($instance, $sql) {
-    return $instance->id.'_'.md5($sql);
+function rss_get_file_name($instance, $sql, $params = array()) {
+    if ($params) {
+        // If a parameters array is passed, then we want to
+        // serialize it and then concatenate it with the sql.
+        // The reason for this is to generate a unique filename
+        // for queries using the same sql but different parameters.
+        asort($parms);
+        $serializearray = serialize($params);
+        return $instance->id.'_'.md5($sql . $serializearray);
+    } else {
+        return $instance->id.'_'.md5($sql);
+    }
 }
 
 /**
index b220781..2d35b9d 100644 (file)
@@ -44,7 +44,7 @@ class filter_active_global_testcase extends advanced_testcase {
     private function assert_only_one_filter_globally($filter, $state) {
         global $DB;
         $recs = $DB->get_records('filter_active');
-        $this->assertEquals(1, count($recs), 'More than one record returned %s.');
+        $this->assertCount(1, $recs);
         $rec = reset($recs);
         unset($rec->id);
         $expectedrec = new stdClass();
@@ -71,25 +71,25 @@ class filter_active_global_testcase extends advanced_testcase {
     public function test_set_filter_globally_on() {
         // Setup fixture.
         // Exercise SUT.
-        filter_set_global_state('filter/name', TEXTFILTER_ON, 1);
+        filter_set_global_state('name', TEXTFILTER_ON);
         // Validate.
-        $this->assert_only_one_filter_globally('filter/name', TEXTFILTER_ON);
+        $this->assert_only_one_filter_globally('name', TEXTFILTER_ON);
     }
 
     public function test_set_filter_globally_off() {
         // Setup fixture.
         // Exercise SUT.
-        filter_set_global_state('filter/name', TEXTFILTER_OFF, 1);
+        filter_set_global_state('name', TEXTFILTER_OFF);
         // Validate.
-        $this->assert_only_one_filter_globally('filter/name', TEXTFILTER_OFF);
+        $this->assert_only_one_filter_globally('name', TEXTFILTER_OFF);
     }
 
     public function test_set_filter_globally_disabled() {
         // Setup fixture.
         // Exercise SUT.
-        filter_set_global_state('filter/name', TEXTFILTER_DISABLED, 1);
+        filter_set_global_state('name', TEXTFILTER_DISABLED);
         // Validate.
-        $this->assert_only_one_filter_globally('filter/name', TEXTFILTER_DISABLED);
+        $this->assert_only_one_filter_globally('name', TEXTFILTER_DISABLED);
     }
 
     /**
@@ -97,135 +97,93 @@ class filter_active_global_testcase extends advanced_testcase {
      * @return void
      */
     public function test_global_config_exception_on_invalid_state() {
-        filter_set_global_state('filter/name', 0, 1);
+        filter_set_global_state('name', 0);
     }
 
-    public function test_set_no_sortorder_clash() {
+    public function test_auto_sort_order() {
         // Setup fixture.
         // Exercise SUT.
-        filter_set_global_state('filter/one', TEXTFILTER_DISABLED, 1);
-        filter_set_global_state('filter/two', TEXTFILTER_DISABLED, 1);
-        // Validate - should have pushed other filters down.
-        $this->assert_global_sort_order(array('filter/two', 'filter/one'));
-    }
-
-    public function test_auto_sort_order_disabled() {
-        // Setup fixture.
-        // Exercise SUT.
-        filter_set_global_state('filter/one', TEXTFILTER_DISABLED);
-        filter_set_global_state('filter/two', TEXTFILTER_DISABLED);
+        filter_set_global_state('one', TEXTFILTER_DISABLED);
+        filter_set_global_state('two', TEXTFILTER_DISABLED);
         // Validate.
-        $this->assert_global_sort_order(array('filter/one', 'filter/two'));
+        $this->assert_global_sort_order(array('one', 'two'));
     }
 
     public function test_auto_sort_order_enabled() {
         // Setup fixture.
         // Exercise SUT.
-        filter_set_global_state('filter/one', TEXTFILTER_ON);
-        filter_set_global_state('filter/two', TEXTFILTER_OFF);
+        filter_set_global_state('one', TEXTFILTER_ON);
+        filter_set_global_state('two', TEXTFILTER_OFF);
         // Validate.
-        $this->assert_global_sort_order(array('filter/one', 'filter/two'));
-    }
-
-    public function test_auto_sort_order_mixed() {
-        // Setup fixture.
-        // Exercise SUT.
-        filter_set_global_state('filter/0', TEXTFILTER_DISABLED);
-        filter_set_global_state('filter/1', TEXTFILTER_ON);
-        filter_set_global_state('filter/2', TEXTFILTER_DISABLED);
-        filter_set_global_state('filter/3', TEXTFILTER_OFF);
-        // Validate.
-        $this->assert_global_sort_order(array('filter/1', 'filter/3', 'filter/0', 'filter/2'));
+        $this->assert_global_sort_order(array('one', 'two'));
     }
 
     public function test_update_existing_dont_duplicate() {
         // Setup fixture.
         // Exercise SUT.
-        filter_set_global_state('filter/name', TEXTFILTER_ON);
-        filter_set_global_state('filter/name', TEXTFILTER_OFF);
+        filter_set_global_state('name', TEXTFILTER_ON);
+        filter_set_global_state('name', TEXTFILTER_OFF);
         // Validate.
-        $this->assert_only_one_filter_globally('filter/name', TEXTFILTER_OFF);
-    }
-
-    /**
-     * @expectedException coding_exception
-     * @return void
-     */
-    public function test_sort_order_not_too_low() {
-        // Setup fixture.
-        filter_set_global_state('filter/1', TEXTFILTER_ON);
-        // Exercise SUT.
-        filter_set_global_state('filter/2', TEXTFILTER_ON, 0);
-    }
-
-    /**
-     * @expectedException coding_exception
-     * @return void
-     */
-    public function test_sort_order_not_too_high() {
-        // Setup fixture.
-        filter_set_global_state('filter/1', TEXTFILTER_ON);
-        // Exercise SUT.
-        filter_set_global_state('filter/2', TEXTFILTER_ON, 3);
+        $this->assert_only_one_filter_globally('name', TEXTFILTER_OFF);
     }
 
     public function test_update_reorder_down() {
         // Setup fixture.
-        filter_set_global_state('filter/1', TEXTFILTER_ON);
-        filter_set_global_state('filter/2', TEXTFILTER_ON);
-        filter_set_global_state('filter/3', TEXTFILTER_ON);
+        filter_set_global_state('one', TEXTFILTER_ON);
+        filter_set_global_state('two', TEXTFILTER_ON);
+        filter_set_global_state('three', TEXTFILTER_ON);
         // Exercise SUT.
-        filter_set_global_state('filter/2', TEXTFILTER_ON, 1);
+        filter_set_global_state('two', TEXTFILTER_ON, -1);
         // Validate.
-        $this->assert_global_sort_order(array('filter/2', 'filter/1', 'filter/3'));
+        $this->assert_global_sort_order(array('two', 'one', 'three'));
     }
 
     public function test_update_reorder_up() {
         // Setup fixture.
-        filter_set_global_state('filter/1', TEXTFILTER_ON);
-        filter_set_global_state('filter/2', TEXTFILTER_ON);
-        filter_set_global_state('filter/3', TEXTFILTER_ON);
-        filter_set_global_state('filter/4', TEXTFILTER_ON);
+        filter_set_global_state('one', TEXTFILTER_ON);
+        filter_set_global_state('two', TEXTFILTER_ON);
+        filter_set_global_state('three', TEXTFILTER_ON);
+        filter_set_global_state('four', TEXTFILTER_ON);
         // Exercise SUT.
-        filter_set_global_state('filter/2', TEXTFILTER_ON, 3);
+        filter_set_global_state('two', TEXTFILTER_ON, 1);
         // Validate.
-        $this->assert_global_sort_order(array('filter/1', 'filter/3', 'filter/2', 'filter/4'));
+        $this->assert_global_sort_order(array('one', 'three', 'two', 'four'));
     }
 
     public function test_auto_sort_order_change_to_enabled() {
         // Setup fixture.
-        filter_set_global_state('filter/1', TEXTFILTER_ON);
-        filter_set_global_state('filter/2', TEXTFILTER_DISABLED);
-        filter_set_global_state('filter/3', TEXTFILTER_DISABLED);
+        filter_set_global_state('one', TEXTFILTER_ON);
+        filter_set_global_state('two', TEXTFILTER_DISABLED);
+        filter_set_global_state('three', TEXTFILTER_DISABLED);
         // Exercise SUT.
-        filter_set_global_state('filter/3', TEXTFILTER_ON);
+        filter_set_global_state('three', TEXTFILTER_ON);
         // Validate.
-        $this->assert_global_sort_order(array('filter/1', 'filter/3', 'filter/2'));
+        $this->assert_global_sort_order(array('one', 'three', 'two'));
     }
 
     public function test_auto_sort_order_change_to_disabled() {
         // Setup fixture.
-        filter_set_global_state('filter/1', TEXTFILTER_ON);
-        filter_set_global_state('filter/2', TEXTFILTER_ON);
-        filter_set_global_state('filter/3', TEXTFILTER_DISABLED);
+        filter_set_global_state('one', TEXTFILTER_ON);
+        filter_set_global_state('two', TEXTFILTER_ON);
+        filter_set_global_state('three', TEXTFILTER_DISABLED);
         // Exercise SUT.
-        filter_set_global_state('filter/1', TEXTFILTER_DISABLED);
+        filter_set_global_state('one', TEXTFILTER_DISABLED);
         // Validate.
-        $this->assert_global_sort_order(array('filter/2', 'filter/1', 'filter/3'));
+        $this->assert_global_sort_order(array('two', 'one', 'three'));
     }
 
     public function test_filter_get_global_states() {
         // Setup fixture.
-        filter_set_global_state('filter/1', TEXTFILTER_ON);
-        filter_set_global_state('filter/2', TEXTFILTER_OFF);
-        filter_set_global_state('filter/3', TEXTFILTER_DISABLED);
+        filter_set_global_state('one', TEXTFILTER_ON);
+        filter_set_global_state('two', TEXTFILTER_OFF);
+        filter_set_global_state('three', TEXTFILTER_DISABLED);
         // Exercise SUT.
         $filters = filter_get_global_states();
         // Validate.
         $this->assertEquals(array(
-            'filter/1' => (object) array('filter' => 'filter/1', 'active' => TEXTFILTER_ON, 'sortorder' => 1),
-            'filter/2' => (object) array('filter' => 'filter/2', 'active' => TEXTFILTER_OFF, 'sortorder' => 2),
-            'filter/3' => (object) array('filter' => 'filter/3', 'active' => TEXTFILTER_DISABLED, 'sortorder' => 3)
+            'one' => (object) array('filter' => 'one', 'active' => TEXTFILTER_ON, 'sortorder' => 1),
+            'two' => (object) array('filter' => 'two', 'active' => TEXTFILTER_OFF, 'sortorder' => 2),
+            'three' => (object) array('filter' => 'three', 'active' => TEXTFILTER_DISABLED, 'sortorder' => 3)
         ), $filters);
     }
 }
@@ -264,21 +222,21 @@ class filter_active_local_testcase extends advanced_testcase {
 
     public function test_local_on() {
         // Exercise SUT.
-        filter_set_local_state('filter/name', 123, TEXTFILTER_ON);
+        filter_set_local_state('name', 123, TEXTFILTER_ON);
         // Validate.
-        $this->assert_only_one_local_setting('filter/name', 123, TEXTFILTER_ON);
+        $this->assert_only_one_local_setting('name', 123, TEXTFILTER_ON);
     }
 
     public function test_local_off() {
         // Exercise SUT.
-        filter_set_local_state('filter/name', 123, TEXTFILTER_OFF);
+        filter_set_local_state('name', 123, TEXTFILTER_OFF);
         // Validate.
-        $this->assert_only_one_local_setting('filter/name', 123, TEXTFILTER_OFF);
+        $this->assert_only_one_local_setting('name', 123, TEXTFILTER_OFF);
     }
 
     public function test_local_inherit() {
         // Exercise SUT.
-        filter_set_local_state('filter/name', 123, TEXTFILTER_INHERIT);
+        filter_set_local_state('name', 123, TEXTFILTER_INHERIT);
         // Validate.
         $this->assert_no_local_setting();
     }
@@ -289,7 +247,7 @@ class filter_active_local_testcase extends advanced_testcase {
      */
     public function test_local_invalid_state_throws_exception() {
         // Exercise SUT.
-        filter_set_local_state('filter/name', 123, -9999);
+        filter_set_local_state('name', 123, -9999);
     }
 
     /**
@@ -298,14 +256,14 @@ class filter_active_local_testcase extends advanced_testcase {
      */
     public function test_throws_exception_when_setting_global() {
         // Exercise SUT.
-        filter_set_local_state('filter/name', context_system::instance()->id, TEXTFILTER_INHERIT);
+        filter_set_local_state('name', context_system::instance()->id, TEXTFILTER_INHERIT);
     }
 
     public function test_local_inherit_deletes_existing() {
         // Setup fixture.
-        filter_set_local_state('filter/name', 123, TEXTFILTER_INHERIT);
+        filter_set_local_state('name', 123, TEXTFILTER_INHERIT);
         // Exercise SUT.
-        filter_set_local_state('filter/name', 123, TEXTFILTER_INHERIT);
+        filter_set_local_state('name', 123, TEXTFILTER_INHERIT);
         // Validate.
         $this->assert_no_local_setting();
     }
@@ -340,28 +298,28 @@ class filter_config_testcase extends advanced_testcase {
 
     public function test_set_new_config() {
         // Exercise SUT.
-        filter_set_local_config('filter/name', 123, 'settingname', 'An arbitrary value');
+        filter_set_local_config('name', 123, 'settingname', 'An arbitrary value');
         // Validate.
-        $this->assert_only_one_config('filter/name', 123, 'settingname', 'An arbitrary value');
+        $this->assert_only_one_config('name', 123, 'settingname', 'An arbitrary value');
     }
 
     public function test_update_existing_config() {
         // Setup fixture.
-        filter_set_local_config('filter/name', 123, 'settingname', 'An arbitrary value');
+        filter_set_local_config('name', 123, 'settingname', 'An arbitrary value');
         // Exercise SUT.
-        filter_set_local_config('filter/name', 123, 'settingname', 'A changed value');
+        filter_set_local_config('name', 123, 'settingname', 'A changed value');
         // Validate.
-        $this->assert_only_one_config('filter/name', 123, 'settingname', 'A changed value');
+        $this->assert_only_one_config('name', 123, 'settingname', 'A changed value');
     }
 
     public function test_filter_get_local_config() {
         // Setup fixture.
-        filter_set_local_config('filter/name', 123, 'setting1', 'An arbitrary value');
-        filter_set_local_config('filter/name', 123, 'setting2', 'Another arbitrary value');
-        filter_set_local_config('filter/name', 122, 'settingname', 'Value from another context');
-        filter_set_local_config('filter/other', 123, 'settingname', 'Someone else\'s value');
+        filter_set_local_config('name', 123, 'setting1', 'An arbitrary value');
+        filter_set_local_config('name', 123, 'setting2', 'Another arbitrary value');
+        filter_set_local_config('name', 122, 'settingname', 'Value from another context');
+        filter_set_local_config('other', 123, 'settingname', 'Someone else\'s value');
         // Exercise SUT.
-        $config = filter_get_local_config('filter/name', 123);
+        $config = filter_get_local_config('name', 123);
         // Validate.
         $this->assertEquals(array('setting1' => 'An arbitrary value', 'setting2' => 'Another arbitrary value'), $config);
     }
@@ -398,18 +356,18 @@ class filter_get_active_available_in_context_testcase extends advanced_testcase
 
     public function test_globally_on_is_returned() {
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_ON);
+        filter_set_global_state('name', TEXTFILTER_ON);
         // Exercise SUT.
         $filters = filter_get_active_in_context(self::$syscontext);
         // Validate.
-        $this->assert_filter_list(array('filter/name'), $filters);
+        $this->assert_filter_list(array('name'), $filters);
         // Check no config returned correctly.
-        $this->assertEquals(array(), $filters['filter/name']);
+        $this->assertEquals(array(), $filters['name']);
     }
 
     public function test_globally_off_not_returned() {
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_OFF);
+        filter_set_global_state('name', TEXTFILTER_OFF);
         // Exercise SUT.
         $filters = filter_get_active_in_context(self::$childcontext2);
         // Validate.
@@ -418,18 +376,18 @@ class filter_get_active_available_in_context_testcase extends advanced_testcase
 
     public function test_globally_off_overridden() {
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_OFF);
-        filter_set_local_state('filter/name', self::$childcontext->id, TEXTFILTER_ON);
+        filter_set_global_state('name', TEXTFILTER_OFF);
+        filter_set_local_state('name', self::$childcontext->id, TEXTFILTER_ON);
         // Exercise SUT.
         $filters = filter_get_active_in_context(self::$childcontext2);
         // Validate.
-        $this->assert_filter_list(array('filter/name'), $filters);
+        $this->assert_filter_list(array('name'), $filters);
     }
 
     public function test_globally_on_overridden() {
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_ON);
-        filter_set_local_state('filter/name', self::$childcontext->id, TEXTFILTER_OFF);
+        filter_set_global_state('name', TEXTFILTER_ON);
+        filter_set_local_state('name', self::$childcontext->id, TEXTFILTER_OFF);
         // Exercise SUT.
         $filters = filter_get_active_in_context(self::$childcontext2);
         // Validate.
@@ -438,8 +396,8 @@ class filter_get_active_available_in_context_testcase extends advanced_testcase
 
     public function test_globally_disabled_not_overridden() {
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_DISABLED);
-        filter_set_local_state('filter/name', self::$childcontext->id, TEXTFILTER_ON);
+        filter_set_global_state('name', TEXTFILTER_DISABLED);
+        filter_set_local_state('name', self::$childcontext->id, TEXTFILTER_ON);
         // Exercise SUT.
         $filters = filter_get_active_in_context(self::$syscontext);
         // Validate.
@@ -448,45 +406,45 @@ class filter_get_active_available_in_context_testcase extends advanced_testcase
 
     public function test_single_config_returned() {
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_ON);
-        filter_set_local_config('filter/name', self::$childcontext->id, 'settingname', 'A value');
+        filter_set_global_state('name', TEXTFILTER_ON);
+        filter_set_local_config('name', self::$childcontext->id, 'settingname', 'A value');
         // Exercise SUT.
         $filters = filter_get_active_in_context(self::$childcontext);
         // Validate.
-        $this->assertEquals(array('settingname' => 'A value'), $filters['filter/name']);
+        $this->assertEquals(array('settingname' => 'A value'), $filters['name']);
     }
 
     public function test_multi_config_returned() {
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_ON);
-        filter_set_local_config('filter/name', self::$childcontext->id, 'settingname', 'A value');
-        filter_set_local_config('filter/name', self::$childcontext->id, 'anothersettingname', 'Another value');
+        filter_set_global_state('name', TEXTFILTER_ON);
+        filter_set_local_config('name', self::$childcontext->id, 'settingname', 'A value');
+        filter_set_local_config('name', self::$childcontext->id, 'anothersettingname', 'Another value');
         // Exercise SUT.
         $filters = filter_get_active_in_context(self::$childcontext);
         // Validate.
-        $this->assertEquals(array('settingname' => 'A value', 'anothersettingname' => 'Another value'), $filters['filter/name']);
+        $this->assertEquals(array('settingname' => 'A value', 'anothersettingname' => 'Another value'), $filters['name']);
     }
 
     public function test_config_from_other_context_not_returned() {
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_ON);
-        filter_set_local_config('filter/name', self::$childcontext->id, 'settingname', 'A value');
-        filter_set_local_config('filter/name', self::$childcontext2->id, 'anothersettingname', 'Another value');
+        filter_set_global_state('name', TEXTFILTER_ON);
+        filter_set_local_config('name', self::$childcontext->id, 'settingname', 'A value');
+        filter_set_local_config('name', self::$childcontext2->id, 'anothersettingname', 'Another value');
         // Exercise SUT.
         $filters = filter_get_active_in_context(self::$childcontext2);
         // Validate.
-        $this->assertEquals(array('anothersettingname' => 'Another value'), $filters['filter/name']);
+        $this->assertEquals(array('anothersettingname' => 'Another value'), $filters['name']);
     }
 
     public function test_config_from_other_filter_not_returned() {
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_ON);
-        filter_set_local_config('filter/name', self::$childcontext->id, 'settingname', 'A value');
-        filter_set_local_config('filter/other', self::$childcontext->id, 'anothersettingname', 'Another value');
+        filter_set_global_state('name', TEXTFILTER_ON);
+        filter_set_local_config('name', self::$childcontext->id, 'settingname', 'A value');
+        filter_set_local_config('other', self::$childcontext->id, 'anothersettingname', 'Another value');
         // Exercise SUT.
         $filters = filter_get_active_in_context(self::$childcontext);
         // Validate.
-        $this->assertEquals(array('settingname' => 'A value'), $filters['filter/name']);
+        $this->assertEquals(array('settingname' => 'A value'), $filters['name']);
     }
 
     protected function assert_one_available_filter($filter, $localstate, $inheritedstate, $filters) {
@@ -502,28 +460,28 @@ class filter_get_active_available_in_context_testcase extends advanced_testcase
 
     public function test_available_in_context_localoverride() {
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_ON);
-        filter_set_local_state('filter/name', self::$childcontext->id, TEXTFILTER_OFF);
+        filter_set_global_state('name', TEXTFILTER_ON);
+        filter_set_local_state('name', self::$childcontext->id, TEXTFILTER_OFF);
         // Exercise SUT.
         $filters = filter_get_available_in_context(self::$childcontext);
         // Validate.
-        $this->assert_one_available_filter('filter/name', TEXTFILTER_OFF, TEXTFILTER_ON, $filters);
+        $this->assert_one_available_filter('name', TEXTFILTER_OFF, TEXTFILTER_ON, $filters);
     }
 
     public function test_available_in_context_nolocaloverride() {
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_ON);
-        filter_set_local_state('filter/name', self::$childcontext->id, TEXTFILTER_OFF);
+        filter_set_global_state('name', TEXTFILTER_ON);
+        filter_set_local_state('name', self::$childcontext->id, TEXTFILTER_OFF);
         // Exercise SUT.
         $filters = filter_get_available_in_context(self::$childcontext2);
         // Validate.
-        $this->assert_one_available_filter('filter/name', TEXTFILTER_INHERIT, TEXTFILTER_OFF, $filters);
+        $this->assert_one_available_filter('name', TEXTFILTER_INHERIT, TEXTFILTER_OFF, $filters);
     }
 
     public function test_available_in_context_disabled_not_returned() {
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_DISABLED);
-        filter_set_local_state('filter/name', self::$childcontext->id, TEXTFILTER_ON);
+        filter_set_global_state('name', TEXTFILTER_DISABLED);
+        filter_set_local_state('name', self::$childcontext->id, TEXTFILTER_ON);
         // Exercise SUT.
         $filters = filter_get_available_in_context(self::$childcontext);
         // Validate.
@@ -610,43 +568,43 @@ class filter_preload_activities_testcase extends advanced_testcase {
         $this->assert_matches($modinfo);
 
         // Enable filter globally, check
-        filter_set_global_state('filter/name', TEXTFILTER_ON);
+        filter_set_global_state('name', TEXTFILTER_ON);
         $this->assert_matches($modinfo);
 
         // Disable for activity 2
-        filter_set_local_state('filter/name', self::$activity2context->id, TEXTFILTER_OFF);
+        filter_set_local_state('name', self::$activity2context->id, TEXTFILTER_OFF);
         $this->assert_matches($modinfo);
 
         // Disable at category
-        filter_set_local_state('filter/name', self::$catcontext->id, TEXTFILTER_OFF);
+        filter_set_local_state('name', self::$catcontext->id, TEXTFILTER_OFF);
         $this->assert_matches($modinfo);
 
         // Enable for activity 1
-        filter_set_local_state('filter/name', self::$activity1context->id, TEXTFILTER_ON);
+        filter_set_local_state('name', self::$activity1context->id, TEXTFILTER_ON);
         $this->assert_matches($modinfo);
 
         // Disable globally
-        filter_set_global_state('filter/name', TEXTFILTER_DISABLED);
+        filter_set_global_state('name', TEXTFILTER_DISABLED);
         $this->assert_matches($modinfo);
 
         // Add another 2 filters
-        filter_set_global_state('filter/frog', TEXTFILTER_ON);
-        filter_set_global_state('filter/zombie', TEXTFILTER_ON);
+        filter_set_global_state('frog', TEXTFILTER_ON);
+        filter_set_global_state('zombie', TEXTFILTER_ON);
         $this->assert_matches($modinfo);
 
         // Disable random one of these in each context
-        filter_set_local_state('filter/zombie', self::$activity1context->id, TEXTFILTER_OFF);
-        filter_set_local_state('filter/frog', self::$activity2context->id, TEXTFILTER_OFF);
+        filter_set_local_state('zombie', self::$activity1context->id, TEXTFILTER_OFF);
+        filter_set_local_state('frog', self::$activity2context->id, TEXTFILTER_OFF);
         $this->assert_matches($modinfo);
 
         // Now do some filter options
-        filter_set_local_config('filter/name', self::$activity1context->id, 'a', 'x');
-        filter_set_local_config('filter/zombie', self::$activity1context->id, 'a', 'y');
-        filter_set_local_config('filter/frog', self::$activity1context->id, 'a', 'z');
+        filter_set_local_config('name', self::$activity1context->id, 'a', 'x');
+        filter_set_local_config('zombie', self::$activity1context->id, 'a', 'y');
+        filter_set_local_config('frog', self::$activity1context->id, 'a', 'z');
         // These last two don't do anything as they are not at final level but I
         // thought it would be good to have that verified in test
-        filter_set_local_config('filter/frog', self::$coursecontext->id, 'q', 'x');
-        filter_set_local_config('filter/frog', self::$catcontext->id, 'q', 'z');
+        filter_set_local_config('frog', self::$coursecontext->id, 'q', 'x');
+        filter_set_local_config('frog', self::$catcontext->id, 'q', 'z');
         $this->assert_matches($modinfo);
     }
 }
@@ -666,19 +624,19 @@ class filter_delete_config_testcase extends advanced_testcase {
         global $DB;
 
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_ON);
-        filter_set_global_state('filter/other', TEXTFILTER_ON);
-        filter_set_local_config('filter/name', context_system::instance()->id, 'settingname', 'A value');
-        filter_set_local_config('filter/other', context_system::instance()->id, 'settingname', 'Other value');
+        filter_set_global_state('name', TEXTFILTER_ON);
+        filter_set_global_state('other', TEXTFILTER_ON);
+        filter_set_local_config('name', context_system::instance()->id, 'settingname', 'A value');
+        filter_set_local_config('other', context_system::instance()->id, 'settingname', 'Other value');
         set_config('configname', 'A config value', 'filter_name');
         set_config('configname', 'Other config value', 'filter_other');
         // Exercise SUT.
-        filter_delete_all_for_filter('filter/name');
+        filter_delete_all_for_filter('name');
         // Validate.
         $this->assertEquals(1, $DB->count_records('filter_active'));
-        $this->assertTrue($DB->record_exists('filter_active', array('filter' => 'filter/other')));
+        $this->assertTrue($DB->record_exists('filter_active', array('filter' => 'other')));
         $this->assertEquals(1, $DB->count_records('filter_config'));
-        $this->assertTrue($DB->record_exists('filter_config', array('filter' => 'filter/other')));
+        $this->assertTrue($DB->record_exists('filter_config', array('filter' => 'other')));
         $expectedconfig = new stdClass;
         $expectedconfig->configname = 'Other config value';
         $this->assertEquals($expectedconfig, get_config('filter_other'));
@@ -689,18 +647,18 @@ class filter_delete_config_testcase extends advanced_testcase {
         global $DB;
 
         // Setup fixture.
-        filter_set_global_state('filter/name', TEXTFILTER_ON);
-        filter_set_local_state('filter/name', 123, TEXTFILTER_OFF);
-        filter_set_local_config('filter/name', 123, 'settingname', 'A value');
-        filter_set_local_config('filter/other', 123, 'settingname', 'Other value');
-        filter_set_local_config('filter/other', 122, 'settingname', 'Other value');
+        filter_set_global_state('name', TEXTFILTER_ON);
+        filter_set_local_state('name', 123, TEXTFILTER_OFF);
+        filter_set_local_config('name', 123, 'settingname', 'A value');
+        filter_set_local_config('other', 123, 'settingname', 'Other value');
+        filter_set_local_config('other', 122, 'settingname', 'Other value');
         // Exercise SUT.
         filter_delete_all_for_context(123);
         // Validate.
         $this->assertEquals(1, $DB->count_records('filter_active'));
         $this->assertTrue($DB->record_exists('filter_active', array('contextid' => context_system::instance()->id)));
         $this->assertEquals(1, $DB->count_records('filter_config'));
-        $this->assertTrue($DB->record_exists('filter_config', array('filter' => 'filter/other')));
+        $this->assertTrue($DB->record_exists('filter_config', array('filter' => 'other')));
     }
 }
 
@@ -735,9 +693,9 @@ class filter_filter_set_applies_to_strings extends advanced_testcase {
         $CFG->filterall = 0;
         $CFG->stringfilters = '';
         // Exercise SUT.
-        filter_set_applies_to_strings('filter/name', true);
+        filter_set_applies_to_strings('name', true);
         // Validate.
-        $this->assertEquals('filter/name', $CFG->stringfilters);
+        $this->assertEquals('name', $CFG->stringfilters);
         $this->assertEquals(1, $CFG->filterall);
     }
 
@@ -745,9 +703,9 @@ class filter_filter_set_applies_to_strings extends advanced_testcase {
         global $CFG;
         // Setup fixture.
         $CFG->filterall = 1;
-        $CFG->stringfilters = 'filter/name';
+        $CFG->stringfilters = 'name';
         // Exercise SUT.
-        filter_set_applies_to_strings('filter/name', false);
+        filter_set_applies_to_strings('name', false);
         // Validate.
         $this->assertEquals('', $CFG->stringfilters);
         $this->assertEquals('', $CFG->filterall);
@@ -757,11 +715,11 @@ class filter_filter_set_applies_to_strings extends advanced_testcase {
         global $CFG;
         // Setup fixture.
         $CFG->filterall = 1;
-        $CFG->stringfilters = 'filter/name,filter/other';
+        $CFG->stringfilters = 'name,other';
         // Exercise SUT.
-        filter_set_applies_to_strings('filter/name', false);
+        filter_set_applies_to_strings('name', false);
         // Validate.
-        $this->assertEquals('filter/other', $CFG->stringfilters);
+        $this->assertEquals('other', $CFG->stringfilters);
         $this->assertEquals(1, $CFG->filterall);
     }
 }
index e45f17f..35f500d 100644 (file)
@@ -293,8 +293,8 @@ class core_textlib_testcase extends advanced_testcase {
      * @return void
      */
     public function test_entities_to_utf8() {
-        $str = "&#x17d;lu&#x165;ou&#x10d;k&#xfd; kon&#237;&#269;ek";
-        $this->assertSame(textlib::entities_to_utf8($str), "Žluťoučký koníček");
+        $str = "&#x17d;lu&#x165;ou&#x10d;k&#xfd; kon&iacute;&#269;ek&copy;&quot;&amp;&lt;&gt;&sect;&laquo;";
+        $this->assertSame("Žluťoučký koníček©\"&<>§«", textlib::entities_to_utf8($str));
     }
 
     /**
@@ -302,10 +302,13 @@ class core_textlib_testcase extends advanced_testcase {
      * @return void
      */
     public function test_utf8_to_entities() {
-        $str = "Žluťoučký koníček";
-        $this->assertSame(textlib::utf8_to_entities($str), "&#x17d;lu&#x165;ou&#x10d;k&#xfd; kon&#xed;&#x10d;ek");
-        $this->assertSame(textlib::utf8_to_entities($str, true), "&#381;lu&#357;ou&#269;k&#253; kon&#237;&#269;ek");
+        $str = "&#x17d;luťoučký kon&iacute;ček&copy;&quot;&amp;&lt;&gt;&sect;&laquo;";
+        $this->assertSame("&#x17d;lu&#x165;ou&#x10d;k&#xfd; kon&iacute;&#x10d;ek&copy;&quot;&amp;&lt;&gt;&sect;&laquo;", textlib::utf8_to_entities($str));
+        $this->assertSame("&#381;lu&#357;ou&#269;k&#253; kon&iacute;&#269;ek&copy;&quot;&amp;&lt;&gt;&sect;&laquo;", textlib::utf8_to_entities($str, true));
 
+        $str = "&#381;luťoučký kon&iacute;ček&copy;&quot;&amp;&lt;&gt;&sect;&laquo;";
+        $this->assertSame("&#x17d;lu&#x165;ou&#x10d;k&#xfd; kon&#xed;&#x10d;ek&#xa9;\"&<>&#xa7;&#xab;", textlib::utf8_to_entities($str, false, true));
+        $this->assertSame("&#381;lu&#357;ou&#269;k&#253; kon&#237;&#269;ek&#169;\"&<>&#167;&#171;", textlib::utf8_to_entities($str, true, true));
     }
 
     /**
index ab0db3c..bddcaa7 100644 (file)
@@ -441,6 +441,34 @@ class textlib {
         return $encoded;
     }
 
+    /**
+     * Returns HTML entity transliteration table.
+     * @return array with (html entity => utf-8) elements
+     */
+    protected static function get_entities_table() {
+        static $trans_tbl = null;
+
+        // Generate/create $trans_tbl
+        if (!isset($trans_tbl)) {
+            if (version_compare(phpversion(), '5.3.4') < 0) {
+                $trans_tbl = array();
+                foreach (get_html_translation_table(HTML_ENTITIES) as $val=>$key) {
+                    $trans_tbl[$key] = textlib::convert($val, 'ISO-8859-1', 'utf-8');
+                }
+
+            } else if (version_compare(phpversion(), '5.4.0') < 0) {
+                $trans_tbl = get_html_translation_table(HTML_ENTITIES, ENT_COMPAT, 'UTF-8');
+                $trans_tbl = array_flip($trans_tbl);
+
+            } else {
+                $trans_tbl = get_html_translation_table(HTML_ENTITIES, ENT_COMPAT | ENT_HTML401, 'UTF-8');
+                $trans_tbl = array_flip($trans_tbl);
+            }
+        }
+
+        return $trans_tbl;
+    }
+
     /**
      * Converts all the numeric entities &#nnnn; or &#xnnn; to UTF-8
      * Original from laurynas dot butkus at gmail at:
@@ -450,28 +478,24 @@ class textlib {
      * @param string $str input string
      * @param boolean $htmlent convert also html entities (defaults to true)
      * @return string encoded UTF-8 string
-     *
-     * NOTE: we could have used typo3 entities_to_utf8() here
-     *       but the direct alternative used runs 400% quicker
-     *       and uses 0.5Mb less memory, so, let's use it
-     *       (tested against 10^6 conversions)
      */
     public static function entities_to_utf8($str, $htmlent=true) {
-        static $trans_tbl; // Going to use static transliteration table
+        static $callback1 = null ;
+        static $callback2 = null ;
+
+        if (!$callback1 or !$callback2) {
+            $callback1 = create_function('$matches', 'return textlib::code2utf8(hexdec($matches[1]));');
+            $callback2 = create_function('$matches', 'return textlib::code2utf8($matches[1]);');
+        }
 
-        // Replace numeric entities
-        $result = preg_replace('~&#x([0-9a-f]+);~ei', 'textlib::code2utf8(hexdec("\\1"))', $str);
-        $result = preg_replace('~&#([0-9]+);~e', 'textlib::code2utf8(\\1)', $result);
+        $result = (string)$str;
+        $result = preg_replace_callback('/&#x([0-9a-f]+);/i', $callback1, $result);
+        $result = preg_replace_callback('/&#([0-9]+);/', $callback2, $result);
 
         // Replace literal entities (if desired)
         if ($htmlent) {
-            // Generate/create $trans_tbl
-            if (!isset($trans_tbl)) {
-                $trans_tbl = array();
-                foreach (get_html_translation_table(HTML_ENTITIES) as $val=>$key) {
-                    $trans_tbl[$key] = utf8_encode($val);
-                }
-            }
+            $trans_tbl = self::get_entities_table();
+            // It should be safe to search for ascii strings and replace them with utf-8 here.
             $result = strtr($result, $trans_tbl);
         }
         // Return utf8-ised string
@@ -487,17 +511,24 @@ class textlib {
      * @return string converted string
      */
     public static function utf8_to_entities($str, $dec=false, $nonnum=false) {
-        // Avoid some notices from Typo3 code
-        $oldlevel = error_reporting(E_PARSE);
+        static $callback = null ;
+
         if ($nonnum) {
-            $str = self::typo3()->entities_to_utf8((string)$str, true);
+            $str = self::entities_to_utf8($str, true);
         }
+
+        // Avoid some notices from Typo3 code
+        $oldlevel = error_reporting(E_PARSE);
         $result = self::typo3()->utf8_to_entities((string)$str);
+        error_reporting($oldlevel);
+
         if ($dec) {
-            $result = preg_replace('/&#x([0-9a-f]+);/ie', "'&#'.hexdec('$1').';'", $result);
+            if (!$callback) {
+                $callback = create_function('$matches', 'return \'&#\'.(hexdec($matches[1])).\';\';');
+            }
+            $result = preg_replace_callback('/&#x([0-9a-f]+);/i', $callback, $result);
         }
-        // Restore original debug level
-        error_reporting($oldlevel);
+
         return $result;
     }
 
index 25b7bb9..c943f9e 100644 (file)
@@ -1384,7 +1384,7 @@ function format_text_email($text, $format) {
         case FORMAT_WIKI:
             // there should not be any of these any more!
             $text = wikify_links($text);
-            return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
+            return textlib::entities_to_utf8(strip_tags($text), true);
             break;
 
         case FORMAT_HTML:
@@ -1395,7 +1395,7 @@ function format_text_email($text, $format) {
         case FORMAT_MARKDOWN:
         default:
             $text = wikify_links($text);
-            return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
+            return textlib::entities_to_utf8(strip_tags($text), true);
             break;
     }
 }
index b355b33..c3a0441 100644 (file)
@@ -266,7 +266,7 @@ class assignfeedback_file_zip_importer {
                     $fileplugin->update_file_count($grade);
 
                     // Update the last modified time on the grade which will trigger student notifications.
-                    $assignment->update_grade($grade);
+                    $assignment->notify_grade_modified($grade);
                 }
             }
         }
index 6ed295e..7c6a12a 100644 (file)
@@ -403,6 +403,7 @@ class assign_feedback_file extends assign_feedback_plugin {
             // Now copy each of these files to the users feedback file area.
             foreach ($users as $userid) {
                 $grade = $this->assignment->get_user_grade($userid, true);
+                $this->assignment->notify_grade_modified($grade);
 
                 $this->copy_area_files($fs,
                                        $this->assignment->get_context()->id,
index d64927f..e01d7b8 100644 (file)
@@ -157,6 +157,7 @@ class assign_feedback_offline extends assign_feedback_plugin {
                 $grade->grade = $record->grade;
                 $grade->grader = $USER->id;
                 if ($this->assignment->update_grade($grade)) {
+                    $this->assignment->notify_grade_modified($grade);
                     $this->assignment->add_to_log('grade submission', $this->assignment->format_grade_for_log($grade));
                     $updatecount += 1;
                 }
@@ -178,6 +179,7 @@ class assign_feedback_offline extends assign_feedback_plugin {
                     if ($newvalue != $oldvalue) {
                         $updatecount += 1;
                         $grade = $this->assignment->get_user_grade($record->user->id, true);
+                        $this->assignment->notify_grade_modified($grade);
                         if ($plugin->set_editor_text($field, $newvalue, $grade->id)) {
                             $logdesc = get_string('feedbackupdate', 'assignfeedback_offline',
                                                   array('field'=>$description,
index 9971cd0..6b4b8ed 100644 (file)
@@ -1476,6 +1476,23 @@ class assign {
         return true;
     }
 
+    /**
+     * Mark in the database that this grade record should have an update notification sent by cron.
+     *
+     * @param stdClass $grade a grade record keyed on id
+     * @return bool true for success
+     */
+    public function notify_grade_modified($grade) {
+        global $DB;
+
+        $grade->timemodified = time();
+        if ($grade->mailed != 1) {
+            $grade->mailed = 0;
+        }
+
+        return $DB->update_record('assign_grades', $grade);
+    }
+
     /**
      * Update a grade in the grade table for the assignment and in the gradebook
      *
@@ -2175,6 +2192,10 @@ class assign {
             $grade->grade = -1;
             $grade->grader = $USER->id;
             $grade->extensionduedate = 0;
+
+            // The mailed flag can be one of 3 values: 0 is unsent, 1 is sent and 2 is do not send yet.
+            // This is because students only want to be notified about certain types of update (grades and feedback).
+            $grade->mailed = 2;
             $gid = $DB->insert_record('assign_grades', $grade);
             $grade->id = $gid;
             return $grade;
@@ -3712,6 +3733,7 @@ class assign {
             }
 
             $this->update_grade($grade);
+            $this->notify_grade_modified($grade);
 
             // save outcomes
             if ($CFG->enableoutcomes) {
@@ -4406,6 +4428,7 @@ class assign {
             }
         }
         $this->update_grade($grade);
+        $this->notify_grade_modified($grade);
         $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
 
         $this->add_to_log('grade submission', $this->format_grade_for_log($grade));
index e4745f2..5844463 100644 (file)
@@ -31,6 +31,39 @@ defined('MOODLE_INTERNAL') || die();
  * @return bool
  */
 function assignsubmission_comments_comment_validate(stdClass $options) {
+    global $USER, $CFG, $DB;
+
+    if ($options->commentarea != 'submission_comments' &&
+            $options->commentarea != 'submission_comments_upgrade') {
+        throw new comment_exception('invalidcommentarea');
+    }
+    if (!$submission = $DB->get_record('assign_submission', array('id'=>$options->itemid))) {
+        throw new comment_exception('invalidcommentitemid');
+    }
+    $context = $options->context;
+
+    require_once($CFG->dirroot . '/mod/assign/locallib.php');
+    $assignment = new assign($context, null, null);
+
+    if ($assignment->get_instance()->id != $submission->assignment) {
+        throw new comment_exception('invalidcontext');
+    }
+    if (!has_capability('mod/assign:grade', $context)) {
+        if (!has_capability('mod/assign:submit', $context)) {
+            throw new comment_exception('nopermissiontocomment');
+        } else if ($assignment->get_instance()->teamsubmission) {
+            $group = $assignment->get_submission_group($USER->id);
+            $groupid = 0;
+            if ($group) {
+                $groupid = $group->id;
+            }
+            if ($groupid != $submission->groupid) {
+                throw new comment_exception('nopermissiontocomment');
+            }
+        } else if ($submission->userid != $USER->id) {
+            throw new comment_exception('nopermissiontocomment');
+        }
+    }
 
     return true;
 }
@@ -42,6 +75,39 @@ function assignsubmission_comments_comment_validate(stdClass $options) {
  * @return array
  */
 function assignsubmission_comments_comment_permissions(stdClass $options) {
+    global $USER, $CFG, $DB;
+
+    if ($options->commentarea != 'submission_comments' &&
+            $options->commentarea != 'submission_comments_upgrade') {
+        throw new comment_exception('invalidcommentarea');
+    }
+    if (!$submission = $DB->get_record('assign_submission', array('id'=>$options->itemid))) {
+        throw new comment_exception('invalidcommentitemid');
+    }
+    $context = $options->context;
+
+    require_once($CFG->dirroot . '/mod/assign/locallib.php');
+    $assignment = new assign($context, null, null);
+
+    if ($assignment->get_instance()->id != $submission->assignment) {
+        throw new comment_exception('invalidcontext');
+    }
+    if (!has_capability('mod/assign:grade', $context)) {
+        if (!has_capability('mod/assign:submit', $context)) {
+            return array('post' => false, 'view' => false);
+        } else if ($assignment->get_instance()->teamsubmission) {
+            $group = $assignment->get_submission_group($USER->id);
+            $groupid = 0;
+            if ($group) {
+                $groupid = $group->id;
+            }
+            if ($groupid != $submission->groupid) {
+                return array('post' => false, 'view' => false);
+            }
+        } else if ($submission->userid != $USER->id) {
+            return array('post' => false, 'view' => false);
+        }
+    }
 
     return array('post' => true, 'view' => true);
 }
index 18861e7..79f1b37 100644 (file)
@@ -48,9 +48,15 @@ M.data_filepicker.callback = function(params) {
 };
 
 /**
+ * Deprecated since 2.5, will be removed in 2.7.
+ * Please don't use this function.
+ * Use the filemanager instead. (/lib/form/filemanager.js)
  * This fucntion is called for each file picker on page.
  */
 M.data_filepicker.init = function(Y, options) {
+    if (M.cfg.developerdebug) {
+        Y.log("You are using a deprecated function call (M.data_filepicker). Please look at rewriting your call to use M.form_filemanager");
+    }
     options.formcallback = M.data_filepicker.callback;
     if (!M.core_filepicker.instances[options.client_id]) {
         M.core_filepicker.init(Y, options);
@@ -96,9 +102,15 @@ M.data_imagepicker.callback = function(params) {
 };
 
 /**
+ * Deprecated since 2.5, will be removed in 2.7.
+ * Please don't use this function.
+ * Use the filemanager instead. (/lib/form/filemanager.js)
  * This fucntion is called for each file picker on page.
  */
 M.data_imagepicker.init = function(Y, options) {
+    if (M.cfg.developerdebug) {
+        Y.log("You are using a deprecated function call (M.data_imagepicker). Please look at rewriting your call to use M.form_filemanager");
+    }
     options.formcallback = M.data_imagepicker.callback;
     if (!M.core_filepicker.instances[options.client_id]) {
         M.core_filepicker.init(Y, options);
index 4178036..c92aa5f 100644 (file)
@@ -26,6 +26,7 @@
 require_once('../../config.php');
 require_once('lib.php');
 require_once("$CFG->libdir/rsslib.php");
+require_once("$CFG->libdir/form/filemanager.php");
 
 $id    = optional_param('id', 0, PARAM_INT);    // course module id
 $d     = optional_param('d', 0, PARAM_INT);    // database id
index 605eab3..6e81ff5 100644 (file)
@@ -71,21 +71,39 @@ class data_field_file extends data_field_base {
         $html .= '<input type="hidden" name="field_'.$this->field->id.'_file" value="'.$itemid.'" />';
 
         $options = new stdClass();
-        $options->maxbytes  = $this->field->param3;
+        $options->areamaxbytes  = $this->field->param3;
+        $options->maxbytes = $this->field->param3;
+        $options->maxfiles  = 1; // Limit to one file for the moment, this may be changed if requested as a feature in the future.
         $options->itemid    = $itemid;
         $options->accepted_types = '*';
         $options->return_types = FILE_INTERNAL;
         $options->context = $PAGE->context;
 
-        $fp = new file_picker($options);
-        // print out file picker
-        $html .= $OUTPUT->render($fp);
+        $fm = new form_filemanager($options);
+        // Print out file manager.
+
+        $output = $PAGE->get_renderer('core', 'files');
+        $html .= $output->render($fm);
 
         $html .= '</fieldset>';
         $html .= '</div>';
 
-        $module = array('name'=>'data_filepicker', 'fullpath'=>'/mod/data/data.js', 'requires'=>array('core_filepicker'));
-        $PAGE->requires->js_init_call('M.data_filepicker.init', array($fp->options), true, $module);
+        $module = array(
+            'name'=>'form_filemanager',
+            'fullpath'=>'/lib/form/filemanager.js',
+            'requires' => array('core_filepicker', 'base', 'io-base', 'node',
+                    'json', 'core_dndupload', 'panel', 'resize-plugin', 'dd-plugin'),
+            'strings' => array(
+                array('error', 'moodle'), array('info', 'moodle'), array('confirmdeletefile', 'repository'),
+                array('draftareanofiles', 'repository'), array('entername', 'repository'), array('enternewname', 'repository'),
+                array('invalidjson', 'repository'), array('popupblockeddownload', 'repository'),
+                array('unknownoriginal', 'repository'), array('confirmdeletefolder', 'repository'),
+                array('confirmdeletefilewithhref', 'repository'), array('confirmrenamefolder', 'repository'),
+                array('confirmrenamefile', 'repository')
+            )
+        );
+
+        $PAGE->requires->js_init_call('M.form_filemanager.init', array($fm->options), true, $module);
 
         return $html;
     }
index 37b320d..3e2ee7e 100644 (file)
@@ -67,13 +67,17 @@ class data_field_picture extends data_field_base {
 
         $str = '<div title="'.s($this->field->description).'">';
         $str .= '<fieldset><legend><span class="accesshide">'.$this->field->name.'</span></legend>';
+        $str .= '<noscript>';
         if ($file) {
             $src = file_encode_url($CFG->wwwroot.'/pluginfile.php/', $this->context->id.'/mod_data/content/'.$content->id.'/'.$file->get_filename());
             $str .= '<img width="'.s($this->previewwidth).'" height="'.s($this->previewheight).'" src="'.$src.'" alt="" />';
         }
+        $str .= '</noscript>';
 
         $options = new stdClass();
+        $options->areamaxbytes = $this->field->param3;
         $options->maxbytes  = $this->field->param3;
+        $options->maxfiles  = 1; // Only one picture permitted.
         $options->itemid    = $itemid;
         $options->accepted_types = array('web_image');
         $options->return_types = FILE_INTERNAL;
@@ -82,9 +86,12 @@ class data_field_picture extends data_field_base {
             $options->filename = $file->get_filename();
             $options->filepath = '/';
         }
-        $fp = new file_picker($options);
-        $str .= $OUTPUT->render($fp);
 
+        $fm = new form_filemanager($options);
+        // Print out file manager.
+
+        $output = $PAGE->get_renderer('core', 'files');
+        $str .= $output->render($fm);
 
         $str .= '<div class="mdl-left">';
         $str .= '<input type="hidden" name="field_'.$this->field->id.'_file" value="'.$itemid.'" />';
@@ -95,8 +102,23 @@ class data_field_picture extends data_field_base {
         $str .= '</fieldset>';
         $str .= '</div>';
 
-        $module = array('name'=>'data_imagepicker', 'fullpath'=>'/mod/data/data.js', 'requires'=>array('core_filepicker'));
-        $PAGE->requires->js_init_call('M.data_imagepicker.init', array($fp->options), true, $module);
+        $module = array(
+            'name'=>'form_filemanager',
+            'fullpath'=>'/lib/form/filemanager.js',
+            'requires' => array('core_filepicker', 'base', 'io-base', 'node',
+                    'json', 'core_dndupload', 'panel', 'resize-plugin', 'dd-plugin'),
+            'strings' => array(
+                array('error', 'moodle'), array('info', 'moodle'), array('confirmdeletefile', 'repository'),
+                array('draftareanofiles', 'repository'), array('entername', 'repository'), array('enternewname', 'repository'),
+                array('invalidjson', 'repository'), array('popupblockeddownload', 'repository'),
+                array('unknownoriginal', 'repository'), array('confirmdeletefolder', 'repository'),
+                array('confirmdeletefilewithhref', 'repository'), array('confirmrenamefolder', 'repository'),
+                array('confirmrenamefile', 'repository')
+            )
+        );
+
+        $PAGE->requires->js_init_call('M.form_filemanager.init', array($fm->options), true, $module);
+
         return $str;
     }
 
index 63fed3d..fdb9521 100644 (file)
@@ -5679,7 +5679,7 @@ function forum_print_latest_discussions($course, $forum, $maxdiscussions=-1, $di
                     $link = true;
                 } else {
                     $modcontext = context_module::instance($cm->id);
-                    $link = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
+                    $link = forum_user_can_see_discussion($forum, $discussion, $modcontext, $USER);
                 }
 
                 $discussion->forum = $forum->id;
index 86bf31b..bb46362 100644 (file)
@@ -56,16 +56,10 @@ function forum_rss_get_feed($context, $args) {
     }
 
     //the sql that will retreive the data for the feed and be hashed to get the cache filename
-    $sql = forum_rss_get_sql($forum, $cm);
+    list($sql, $params) = forum_rss_get_sql($forum, $cm);
 
     // Hash the sql to get the cache file name.
-    // If the forum is Q and A then we need to cache the files per user. This can
-    // have a large impact on performance, so we want to only do it on this type of forum.
-    if ($forum->type == 'qanda') {
-        $filename = rss_get_file_name($forum, $sql . $USER->id);
-    } else {
-        $filename = rss_get_file_name($forum, $sql);
-    }
+    $filename = rss_get_file_name($forum, $sql, $params);
     $cachedfilepath = rss_get_file_full_name('mod_forum', $filename);
 
     //Is the cache out of date?
@@ -75,9 +69,9 @@ function forum_rss_get_feed($context, $args) {
     }
     //if the cache is more than 60 seconds old and there's new stuff
     $dontrecheckcutoff = time()-60;
-    if ( $dontrecheckcutoff > $cachedfilelastmodified && forum_rss_newstuff($forum, $cm, $cachedfilelastmodified)) {
+    if ($dontrecheckcutoff > $cachedfilelastmodified && forum_rss_newstuff($forum, $cm, $cachedfilelastmodified)) {
         //need to regenerate the cached version
-        $result = forum_rss_feed_contents($forum, $sql, $modcontext);
+        $result = forum_rss_feed_contents($forum, $sql, $params, $modcontext);
         if (!empty($result)) {
             $status = rss_save_file('mod_forum',$filename,$result);
         }
@@ -111,10 +105,12 @@ function forum_rss_delete_file($forum) {
 function forum_rss_newstuff($forum, $cm, $time) {
     global $DB;
 
-    $sql = forum_rss_get_sql($forum, $cm, $time);
+    list($sql, $params) = forum_rss_get_sql($forum, $cm, $time);
+    if ($DB->count_records_sql($sql, $params) > 0) {
+        return true;
+    }
 
-    $recs = $DB->get_records_sql($sql, null, 0, 1);//limit of 1. If we get even 1 back we have new stuff
-    return ($recs && !empty($recs));
+    return false;
 }
 
 /**
@@ -126,17 +122,11 @@ function forum_rss_newstuff($forum, $cm, $time) {
  * @return string the SQL query to be used to get the Discussion/Post details from the forum table of the database
  */
 function forum_rss_get_sql($forum, $cm, $time=0) {
-    $sql = null;
-
-    if (!empty($forum->rsstype)) {
-        if ($forum->rsstype == 1) {    //Discussion RSS
-            $sql = forum_rss_feed_discussions_sql($forum, $cm, $time);
-        } else {                //Post RSS
-            $sql = forum_rss_feed_posts_sql($forum, $cm, $time);
-        }
+    if ($forum->rsstype == 1) { // Discussion RSS
+        return forum_rss_feed_discussions_sql($forum, $cm, $time);
+    } else { // Post RSS
+        return forum_rss_feed_posts_sql($forum, $cm, $time);
     }
-
-    return $sql;
 }
 
 /**
@@ -155,7 +145,7 @@ function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
     $modcontext = null;
 
     $now = round(time(), -2);
-    $params = array($cm->instance);
+    $params = array();
 
     $modcontext = context_module::instance($cm->id);
 
@@ -172,21 +162,21 @@ function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
         }
     }
 
-    //do we only want new posts?
+    // Do we only want new posts?
     if ($newsince) {
-        $newsince = " AND p.modified > '$newsince'";
+        $params['newsince'] = $newsince;
+        $newsince = " AND p.modified > :newsince";
     } else {
         $newsince = '';
     }
 
-    //get group enforcing SQL
-    $groupmode    = groups_get_activity_groupmode($cm);
+    // Get group enforcing SQL.
+    $groupmode = groups_get_activity_groupmode($cm);
     $currentgroup = groups_get_activity_group($cm);
-    $groupselect = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
+    list($groupselect, $groupparams) = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
 
-    if ($groupmode && $currentgroup) {
-        $params['groupid'] = $currentgroup;
-    }
+    // Add the groupparams to the params array.
+    $params = array_merge($params, $groupparams);
 
     $forumsort = "d.timemodified DESC";
     $postdata = "p.id AS postid, p.subject, p.created as postcreated, p.modified, p.discussion, p.userid, p.message as postmessage, p.messageformat AS postformat, p.messagetrust AS posttrust";
@@ -199,7 +189,7 @@ function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
              WHERE d.forum = {$forum->id} AND p.parent = 0
                    $timelimit $groupselect $newsince
           ORDER BY $forumsort";
-    return $sql;
+    return array($sql, $params);
 }
 
 /**
@@ -213,19 +203,20 @@ function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
 function forum_rss_feed_posts_sql($forum, $cm, $newsince=0) {
     $modcontext = context_module::instance($cm->id);
 
-    //get group enforcement SQL
-    $groupmode    = groups_get_activity_groupmode($cm);
+    // Get group enforcement SQL.
+    $groupmode = groups_get_activity_groupmode($cm);
     $currentgroup = groups_get_activity_group($cm);
+    $params = array();
 
-    $groupselect = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
+    list($groupselect, $groupparams) = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
 
-    if ($groupmode && $currentgroup) {
-        $params['groupid'] = $currentgroup;
-    }
+    // Add the groupparams to the params array.
+    $params = array_merge($params, $groupparams);
 
-    //do we only want new posts?
+    // Do we only want new posts?
     if ($newsince) {
-        $newsince = " AND p.modified > '$newsince'";
+        $params['newsince'] = $newsince;
+        $newsince = " AND p.modified > :newsince";
     } else {
         $newsince = '';
     }
@@ -250,7 +241,7 @@ function forum_rss_feed_posts_sql($forum, $cm, $newsince=0) {
                 $groupselect
             ORDER BY p.created desc";
 
-    return $sql;
+    return array($sql, $params);
 }
 
 /**
@@ -264,6 +255,7 @@ function forum_rss_feed_posts_sql($forum, $cm, $newsince=0) {
  */
 function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=null) {
     $groupselect = '';
+    $params = array();
 
     if ($groupmode) {
         if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
@@ -272,7 +264,7 @@ function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=nul
                 $params['groupid'] = $currentgroup;
             }
         } else {
-            //seprate groups without access all
+            // Separate groups without access all.
             if ($currentgroup) {
                 $groupselect = "AND (d.groupid = :groupid OR d.groupid = -1)";
                 $params['groupid'] = $currentgroup;
@@ -282,7 +274,7 @@ function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=nul
         }
     }
 
-    return $groupselect;
+    return array($groupselect, $params);
 }
 
 /**
@@ -290,21 +282,19 @@ function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=nul
  * It returns false if something is wrong
  *
  * @param stdClass $forum the forum object
- * @param string   $sql   The SQL used to retrieve the contents from the database
+ * @param string $sql the SQL used to retrieve the contents from the database
+ * @param array $params the SQL parameters used
  * @param object $context the context this forum relates to
  * @return bool|string false if the contents is empty, otherwise the contents of the feed is returned
  *
  * @Todo MDL-31129 implement post attachment handling
  */
 
-function forum_rss_feed_contents($forum, $sql) {
+function forum_rss_feed_contents($forum, $sql, $params, $context) {
     global $CFG, $DB, $USER;
 
-
     $status = true;
 
-    $params = array();
-    //$params['forumid'] = $forum->id;
     $recs = $DB->get_recordset_sql($sql, $params, 0, $forum->rssarticles);
 
     //set a flag. Are we displaying discussions or posts?
@@ -316,7 +306,6 @@ function forum_rss_feed_contents($forum, $sql) {
     if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
         print_error('invalidcoursemodule');
     }
-    $context = context_module::instance($cm->id);
 
     $formatoptions = new stdClass();
     $items = array();
index 8ea5525..21aa851 100644 (file)
@@ -68,14 +68,16 @@ function get_scorm_question_count($scormid) {
     $params[] = "cmi.interactions_%.id";
     $rs = $DB->get_recordset_select("scorm_scoes_track", $select, $params, 'element');
     $keywords = array("cmi.interactions_", ".id");
-    foreach ($rs as $record) {
-        $num = trim(str_ireplace($keywords, '', $record->element));
-        if (is_numeric($num) && $num > $count) {
-            $count = $num;
+    if ($rs->valid()) {
+        // Done as interactions start at 0 (do only if we have something to report).
+        $count++;
+        foreach ($rs as $record) {
+            $num = trim(str_ireplace($keywords, '', $record->element));
+            if (is_numeric($num) && $num > $count) {
+                $count = $num;
+            }
         }
     }
-    //done as interactions start at 0
-    $count++;
     $rs->close(); // closing recordset
     return $count;
 }
index f83380d..c830266 100644 (file)
@@ -2,6 +2,10 @@ This files describes API changes in /mod/* - activity modules,
 information provided here is intended especially for developers.
 
 
+=== 2.5 ===
+
+* support for 'mod/*' filters was removed
+
 === 2.4 ===
 
 new features:
index 7809d12..c306eb1 100644 (file)
@@ -31,7 +31,7 @@ require_once("$CFG->dirroot/repository/lib.php");
 $subwikiid = required_param('subwiki', PARAM_INT);
 // not being used for file management, we use it to generate navbar link
 $pageid    = optional_param('pageid', 0, PARAM_INT);
-$returnurl = optional_param('returnurl', '', PARAM_URL);
+$returnurl = optional_param('returnurl', '', PARAM_LOCALURL);
 
 if (!$subwiki = wiki_get_subwiki($subwikiid)) {
     print_error('incorrectsubwikiid', 'wiki');
index 0fbc450..873da65 100644 (file)
@@ -42,10 +42,14 @@ defined('MOODLE_INTERNAL') || die();
  */
 class qformat_learnwise extends qformat_default {
 
-    function provide_import() {
+    public function provide_import() {
         return true;
     }
 
+    public function export_file_extension() {
+        return '.xml';
+    }
+
     protected function readquestions($lines) {
         $questions = array();
         $currentquestion = array();
@@ -62,9 +66,9 @@ class qformat_learnwise extends qformat_default {
         return $questions;
     }
 
-    function readquestion($lines) {
+    protected function readquestion($lines) {
         $text = implode(' ', $lines);
-        $text = str_replace(array('\t','\n','\r','\''), array('','','','\\\''), $text);
+        $text = str_replace(array('\t','\n','\r'), array('','',''), $text);
 
         $startpos = strpos($text, '<question type');
         $endpos = strpos($text, '</question>');
@@ -75,8 +79,8 @@ class qformat_learnwise extends qformat_default {
         preg_match("/<question type=[\"\']([^\"\']+)[\"\']>/i", $text, $matches);
         $type = strtolower($matches[1]); // multichoice or multianswerchoice
 
-        $questiontext = $this->unhtmlentities($this->stringbetween($text, '<text>', '</text>'));
-        $questionhint = $this->unhtmlentities($this->stringbetween($text, '<hint>', '</hint>'));
+        $questiontext = textlib::entities_to_utf8($this->stringbetween($text, '<text>', '</text>'));
+        $questionhint = textlib::entities_to_utf8($this->stringbetween($text, '<hint>', '</hint>'));
         $questionaward = $this->stringbetween($text, '<award>', '</award>');
         $optionlist = $this->stringbetween($text, '<answer>', '</answer>');
 
@@ -89,10 +93,13 @@ class qformat_learnwise extends qformat_default {
 
         if ($type == 'multichoice') {
             foreach ($optionlist as $option) {
+                if (trim($option) === '') {
+                    continue;
+                }
                 $correct = $this->stringbetween($option, ' correct="', '">');
                 $answer = $this->stringbetween($option, '">', '</option>');
                 $optionscorrect[$n] = $correct;
-                $optionstext[$n] = $this->unhtmlentities($answer);
+                $optionstext[$n] = textlib::entities_to_utf8($answer);
                 ++$n;
             }
         } else if ($type == 'multianswerchoice') {
@@ -102,6 +109,9 @@ class qformat_learnwise extends qformat_default {
             $optionsaward = array();
 
             foreach ($optionlist as $option) {
+                if (trim($option) === '') {
+                    continue;
+                }
                 preg_match("/correct=\"([^\"]*)\"/i", $option, $correctmatch);
                 preg_match("/award=\"([^\"]*)\"/i", $option, $awardmatch);
 
@@ -115,7 +125,7 @@ class qformat_learnwise extends qformat_default {
                 $answer = $this->stringbetween($option, '">', '</option>');
 
                 $optionscorrect[$n] = $correct;
-                $optionstext[$n] = $this->unhtmlentities($answer);
+                $optionstext[$n] = textlib::entities_to_utf8($answer);
                 $optionsaward[$n] = $award;
                 ++$n;
             }
@@ -127,22 +137,25 @@ class qformat_learnwise extends qformat_default {
         $question = $this->defaultquestion();
         $question->qtype = 'multichoice';
         $question->name = $this->create_default_question_name($questiontext, get_string('questionname', 'question'));
+        $this->add_blank_combined_feedback($question);
 
         $question->questiontext = $questiontext;
+        $question->questiontextformat = FORMAT_HTML;
         $question->single = ($type == 'multichoice') ? 1 : 0;
-        $question->feedback[] = '';
 
         $question->fraction = array();
         $question->answer = array();
         for ($n = 0; $n < count($optionstext); ++$n) {
             if ($optionstext[$n]) {
-                if (!isset($numcorrect)) { // single answer
+                if (!isset($numcorrect)) {
+                    // Single answer.
                     if ($optionscorrect[$n] == 'yes') {
                         $fraction = (int) $questionaward;
                     } else {
                         $fraction = 0;
                     }
-                } else { // mulitple answers
+                } else {
+                    // Multiple answers.
                     if ($optionscorrect[$n] == 'yes') {
                         $fraction = $optionsaward[$n] / $totalaward;
                     } else {
@@ -150,15 +163,22 @@ class qformat_learnwise extends qformat_default {
                     }
                 }
                 $question->fraction[] = $fraction;
-                $question->answer[] = $optionstext[$n];
-                $question->feedback[] = ''; // no feedback in this type
+                $question->answer[] = array('text' => $optionstext[$n], 'format' => FORMAT_HTML);
+                $question->feedback[] = array('text' => '', 'format' => FORMAT_HTML); // No feedback in this type.
             }
         }
 
         return $question;
     }
 
-    function stringbetween($text, $start, $end) {
+    /**
+     * Extract the substring of $text between $start and $end.
+     * @param string $text text to analyse.
+     * @param string $start opening delimiter.
+     * @param string $end closing delimiter.
+     * @return string the requested substring.
+     */
+    protected function stringbetween($text, $start, $end) {
         $startpos = strpos($text, $start) + strlen($start);
         $endpos = strpos($text, $end);
 
@@ -166,13 +186,4 @@ class qformat_learnwise extends qformat_default {
             return substr($text, $startpos, $endpos - $startpos);
         }
     }
-
-    function unhtmlentities($string) {
-        $transtable = get_html_translation_table(HTML_ENTITIES);
-        $transtable = array_flip($transtable);
-        return strtr($string, $transtable);
-    }
-
 }
-
-
index f94cf1d..9c2229e 100644 (file)
@@ -24,4 +24,4 @@
  */
 
 $string['pluginname'] = 'Learnwise format';
-$string['plugidnname_help'] = 'This format enables the import of multiple choice questions saved in Learnwise\'s XML format.';
+$string['pluginname_help'] = 'This format enables the import of multiple choice questions saved in Learnwise\'s XML format.';
index 15c7296..a4144b1 100644 (file)
@@ -42,7 +42,7 @@ add_to_log($course->id, 'course', 'report outline', "report/outline/index.php?id
 $showlastaccess = true;
 $hiddenfields = explode(',', $CFG->hiddenuserfields);
 
-if (array_search('lastaccess', $hiddenfields) and !has_capability('moodle/user:viewhiddendetails', $context)) {
+if (array_search('lastaccess', $hiddenfields) !== false and !has_capability('moodle/user:viewhiddendetails', $context)) {
     $showlastaccess = false;
 }
 
index e30944f..8191a67 100644 (file)
@@ -218,7 +218,7 @@ function report_security_check_mediafilterswf($detailed=false) {
 
     $activefilters = filter_get_globally_enabled();
 
-    if (array_search('filter/mediaplugin', $activefilters) !== false and !empty($CFG->filter_mediaplugin_enable_swf)) {
+    if (array_search('mediaplugin', $activefilters) !== false and !empty($CFG->filter_mediaplugin_enable_swf)) {
         $result->status = REPORT_SECURITY_CRITICAL;
         $result->info   = get_string('check_mediafilterswf_error', 'report_security');
     } else {
index 2ad1777..36573d8 100644 (file)
@@ -35,7 +35,7 @@ if (empty($CFG->usetags)) {
     print_error('tagsaredisabled', 'tag');
 }
 
-$returnurl = optional_param('returnurl', null, PARAM_TEXT);
+$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
 $keyword = optional_param('coursetag_new_tag', '', PARAM_TEXT);
 $courseid = optional_param('entryid', 0, PARAM_INT);
 $userid = optional_param('userid', 0, PARAM_INT);
index d4ec489..d7043c5 100644 (file)
@@ -32,7 +32,7 @@ if (isguestuser()) {
     die();
 }
 
-$returnurl = optional_param('returnurl', '', PARAM_URL);
+$returnurl = optional_param('returnurl', '', PARAM_LOCALURL);
 
 if (empty($returnurl)) {
     $returnurl = new moodle_url('/user/files.php');
index cb5b903..7c179c3 100644 (file)
@@ -30,7 +30,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 
-$version  = 2012120300.06;              // YYYYMMDD      = weekly release date of this DEV branch
+$version  = 2012120300.07;              // YYYYMMDD      = weekly release date of this DEV branch
                                         //         RR    = release increments - 00 in DEV branches
                                         //           .XX = incremental changes