Merge branch 'MDL-29226' of git://github.com/lazydaisy/moodle
authorAparup Banerjee <aparup@moodle.com>
Thu, 24 Nov 2011 06:39:53 +0000 (14:39 +0800)
committerAparup Banerjee <aparup@moodle.com>
Thu, 24 Nov 2011 06:39:53 +0000 (14:39 +0800)
151 files changed:
admin/blocks.php
admin/cli/install.php
admin/cli/purge_caches.php [new file with mode: 0644]
admin/tool/langimport/index.php
admin/tool/langimport/lang/en/tool_langimport.php
admin/tool/unittest/other/pdflibtestpage.php
admin/tool/upgrade.txt [new file with mode: 0644]
admin/tool/xmldb/lang/en/tool_xmldb.php
admin/webservice/forms.php
auth/mnet/jump.php
backup/moodle2/restore_stepslib.php
backup/util/dbops/restore_dbops.class.php
backup/util/ui/renderer.php
backup/util/ui/yui/confirmcancel/confirmcancel.js
blocks/comments/block_comments.php
blocks/section_links/block_section_links.php
comment/comment.js
enrol/externallib.php
enrol/imsenterprise/lib.php
filter/mediaplugin/filter.php
filter/upgrade.txt [new file with mode: 0644]
grade/grading/form/rubric/edit.php
grade/grading/form/rubric/edit_form.php
grade/grading/form/rubric/js/rubriceditor.js
grade/grading/form/rubric/lang/en/gradingform_rubric.php
grade/grading/form/rubric/lib.php
grade/grading/form/rubric/renderer.php
grade/grading/form/rubric/rubriceditor.php
grade/grading/form/rubric/styles.css
grade/report/user/lib.php
group/lib.php
install.php
install/lang/az/langconfig.php
install/lang/cy/moodle.php
install/stringnames.txt
lang/en/admin.php
lang/en/error.php
lang/en/install.php
lang/en/moodle.php
lang/en/webservice.php
lib/adminlib.php
lib/bennu/iCalendar_components.php
lib/bennu/iCalendar_parameters.php
lib/bennu/iCalendar_properties.php
lib/datalib.php
lib/db/install.xml
lib/db/services.php
lib/db/upgrade.php
lib/ddl/simpletest/testddl.php
lib/dml/moodle_database.php
lib/dml/mysqli_native_moodle_database.php
lib/dml/simpletest/testdml.php
lib/dml/sqlsrv_native_moodle_database.php
lib/dmllib.php
lib/editor/tinymce/lib.php
lib/editor/tinymce/settings.php
lib/filestorage/file_storage.php
lib/filterlib.php
lib/form/form.js
lib/formslib.php
lib/gradelib.php
lib/installlib.php
lib/moodlelib.php
lib/outputrequirementslib.php
lib/plagiarismlib.php
lib/questionlib.php
lib/resourcelib.php
lib/rsslib.php
lib/setup.php
lib/setuplib.php
lib/upgradelib.php
lib/weblib.php
login/token.php
message/lib.php
mod/assignment/lib.php
mod/forum/db/access.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/post.php
mod/forum/version.php
mod/lesson/lang/en/lesson.php
mod/lesson/mod_form.php
mod/lti/ajax.php
mod/lti/backup/moodle1/lib.php
mod/lti/backup/moodle2/backup_lti_stepslib.php
mod/lti/db/access.php
mod/lti/db/install.xml
mod/lti/db/log.php [new file with mode: 0644]
mod/lti/db/upgrade.php
mod/lti/edit_form.php
mod/lti/grade.php
mod/lti/index.php
mod/lti/instructor_edit_tool_type.php
mod/lti/lang/en/lti.php
mod/lti/launch.php
mod/lti/lib.php
mod/lti/localadminlib.php
mod/lti/locallib.php
mod/lti/mod_form.js
mod/lti/mod_form.php
mod/lti/request_tool.php
mod/lti/return.php
mod/lti/service.php
mod/lti/servicelib.php
mod/lti/settings.php
mod/lti/simpletest/testlocallib.php
mod/lti/styles.css
mod/lti/submissions.js
mod/lti/typessettings.php
mod/lti/version.php
mod/lti/view.php
mod/scorm/datamodels/aicclib.php
mod/scorm/datamodels/scorm_12.js.php
mod/wiki/pagelib.php
plagiarism/lib.php
question/format.php
question/format/xml/format.php
question/type/questiontypebase.php
question/type/shortanswer/edit_shortanswer_form.php
question/type/shortanswer/questiontype.php
question/type/truefalse/edit_truefalse_form.php
report/backups/index.php
report/configlog/index.php
report/courseoverview/index.php
report/log/index.php
report/loglive/index.php
report/questioninstances/index.php
report/security/index.php
report/stats/index.php
report/upgrade.txt
repository/lib.php
tag/coursetagslib.php
theme/anomaly/config.php
theme/anomaly/javascript/navigation.js [deleted file]
theme/anomaly/style/general.css
theme/base/style/blocks.css
theme/base/style/core.css
theme/base/style/course.css
theme/base/style/user.css
theme/image.php
theme/javascript.php
theme/mymobile/lang/en/theme_mymobile.php
theme/sky_high/lang/en/theme_sky_high.php
theme/standard/style/css3.css
theme/styles.php
user/lib.php
user/message.html
user/messageselect.php
version.php
webservice/externallib.php
webservice/lib.php

index 8f4fdc9..ca9633d 100644 (file)
             exit;
 
         } else {
-            // Inform block it's about to be deleted
-            if (file_exists("$CFG->dirroot/blocks/$block->name/block_$block->name.php")) {
-                $blockobject = block_instance($block->name);
-                if ($blockobject) {
-                    $blockobject->before_delete();  //only if we can create instance, block might have been already removed
-                }
-            }
-
-            // First delete instances and then block
-            $instances = $DB->get_records('block_instances', array('blockname' => $block->name));
-            if(!empty($instances)) {
-                foreach($instances as $instance) {
-                    blocks_delete_instance($instance);
-                }
-            }
-
-            // Delete block
-            $DB->delete_records('block', array('id'=>$block->id));
-
-            drop_plugin_tables($block->name, "$CFG->dirroot/blocks/$block->name/db/install.xml", false); // old obsoleted table names
-            drop_plugin_tables('block_'.$block->name, "$CFG->dirroot/blocks/$block->name/db/install.xml", false);
-
-            // Delete the capabilities that were defined by this block
-            capabilities_cleanup('block/'.$block->name);
-
-            // Remove event handlers and dequeue pending events
-            events_uninstall('block/'.$block->name);
+            uninstall_plugin('block', $block->name);
 
             $a->block = $strblockname;
             $a->directory = $CFG->dirroot.'/blocks/'.$block->name;
index 68ed892..04d1ee8 100644 (file)
@@ -133,9 +133,6 @@ $CFG->dirroot              = dirname(dirname(dirname(__FILE__)));
 $CFG->libdir               = "$CFG->dirroot/lib";
 $CFG->wwwroot              = "http://localhost";
 $CFG->httpswwwroot         = $CFG->wwwroot;
-$CFG->dataroot             = str_replace('\\', '/', dirname(dirname(dirname(dirname(__FILE__)))).'/moodledata');
-$CFG->tempdir              = $CFG->dataroot.'/temp';
-$CFG->cachedir             = $CFG->dataroot.'/temp';
 $CFG->docroot              = 'http://docs.moodle.org';
 $CFG->running_installer    = true;
 $CFG->early_install_lang   = true;
@@ -186,7 +183,7 @@ list($options, $unrecognized) = cli_get_params(
         'chmod'             => '2777',
         'lang'              => $CFG->lang,
         'wwwroot'           => '',
-        'dataroot'          => $CFG->dataroot,
+        'dataroot'          => str_replace('\\', '/', dirname(dirname(dirname(dirname(__FILE__)))).'/moodledata'),
         'dbtype'            => $defaultdb,
         'dbhost'            => 'localhost',
         'dbname'            => 'moodle',
@@ -270,7 +267,7 @@ if ($interactive) {
 $chmod = octdec(clean_param($options['chmod'], PARAM_INT));
 if ($interactive) {
     cli_separator();
-    cli_heading('Data directories permission'); // todo localize
+    cli_heading(get_string('datarootpermission', 'install'));
     $prompt = get_string('clitypevaluedefault', 'admin', decoct($chmod));
     $error = '';
     do {
@@ -330,9 +327,12 @@ $CFG->httpswwwroot  = $CFG->wwwroot;
 
 
 //We need dataroot before lang download
-if (!empty($options['dataroot'])) {
-    $CFG->dataroot = $options['dataroot'];
+$dataroot = clean_param($options['dataroot'], PARAM_PATH);
+if ($dataroot !== $options['dataroot']) {
+    $a = (object)array('option' => 'dataroot', 'value' => $options['dataroot']);
+    cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
 }
+$CFG->dataroot = $dataroot;
 if ($interactive) {
     cli_separator();
     $i=0;
@@ -380,6 +380,8 @@ if ($interactive) {
         cli_error(get_string('pathserrcreatedataroot', 'install', $a));
     }
 }
+$CFG->tempdir  = $CFG->dataroot.'/temp';
+$CFG->cachedir = $CFG->dataroot.'/cache';
 
 // download required lang packs
 if ($CFG->lang !== 'en') {
diff --git a/admin/cli/purge_caches.php b/admin/cli/purge_caches.php
new file mode 100644 (file)
index 0000000..f008da2
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * @package    core
+ * @subpackage cli
+ * @copyright  2011 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('CLI_SCRIPT', true);
+
+require(dirname(dirname(dirname(__FILE__))).'/config.php');
+require_once($CFG->libdir.'/clilib.php');
+
+list($options, $unrecognized) = cli_get_params(array('help' => false), array('h' => 'help'));
+
+if ($unrecognized) {
+    $unrecognized = implode("\n  ", $unrecognized);
+    cli_error(get_string('cliunknowoption', 'admin', $unrecognized), 2);
+}
+
+if ($options['help']) {
+    $help =
+"Invalidates all Moodle internal caches
+
+Options:
+-h, --help            Print out this help
+
+Example:
+\$sudo -u www-data /usr/bin/php admin/cli/purge_caches.php
+";
+
+    echo $help;
+    exit(0);
+}
+
+purge_all_caches();
+
+exit(0);
\ No newline at end of file
index 964c64c..80d8829 100644 (file)
@@ -35,17 +35,29 @@ require_once($CFG->libdir.'/componentlib.class.php');
 
 admin_externalpage_setup('toollangimport');
 
-if (!empty($CFG->skiplangupgrade)) {
-    echo $OUTPUT->header();
-    echo $OUTPUT->box(get_string('langimportdisabled', 'tool_langimport'));
-    echo $OUTPUT->footer();
-    die;
+if (empty($CFG->langotherroot)) {
+    throw new moodle_exception('missingcfglangotherroot', 'tool_langimport');
 }
 
 $mode          = optional_param('mode', 0, PARAM_INT);              // action
 $pack          = optional_param_array('pack', array(), PARAM_SAFEDIR);    // pack to install
 $uninstalllang = optional_param('uninstalllang', '', PARAM_LANG);   // installed pack to uninstall
 $confirm       = optional_param('confirm', 0, PARAM_BOOL);          // uninstallation confirmation
+$purgecaches   = optional_param('purgecaches', false, PARAM_BOOL);  // explicit caches reset
+
+if ($purgecaches) {
+    require_sesskey();
+    get_string_manager()->reset_caches();
+    redirect($PAGE->url);
+}
+
+if (!empty($CFG->skiplangupgrade)) {
+    echo $OUTPUT->header();
+    echo $OUTPUT->box(get_string('langimportdisabled', 'tool_langimport'));
+    echo $OUTPUT->single_button(new moodle_url($PAGE->url, array('purgecaches' => 1)), get_string('purgestringcaches', 'tool_langimport'));
+    echo $OUTPUT->footer();
+    die;
+}
 
 define('INSTALLATION_OF_SELECTED_LANG', 2);
 define('DELETION_OF_SELECTED_LANG', 4);
index e2fbf51..a5ead16 100644 (file)
 $string['install'] = 'Install selected language pack';
 $string['installedlangs'] = 'Installed language packs';
 $string['langimport'] = 'Language import utility';
-$string['langimportdisabled'] = 'Language import feature has been disabled. You have to update your language packs manually at the file-system level.';
+$string['langimportdisabled'] = 'Language import feature has been disabled. You have to update your language packs manually at the file-system level. Do not forget to purge string caches after you do so.';
 $string['langpackinstalled'] = 'Language pack {$a} was successfully installed';
 $string['langpackremoved'] = 'Language pack was uninstalled';
 $string['langpackupdateskipped'] = 'Update of {$a} language pack skipped';
 $string['langpackuptodate'] = 'Language pack {$a} is up-to-date';
 $string['langupdatecomplete'] = 'Language pack update completed';
+$string['missingcfglangotherroot'] = 'Missing configuration value $CFG->langotherroot';
 $string['missinglangparent'] = 'Missing parent language <em>{$a->parent}</em> of <em>{$a->lang}</em>.';
 $string['nolangupdateneeded'] = 'All your language packs are up to date, no update is needed';
 $string['pluginname'] = 'Language packs';
+$string['purgestringcaches'] = 'Purge string caches';
 $string['remotelangnotavailable'] = 'Because Moodle can not connect to download.moodle.org, we are unable to do language pack installation automatically. Please download the appropriate zip file(s) from http://download.moodle.org, copy them to your {$a} directory and unzip them manually.';
 $string['uninstall'] = 'Uninstall selected language pack';
 $string['uninstallconfirm'] = 'You are about to completely uninstall language pack {$a}, are you sure?';
index 644a86e..5266bfe 100644 (file)
@@ -19,8 +19,7 @@
  *
  * @package    tool
  * @subpackage unittest
- * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
- * @author     N.D.Freear@open.ac.uk, T.J.Hunt@open.ac.uk
+ * @copyright  2009 David Mudrak <david.mudrak@gmail.com>
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
diff --git a/admin/tool/upgrade.txt b/admin/tool/upgrade.txt
new file mode 100644 (file)
index 0000000..3af7d3a
--- /dev/null
@@ -0,0 +1,21 @@
+This files describes API changes in /admin/tool/* - plugins,
+information provided here is intended especially for developers.
+
+
+=== 2.2 ===
+
+API changes:
+* new admin tool plugin type introduced
+
+
+How to migrate existing admin reports:
+# move all files to new /admin/tool/yourplugin/ location
+# update all links to admin tools /$CFG->admin/report/ to /$CFG->admin/tool/
+# add language pack with at least 'pluginname' string
+# update all language strings (use 'tool_yourplugin' instead of 'report_yourplugin') - use AMOS hints in commit message
+# update all capability names
+# create db/install.php migration script - delete old settings and capabilities (see converted plugins for examples)
+# grep the plugin codebase and look for any remaining 'coursereport' occurrences
+# update CSS selectors
+
+See http://docs.moodle.org/dev/Admin_tools for more details and explanation.
index 1c2a350..b24a219 100644 (file)
@@ -71,6 +71,7 @@ $string['down'] = 'Down';
 $string['duplicate'] = 'Duplicate';
 $string['duplicatefieldname'] = 'Another field with that name exists';
 $string['duplicatekeyname'] = 'Another key with that name exists';
+$string['duplicatetablename'] = 'Another table with that name exists';
 $string['edit'] = 'Edit';
 $string['edit_field'] = 'Edit field';
 $string['edit_field_save'] = 'Save field';
@@ -113,6 +114,7 @@ $string['incorrectfieldname'] = 'Incorrect name';
 $string['index'] = 'Index';
 $string['indexes'] = 'Indexes';
 $string['integerincorrectlength'] = 'Incorrect length for integer field';
+$string['incorrecttablename'] = 'Incorrect table name';
 $string['key'] = 'Key';
 $string['keys'] = 'Keys';
 $string['listreservedwords'] = 'List of Reserved Words<br />(used to keep <a href="http://docs.moodle.org/en/XMLDB_reserved_words" target="_blank">XMLDB_reserved_words</a> updated)';
@@ -156,6 +158,7 @@ $string['selectonecommand'] = 'Please select one action from the list to view PH
 $string['selectonefieldkeyindex'] = 'Please select one field/key/index from the list to view the PHP code';
 $string['selecttable'] = 'Select table:';
 $string['table'] = 'Table';
+$string['tablenameempty'] = 'The table name cannot be empty';
 $string['tables'] = 'Tables';
 $string['textincorrectlength'] = 'Incorrect length for text field';
 $string['unload'] = 'Unload';
index 377d8cf..7520228 100644 (file)
@@ -68,6 +68,11 @@ class external_service_form extends moodleform {
                 get_string('restrictedusers', 'webservice'));
         $mform->addHelpButton('restrictedusers', 'restrictedusers', 'webservice');
 
+        //can users download files
+        $mform->addElement('advcheckbox', 'downloadfiles', get_string('downloadfiles', 'webservice'));
+        $mform->setAdvanced('downloadfiles');
+        $mform->addHelpButton('downloadfiles', 'downloadfiles', 'webservice');
+
         /// needed to select automatically the 'No required capability" option
         $currentcapabilityexist = false;
         if (empty($service->requiredcapability)) {
index d55346c..d44d058 100644 (file)
@@ -37,6 +37,13 @@ if (!is_enabled_auth('mnet')) {
 
 // If hostid hasn't been specified, try getting it using wwwroot
 if (!$hostid) {
+    $hostwwwroot = trim($hostwwwroot);
+    $hostwwwroot = rtrim($hostwwwroot, '/');
+
+    // ensure the wwwroot starts with a http or https prefix
+    if (strtolower(substr($hostwwwroot, 0, 4)) != 'http') {
+        $hostwwwroot = 'http://'.$hostwwwroot;
+    }
     $hostid = $DB->get_field('mnet_host', 'id', array('wwwroot' => $hostwwwroot));
 }
 
index c9705a9..9c0358b 100644 (file)
@@ -1109,7 +1109,6 @@ class restore_course_structure_step extends restore_structure_step {
         global $CFG, $DB;
 
         $data = (object)$data;
-        $oldid = $data->id; // We'll need this later
 
         $fullname  = $this->get_setting_value('course_fullname');
         $shortname = $this->get_setting_value('course_shortname');
@@ -1122,7 +1121,13 @@ class restore_course_structure_step extends restore_structure_step {
         $data->id = $this->get_courseid();
         $data->fullname = $fullname;
         $data->shortname= $shortname;
-        $data->idnumber = '';
+
+        $context = get_context_instance_by_id($this->task->get_contextid());
+        if (has_capability('moodle/course:changeidnumber', $context, $this->task->get_userid())) {
+            $data->idnumber = '';
+        } else {
+            unset($data->idnumber);
+        }
 
         // Only restrict modules if original course was and target site too for new courses
         $data->restrictmodules = $data->restrictmodules && !empty($CFG->restrictmodulesfor) && $CFG->restrictmodulesfor == 'all';
@@ -1988,7 +1993,7 @@ class restore_activity_grading_structure_step extends restore_structure_step {
         $newitemid = $this->get_mappingid(restore_gradingform_plugin::itemid_mapping($areaname), $data->itemid);
 
         $oldid = $data->id;
-        $data->formid = $newformid;
+        $data->definitionid = $newformid;
         $data->raterid = $this->get_mappingid('user', $data->raterid);
         $data->itemid = $newitemid;
 
index f036470..608d2d6 100644 (file)
@@ -824,54 +824,58 @@ abstract class restore_dbops {
             $newuserid = $DB->insert_record('user', $user);
             self::set_backup_ids_record($restoreid, 'user', $recuser->itemid, $newuserid);
             // Let's create the user context and annotate it (we need it for sure at least for files)
-            $newuserctxid = get_context_instance(CONTEXT_USER, $newuserid)->id;
-            self::set_backup_ids_record($restoreid, 'context', $recuser->parentitemid, $newuserctxid);
-
-            // Process custom fields
-            if (isset($user->custom_fields)) { // if present in backup
-                foreach($user->custom_fields['custom_field'] as $udata) {
-                    $udata = (object)$udata;
-                    // If the profile field has data and the profile shortname-datatype is defined in server
-                    if ($udata->field_data) {
-                        if ($field = $DB->get_record('user_info_field', array('shortname'=>$udata->field_name, 'datatype'=>$udata->field_type))) {
-                        /// Insert the user_custom_profile_field
-                            $rec = new stdClass();
-                            $rec->userid  = $newuserid;
-                            $rec->fieldid = $field->id;
-                            $rec->data    = $udata->field_data;
-                            $DB->insert_record('user_info_data', $rec);
+            // but for deleted users that don't have a context anymore (MDL-30192). We are done for them
+            // and nothing else (custom fields, prefs, tags, files...) will be created.
+            if (empty($user->deleted)) {
+                $newuserctxid = $user->deleted ? 0 : get_context_instance(CONTEXT_USER, $newuserid)->id;
+                self::set_backup_ids_record($restoreid, 'context', $recuser->parentitemid, $newuserctxid);
+
+                // Process custom fields
+                if (isset($user->custom_fields)) { // if present in backup
+                    foreach($user->custom_fields['custom_field'] as $udata) {
+                        $udata = (object)$udata;
+                        // If the profile field has data and the profile shortname-datatype is defined in server
+                        if ($udata->field_data) {
+                            if ($field = $DB->get_record('user_info_field', array('shortname'=>$udata->field_name, 'datatype'=>$udata->field_type))) {
+                            /// Insert the user_custom_profile_field
+                                $rec = new stdClass();
+                                $rec->userid  = $newuserid;
+                                $rec->fieldid = $field->id;
+                                $rec->data    = $udata->field_data;
+                                $DB->insert_record('user_info_data', $rec);
+                            }
                         }
                     }
                 }
-            }
 
-            // Process tags
-            if (!empty($CFG->usetags) && isset($user->tags)) { // if enabled in server and present in backup
-                $tags = array();
-                foreach($user->tags['tag'] as $usertag) {
-                    $usertag = (object)$usertag;
-                    $tags[] = $usertag->rawname;
+                // Process tags
+                if (!empty($CFG->usetags) && isset($user->tags)) { // if enabled in server and present in backup
+                    $tags = array();
+                    foreach($user->tags['tag'] as $usertag) {
+                        $usertag = (object)$usertag;
+                        $tags[] = $usertag->rawname;
+                    }
+                    tag_set('user', $newuserid, $tags);
                 }
-                tag_set('user', $newuserid, $tags);
-            }
 
-            // Process preferences
-            if (isset($user->preferences)) { // if present in backup
-                foreach($user->preferences['preference'] as $preference) {
-                    $preference = (object)$preference;
-                    // Prepare the record and insert it
-                    $preference->userid = $newuserid;
-                    $status = $DB->insert_record('user_preferences', $preference);
+                // Process preferences
+                if (isset($user->preferences)) { // if present in backup
+                    foreach($user->preferences['preference'] as $preference) {
+                        $preference = (object)$preference;
+                        // Prepare the record and insert it
+                        $preference->userid = $newuserid;
+                        $status = $DB->insert_record('user_preferences', $preference);
+                    }
                 }
-            }
 
-            // Create user files in pool (profile, icon, private) by context
-            restore_dbops::send_files_to_pool($basepath, $restoreid, 'user', 'icon', $recuser->parentitemid, $userid);
-            restore_dbops::send_files_to_pool($basepath, $restoreid, 'user', 'profile', $recuser->parentitemid, $userid);
-            if ($userfiles) { // private files only if enabled in settings
-                restore_dbops::send_files_to_pool($basepath, $restoreid, 'user', 'private', $recuser->parentitemid, $userid);
-            }
+                // Create user files in pool (profile, icon, private) by context
+                restore_dbops::send_files_to_pool($basepath, $restoreid, 'user', 'icon', $recuser->parentitemid, $userid);
+                restore_dbops::send_files_to_pool($basepath, $restoreid, 'user', 'profile', $recuser->parentitemid, $userid);
+                if ($userfiles) { // private files only if enabled in settings
+                    restore_dbops::send_files_to_pool($basepath, $restoreid, 'user', 'private', $recuser->parentitemid, $userid);
+                }
 
+            }
         }
         $rs->close();
     }
index 4ca5414..0845440 100644 (file)
@@ -232,15 +232,9 @@ class core_backup_renderer extends plugin_renderer_base {
             $html .= $this->output->heading(get_string('restoretonewcourse', 'backup'), 2, array('class'=>'header'));
             $html .= $this->backup_detail_input(get_string('restoretonewcourse', 'backup'), 'radio', 'target', backup::TARGET_NEW_COURSE, array('checked'=>'checked'));
             $html .= $this->backup_detail_pair(get_string('selectacategory', 'backup'), $this->render($categories));
-            $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('continue'), 'class'=>'newcoursecontinue')));
+            $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('continue'))));
             $html .= html_writer::end_tag('div');
             $html .= html_writer::end_tag('form');
-            $config = new stdClass;
-            $config->title = get_string('confirmnewcoursecontinue', 'backup');
-            $config->question = get_string('confirmnewcoursecontinuequestion', 'backup');
-            $config->yesLabel = get_string('continue');
-            $config->noLabel = get_string('cancel');
-            $PAGE->requires->yui_module('moodle-backup-confirmcancel', 'M.core_backup.watch_newcoursecontinue_buttons', array($config));
         }
 
         if ($wholecourse && !empty($currentcourse)) {
index 05395e4..267b853 100644 (file)
@@ -27,24 +27,4 @@ M.core_backup.watch_cancel_buttons = function(config) {
     });
 }
 
-M.core_backup.watch_newcoursecontinue_buttons = function(config) {
-    Y.all('.newcoursecontinue').each(function(){
-        this._confirmationListener = this._confirmationListener || this.on('click', function(e){
-            // Prevent the default event (sumbit) from firing
-            e.preventDefault();
-            // Create the confirm box
-            var confirm = new M.core.confirm(config);
-            // If the user clicks yes
-            confirm.on('complete-yes', function(e){
-                // Detach the listener for the confirm box so it doesn't fire again.
-                this._confirmationListener.detach();
-                // Simulate the original cancel button click
-                this.simulate('click');
-            }, this);
-            // Show the confirm box
-            confirm.show();
-        }, this);
-    });
-}
-
 }, '@VERSION@', {'requires':['base','node','node-event-simulate','moodle-enrol-notification']});
\ No newline at end of file
index 60bca8d..a00cd09 100644 (file)
@@ -74,7 +74,7 @@ class block_comments extends block_base {
         $args->linktext  = get_string('showcomments');
         $args->notoggle  = true;
         $args->autostart = true;
-        $args->displaycancel = true;
+        $args->displaycancel = false;
         $comment = new comment($args);
         $comment->set_view_permission(true);
 
index 1e80286..4fcde0f 100644 (file)
@@ -55,6 +55,7 @@ class block_section_links extends block_base {
         if(isset($this->config)){
             $config = $this->config;
         } else{
+            // TODO: Move these config settings to proper ones using component name
             $config = get_config('blocks/section_links');
         }
 
@@ -155,7 +156,8 @@ class block_section_links extends block_base {
     }
     function before_delete() {
         global $DB;
-        $DB->delete_records('config_plugins', 'plugin', 'blocks/section_links');
+        // TODO: Move these config settings to proper ones using component name
+        $DB->delete_records('config_plugins', array('plugin' => 'blocks/section_links'));
     }
 
     function has_config() {
index 87f0e9e..dcf669e 100644 (file)
@@ -58,6 +58,7 @@ M.core_comment = {
                         return false;
                     }, this);
                 }
+                scope.toggle_textarea(false);
                 CommentHelper.confirmoverlay = new Y.Overlay({
 bodyContent: '<div class="comment-delete-confirm"><a href="#" id="confirmdelete-'+this.client_id+'">'+M.str.moodle.yes+'</a> <a href="#" id="canceldelete-'+this.client_id+'">'+M.str.moodle.no+'</a></div>',
                                         visible: false
@@ -79,6 +80,7 @@ bodyContent: '<div class="comment-delete-confirm"><a href="#" id="confirmdelete-
                             var cid = scope.client_id;
                             var ta = Y.one('#dlg-content-'+cid);
                             ta.set('value', '');
+                            scope.toggle_textarea(false);
                             var container = Y.one('#comment-list-'+cid);
                             var result = scope.render([obj], true);
                             var newcomment = Y.Node.create(result.html);
index 2b69aaf..9e186ce 100644 (file)
@@ -57,7 +57,7 @@ class core_enrol_external extends external_api {
      * @return array of courses
      */
     public static function get_users_courses($userid) {
-        global $USER;
+        global $USER, $DB;
 
         // Do basic automatic PARAM checks on incoming data, using params description
         // If any problems are found then exceptions are thrown with helpful error messages
@@ -74,12 +74,17 @@ class core_enrol_external extends external_api {
                 // current user can not access this course, sorry we can not disclose who is enrolled in this course!
                 continue;
             }
+
             if ($userid != $USER->id and !has_capability('moodle/course:viewparticipants', $context)) {
                 // we need capability to view participants
                 continue;
             }
 
-            $result[] = array('id'=>$course->id, 'shortname'=>$course->shortname, 'fullname'=>$course->fullname, 'idnumber'=>$course->idnumber,'visible'=>$course->visible);
+            list($enrolledsqlselect, $enrolledparams) = get_enrolled_sql($context);
+            $enrolledsql = "SELECT COUNT(*) FROM ($enrolledsqlselect) AS enrolleduserids";
+            $enrolledusercount = $DB->count_records_sql($enrolledsql, $enrolledparams);
+
+            $result[] = array('id'=>$course->id, 'shortname'=>$course->shortname, 'fullname'=>$course->fullname, 'idnumber'=>$course->idnumber,'visible'=>$course->visible, 'enrolledusercount'=>$enrolledusercount);
         }
 
         return $result;
@@ -96,6 +101,7 @@ class core_enrol_external extends external_api {
                     'id'        => new external_value(PARAM_INT, 'id of course'),
                     'shortname' => new external_value(PARAM_RAW, 'short name of course'),
                     'fullname'  => new external_value(PARAM_RAW, 'long name of course'),
+                    'enrolledusercount' => new external_value(PARAM_INT, 'Number of enrolled users in this course'),
                     'idnumber'  => new external_value(PARAM_RAW, 'id number of course'),
                     'visible'   => new external_value(PARAM_INT, '1 means visible, 0 means hidden course'),
                 )
@@ -145,6 +151,7 @@ class core_enrol_external extends external_api {
         $withcapability = '';
         $groupid        = 0;
         $onlyactive     = false;
+        $userfields     = array();
         foreach ($options as $option) {
             switch ($option['name']) {
             case 'withcapability':
@@ -156,6 +163,12 @@ class core_enrol_external extends external_api {
             case 'onlyactive':
                 $onlyactive = !empty($option['value']);
                 break;
+            case 'userfields':
+                $thefields = explode(',', $option['value']);
+                foreach ($thefields as $f) {
+                    $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
+                }
+                break;
             }
         }
 
@@ -208,7 +221,7 @@ class core_enrol_external extends external_api {
                 continue;
             }
             context_instance_preload($user);
-            if ($userdetails = user_get_user_details($user, $course)) {
+            if ($userdetails = user_get_user_details($user, $course, $userfields)) {
                 $users[] = $userdetails;
             }
         }
@@ -249,8 +262,8 @@ class core_enrol_external extends external_api {
                     'city'        => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
                     'url'         => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
                     'country'     => new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
-                    'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version'),
-                    'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version'),
+                    'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version', VALUE_OPTIONAL),
+                    'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version', VALUE_OPTIONAL),
                     'customfields' => new external_multiple_structure(
                         new external_single_structure(
                             array(
index a04d9d5..2dec772 100644 (file)
@@ -302,7 +302,7 @@ function get_recstatus($tagdata, $tagname){
 * Process the group tag. This defines a Moodle course.
 * @param string $tagconents The raw contents of the XML element
 */
-function process_group_tag($tagcontents){
+function process_group_tag($tagcontents) {
     global $DB;
 
     // Get configs
@@ -312,24 +312,24 @@ function process_group_tag($tagcontents){
 
     // Process tag contents
     $group = new stdClass();
-    if(preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)){
+    if (preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)) {
         $group->coursecode = trim($matches[1]);
     }
-    if(preg_match('{<description>.*?<short>(.*?)</short>.*?</description>}is', $tagcontents, $matches)){
+    if (preg_match('{<description>.*?<short>(.*?)</short>.*?</description>}is', $tagcontents, $matches)) {
         $group->description = trim($matches[1]);
     }
-    if(preg_match('{<org>.*?<orgunit>(.*?)</orgunit>.*?</org>}is', $tagcontents, $matches)){
+    if (preg_match('{<org>.*?<orgunit>(.*?)</orgunit>.*?</org>}is', $tagcontents, $matches)) {
         $group->category = trim($matches[1]);
     }
 
     $recstatus = ($this->get_recstatus($tagcontents, 'group'));
     //echo "<p>get_recstatus for this group returned $recstatus</p>";
 
-    if(!(strlen($group->coursecode)>0)){
+    if (!(strlen($group->coursecode)>0)) {
         $this->log_line('Error at line '.$line.': Unable to find course code in \'group\' element.');
-    }else{
+    } else {
         // First, truncate the course code if desired
-        if(intval($truncatecoursecodes)>0){
+        if (intval($truncatecoursecodes)>0) {
             $group->coursecode = ($truncatecoursecodes > 0)
                      ? substr($group->coursecode, 0, intval($truncatecoursecodes))
                      : $group->coursecode;
@@ -349,66 +349,75 @@ function process_group_tag($tagcontents){
         $group->coursecode = array($group->coursecode);
 
         // Third, check if the course(s) exist
-        foreach($group->coursecode as $coursecode){
+        foreach ($group->coursecode as $coursecode) {
             $coursecode = trim($coursecode);
-            if(!$DB->get_field('course', 'id', array('idnumber'=>$coursecode))) {
-              if(!$createnewcourses) {
-                  $this->log_line("Course $coursecode not found in Moodle's course idnumbers.");
-              } else {
-                // Create the (hidden) course(s) if not found
-                $course = new stdClass();
-                $course->fullname = $group->description;
-                $course->shortname = $coursecode;
-                $course->idnumber = $coursecode;
-                $course->format = 'topics';
-                $course->visible = 0;
-                // Insert default names for teachers/students, from the current language
-                $site = get_site();
-
-                // Handle course categorisation (taken from the group.org.orgunit field if present)
-                if(strlen($group->category)>0){
-                    // If the category is defined and exists in Moodle, we want to store it in that one
-                    if($catid = $DB->get_field('course_categories', 'id', array('name'=>$group->category))){
-                        $course->category = $catid;
-                    } elseif($createnewcategories) {
-                        // Else if we're allowed to create new categories, let's create this one
-                        $newcat = new stdClass();
-                        $newcat->name = $group->category;
-                        $newcat->visible = 0;
-                        $catid = $DB->insert_record('course_categories', $newcat);
-                        $course->category = $catid;
-                        $this->log_line("Created new (hidden) category, #$catid: $newcat->name");
-                    }else{
-                        // If not found and not allowed to create, stick with default
-                        $this->log_line('Category '.$group->category.' not found in Moodle database, so using default category instead.');
+            if (!$DB->get_field('course', 'id', array('idnumber'=>$coursecode))) {
+                if (!$createnewcourses) {
+                    $this->log_line("Course $coursecode not found in Moodle's course idnumbers.");
+                } else {
+                    // Create the (hidden) course(s) if not found
+                    $courseconfig = get_config('moodlecourse'); // Load Moodle Course shell defaults
+                    $course = new stdClass();
+                    $course->fullname = $group->description;
+                    $course->shortname = $coursecode;
+                    $course->idnumber = $coursecode;
+                    $course->format = $courseconfig->format;
+                    $course->visible = $courseconfig->visible;
+                    $course->numsections = $courseconfig->numsections;
+                    $course->hiddensections = $courseconfig->hiddensections;
+                    $course->newsitems = $courseconfig->newsitems;
+                    $course->showgrades = $courseconfig->showgrades;
+                    $course->showreports = $courseconfig->showreports;
+                    $course->maxbytes = $courseconfig->maxbytes;
+                    $course->groupmode = $courseconfig->groupmode;
+                    $course->groupmodeforce = $courseconfig->groupmodeforce;
+                    $course->enablecompletion = $courseconfig->enablecompletion;
+                    $course->completionstartonenrol = $courseconfig->completionstartonenrol;
+                    // Insert default names for teachers/students, from the current language
+
+                    // Handle course categorisation (taken from the group.org.orgunit field if present)
+                    if (strlen($group->category)>0) {
+                        // If the category is defined and exists in Moodle, we want to store it in that one
+                        if ($catid = $DB->get_field('course_categories', 'id', array('name'=>$group->category))) {
+                            $course->category = $catid;
+                        } else if ($createnewcategories) {
+                            // Else if we're allowed to create new categories, let's create this one
+                            $newcat = new stdClass();
+                            $newcat->name = $group->category;
+                            $newcat->visible = 0;
+                            $catid = $DB->insert_record('course_categories', $newcat);
+                            $course->category = $catid;
+                            $this->log_line("Created new (hidden) category, #$catid: $newcat->name");
+                        } else {
+                            // If not found and not allowed to create, stick with default
+                            $this->log_line('Category '.$group->category.' not found in Moodle database, so using default category instead.');
+                            $course->category = 1;
+                        }
+                    } else {
                         $course->category = 1;
                     }
-                }else{
-                    $course->category = 1;
-                }
-                $course->timecreated = time();
-                $course->startdate = time();
-                $course->numsections = 1;
-                // Choose a sort order that puts us at the start of the list!
-                $course->sortorder = 0;
+                    $course->timecreated = time();
+                    $course->startdate = time();
+                    // Choose a sort order that puts us at the start of the list!
+                    $course->sortorder = 0;
 
-                $courseid = $DB->insert_record('course', $course);
+                    $courseid = $DB->insert_record('course', $course);
 
-                // Setup the blocks
-                $course = $DB->get_record('course', array('id' => $courseid));
-                blocks_add_default_course_blocks($course);
+                    // Setup the blocks
+                    $course = $DB->get_record('course', array('id' => $courseid));
+                    blocks_add_default_course_blocks($course);
 
-                $section = new stdClass();
-                $section->course = $course->id;   // Create a default section.
-                $section->section = 0;
-                $section->summaryformat = FORMAT_HTML;
-                $section->id = $DB->insert_record("course_sections", $section);
+                    $section = new stdClass();
+                    $section->course = $course->id;   // Create a default section.
+                    $section->section = 0;
+                    $section->summaryformat = FORMAT_HTML;
+                    $section->id = $DB->insert_record("course_sections", $section);
 
-                add_to_log(SITEID, "course", "new", "view.php?id=$course->id", "$course->fullname (ID $course->id)");
+                    add_to_log(SITEID, "course", "new", "view.php?id=$course->id", "$course->fullname (ID $course->id)");
 
-                $this->log_line("Created course $coursecode in Moodle (Moodle ID is $course->id)");
-              }
-            }elseif($recstatus==3 && ($courseid = $DB->get_field('course', 'id', array('idnumber'=>$coursecode)))){
+                    $this->log_line("Created course $coursecode in Moodle (Moodle ID is $course->id)");
+                }
+            } else if ($recstatus==3 && ($courseid = $DB->get_field('course', 'id', array('idnumber'=>$coursecode)))) {
                 // If course does exist, but recstatus==3 (delete), then set the course as hidden
                 $DB->set_field('course', 'visible', '0', array('id'=>$courseid));
             }
index a94d298..e1e61ca 100644 (file)
@@ -171,10 +171,32 @@ class filter_mediaplugin extends moodle_text_filter {
 ///===========================
 /// utility functions
 
+/**
+ * Get mimetype of given url, useful for # alternative urls.
+ *
+ * @private
+ * @param string $url
+ * @return string $mimetype
+ */
+function filter_mediaplugin_get_mimetype($url) {
+    $matches = null;
+    if (preg_match("|^(.*)/[a-z]*file.php(\?file=)?(/[^&\?#]*)|", $url, $matches)) {
+        // remove the special moodle file serving hacks so that the *file.php is ignored
+        $url = $matches[1].$matches[3];
+    } else {
+        $url = preg_replace('/[#\?].*$/', '', $url);
+    }
+
+    $mimetype = mimeinfo('type', $url);
+
+    return $mimetype;
+}
 
 /**
  * Parse list of alternative URLs
  * @param string $url urls separated with '#', size specified as ?d=640x480 or #d=640x480
+ * @param int $defaultwidth
+ * @param int $defaultheight
  * @return array (urls, width, height)
  */
 function filter_mediaplugin_parse_alternatives($url, $defaultwidth = 0, $defaultheight = 0) {
@@ -252,7 +274,7 @@ function filter_mediaplugin_html5audio_callback(array $link) {
     $fallbacklink = null;
 
     foreach ($urls as $url) {
-        $mimetype = mimeinfo('type', $url);
+        $mimetype = filter_mediaplugin_get_mimetype($url);
         if (strpos($mimetype, 'audio/') !== 0) {
             continue;
         }
@@ -344,7 +366,7 @@ function filter_mediaplugin_html5video_callback(array $link) {
     $fallbacklink = null;
 
     foreach ($urls as $url) {
-        $mimetype = mimeinfo('type', $url);
+        $mimetype = filter_mediaplugin_get_mimetype($url);
         if (strpos($mimetype, 'video/') !== 0) {
             continue;
         }
@@ -546,7 +568,7 @@ function filter_mediaplugin_flv_callback($link) {
     $sources  = array();
 
     foreach ($urls as $url) {
-        $mimetype = mimeinfo('type', $url);
+        $mimetype = filter_mediaplugin_get_mimetype($url);
         if (strpos($mimetype, 'video/') !== 0) {
             continue;
         }
@@ -559,7 +581,7 @@ function filter_mediaplugin_flv_callback($link) {
         }
 
         if ($flashurl === null) {
-            $flashurl  = str_replace('&', '&amp;', $url);
+            $flashurl  = $url;
         }
     }
     if (!$sources) {
@@ -592,7 +614,7 @@ OET;
     // note: no need to print "this is flv link" because it is printed automatically if JS or Flash not available
 
     $output = html_writer::tag('span', $printlink, array('id'=>$id, 'class'=>'mediaplugin mediaplugin_flv'));
-    $output .= html_writer::script(js_writer::function_call('M.util.add_video_player', array($id, $flashurl, $width, $height, $autosize))); // we can not use standard JS init because this may be cached
+    $output .= html_writer::script(js_writer::function_call('M.util.add_video_player', array($id, rawurlencode($flashurl), $width, $height, $autosize))); // we can not use standard JS init because this may be cached
 
     return $output;
 }
@@ -799,7 +821,7 @@ function filter_mediaplugin_wmp_callback($link) {
         $mpsize = 'width="'.$link[4].'" height="'.($link[5] + 64).'"';
         $autosize = 'false';
     }
-    $mimetype = mimeinfo('type', $url);
+    $mimetype = filter_mediaplugin_get_mimetype($url);
 
 
 
@@ -861,7 +883,7 @@ function filter_mediaplugin_qt_callback($link) {
     } else {
         $size = 'width="'.$link[4].'" height="'.($link[5]+15).'"';
     }
-    $mimetype = mimeinfo('type', $url);
+    $mimetype = filter_mediaplugin_get_mimetype($url);
 
     // this is the safest fallback for incomplete or missing browser support for this format
     return <<<OET
diff --git a/filter/upgrade.txt b/filter/upgrade.txt
new file mode 100644 (file)
index 0000000..bb0522e
--- /dev/null
@@ -0,0 +1,11 @@
+This file describes API changes in core filter API and plugins,
+information provided here is intended especially for developers.
+
+=== 2.2 ===
+
+* legacy filters and legacy locations have been deprecated, so any
+  old filter should be updated to use the new moodle_text_filter,
+  and any filter bundled under mod/xxxx directories be moved to
+  /filter/xxxx (MDL-29995). They will stop working completely in
+  Moodle 2.3 (MDL-29996). See the glossary or data filters for
+  examples of legacy module filters and locations already updated.
index d9390bb..09daca9 100644 (file)
@@ -44,8 +44,8 @@ $PAGE->set_url(new moodle_url('/grade/grading/form/rubric/edit.php', array('area
 $PAGE->set_title(get_string('definerubric', 'gradingform_rubric'));
 $PAGE->set_heading(get_string('definerubric', 'gradingform_rubric'));
 
-$mform = new gradingform_rubric_editrubric(null, array('areaid' => $areaid, 'context' => $context, 'allowdraft' => !$controller->has_active_instances()));
-$data = $controller->get_definition_for_editing();
+$mform = new gradingform_rubric_editrubric(null, array('areaid' => $areaid, 'context' => $context, 'allowdraft' => !$controller->has_active_instances()), 'post', '', array('class' => 'gradingform_rubric_editform'));
+$data = $controller->get_definition_for_editing(true);
 $returnurl = optional_param('returnurl', $manager->get_management_url(), PARAM_LOCALURL);
 $data->returnurl = $returnurl;
 $mform->set_data($data);
index 3bae783..64cf8f9 100644 (file)
@@ -58,8 +58,8 @@ class gradingform_rubric_editrubric extends moodleform {
 
         // rubric completion status
         $choices = array();
-        $choices[gradingform_controller::DEFINITION_STATUS_DRAFT]    = get_string('statusdraft', 'grading');
-        $choices[gradingform_controller::DEFINITION_STATUS_READY]    = get_string('statusready', 'grading');
+        $choices[gradingform_controller::DEFINITION_STATUS_DRAFT]    = html_writer::tag('span', get_string('statusdraft', 'core_grading'), array('class' => 'status draft'));
+        $choices[gradingform_controller::DEFINITION_STATUS_READY]    = html_writer::tag('span', get_string('statusready', 'core_grading'), array('class' => 'status ready'));
         $form->addElement('select', 'status', get_string('rubricstatus', 'gradingform_rubric'), $choices)->freeze();
 
         // rubric editor
@@ -80,6 +80,27 @@ class gradingform_rubric_editrubric extends moodleform {
         $form->closeHeaderBefore('buttonar');
     }
 
+    /**
+     * Setup the form depending on current values. This method is called after definition(),
+     * data submission and set_data().
+     * All form setup that is dependent on form values should go in here.
+     *
+     * We remove the element status if there is no current status (i.e. rubric is only being created)
+     * so the users do not get confused
+     */
+    public function definition_after_data() {
+        $form = $this->_form;
+        $el = $form->getElement('status');
+        if (!$el->getValue()) {
+            $form->removeElement('status');
+        } else {
+            $vals = array_values($el->getValue());
+            if ($vals[0] == gradingform_controller::DEFINITION_STATUS_READY) {
+                $this->findButton('saverubric')->setValue(get_string('save', 'gradingform_rubric'));
+            }
+        }
+    }
+
     /**
      * Form vlidation.
      * If there are errors return array of errors ("fieldname"=>"error message"),
index de3dd19..1d5d524 100644 (file)
@@ -12,6 +12,10 @@ M.gradingform_rubriceditor.init = function(Y, options) {
     }
     M.gradingform_rubriceditor.disablealleditors()
     Y.on('click', M.gradingform_rubriceditor.clickanywhere, 'body', null)
+    YUI().use('event-touch', function (Y) {
+        Y.one('body').on('touchstart', M.gradingform_rubriceditor.clickanywhere);
+        Y.one('body').on('touchend', M.gradingform_rubriceditor.clickanywhere);
+    })
     M.gradingform_rubriceditor.addhandlers()
 };
 
@@ -35,6 +39,7 @@ M.gradingform_rubriceditor.disablealleditors = function() {
 // it switches this element to edit mode. If rubric button is clicked it does nothing so the 'buttonclick'
 // function is invoked
 M.gradingform_rubriceditor.clickanywhere = function(e) {
+    if (e.type == 'touchstart') return
     var el = e.target
     // if clicked on button - disablecurrenteditor, continue
     if (el.get('tagName') == 'INPUT' && el.get('type') == 'submit') {
@@ -48,7 +53,7 @@ M.gradingform_rubriceditor.clickanywhere = function(e) {
         el = el.get('parentNode')
     }
     if (el) {
-        if (el.one('textarea').getStyle('display') == 'none') {
+        if (el.one('textarea').hasClass('hiddenelement')) {
             M.gradingform_rubriceditor.disablealleditors()
             M.gradingform_rubriceditor.editmode(el, true, focustb)
         }
@@ -61,19 +66,19 @@ M.gradingform_rubriceditor.clickanywhere = function(e) {
 // switch the criterion description or level to edit mode or switch back
 M.gradingform_rubriceditor.editmode = function(el, editmode, focustb) {
     var ta = el.one('textarea')
-    if (!editmode && ta.getStyle('display') == 'none') return;
-    if (editmode && ta.getStyle('display') == 'block') return;
-    var pseudotablink = '<a href="#" class="pseudotablink">&nbsp;</a>',
+    if (!editmode && ta.hasClass('hiddenelement')) return;
+    if (editmode && !ta.hasClass('hiddenelement')) return;
+    var pseudotablink = '<input type="text" size="1" class="pseudotablink"/>',
         taplain = ta.get('parentNode').one('.plainvalue'),
         tbplain = null,
-        tb = el.one('input[type=text]')
+        tb = el.one('.score input[type=text]')
     // add 'plainvalue' next to textarea for description/definition and next to input text field for score (if applicable)
     if (!taplain) {
-        ta.get('parentNode').append('<div class="plainvalue"><span class="textvalue">&nbsp;</span>'+pseudotablink+'</div>')
+        ta.get('parentNode').append('<div class="plainvalue">'+pseudotablink+'<span class="textvalue">&nbsp;</span></div>')
         taplain = ta.get('parentNode').one('.plainvalue')
         taplain.one('.pseudotablink').on('focus', M.gradingform_rubriceditor.clickanywhere)
         if (tb) {
-            tb.get('parentNode').append('<div class="plainvalue"><span class="textvalue">&nbsp;</span>'+pseudotablink+'</div>')
+            tb.get('parentNode').append('<span class="plainvalue">'+pseudotablink+'<span class="textvalue">&nbsp;</span></span>')
             tbplain = tb.get('parentNode').one('.plainvalue')
             tbplain.one('.pseudotablink').on('focus', M.gradingform_rubriceditor.clickanywhere)
         }
@@ -90,20 +95,33 @@ M.gradingform_rubriceditor.editmode = function(el, editmode, focustb) {
         }
         taplain.one('.textvalue').set('innerHTML', value)
         if (tb) tbplain.one('.textvalue').set('innerHTML', tb.get('value'))
+        // hide/display textarea, textbox and plaintexts
+        taplain.removeClass('hiddenelement')
+        ta.addClass('hiddenelement')
+        if (tb) {
+            tbplain.removeClass('hiddenelement')
+            tb.addClass('hiddenelement')
+        }
     } else {
         // if we need to show the input fields, set the width/height for textarea so it fills the cell
-        var width = parseFloat(ta.get('parentNode').getComputedStyle('width')),
-            height
-        if (el.hasClass('level')) height = parseFloat(el.getComputedStyle('height')) - parseFloat(el.one('.score').getComputedStyle('height'))
-        else height = parseFloat(ta.get('parentNode').getComputedStyle('height'))
-        ta.setStyle('width', Math.max(width,50)+'px').setStyle('height', Math.max(height,20)+'px')
-    }
-    // hide/display textarea, textbox and plaintexts
-    taplain.setStyle('display', editmode ? 'none' : 'block')
-    ta.setStyle('display', editmode ? 'block' : 'none')
-    if (tb) {
-        tbplain.setStyle('display', editmode ? 'none' : 'inline-block')
-        tb.setStyle('display', editmode ? 'inline-block' : 'none')
+        try {
+            var width = parseFloat(ta.get('parentNode').getComputedStyle('width')),
+                height
+            if (el.hasClass('level')) height = parseFloat(el.getComputedStyle('height')) - parseFloat(el.one('.score').getComputedStyle('height'))
+            else height = parseFloat(ta.get('parentNode').getComputedStyle('height'))
+            ta.setStyle('width', Math.max(width,50)+'px')
+            ta.setStyle('height', Math.max(height,20)+'px')
+        }
+        catch (err) {
+            // this browser do not support 'computedStyle', leave the default size of the textbox
+        }
+        // hide/display textarea, textbox and plaintexts
+        taplain.addClass('hiddenelement')
+        ta.removeClass('hiddenelement')
+        if (tb) {
+            tbplain.addClass('hiddenelement')
+            tb.removeClass('hiddenelement')
+        }
     }
     // focus the proper input field in edit mode
     if (editmode) { if (tb && focustb) tb.focus(); else ta.focus() }
index 0d78aa6..d7f605c 100644 (file)
@@ -53,12 +53,18 @@ $string['regradeoption0'] = 'Do not mark for regrade';
 $string['regradeoption1'] = 'Mark for regrade';
 $string['restoredfromdraft'] = 'NOTE: The last attempt to grade this person was not saved properly so draft grades have been restored. If you want to cancel these changes use the \'Cancel\' button below.';
 $string['rubric'] = 'Rubric';
+$string['rubricmapping'] = 'Score to grade mapping rules';
+$string['rubricmappingexplained'] = 'The minimum possible score for this rubric is <b>{$a->minscore} points</b> and it will be converted to the minimum grade available in this module (which is zero unless the scale is used).
+    The maximum score <b>{$a->maxscore} points</b> will be converted to the maximum grade.<br />
+    Intermediate scores will be converted respectively and rounded to the nearest available grade.<br />
+    If a scale is used instead of a grade, the score will be converted to the scale elements as if they were consecutive integers.';
 $string['rubricnotcompleted'] = 'Please choose something for each criterion';
 $string['rubricoptions'] = 'Rubric options';
 $string['rubricstatus'] = 'Current rubric status';
+$string['save'] = 'Save';
 $string['saverubric'] = 'Save rubric and make it ready';
 $string['saverubricdraft'] = 'Save as draft';
-$string['scorepostfix'] = '{$a} points';
+$string['scorepostfix'] = '{$a}points';
 $string['showdescriptionstudent'] = 'Display rubric description to those being graded';
 $string['showdescriptionteacher'] = 'Display rubric description during evaluation';
 $string['showremarksstudent'] = 'Show remarks to those being graded';
@@ -66,6 +72,4 @@ $string['showscorestudent'] = 'Display points for each level to those being grad
 $string['showscoreteacher'] = 'Display points for each level during evaluation';
 $string['sortlevelsasc'] = 'Sort order for levels:';
 $string['sortlevelsasc0'] = 'Descending by number of points';
-$string['sortlevelsasc1'] = 'Ascending by number of points';
-$string['statusdraft'] = 'Draft';
-$string['statusready'] = 'Ready';
\ No newline at end of file
+$string['sortlevelsasc1'] = 'Ascending by number of points';
\ No newline at end of file
index 6aa0d98..ff24e85 100644 (file)
@@ -360,9 +360,10 @@ class gradingform_rubric_controller extends gradingform_controller {
     /**
      * Converts the current definition into an object suitable for the editor form's set_data()
      *
+     * @param boolean $addemptycriterion whether to add an empty criterion if the rubric is completely empty (just being created)
      * @return stdClass
      */
-    public function get_definition_for_editing() {
+    public function get_definition_for_editing($addemptycriterion = false) {
 
         $definition = $this->get_definition();
         $properties = new stdClass();
@@ -378,6 +379,8 @@ class gradingform_rubric_controller extends gradingform_controller {
         $properties->rubric = array('criteria' => array(), 'options' => $this->get_options());
         if (!empty($definition->rubric_criteria)) {
             $properties->rubric['criteria'] = $definition->rubric_criteria;
+        } else if (!$definition && $addemptycriterion) {
+            $properties->rubric['criteria'] = array('addcriterion' => 1);
         }
 
         return $properties;
@@ -481,7 +484,8 @@ class gradingform_rubric_controller extends gradingform_controller {
         $output = $this->get_renderer($page);
         $criteria = $this->definition->rubric_criteria;
         $options = $this->get_options();
-        $rubric = $output->display_rubric($criteria, $options, self::DISPLAY_PREVIEW, 'rubric');
+        $rubric = $output->display_rubric_mapping_explained($this->get_min_max_score());
+        $rubric .= $output->display_rubric($criteria, $options, self::DISPLAY_PREVIEW, 'rubric');
 
         return $rubric;
     }
@@ -590,6 +594,28 @@ class gradingform_rubric_controller extends gradingform_controller {
 
         return array($subsql, $params);
     }
+
+    /**
+     * Calculates and returns the possible minimum and maximum score (in points) for this rubric
+     *
+     * @return array
+     */
+    public function get_min_max_score() {
+        if (!$this->is_form_available()) {
+            return null;
+        }
+        $returnvalue = array('minscore' => 0, 'maxscore' => 0);
+        foreach ($this->get_definition()->rubric_criteria as $id => $criterion) {
+            $scores = array();
+            foreach ($criterion['levels'] as $level) {
+                $scores[] = $level['score'];
+            }
+            sort($scores);
+            $returnvalue['minscore'] += $scores[0];
+            $returnvalue['maxscore'] += $scores[sizeof($scores)-1];
+        }
+        return $returnvalue;
+    }
 }
 
 /**
@@ -717,19 +743,7 @@ class gradingform_rubric_instance extends gradingform_instance {
         global $DB, $USER;
         $grade = $this->get_rubric_filling();
 
-        $minscore = 0;
-        $maxscore = 0;
-        foreach ($this->get_controller()->get_definition()->rubric_criteria as $id => $criterion) {
-            $scores = array();
-            foreach ($criterion['levels'] as $level) {
-                $scores[] = $level['score'];
-            }
-            sort($scores);
-            $minscore += $scores[0];
-            $maxscore += $scores[sizeof($scores)-1];
-        }
-
-        if ($maxscore <= $minscore) {
+        if (!($scores = $this->get_controller()->get_min_max_score()) || $scores['maxscore'] <= $scores['minscore']) {
             return -1;
         }
 
@@ -745,7 +759,7 @@ class gradingform_rubric_instance extends gradingform_instance {
         foreach ($grade['criteria'] as $id => $record) {
             $curscore += $this->get_controller()->get_definition()->rubric_criteria[$id]['levels'][$record['levelid']]['score'];
         }
-        return round(($curscore-$minscore)/($maxscore-$minscore)*($maxgrade-$mingrade), 0) + $mingrade; // TODO mapping
+        return round(($curscore-$scores['minscore'])/($scores['maxscore']-$scores['minscore'])*($maxgrade-$mingrade), 0) + $mingrade;
     }
 
     /**
index 8086833..f037263 100644 (file)
@@ -164,7 +164,7 @@ class gradingform_rubric_renderer extends plugin_renderer_base {
         $leveltemplate .= html_writer::start_tag('div', array('class' => 'level-wrapper'));
         if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) {
             $definition = html_writer::tag('textarea', htmlspecialchars($level['definition']), array('name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][definition]', 'cols' => '10', 'rows' => '4'));
-            $score = html_writer::empty_tag('input', array('type' => 'text', 'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][score]', 'size' => '4', 'value' => $level['score']));
+            $score = html_writer::empty_tag('input', array('type' => 'text', 'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][score]', 'size' => '3', 'value' => $level['score']));
         } else {
             if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN) {
                 $leveltemplate .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][definition]', 'value' => $level['definition']));
@@ -181,7 +181,7 @@ class gradingform_rubric_renderer extends plugin_renderer_base {
         if ($mode == gradingform_rubric_controller::DISPLAY_EVAL_FROZEN && $level['checked']) {
             $leveltemplate .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][levelid]', 'value' => $level['id']));
         }
-        $score = html_writer::tag('span', $score, array('id' => '{NAME}-criteria-{CRITERION-id}-levels-{LEVEL-id}-score'));
+        $score = html_writer::tag('span', $score, array('id' => '{NAME}-criteria-{CRITERION-id}-levels-{LEVEL-id}-score', 'class' => 'scorevalue'));
         $definitionclass = 'definition';
         if (isset($level['error_definition'])) {
             $definitionclass .= ' error';
@@ -453,4 +453,22 @@ class gradingform_rubric_renderer extends plugin_renderer_base {
         $html .= html_writer::end_tag('div');
         return $html;
     }
+
+    /**
+     * Generates and returns HTML code to display information box about how rubric score is converted to the grade
+     *
+     * @param array $scores
+     * @return string
+     */
+    public function display_rubric_mapping_explained($scores) {
+        $html = '';
+        if (!$scores) {
+            return $html;
+        }
+        $html .= $this->box(
+                html_writer::tag('h4', get_string('rubricmapping', 'gradingform_rubric')).
+                html_writer::tag('div', get_string('rubricmappingexplained', 'gradingform_rubric', (object)$scores))
+                , 'generalbox rubricmappingexplained');
+        return $html;
+    }
 }
index 7fb2331..92cd3be 100644 (file)
@@ -184,14 +184,14 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
                 // when adding new criterion copy the number of levels and their scores from the last criterion
                 if (!empty($value['criteria'][$lastid]['levels'])) {
                     foreach ($value['criteria'][$lastid]['levels'] as $lastlevel) {
-                        $criterion['levels']['NEWID'+($i++)]['score'] = $lastlevel['score'];
+                        $criterion['levels']['NEWID'.($i++)]['score'] = $lastlevel['score'];
                     }
                 } else {
-                    $criterion['levels']['NEWID'+($i++)]['score'] = 0;
+                    $criterion['levels']['NEWID'.($i++)]['score'] = 0;
                 }
                 // add more levels so there are at least 3 in the new criterion. Increment by 1 the score for each next one
-                for ($i; $i<3; $i++) {
-                    $criterion['levels']['NEWID'+$i]['score'] = $criterion['levels']['NEWID'+($i-1)]['score'] + 1;
+                for ($i=$i; $i<3; $i++) {
+                    $criterion['levels']['NEWID'.$i]['score'] = $criterion['levels']['NEWID'.($i-1)]['score'] + 1;
                 }
                 // set other necessary fields (definition) for the levels in the new criterion
                 foreach (array_keys($criterion['levels']) as $i) {
index 4428ac3..b88df4e 100644 (file)
 
 */
 
+.gradingform_rubric_editform .status {font-weight:normal;text-transform:uppercase;font-size:60%;padding:0.25em;border:1px solid #EEE;}
+.gradingform_rubric_editform .status.ready {background-color:#e7f1c3;border-color:#AAEEAA;}
+.gradingform_rubric_editform .status.draft {background-color:#f3f2aa;border-color:#EEEE22;}
+
 .gradingform_rubric.editor .criterion .controls,
 .gradingform_rubric .criterion .description,
 .gradingform_rubric .criterion .levels,
@@ -75,7 +79,8 @@
 .gradingform_rubric .plainvalue.empty {font-style: italic; color: #AAA;}
 
 .gradingform_rubric.editor .criterion .levels .level .delete {position:absolute;right:0;bottom:0;}
-.gradingform_rubric .criterion .levels .level .score {font-style:italic;color:#575;font-weight: bold;margin-top:5px;}
+.gradingform_rubric .criterion .levels .level .score {font-style:italic;color:#575;font-weight: bold;margin-top:5px;white-space:nowrap;}
+.gradingform_rubric .criterion .levels .level .score .scorevalue {padding-right:5px;}
 
 /* Make invisible the buttons 'Move up' for the first criterion and 'Move down' for the last, because those buttons will make no change */
 .gradingform_rubric.editor .criterion.first .controls .moveup input,
 .gradingform_rubric .criterion .levels .level .definition.error,
 .gradingform_rubric .criterion .levels .level .score.error {background:#FFDDDD;}
 
-/**
- *
- */
-
 .gradingform_rubric-regrade {padding:10px;background:#FFDDDD;border:1px solid #F00;margin-bottom:10px;}
 .gradingform_rubric-restored {padding:10px;background:#FFFFDD;border:1px solid #FF0;margin-bottom:10px;}
 .gradingform_rubric-error {color:red;font-weight:bold;}
+
+/* special classes for elements created by rubriceditor.js */
+.gradingform_rubric.editor .hiddenelement {display:none;}
+.gradingform_rubric.editor .pseudotablink {background-color:transparent;border:0 solid;height:1px;width:1px;color:transparent;padding:0;margin:0;position:relative;float:right;}
\ No newline at end of file
index d96b75e..bc93369 100644 (file)
@@ -651,7 +651,7 @@ class grade_report_user extends grade_report {
             // Then left join with grade_grades and look for rows with null final grade (which includes grade items with no grade_grade)
             $sql = "SELECT gi.id, COUNT(u.id) AS count
                       FROM {grade_items} gi
-                      JOIN {user} u
+                      JOIN {user} u ON u.deleted = 0
                       JOIN ($enrolledsql) je ON je.id = u.id
                       JOIN (
                                SELECT DISTINCT ra.userid
@@ -660,10 +660,9 @@ class grade_report_user extends grade_report {
                                   AND ra.contextid " . get_related_contexts_string($this->context) . "
                            ) rainner ON rainner.userid = u.id
                       LEFT JOIN {grade_grades} gg
-                           ON (gg.itemid = gi.id AND gg.userid = u.id AND gg.finalgrade IS NOT NULL AND gg.hidden = 0)
+                             ON (gg.itemid = gi.id AND gg.userid = u.id AND gg.finalgrade IS NOT NULL AND gg.hidden = 0)
                       $groupsql
                      WHERE gi.courseid = :courseid
-                           AND u.deleted = 0
                            AND gg.finalgrade IS NULL
                            $groupwheresql
                   GROUP BY gi.id";
index 6c9e52c..aef61d7 100644 (file)
@@ -316,7 +316,7 @@ function groups_delete_group($grouporid) {
 
 /**
  * Delete grouping
- * @param int $groupingid
+ * @param int $groupingorid
  * @return bool success
  */
 function groups_delete_grouping($groupingorid) {
@@ -389,7 +389,7 @@ function groups_delete_group_members($courseid, $userid=0, $showfeedback=false)
     events_trigger('groups_members_removed', $eventdata);
 
     if ($showfeedback) {
-        echo $OUTPUT->notification(get_string('deleted').' groups_members');
+        echo $OUTPUT->notification(get_string('deleted').' - '.get_string('groupmembers', 'group'), 'notifysuccess');
     }
 
     return true;
@@ -407,15 +407,10 @@ function groups_delete_groupings_groups($courseid, $showfeedback=false) {
     $groupssql = "SELECT id FROM {groups} g WHERE g.courseid = ?";
     $DB->delete_records_select('groupings_groups', "groupid IN ($groupssql)", array($courseid));
 
-    // Delete all files associated with groupings for this course
-    $context = get_context_instance(CONTEXT_COURSE, $courseid);
-
     //trigger groups events
     events_trigger('groups_groupings_groups_removed', $courseid);
 
-    if ($showfeedback) {
-        echo $OUTPUT->notification(get_string('deleted').' groupings_groups');
-    }
+    // no need to show any feedback here - we delete usually first groupings and then groups
 
     return true;
 }
@@ -449,11 +444,11 @@ function groups_delete_groups($courseid, $showfeedback=false) {
 
     $DB->delete_records('groups', array('courseid'=>$courseid));
 
-    //trigger groups events
+    // trigger groups events
     events_trigger('groups_groups_deleted', $courseid);
 
     if ($showfeedback) {
-        echo $OUTPUT->notification(get_string('deleted').' groups');
+        echo $OUTPUT->notification(get_string('deleted').' - '.get_string('groups', 'group'), 'notifysuccess');
     }
 
     return true;
@@ -468,9 +463,6 @@ function groups_delete_groups($courseid, $showfeedback=false) {
 function groups_delete_groupings($courseid, $showfeedback=false) {
     global $DB, $OUTPUT;
 
-    $context = get_context_instance(CONTEXT_COURSE, $courseid);
-    $fs = get_file_storage();
-
     // delete any uses of groupings
     $sql = "DELETE FROM {groupings_groups}
              WHERE groupingid in (SELECT id FROM {groupings} g WHERE g.courseid = ?)";
@@ -488,11 +480,11 @@ function groups_delete_groupings($courseid, $showfeedback=false) {
 
     $DB->delete_records('groupings', array('courseid'=>$courseid));
 
-    //trigger groups events
+    // trigger groups events
     events_trigger('groups_groupings_deleted', $courseid);
 
     if ($showfeedback) {
-        echo $OUTPUT->notification(get_string('deleted').' groupings');
+        echo $OUTPUT->notification(get_string('deleted').' - '.get_string('groupings', 'group'), 'notifysuccess');
     }
 
     return true;
index 533c7ae..341a147 100644 (file)
@@ -166,7 +166,7 @@ $CFG->wwwroot              = install_guess_wwwroot(); // can not be changed - pp
 $CFG->httpswwwroot         = $CFG->wwwroot;
 $CFG->dataroot             = $config->dataroot;
 $CFG->tempdir              = $CFG->dataroot.'/temp';
-$CFG->cachedir             = $CFG->dataroot.'/temp';
+$CFG->cachedir             = $CFG->dataroot.'/cache';
 $CFG->admin                = $config->admin;
 $CFG->docroot              = 'http://docs.moodle.org';
 $CFG->langotherroot        = $CFG->dataroot.'/lang';
index 6d45ef1..9245032 100644 (file)
@@ -30,4 +30,5 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['thisdirection'] = 'ltr';
 $string['thislanguage'] = 'Azərbaycanca';
index 389ab2f..a68ee93 100644 (file)
@@ -33,3 +33,4 @@ defined('MOODLE_INTERNAL') || die();
 $string['language'] = 'Iaith';
 $string['next'] = 'Nesaf';
 $string['previous'] = 'Blaenorol';
+$string['reload'] = 'Ail-lwytho';
index 260fe97..68507dc 100644 (file)
@@ -26,6 +26,7 @@ databasehost,install
 databasename,install
 databasetypehead,install
 dataroot,install
+datarootpermission,install
 dbprefix,install
 dirroot,install
 downloadedfilecheckfailed,error
index f554e6b..1ce4f9e 100644 (file)
@@ -901,6 +901,7 @@ $string['slasharguments'] = 'Use slash arguments';
 $string['smartpix'] = 'Smart pix search';
 $string['soaprecommended'] = 'Installing the optional soap extension is useful for web services and some contrib modules.';
 $string['spellengine'] = 'Spell engine';
+$string['spelllanguagelist'] = 'Spell language list';
 $string['splrequired'] = 'The SPL PHP extension is now required by Moodle.';
 $string['stats'] = 'Statistics';
 $string['statsfirstrun'] = 'Maximum processing interval';
index ce65d59..f0e125a 100644 (file)
@@ -319,6 +319,7 @@ $string['invalidstatedetected'] = 'Something has gone wrong: {$a}. This should n
 $string['invalidurl'] = 'Invalid URL';
 $string['invaliduser'] = 'Invalid user';
 $string['invaliduserid'] = 'Invalid user id';
+$string['invaliduserfield'] = 'Invalid user field: {$a}';
 $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 cdaaa0c..75ba827 100644 (file)
@@ -154,6 +154,7 @@ $string['databasetypesub'] = 'Moodle supports several types of database servers.
 $string['databaseuser'] = 'Database user';
 $string['dataroot'] = 'Data directory';
 $string['datarooterror'] = 'The \'data directory\' you specified could not be found or created.  Either correct the path or create that directory manually.';
+$string['datarootpermission'] = 'Data directories permission';
 $string['datarootpublicerror'] = 'The \'data directory\' you specified is directly accessible via web, you must use different directory.';
 $string['dbconnectionerror'] = 'We could not connect to the database you specified. Please check your database settings.';
 $string['dbcreationerror'] = 'Database creation error. Could not create the given database name with the settings provided';
index 52caf4a..9cae46d 100644 (file)
@@ -531,6 +531,7 @@ $string['emaildisable'] = 'This email address is disabled';
 $string['emaildisableclick'] = 'Click here to disable all email from being sent to this address';
 $string['emaildisplay'] = 'Email display';
 $string['emaildisplaycourse'] = 'Allow only other course members to see my email address';
+$string['emaildisplayhidden'] = 'Email hidden';
 $string['emaildisplayno'] = 'Hide my email address from everyone';
 $string['emaildisplayyes'] = 'Allow everyone to see my email address';
 $string['emailenable'] = 'This email address is enabled';
index 4876027..2d28d61 100644 (file)
@@ -62,6 +62,8 @@ $string['disabledwarning'] = 'All web service protocols are disabled.  The "Enab
 $string['doc'] = 'Documentation';
 $string['docaccessrefused'] = 'You are not allowed to see the documentation for this token';
 $string['documentation'] = 'web service documentation';
+$string['downloadfiles'] = 'Can download files';
+$string['downloadfiles_help'] = 'If enabled, any user can download files with their security keys. Of course they are restricted to the files they are allowed to download in the site.';
 $string['editaservice'] = 'Edit service';
 $string['editservice'] = 'Edit the service: {$a->name} (id: {$a->id})';
 $string['enabled'] = 'Enabled';
@@ -188,6 +190,11 @@ $string['webservices'] = 'Web services';
 $string['webservicesoverview'] = 'Overview';
 $string['webservicetokens'] = 'Web service tokens';
 $string['wrongusernamepassword'] = 'Wrong username or password';
+$string['wsaccessuserdeleted'] = 'Refused web service access for deleted username: {$a}';
+$string['wsaccessuserexpired'] = 'Refused web service access for password expired username: {$a}';
+$string['wsaccessusernologin'] = 'Refused web service access for nologin authentication username: {$a}';
+$string['wsaccessusersuspended'] = 'Refused web service access for suspended username: {$a}';
+$string['wsaccessuserunconfirmed'] = 'Refused web service access for unconfirmed username: {$a}';
 $string['wsauthmissing'] = 'The web service authentication plugin is missing.';
 $string['wsauthnotenabled'] = 'The web service authentication plugin is disabled.';
 $string['wsclientdoc'] = 'Moodle web service client documentation';
index 68c1761..4eaaecb 100644 (file)
@@ -245,6 +245,26 @@ function uninstall_plugin($type, $name) {
                 set_config('enrol_plugins_enabled', implode(',', $enabledenrols));
             }
         }
+
+    } else if ($type === 'block') {
+        if ($block = $DB->get_record('block', array('name'=>$name))) {
+            // Inform block it's about to be deleted
+            if (file_exists("$CFG->dirroot/blocks/$block->name/block_$block->name.php")) {
+                $blockobject = block_instance($block->name);
+                if ($blockobject) {
+                    $blockobject->before_delete();  //only if we can create instance, block might have been already removed
+                }
+            }
+
+            // First delete instances and related contexts
+            $instances = $DB->get_records('block_instances', array('blockname' => $block->name));
+            foreach($instances as $instance) {
+                blocks_delete_instance($instance);
+            }
+
+            // Delete block
+            $DB->delete_records('block', array('id'=>$block->id));
+        }
     }
 
     // perform clean-up task common for all the plugin/subplugin types
@@ -271,7 +291,11 @@ function uninstall_plugin($type, $name) {
 
     // delete the plugin tables
     $xmldbfilepath = $plugindirectory . '/db/install.xml';
-    drop_plugin_tables($pluginname, $xmldbfilepath, false);
+    drop_plugin_tables($component, $xmldbfilepath, false);
+    if ($type === 'mod' or $type === 'block') {
+        // non-frankenstyle table prefixes
+        drop_plugin_tables($name, $xmldbfilepath, false);
+    }
 
     // delete the capabilities that were defined by this module
     capabilities_cleanup($component);
index 9235098..3c2936b 100644 (file)
@@ -237,6 +237,11 @@ class iCalendar_component {
         $components = array(); // Initialise a stack of components
         $this->clear_errors();
         foreach ($lines as $key => $line) {
+            // ignore empty lines
+            if (trim($line) == '') {
+                continue;
+            }
+
             // Divide the line up into label, parameters and data fields.
             if (!preg_match('#^(?P<label>[-[:alnum:]]+)(?P<params>(?:;(?:(?:[-[:alnum:]]+)=(?:[^[:cntrl:]";:,]+|"[^[:cntrl:]"]+")))*):(?P<data>.*)$#', $line, $match)) {
                 $this->parser_error('Invalid line: '.$key.', ignoring');
index 73aa42c..6614b39 100644 (file)
@@ -54,9 +54,9 @@ class iCalendar_parameter {
                 return rfc2445_is_valid_value($value, RFC2445_TYPE_CAL_ADDRESS);
             break;
 
-            // These are textual parameters, so the MUST NOT contain double quotes
+            // RFC-2445: can contain quotes.
             case 'CN':
-                return (strpos($value, '"') === false);
+                return true;
             break;
 
             // These have enumerated legal values
index bb621ce..8a557a4 100644 (file)
@@ -218,6 +218,7 @@ class iCalendar_property_calscale extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -235,6 +236,7 @@ class iCalendar_property_method extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -255,6 +257,7 @@ class iCalendar_property_prodid extends iCalendar_property {
     var $val_default = NULL;
 
     function __construct() {
+        parent::__construct();
         $this->val_default = '-//John Papaioannou/NONSGML Bennu '._BENNU_VERSION.'//EN';
 
         $this->valid_parameters = array(
@@ -270,6 +273,7 @@ class iCalendar_property_version extends iCalendar_property {
     var $val_default = '2.0';
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -290,6 +294,7 @@ class iCalendar_property_attach extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_URI;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'FMTTYPE'     => RFC2445_OPTIONAL | RFC2445_ONCE,
             'ENCODING'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -336,6 +341,7 @@ class iCalendar_property_categories extends iCalendar_property {
     var $val_multi   = true;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
             RFC2445_XNAME => RFC2445_OPTIONAL
@@ -350,6 +356,7 @@ class iCalendar_property_class extends iCalendar_property {
     var $val_default = 'PUBLIC';
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -367,6 +374,7 @@ class iCalendar_property_comment extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -381,6 +389,7 @@ class iCalendar_property_description extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -395,6 +404,7 @@ class iCalendar_property_geo extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -435,6 +445,7 @@ class iCalendar_property_location extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -449,6 +460,7 @@ class iCalendar_property_percent_complete extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_INTEGER;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -471,6 +483,7 @@ class iCalendar_property_priority extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_INTEGER;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -494,6 +507,7 @@ class iCalendar_property_resources extends iCalendar_property {
     var $val_multi   = true;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -508,6 +522,7 @@ class iCalendar_property_status extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -538,6 +553,7 @@ class iCalendar_property_summary extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -555,6 +571,7 @@ class iCalendar_property_completed extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -575,6 +592,7 @@ class iCalendar_property_dtend extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'VALUE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
             'TZID'        => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -617,6 +635,7 @@ class iCalendar_property_due extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'VALUE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
             'TZID'        => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -659,6 +678,7 @@ class iCalendar_property_dtstart extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'VALUE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
             'TZID'        => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -702,6 +722,7 @@ class iCalendar_property_duration extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DURATION;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -724,6 +745,7 @@ class iCalendar_property_freebusy extends iCalendar_property {
     var $val_multi   = true;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'FBTYPE'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             RFC2445_XNAME => RFC2445_OPTIONAL
@@ -758,6 +780,7 @@ class iCalendar_property_transp extends iCalendar_property {
     var $val_default = 'OPAQUE';
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -783,6 +806,7 @@ class iCalendar_property_attendee extends iCalendar_property {
     // TODO: standard has lots of detail here, make triple sure that we eventually conform
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'LANGUAGE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
             'CN'             => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -823,6 +847,7 @@ class iCalendar_property_contact extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -837,6 +862,7 @@ class iCalendar_property_organizer extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_CAL_ADDRESS;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'CN'          => RFC2445_OPTIONAL | RFC2445_ONCE,
             'DIR'         => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -877,6 +903,7 @@ class iCalendar_property_recurrence_id extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'RANGE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
             'TZID'        => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -909,6 +936,7 @@ class iCalendar_property_related_to extends iCalendar_property {
     // TODO: the value of this property must reference another component's UID
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'RELTYPE'     => RFC2445_OPTIONAL | RFC2445_ONCE,
             RFC2445_XNAME => RFC2445_OPTIONAL
@@ -922,6 +950,7 @@ class iCalendar_property_url extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_URI;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -934,6 +963,7 @@ class iCalendar_property_uid extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -955,6 +985,7 @@ class iCalendar_property_exdate extends iCalendar_property {
     var $val_multi   = true;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'TZID'        => RFC2445_OPTIONAL | RFC2445_ONCE,
             'VALUE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -984,6 +1015,7 @@ class iCalendar_property_exrule extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_RECUR;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -997,6 +1029,7 @@ class iCalendar_property_rdate extends iCalendar_property {
     var $val_multi   = true;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'TZID'        => RFC2445_OPTIONAL | RFC2445_ONCE,
             'VALUE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -1026,6 +1059,7 @@ class iCalendar_property_rrule extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_RECUR;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1039,6 +1073,7 @@ class iCalendar_property_action extends iCalendar_property {
     var $val_type   = RFC2445_TYPE_TEXT;
     
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1061,6 +1096,7 @@ class iCalendar_property_repeat extends iCalendar_property {
     var $val_type   = RFC2445_TYPE_INTEGER;
     
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1072,6 +1108,7 @@ class iCalendar_property_trigger extends iCalendar_property {
     var $val_type   = RFC2445_TYPE_TEXT;
     
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
             'RELATED' => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -1100,6 +1137,7 @@ class iCalendar_property_created extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1120,6 +1158,7 @@ class iCalendar_property_dtstamp extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1140,6 +1179,7 @@ class iCalendar_property_last_modified extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1161,6 +1201,7 @@ class iCalendar_property_sequence extends iCalendar_property {
     var $val_default = 0;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1184,6 +1225,7 @@ class iCalendar_property_x extends iCalendar_property {
     var $val_type    = NULL;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
             RFC2445_XNAME => RFC2445_OPTIONAL
@@ -1214,6 +1256,7 @@ class iCalendar_property_request_status extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
             RFC2445_XNAME => RFC2445_OPTIONAL
@@ -1339,6 +1382,7 @@ class iCalendar_property_tzid extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1359,6 +1403,7 @@ class iCalendar_property_tzname extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
             RFC2445_XNAME => RFC2445_OPTIONAL
@@ -1380,6 +1425,7 @@ class iCalendar_property_tzoffsetfrom extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_UTC_OFFSET;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1400,6 +1446,7 @@ class iCalendar_property_tzoffsetto extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_UTC_OFFSET;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1424,6 +1471,7 @@ class iCalendar_property_class extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
index 285729c..e888b23 100644 (file)
@@ -1710,7 +1710,7 @@ function add_to_log($courseid, $module, $action, $url='', $info='', $cm=0, $user
 
     try {
         $DB->insert_record_raw('log', $log, false);
-    } catch (dml_write_exception $e) {
+    } catch (dml_exception $e) {
         debugging('Error: Could not insert a new entry to the Moodle log', DEBUG_ALL);
         // MDL-11893, alert $CFG->supportemail if insert into log failed
         if ($CFG->supportemail and empty($CFG->noemailever)) {
@@ -1954,7 +1954,9 @@ function count_login_failures($mode, $username, $lastlogin) {
  */
 function print_object($object) {
     echo '<pre class="notifytiny">';
-    print_r($object);  // Direct to output because some objects get too big for memory otherwise!
+    // we may need a lot of memory here
+    raise_memory_limit(MEMORY_EXTRA);
+    echo s(print_r($object, true));
     echo '</pre>';
 }
 
index 345c5c2..8a9fdac 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20111101" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20111118" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
         <FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false" PREVIOUS="restrictedusers" NEXT="timecreated"/>
         <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="component" NEXT="timemodified"/>
         <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" PREVIOUS="timecreated" NEXT="shortname"/>
-        <FIELD NAME="shortname" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="a unique shortname" PREVIOUS="timemodified"/>
+        <FIELD NAME="shortname" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="a unique shortname" PREVIOUS="timemodified" NEXT="downloadfiles"/>
+        <FIELD NAME="downloadfiles" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="1 if the service allow people to download file from webservice/plugins.php - 0 if not" PREVIOUS="shortname"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
index 73eed2c..f91ec49 100644 (file)
@@ -477,6 +477,7 @@ $services = array(
             'moodle_message_send_instantmessages'),
         'enabled' => 0,
         'restrictedusers' => 0,
-        'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE
+        'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE,
+        'downloadfiles' => 1
     ),
 );
index 75e3088..f209c40 100644 (file)
@@ -6933,6 +6933,20 @@ FROM
         upgrade_main_savepoint(true, 2011111500.01);
     }
 
+    if ($oldversion < 2011111800.01) {
+        // Define field downloadfiles to be added to external_services
+        $table = new xmldb_table('external_services');
+        $field = new xmldb_field('downloadfiles', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'shortname');
+
+        // Conditionally launch add field downloadfiles
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2011111800.01);
+    }
+
     return true;
 }
 
index 7ea1275..a937753 100644 (file)
@@ -180,7 +180,7 @@ class ddl_test extends UnitTestCase {
         ob_start(); // hide debug warning
         try {
             $result = $DB->get_records('test_table0');
-        } catch (dml_read_exception $e) {
+        } catch (dml_exception $e) {
             $result = false;
         }
         ob_end_clean();
@@ -203,7 +203,7 @@ class ddl_test extends UnitTestCase {
         ob_start(); // hide debug warning
         try {
             $result = $DB->get_records('test_table0');
-        } catch (dml_read_exception $e) {
+        } catch (dml_exception $e) {
             $result = false;
         }
         ob_end_clean();
@@ -337,14 +337,14 @@ class ddl_test extends UnitTestCase {
 
         try { // columns cache must be empty, so sentence throw exception
             $columns = $DB->get_columns('test_table0');
-        } catch (dml_read_exception $e) {
+        } catch (dml_exception $e) {
             $columns = false;
         }
         $this->assertFalse($columns);
 
         try { /// throw exception
             $indexes = $DB->get_indexes('test_table0');
-        } catch (dml_read_exception $e) {
+        } catch (dml_exception $e) {
             $indexes = false;
         }
         $this->assertFalse($indexes);
@@ -931,7 +931,7 @@ class ddl_test extends UnitTestCase {
         ob_start(); // hide debug warning
         try {
             $result = $DB->insert_record('test_table_cust0', $record, false);
-        } catch (dml_write_exception $e) {
+        } catch (dml_exception $e) {
             $result = false;
         }
         ob_end_clean();
@@ -953,7 +953,7 @@ class ddl_test extends UnitTestCase {
         ob_start(); // hide debug warning
         try {
             $result = $DB->insert_record('test_table_cust0', $record, false);
-        } catch (dml_write_exception $e) {
+        } catch (dml_exception $e) {
             $result = false;
         }
         ob_end_clean();
@@ -1022,7 +1022,7 @@ class ddl_test extends UnitTestCase {
         ob_start(); // hide debug warning
         try {
             $result = $DB->insert_record('test_table_cust0', $record, false);
-        } catch (dml_write_exception $e) {
+        } catch (dml_exception $e) {
             $result = false;;
         }
         ob_end_clean();
@@ -1644,7 +1644,7 @@ class ddl_test extends UnitTestCase {
 
             $rec = $DB->get_record($tablename, array('id'=>$id));
             $this->assertIdentical($rec->name, $maxstr);
-        } catch (dml_write_exception $e) {
+        } catch (dml_exception $e) {
             if ($DB->get_dbfamily() === 'oracle') {
                 $this->fail('Oracle does not support text fields larger than 4000 bytes, this is not a big problem for mostly ascii based languages');
             } else {
index 00bbd96..6e00b4c 100644 (file)
@@ -495,15 +495,15 @@ abstract class moodle_database {
      * @return array sql part and params
      */
     protected function where_clause($table, array $conditions=null) {
-        $allowed_types = $this->allowed_param_types();
-        if (empty($conditions)) {
-            return array('', array());
-        }
-        $where = array();
-        $params = array();
-
+        // We accept nulls in conditions
+        $conditions = is_null($conditions) ? array() : $conditions;
+        // Some checks performed under debugging only
         if (debugging()) {
             $columns = $this->get_columns($table);
+            if (empty($columns)) {
+                // no supported columns means most probably table does not exist
+                throw new dml_exception('ddltablenotexist', $table);
+            }
             foreach ($conditions as $key=>$value) {
                 if (!isset($columns[$key])) {
                     $a = new stdClass();
@@ -519,6 +519,13 @@ abstract class moodle_database {
             }
         }
 
+        $allowed_types = $this->allowed_param_types();
+        if (empty($conditions)) {
+            return array('', array());
+        }
+        $where = array();
+        $params = array();
+
         foreach ($conditions as $key=>$value) {
             if (is_int($key)) {
                 throw new dml_exception('invalidnumkey');
index 7b6f2f3..fabfb3f 100644 (file)
@@ -435,7 +435,7 @@ class mysqli_native_moodle_database extends moodle_database {
         $sql = "SHOW COLUMNS FROM {$this->prefix}$table";
         $this->query_start($sql, null, SQL_QUERY_AUX);
         $result = $this->mysqli->query($sql);
-        $this->query_end($result);
+        $this->query_end(true); // Don't want to throw anything here ever. MDL-30147
 
         if ($result === false) {
             return array();
index 627c6c1..fce8f40 100644 (file)
@@ -821,6 +821,10 @@ class dml_test extends UnitTestCase {
 
             $this->assertEqual($next_column->name, $next_field->name);
         }
+
+        // Test get_columns for non-existing table returns empty array. MDL-30147
+        $columns = $DB->get_columns('xxxx');
+        $this->assertEqual(array(), $columns);
     }
 
     public function test_get_manager() {
@@ -905,7 +909,7 @@ class dml_test extends UnitTestCase {
             $DB->execute($sql);
             $this->fail("Expecting an exception, none occurred");
         } catch (Exception $e) {
-            $this->assertTrue($e instanceof dml_write_exception);
+            $this->assertTrue($e instanceof dml_exception);
         }
 
         // update records
@@ -999,7 +1003,10 @@ class dml_test extends UnitTestCase {
         $conditions = array('onetext' => '1');
         try {
             $rs = $DB->get_recordset($tablename, $conditions);
-            $this->fail('An Exception is missing, expected due to equating of text fields');
+            if (debugging()) {
+                // only in debug mode - hopefully all devs test code in debug mode...
+                $this->fail('An Exception is missing, expected due to equating of text fields');
+            }
         } catch (exception $e) {
             $this->assertTrue($e instanceof dml_exception);
             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
@@ -1256,12 +1263,51 @@ class dml_test extends UnitTestCase {
         $conditions = array('onetext' => '1');
         try {
             $records = $DB->get_records($tablename, $conditions);
-            $this->fail('An Exception is missing, expected due to equating of text fields');
+            if (debugging()) {
+                // only in debug mode - hopefully all devs test code in debug mode...
+                $this->fail('An Exception is missing, expected due to equating of text fields');
+            }
         } catch (exception $e) {
             $this->assertTrue($e instanceof dml_exception);
             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
         }
 
+        // test get_records passing non-existing table
+        // with params
+        try {
+            $records = $DB->get_records('xxxx', array('id' => 0));
+            $this->fail('An Exception is missing, expected due to query against non-existing table');
+        } catch (exception $e) {
+            $this->assertTrue($e instanceof dml_exception);
+            if (debugging()) {
+                // information for developers only, normal users get general error message
+                $this->assertEqual($e->errorcode, 'ddltablenotexist');
+            }
+        }
+        // and without params
+        try {
+            $records = $DB->get_records('xxxx', array());
+            $this->fail('An Exception is missing, expected due to query against non-existing table');
+        } catch (exception $e) {
+            $this->assertTrue($e instanceof dml_exception);
+            if (debugging()) {
+                // information for developers only, normal users get general error message
+                $this->assertEqual($e->errorcode, 'ddltablenotexist');
+            }
+        }
+
+        // test get_records passing non-existing column
+        try {
+            $records = $DB->get_records($tablename, array('xxxx' => 0));
+            $this->fail('An Exception is missing, expected due to query against non-existing column');
+        } catch (exception $e) {
+            $this->assertTrue($e instanceof dml_exception);
+            if (debugging()) {
+                // information for developers only, normal users get general error message
+                $this->assertEqual($e->errorcode, 'ddlfieldnotexist');
+            }
+        }
+
         // note: delegate limits testing to test_get_records_sql()
     }
 
@@ -1641,7 +1687,10 @@ class dml_test extends UnitTestCase {
         $conditions = array('onetext' => '1');
         try {
             $DB->get_field($tablename, 'course', $conditions);
-            $this->fail('An Exception is missing, expected due to equating of text fields');
+            if (debugging()) {
+                // only in debug mode - hopefully all devs test code in debug mode...
+                $this->fail('An Exception is missing, expected due to equating of text fields');
+            }
         } catch (exception $e) {
             $this->assertTrue($e instanceof dml_exception);
             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
@@ -1785,7 +1834,7 @@ class dml_test extends UnitTestCase {
         try {
             $DB->insert_record_raw($tablename, array('xxxxx' => 3, 'onechar' => 'bb'));
             $this->assertFail('Exception expected due to invalid column');
-        } catch (dml_write_exception $ex) {
+        } catch (dml_exception $ex) {
             $this->assertTrue(true);
         }
     }
@@ -2494,7 +2543,10 @@ class dml_test extends UnitTestCase {
         $conditions = array('onetext' => '1');
         try {
             $DB->set_field($tablename, 'onechar', 'frog', $conditions);
-            $this->fail('An Exception is missing, expected due to equating of text fields');
+            if (debugging()) {
+                // only in debug mode - hopefully all devs test code in debug mode...
+                $this->fail('An Exception is missing, expected due to equating of text fields');
+            }
         } catch (exception $e) {
             $this->assertTrue($e instanceof dml_exception);
             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
@@ -2704,7 +2756,10 @@ class dml_test extends UnitTestCase {
         $conditions = array('onetext' => '1');
         try {
             $DB->count_records($tablename, $conditions);
-            $this->fail('An Exception is missing, expected due to equating of text fields');
+            if (debugging()) {
+                // only in debug mode - hopefully all devs test code in debug mode...
+                $this->fail('An Exception is missing, expected due to equating of text fields');
+            }
         } catch (exception $e) {
             $this->assertTrue($e instanceof dml_exception);
             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
@@ -2779,7 +2834,10 @@ class dml_test extends UnitTestCase {
         $conditions = array('onetext' => '1');
         try {
             $DB->record_exists($tablename, $conditions);
-            $this->fail('An Exception is missing, expected due to equating of text fields');
+            if (debugging()) {
+                // only in debug mode - hopefully all devs test code in debug mode...
+                $this->fail('An Exception is missing, expected due to equating of text fields');
+            }
         } catch (exception $e) {
             $this->assertTrue($e instanceof dml_exception);
             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
@@ -2927,7 +2985,10 @@ class dml_test extends UnitTestCase {
         $conditions = array('onetext'=>'1');
         try {
             $DB->delete_records($tablename, $conditions);
-            $this->fail('An Exception is missing, expected due to equating of text fields');
+            if (debugging()) {
+                // only in debug mode - hopefully all devs test code in debug mode...
+                $this->fail('An Exception is missing, expected due to equating of text fields');
+            }
         } catch (exception $e) {
             $this->assertTrue($e instanceof dml_exception);
             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
@@ -2937,7 +2998,10 @@ class dml_test extends UnitTestCase {
         $conditions = array('onetext' => 1);
         try {
             $DB->delete_records($tablename, $conditions);
-            $this->fail('An Exception is missing, expected due to equating of text fields');
+            if (debugging()) {
+                // only in debug mode - hopefully all devs test code in debug mode...
+                $this->fail('An Exception is missing, expected due to equating of text fields');
+            }
         } catch (exception $e) {
             $this->assertTrue($e instanceof dml_exception);
             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
index 6c30cf4..6156759 100644 (file)
@@ -1301,7 +1301,18 @@ class sqlsrv_native_moodle_database extends moodle_database {
         $timeoutmilli = $timeout * 1000;
 
         $fullname = $this->dbname.'-'.$this->prefix.'-session-'.$rowid;
-        $sql = "sp_getapplock '$fullname', 'Exclusive', 'Session',  $timeoutmilli";
+        // While this may work using proper {call sp_...} calls + binding +
+        // executing + consuming recordsets, the solution used for the mssql
+        // driver is working perfectly, so 100% mimic-ing that code.
+        // $sql = "sp_getapplock '$fullname', 'Exclusive', 'Session',  $timeoutmilli";
+        $sql = "BEGIN
+                    DECLARE @result INT
+                    EXECUTE @result = sp_getapplock @Resource='$fullname',
+                                                    @LockMode='Exclusive',
+                                                    @LockOwner='Session',
+                                                    @LockTimeout='$timeoutmilli'
+                    SELECT @result
+                END";
         $this->query_start($sql, null, SQL_QUERY_AUX);
         $result = sqlsrv_query($this->sqlsrv, $sql);
         $this->query_end($result);
index 19546ae..2e59454 100644 (file)
@@ -91,7 +91,7 @@ class dml_sessionwait_exception extends dml_exception {
 }
 
 /**
- * DML read exception - triggered by SQL syntax errors, missing tables, etc.
+ * DML read exception - triggered by some SQL syntax errors, etc.
  */
 class dml_read_exception extends dml_exception {
     /** @var string */
@@ -185,7 +185,7 @@ class dml_missing_record_exception extends dml_exception {
 }
 
 /**
- * DML write exception - triggered by SQL syntax errors, missing tables, etc.
+ * DML write exception - triggered by some SQL syntax errors, etc.
  */
 class dml_write_exception extends dml_exception {
     /** @var string */
index c3c68d8..1e46afb 100644 (file)
@@ -147,7 +147,8 @@ class tinymce_texteditor extends texteditor {
                     'theme_advanced_resizing_min_height' => 30,
                     'theme_advanced_toolbar_location' => "top",
                     'theme_advanced_statusbar_location' => "bottom",
-                    'spellchecker_rpc_url' => $CFG->wwwroot."/lib/editor/tinymce/tiny_mce/$this->version/plugins/spellchecker/rpc.php"
+                    'spellchecker_rpc_url' => $CFG->wwwroot."/lib/editor/tinymce/tiny_mce/$this->version/plugins/spellchecker/rpc.php",
+                    'spellchecker_languages' => get_config('editor_tinymce', 'spelllanguagelist')
                   );
 
         if ($xemoticon) {
index 9f406d6..db02700 100644 (file)
@@ -31,6 +31,10 @@ if ($ADMIN->fulltree) {
         'PSpell'=>'PSpell',
         'GoogleSpell'=>'Google Spell',
         'PSpellShell'=>'PSpellShell');
-    $settings->add(new admin_setting_configselect('editor_tinymce/spellengine', get_string('spellengine', 'admin'), '', 'GoogleSpell', $options));
+    $settings->add(new admin_setting_configselect('editor_tinymce/spellengine',
+            get_string('spellengine', 'admin'), '', 'GoogleSpell', $options));
+    $settings->add(new admin_setting_configtext('editor_tinymce/spelllanguagelist',
+            get_string('spelllanguagelist', 'admin'), '',
+            '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,' .
+            'Portuguese=pt,Spanish=es,Swedish=sv', PARAM_RAW));
 }
-
index 21779e8..9b22b01 100644 (file)
@@ -1002,7 +1002,7 @@ class file_storage {
         }
 
         if (!isset($file_record['filename'])) {
-            $file_record['filename'] == $file->get_filename();
+            $file_record['filename'] = $file->get_filename();
         }
 
         if (!isset($file_record['mimetype'])) {
index 3feb6de..bb5ed88 100644 (file)
@@ -136,6 +136,7 @@ class filter_manager {
             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);
@@ -370,6 +371,8 @@ abstract class moodle_text_filter {
  * 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}
@@ -475,6 +478,7 @@ class filterobject {
  * @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':
@@ -485,6 +489,7 @@ function filter_get_name($filter) {
             }
             // 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) == '[[') {
@@ -508,7 +513,7 @@ function filter_get_name($filter) {
 function filter_get_all_installed() {
     global $CFG;
     $filternames = array();
-    // TODO: deprecated in 2.2, delete support for "mod" legacy filters location in 2.3. MDL-29996
+    // 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()
index ee473eb..938fab6 100644 (file)
@@ -237,6 +237,10 @@ M.form.initFormDependencies = function(Y, formid, dependencies) {
             _dependency_notchecked : function(elements, value) {
                 var lock = false;
                 elements.each(function(){
+                    if (this.getAttribute('type').toLowerCase()=='hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
+                        // This is the hidden input that is part of an advcheckbox.
+                        return;
+                    }
                     if (this.getAttribute('type').toLowerCase()=='radio' && this.get('value') != value) {
                         return;
                     }
@@ -250,6 +254,10 @@ M.form.initFormDependencies = function(Y, formid, dependencies) {
             _dependency_checked : function(elements, value) {
                 var lock = false;
                 elements.each(function(){
+                    if (this.getAttribute('type').toLowerCase()=='hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
+                        // This is the hidden input that is part of an advcheckbox.
+                        return;
+                    }
                     if (this.getAttribute('type').toLowerCase()=='radio' && this.get('value') != value) {
                         return;
                     }
@@ -272,10 +280,16 @@ M.form.initFormDependencies = function(Y, formid, dependencies) {
             },
             _dependency_eq : function(elements, value) {
                 var lock = false;
+                var hidden_val = false;
                 elements.each(function(){
                     if (this.getAttribute('type').toLowerCase()=='radio' && !Y.Node.getDOMNode(this).checked) {
                         return;
+                    } else if (this.getAttribute('type').toLowerCase() == 'hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
+                        // This is the hidden input that is part of an advcheckbox.
+                        hidden_val = (this.get('value') == value);
+                        return;
                     } else if (this.getAttribute('type').toLowerCase() == 'checkbox' && !Y.Node.getDOMNode(this).checked) {
+                        lock = lock || hidden_val;
                         return;
                     }
                     //check for filepicker status
@@ -303,10 +317,16 @@ M.form.initFormDependencies = function(Y, formid, dependencies) {
             },
             _dependency_default : function(elements, value, ev) {
                 var lock = false;
+                var hidden_val = false;
                 elements.each(function(){
                     if (this.getAttribute('type').toLowerCase()=='radio' && !Y.Node.getDOMNode(this).checked) {
                         return;
+                    } else if (this.getAttribute('type').toLowerCase() == 'hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
+                        // This is the hidden input that is part of an advcheckbox.
+                        hidden_val = (this.get('value') != value);
+                        return;
                     } else if (this.getAttribute('type').toLowerCase() == 'checkbox' && !Y.Node.getDOMNode(this).checked) {
+                        lock = lock || hidden_val;
                         return;
                     }
                     //check for filepicker status
index f02469e..9b1ce04 100644 (file)
@@ -900,6 +900,30 @@ abstract class moodleform {
         return array();
     }
 
+    /**
+     * Helper used by {@link repeat_elements()}.
+     * @param int $i the index of this element.
+     * @param HTML_QuickForm_element $elementclone
+     * @param array $namecloned array of names
+     */
+    function repeat_elements_fix_clone($i, $elementclone, &$namecloned) {
+        $name = $elementclone->getName();
+        $namecloned[] = $name;
+
+        if (!empty($name)) {
+            $elementclone->setName($name."[$i]");
+        }
+
+        if (is_a($elementclone, 'HTML_QuickForm_header')) {
+            $value = $elementclone->_text;
+            $elementclone->setValue(str_replace('{no}', ($i+1), $value));
+
+        } else {
+            $value=$elementclone->getLabel();
+            $elementclone->setLabel(str_replace('{no}', ($i+1), $value));
+        }
+    }
+
     /**
      * Method to add a repeating group of elements to a form.
      *
@@ -942,19 +966,13 @@ abstract class moodleform {
         for ($i = 0; $i < $repeats; $i++) {
             foreach ($elementobjs as $elementobj){
                 $elementclone = fullclone($elementobj);
-                $name = $elementclone->getName();
-                $namecloned[] = $name;
-                if (!empty($name)) {
-                    $elementclone->setName($name."[$i]");
-                }
-                if (is_a($elementclone, 'HTML_QuickForm_header')) {
-                    $value = $elementclone->_text;
-                    $elementclone->setValue(str_replace('{no}', ($i+1), $value));
-
-                } else {
-                    $value=$elementclone->getLabel();
-                    $elementclone->setLabel(str_replace('{no}', ($i+1), $value));
+                $this->repeat_elements_fix_clone($i, $elementclone, $namecloned);
 
+                if ($elementclone instanceof HTML_QuickForm_group && !$elementclone->_appendName) {
+                    foreach ($elementclone->getElements() as $el) {
+                        $this->repeat_elements_fix_clone($i, $el, $namecloned);
+                    }
+                    $elementclone->setLabel(str_replace('{no}', $i + 1, $elementclone->getLabel()));
                 }
 
                 $mform->addElement($elementclone);
@@ -1970,7 +1988,11 @@ function validate_' . $this->_formName . '(frm) {
         } else if (is_a($element, 'HTML_QuickForm_hidden')) {
             return array();
 
-        } else if (method_exists($element, 'getPrivateName')) {
+        } else if (method_exists($element, 'getPrivateName') &&
+                !($element instanceof HTML_QuickForm_advcheckbox)) {
+            // The advcheckbox element implements a method called getPrivateName,
+            // but in a way that is not compatible with the generic API, so we
+            // have to explicitly exclude it.
             return array($element->getPrivateName());
 
         } else {
index b78fde9..69d294d 100644 (file)
@@ -1195,8 +1195,7 @@ function grade_update_mod_grades($modinstance, $userid=0) {
 /**
  * Remove grade letters for given context
  *
- * @global object
- * @param object $context
+ * @param context $context
  * @param bool $showfeedback
  */
 function remove_grade_letters($context, $showfeedback) {
@@ -1206,25 +1205,27 @@ function remove_grade_letters($context, $showfeedback) {
 
     $DB->delete_records('grade_letters', array('contextid'=>$context->id));
     if ($showfeedback) {
-        echo $OUTPUT->notification($strdeleted.' - '.get_string('letters', 'grades'));
+        echo $OUTPUT->notification($strdeleted.' - '.get_string('letters', 'grades'), 'notifysuccess');
     }
 }
+
 /**
  * Remove all grade related course data - history is kept
  *
- * @global object
  * @param int $courseid
  * @param bool $showfeedback print feedback
  */
 function remove_course_grades($courseid, $showfeedback) {
     global $DB, $OUTPUT;
 
+    $fs = get_file_storage();
     $strdeleted = get_string('deleted');
 
     $course_category = grade_category::fetch_course_category($courseid);
     $course_category->delete('coursedelete');
+    $fs->delete_area_files(get_context_instance(CONTEXT_COURSE, $courseid)->id, 'grade', 'feedback');
     if ($showfeedback) {
-        echo $OUTPUT->notification($strdeleted.' - '.get_string('grades', 'grades').', '.get_string('items', 'grades').', '.get_string('categories', 'grades'));
+        echo $OUTPUT->notification($strdeleted.' - '.get_string('grades', 'grades').', '.get_string('items', 'grades').', '.get_string('categories', 'grades'), 'notifysuccess');
     }
 
     if ($outcomes = grade_outcome::fetch_all(array('courseid'=>$courseid))) {
@@ -1234,7 +1235,7 @@ function remove_course_grades($courseid, $showfeedback) {
     }
     $DB->delete_records('grade_outcomes_courses', array('courseid'=>$courseid));
     if ($showfeedback) {
-        echo $OUTPUT->notification($strdeleted.' - '.get_string('outcomes', 'grades'));
+        echo $OUTPUT->notification($strdeleted.' - '.get_string('outcomes', 'grades'), 'notifysuccess');
     }
 
     if ($scales = grade_scale::fetch_all(array('courseid'=>$courseid))) {
@@ -1243,12 +1244,12 @@ function remove_course_grades($courseid, $showfeedback) {
         }
     }
     if ($showfeedback) {
-        echo $OUTPUT->notification($strdeleted.' - '.get_string('scales'));
+        echo $OUTPUT->notification($strdeleted.' - '.get_string('scales'), 'notifysuccess');
     }
 
     $DB->delete_records('grade_settings', array('courseid'=>$courseid));
     if ($showfeedback) {
-        echo $OUTPUT->notification($strdeleted.' - '.get_string('settings', 'grades'));
+        echo $OUTPUT->notification($strdeleted.' - '.get_string('settings', 'grades'), 'notifysuccess');
     }
 }
 
index c6cb423..566bc8c 100644 (file)
@@ -106,7 +106,27 @@ function install_init_dataroot($dataroot, $dirpermissions) {
         return false; // we can not continue
     }
 
-    // now create the lang folder - we need it and it makes sure we can really write in dataroot
+    // create the directory for $CFG->tempdir
+    if (!is_dir("$dataroot/temp")) {
+        if (!mkdir("$dataroot/temp", $dirpermissions, true)) {
+            return false;
+        }
+    }
+    if (!is_writable("$dataroot/temp")) {
+        return false; // we can not continue
+    }
+
+    // create the directory for $CFG->cachedir
+    if (!is_dir("$dataroot/cache")) {
+        if (!mkdir("$dataroot/cache", $dirpermissions, true)) {
+            return false;
+        }
+    }
+    if (!is_writable("$dataroot/cache")) {
+        return false; // we can not continue
+    }
+
+    // create the directory for $CFG->langotherroot
     if (!is_dir("$dataroot/lang")) {
         if (!mkdir("$dataroot/lang", $dirpermissions, true)) {
             return false;
index b5ea458..d6bba11 100644 (file)
@@ -1437,8 +1437,6 @@ function purge_all_caches() {
     // hack: this script may get called after the purifier was initialised,
     // but we do not want to verify repeatedly this exists in each call
     make_cache_directory('htmlpurifier');
-
-    clearstatcache();
 }
 
 /**
@@ -4287,58 +4285,122 @@ function delete_course($courseorid, $showfeedback = true) {
  * This function does not verify any permissions.
  *
  * Please note this function also deletes all user enrolments,
- * enrolment instances and role assignments.
+ * enrolment instances and role assignments by default.
+ *
+ * $options:
+ *  - 'keep_roles_and_enrolments' - false by default
+ *  - 'keep_groups_and_groupings' - false by default
  *
  * @param int $courseid The id of the course that is being deleted
  * @param bool $showfeedback Whether to display notifications of each action the function performs.
+ * @param array $options extra options
  * @return bool true if all the removals succeeded. false if there were any failures. If this
  *             method returns false, some of the removals will probably have succeeded, and others
  *             failed, but you have no way of knowing which.
  */
-function remove_course_contents($courseid, $showfeedback = true) {
+function remove_course_contents($courseid, $showfeedback = true, array $options = null) {
     global $CFG, $DB, $OUTPUT;
     require_once($CFG->libdir.'/completionlib.php');
     require_once($CFG->libdir.'/questionlib.php');
     require_once($CFG->libdir.'/gradelib.php');
     require_once($CFG->dirroot.'/group/lib.php');
     require_once($CFG->dirroot.'/tag/coursetagslib.php');
+    require_once($CFG->dirroot.'/comment/lib.php');
+    require_once($CFG->dirroot.'/rating/lib.php');
 
-    $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
-    $context = get_context_instance(CONTEXT_COURSE, $courseid, MUST_EXIST);
+    // NOTE: these concatenated strings are suboptimal, but it is just extra info...
+    $strdeleted = get_string('deleted').' - ';
 
-    $strdeleted = get_string('deleted');
+    // Some crazy wishlist of stuff we should skip during purging of course content
+    $options = (array)$options;
 
-    // Delete course completion information,
-    // this has to be done before grades and enrols
+    $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
+    $coursecontext = context_course::instance($courseid);
+    $fs = get_file_storage();
+
+    // Delete course completion information, this has to be done before grades and enrols
     $cc = new completion_info($course);
     $cc->clear_criteria();
-
-    // remove roles and enrolments
-    role_unassign_all(array('contextid'=>$context->id), true);
-    enrol_course_delete($course);
-
-    // Clean up course formats (iterate through all formats in the even the course format was ever changed)
-    $formats = get_plugin_list('format');
-    foreach ($formats as $format=>$formatdir) {
-        $formatdelete = 'format_'.$format.'_delete_course';
-        $formatlib    = "$formatdir/lib.php";
-        if (file_exists($formatlib)) {
-            include_once($formatlib);
-            if (function_exists($formatdelete)) {
-                if ($showfeedback) {
-                    echo $OUTPUT->notification($strdeleted.' '.$format);
-                }
-                $formatdelete($course->id);
-            }
-        }
+    if ($showfeedback) {
+        echo $OUTPUT->notification($strdeleted.get_string('completion', 'completion'), 'notifysuccess');
     }
 
     // Remove all data from gradebook - this needs to be done before course modules
     // because while deleting this information, the system may need to reference
     // the course modules that own the grades.
     remove_course_grades($courseid, $showfeedback);
-    remove_grade_letters($context, $showfeedback);
+    remove_grade_letters($coursecontext, $showfeedback);
+
+    // Delete course blocks in any all child contexts,
+    // they may depend on modules so delete them first
+    $childcontexts = $coursecontext->get_child_contexts(); // returns all subcontexts since 2.2
+    foreach ($childcontexts as $childcontext) {
+        blocks_delete_all_for_context($childcontext->id);
+    }
+    unset($childcontexts);
+    blocks_delete_all_for_context($coursecontext->id);
+    if ($showfeedback) {
+        echo $OUTPUT->notification($strdeleted.get_string('type_block_plural', 'plugin'), 'notifysuccess');
+    }
+
+    // Delete every instance of every module,
+    // this has to be done before deleting of course level stuff
+    $locations = get_plugin_list('mod');
+    foreach ($locations as $modname=>$moddir) {
+        if ($modname === 'NEWMODULE') {
+            continue;
+        }
+        if ($module = $DB->get_record('modules', array('name'=>$modname))) {
+            include_once("$moddir/lib.php");                 // Shows php warning only if plugin defective
+            $moddelete = $modname .'_delete_instance';       // Delete everything connected to an instance
+            $moddeletecourse = $modname .'_delete_course';   // Delete other stray stuff (uncommon)
+
+            if ($instances = $DB->get_records($modname, array('course'=>$course->id))) {
+                foreach ($instances as $instance) {
+                    if ($cm = get_coursemodule_from_instance($modname, $instance->id, $course->id)) {
+                        /// Delete activity context questions and question categories
+                        question_delete_activity($cm,  $showfeedback);
+                    }
+                    if (function_exists($moddelete)) {
+                        // This purges all module data in related tables, extra user prefs, settings, etc.
+                        $moddelete($instance->id);
+                    } else {
+                        // NOTE: we should not allow installation of modules with missing delete support!
+                        debugging("Defective module '$modname' detected when deleting course contents: missing function $moddelete()!");
+                        $DB->delete_records($modname, array('id'=>$instance->id));
+                    }
 
+                    if ($cm) {
+                        // Delete cm and its context - orphaned contexts are purged in cron in case of any race condition
+                        context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
+                        $DB->delete_records('course_modules', array('id'=>$cm->id));
+                    }
+                }
+            }
+            if (function_exists($moddeletecourse)) {
+                // Execute ptional course cleanup callback
+                $moddeletecourse($course, $showfeedback);
+            }
+            if ($instances and $showfeedback) {
+                echo $OUTPUT->notification($strdeleted.get_string('pluginname', $modname), 'notifysuccess');
+            }
+        } else {
+            // Ooops, this module is not properly installed, force-delete it in the next block
+        }
+    }
+    // We have tried to delete everything the nice way - now let's force-delete any remaining module data
+    $cms = $DB->get_records('course_modules', array('course'=>$course->id));
+    foreach ($cms as $cm) {
+        if ($module = $DB->get_record('module', array('id'=>$cm->module))) {
+            try {
+                $DB->delete_records($module->name, array('id'=>$cm->instance));
+            } catch (Exception $e) {
+                // Ignore weird or missing table problems
+            }
+        }
+        context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
+        $DB->delete_records('course_modules', array('id'=>$cm->id));
+    }
     // Remove all data from availability and completion tables that is associated
     // with course-modules belonging to this course. Note this is done even if the
     // features are not enabled now, in case they were enabled previously
@@ -4348,101 +4410,122 @@ function remove_course_contents($courseid, $showfeedback = true) {
     $DB->delete_records_select('course_modules_availability',
            'coursemoduleid IN (SELECT id from {course_modules} WHERE course=?)',
            array($courseid));
+    if ($showfeedback) {
+        echo $OUTPUT->notification($strdeleted.get_string('type_mod_plural', 'plugin'), 'notifysuccess');
+    }
 
-    // Delete course blocks - they may depend on modules so delete them first
-    blocks_delete_all_for_context($context->id);
+    // Cleanup the rest of plugins
+    $cleanuplugintypes = array('report', 'coursereport', 'format');
+    foreach ($cleanuplugintypes as $type) {
+        $plugins = get_plugin_list_with_function($type, 'delete_course', 'lib.php');
+        foreach ($plugins as $plugin=>$pluginfunction) {
+            $pluginfunction($course->id, $showfeedback);
+        }
+        if ($showfeedback) {
+            echo $OUTPUT->notification($strdeleted.get_string('type_'.$type.'_plural', 'plugin'), 'notifysuccess');
+        }
+    }
 
-    // Delete every instance of every module
-    if ($allmods = $DB->get_records('modules') ) {
-        foreach ($allmods as $mod) {
-            $modname = $mod->name;
-            $modfile = $CFG->dirroot .'/mod/'. $modname .'/lib.php';
-            $moddelete = $modname .'_delete_instance';       // Delete everything connected to an instance
-            $moddeletecourse = $modname .'_delete_course';   // Delete other stray stuff (uncommon)
-            $count=0;
-            if (file_exists($modfile)) {
-                include_once($modfile);
-                if (function_exists($moddelete)) {
-                    if ($instances = $DB->get_records($modname, array('course'=>$course->id))) {
-                        foreach ($instances as $instance) {
-                            if ($cm = get_coursemodule_from_instance($modname, $instance->id, $course->id)) {
-                                /// Delete activity context questions and question categories
-                                question_delete_activity($cm,  $showfeedback);
-                            }
-                            if ($moddelete($instance->id)) {
-                                $count++;
+    // Delete questions and question categories
+    question_delete_course($course, $showfeedback);
+    if ($showfeedback) {
+        echo $OUTPUT->notification($strdeleted.get_string('questions', 'question'), 'notifysuccess');
+    }
 
-                            } else {
-                                echo $OUTPUT->notification('Could not delete '. $modname .' instance '. $instance->id .' ('. format_string($instance->name) .')');
-                            }
-                            if ($cm) {
-                                // delete cm and its context in correct order
-                                delete_context(CONTEXT_MODULE, $cm->id); // some callbacks may try to fetch context, better delete first
-                                $DB->delete_records('course_modules', array('id'=>$cm->id));
-                            }
-                        }
-                    }
-                } else {
-                    //note: we should probably delete these anyway
-                    echo $OUTPUT->notification('Function '.$moddelete.'() doesn\'t exist!');
-                }
+    // Make sure there are no subcontexts left - all valid blocks and modules should be already gone
+    $childcontexts = $coursecontext->get_child_contexts(); // returns all subcontexts since 2.2
+    foreach ($childcontexts as $childcontext) {
+        $childcontext->delete();
+    }
+    unset($childcontexts);
 
-                if (function_exists($moddeletecourse)) {
-                    $moddeletecourse($course, $showfeedback);
-                }
-            }
-            if ($showfeedback) {
-                echo $OUTPUT->notification($strdeleted .' '. $count .' x '. $modname);
-            }
+    // Remove all roles and enrolments by default
+    if (empty($options['keep_roles_and_enrolments'])) {
+        // this hack is used in restore when deleting contents of existing course
+        role_unassign_all(array('contextid'=>$coursecontext->id), true);
+        enrol_course_delete($course);
+        if ($showfeedback) {
+            echo $OUTPUT->notification($strdeleted.get_string('type_enrol_plural', 'plugin'), 'notifysuccess');
         }
     }
 
     // Delete any groups, removing members and grouping/course links first.
-    groups_delete_groupings($course->id, $showfeedback);
-    groups_delete_groups($course->id, $showfeedback);
+    if (empty($options['keep_groups_and_groupings'])) {
+        groups_delete_groupings($course->id, $showfeedback);
+        groups_delete_groups($course->id, $showfeedback);
+    }
 
-    // Delete questions and question categories
-    question_delete_course($course, $showfeedback);
+    // filters be gone!
+    filter_delete_all_for_context($coursecontext->id);
+
+    // die comments!
+    comment::delete_comments($coursecontext->id);
+
+    // ratings are history too
+    $delopt = new stdclass();
+    $delopt->contextid = $coursecontext->id;
+    $rm = new rating_manager();
+    $rm->delete_ratings($delopt);
 
     // Delete course tags
     coursetag_delete_course_tags($course->id, $showfeedback);
 
-    // Delete legacy files (just in case some files are still left there after conversion to new file api)
-    fulldelete($CFG->dataroot.'/'.$course->id);
+    // Delete calendar events
+    $DB->delete_records('event', array('courseid'=>$course->id));
+    $fs->delete_area_files($coursecontext->id, 'calendar');
 
-    // cleanup course record - remove links to delted stuff
-    $oldcourse = new stdClass();
-    $oldcourse->id                = $course->id;
-    $oldcourse->summary           = '';
-    $oldcourse->modinfo           = NULL;
-    $oldcourse->legacyfiles       = 0;
-    $oldcourse->defaultgroupingid = 0;
-    $oldcourse->enablecompletion  = 0;
-    $DB->update_record('course', $oldcourse);
-
-    // Delete all related records in other tables that may have a courseid
+    // Delete all related records in other core tables that may have a courseid
     // This array stores the tables that need to be cleared, as
     // table_name => column_name that contains the course id.
     $tablestoclear = array(
-        'event' => 'courseid', // Delete events
-        'log' => 'course', // Delete logs
-        'course_sections' => 'course', // Delete any course stuff
-        'course_modules' => 'course',
-        'course_display' => 'course',
-        'backup_courses' => 'courseid', // Delete scheduled backup stuff
-        'user_lastaccess' => 'courseid',
+        'log' => 'course',               // Course logs (NOTE: this might be changed in the future)
+        'backup_courses' => 'courseid',  // Scheduled backup stuff
+        'user_lastaccess' => 'courseid', // User access info
     );
     foreach ($tablestoclear as $table => $col) {
         $DB->delete_records($table, array($col=>$course->id));
     }
 
-    // Delete all remaining stuff linked to context,
-    // such as remaining roles, files, comments, etc.
-    // Keep the context record for now.
-    delete_context(CONTEXT_COURSE, $course->id, false);
+    // delete all course backup files
+    $fs->delete_area_files($coursecontext->id, 'backup');
 
-    //trigger events
-    $course->context = $context; // you can not access context in cron event later after course is deleted
+    // cleanup course record - remove links to deleted stuff
+    $oldcourse = new stdClass();
+    $oldcourse->id               = $course->id;
+    $oldcourse->summary          = '';
+    $oldcourse->modinfo          = NULL;
+    $oldcourse->legacyfiles      = 0;
+    $oldcourse->enablecompletion = 0;
+    if (!empty($options['keep_groups_and_groupings'])) {
+        $oldcourse->defaultgroupingid = 0;
+    }
+    $DB->update_record('course', $oldcourse);
+
+    // Delete course sections and user selections
+    $DB->delete_records('course_sections', array('course'=>$course->id));
+    $DB->delete_records('course_display', array('course'=>$course->id));
+
+    // delete legacy, section and any other course files
+    $fs->delete_area_files($coursecontext->id, 'course'); // files from summary and section
+
+    // Delete all remaining stuff linked to context such as files, comments, ratings, etc.
+    if (empty($options['keep_roles_and_enrolments']) and empty($options['keep_groups_and_groupings'])) {
+        // Easy, do not delete the context itself...
+        $coursecontext->delete_content();
+
+    } else {
+        // Hack alert!!!!
+        // We can not drop all context stuff because it would bork enrolments and roles,
+        // there might be also files used by enrol plugins...
+    }
+
+    // Delete legacy files - just in case some files are still left there after conversion to new file api,
+    // also some non-standard unsupported plugins may try to store something there
+    fulldelete($CFG->dataroot.'/'.$course->id);
+
+    // Finally trigger the event
+    $course->context = $coursecontext; // you can not access context in cron event later after course is deleted
+    $course->options = $options;       // not empty if we used any crazy hack
     events_trigger('course_content_removed', $course);
 
     return true;
@@ -6213,7 +6296,17 @@ class core_string_manager implements string_manager {
             }
             if (!isset($string[$identifier])) {
                 // the string is still missing - should be fixed by developer
-                debugging("Invalid get_string() identifier: '$identifier' or component '$component'", DEBUG_DEVELOPER);
+                list($plugintype, $pluginname) = normalize_component($component);
+                if ($plugintype == 'core') {
+                    $file = "lang/en/{$component}.php";
+                } else if ($plugintype == 'mod') {
+                    $file = "mod/{$pluginname}/lang/en/{$pluginname}.php";
+                } else {
+                    $path = get_plugin_directory($plugintype, $pluginname);
+                    $file = "{$path}/lang/en/{$plugintype}_{$pluginname}.php";
+                }
+                debugging("Invalid get_string() identifier: '{$identifier}' or component '{$component}'. " .
+                        "Perhaps you are missing \$string['{$identifier}'] = ''; in {$file}?", DEBUG_DEVELOPER);
                 return "[[$identifier]]";
             }
         }
@@ -7690,7 +7783,7 @@ function plugin_callback($type, $name, $feature, $action, $options = null, $defa
 
     $component = clean_param($type . '_' . $name, PARAM_COMPONENT);
     if (empty($component)) {
-        throw coding_exception('Invalid component used in plugin_callback():' . $type . '_' . $name);
+        throw new coding_exception('Invalid component used in plugin_callback():' . $type . '_' . $name);
     }
 
     list($type, $name) = normalize_component($component);
@@ -7701,7 +7794,7 @@ function plugin_callback($type, $name, $feature, $action, $options = null, $defa
 
     $dir = get_component_directory($component);
     if (empty($dir)) {
-        throw coding_exception('Invalid component used in plugin_callback():' . $type . '_' . $name);
+        throw new coding_exception('Invalid component used in plugin_callback():' . $type . '_' . $name);
     }
 
     // Load library and look for function
@@ -7748,7 +7841,7 @@ function plugin_supports($type, $name, $feature, $default = NULL) {
 
     $component = clean_param($type . '_' . $name, PARAM_COMPONENT);
     if (empty($component)) {
-        throw coding_exception('Invalid component used in plugin_supports():' . $type . '_' . $name);
+        throw new coding_exception('Invalid component used in plugin_supports():' . $type . '_' . $name);
     }
 
     $function = null;
@@ -9867,9 +9960,12 @@ function remove_dir($dir, $content_only=false) {
     }
     closedir($handle);
     if ($content_only) {
+        clearstatcache(); // make sure file stat cache is properly invalidated
         return $result;
     }
-    return rmdir($dir); // if anything left the result will be false, no need for && $result
+    $result = rmdir($dir); // if anything left the result will be false, no need for && $result
+    clearstatcache(); // make sure file stat cache is properly invalidated
+    return $result;
 }
 
 /**
index 5d90384..93c5cbe 100644 (file)
@@ -973,9 +973,9 @@ class page_requirements_manager {
         }
 
         if (debugging('', DEBUG_DEVELOPER)) {
-            $code .= '<script src="'.$this->yui3loader->base.'yui/yui-debug.js"></script>';
+            $code .= '<script type="text/javascript" src="'.$this->yui3loader->base.'yui/yui-debug.js"></script>';
         } else {
-            $code .= '<script src="'.$this->yui3loader->base.'yui/yui-min.js"></script>';
+            $code .= '<script type="text/javascript" src="'.$this->yui3loader->base.'yui/yui-min.js"></script>';
         }
 
         return $code;
index b8c8546..2ff95ab 100644 (file)
@@ -53,6 +53,31 @@ function plagiarism_get_links($linkarray) {
     return $output;
 }
 
+/**
+ * returns array of plagiarism details about specified file
+ *
+ * @param int $cmid
+ * @param int $userid
+ * @param object $file moodle file object
+ * @return array - sets of details about specified file, one array of details per plagiarism plugin
+ *  - each set contains at least 'analyzed', 'score', 'reporturl'
+ */
+function plagiarism_get_file_results($cmid, $userid, $file) {
+    global $CFG;
+    $allresults = array();
+    if (empty($CFG->enableplagiarism)) {
+        return $allresults;
+    }
+    $plagiarismplugins = plagiarism_load_available_plugins();
+    foreach($plagiarismplugins as $plugin => $dir) {
+        require_once($dir.'/lib.php');
+        $plagiarismclass = "plagiarism_plugin_$plugin";
+        $plagiarismplugin = new $plagiarismclass;
+        $allresults[] = $plagiarismplugin->get_file_results($cmid, $userid, $file);
+    }
+    return $allresults;
+}
+
 /**
  * saves/updates plagiarism settings from a modules config page - called by course/modedit.php
  *
index 081a0e6..6e605db 100644 (file)
@@ -343,7 +343,7 @@ function question_delete_question($questionid) {
 /**
  * All question categories and their questions are deleted for this course.
  *
- * @param object $mod an object representing the activity
+ * @param stdClass $course an object representing the activity
  * @param boolean $feedback to specify if the process must output a summary of its work
  * @return boolean
  */
index 4df0be9..8988c61 100644 (file)
@@ -280,7 +280,7 @@ function resourcelib_embed_flashvideo($fullurl, $title, $clicktoopen) {
     }
     $output = '<div class="resourcecontent resourceflv">';
     $output .= html_writer::tag('span', $clicktoopen, array('id'=>$id, 'class'=>'resourcemediaplugin resourcemediaplugin_flv', 'title'=>$title));
-    $output .= html_writer::script(js_writer::function_call('M.util.add_video_player', array($id, $fullurl, $width, $height, $autosize)));
+    $output .= html_writer::script(js_writer::function_call('M.util.add_video_player', array($id, rawurlencode($fullurl), $width, $height, $autosize)));
     $output .= '</div>';
 
     return $output;
index 36973a5..5cec604 100644 (file)
@@ -409,12 +409,8 @@ function rss_full_tag($tag,$level=0,$endline=true,$content,$attributes=null) {
 }
 
 /**
- * Adds RSS Media Enclosures for "podcasting" by examining links to media files,
- * and attachments which are media files. Please note that the RSS that is
- * produced cannot be strictly valid for the linked files, since we do not know
- * the files' sizes and cannot include them in the "length" attribute. At
- * present, the validity (and therefore the podcast working in most software)
- * can only be ensured for attachments, and not for links.
+ * Adds RSS Media Enclosures for "podcasting" by including attachments that
+ * are specified in the item->attachments field.
  * Note also that iTunes does some things very badly - one thing it does is
  * refuse to download ANY of your files if you're using "file.php?file=blah"
  * and can't use the more elegant "file.php/blah" slasharguments setting. It
@@ -433,15 +429,11 @@ function rss_add_enclosures($item){
     global $CFG;
 
     $returnstring = '';
-    $rss_text = $item->description;
 
     // list of media file extensions and their respective mime types
     include_once($CFG->libdir.'/filelib.php');
     $mediafiletypes = get_mimetypes_array();
 
-    // regular expression (hopefully) matching all links to media files
-    $medialinkpattern = '@href\s*=\s*(\'|")(\S+(' . implode('|', array_keys($mediafiletypes)) . '))\1@Usie';
-
     // take into account attachments (e.g. from forum) - with these, we are able to know the file size
     if (isset($item->attachments) && is_array($item->attachments)) {
         foreach ($item->attachments as $attachment){
@@ -455,23 +447,5 @@ function rss_add_enclosures($item){
         }
     }
 
-    if (!preg_match_all($medialinkpattern, $rss_text, $matches)){
-        return $returnstring;
-    }
-
-    // loop over matches of regular expression
-    for ($i = 0; $i < count($matches[2]); $i++){
-        $url = htmlspecialchars($matches[2][$i]);
-        $extension = strtolower($matches[3][$i]);
-        if (isset($mediafiletypes[$extension]['type'])) {
-            $type = $mediafiletypes[$extension]['type'];
-        } else {
-            $type = 'document/unknown';
-        }
-
-        // the rss_*_tag functions can't deal with methods, unfortunately
-        $returnstring .= "\n<enclosure url='$url' type='$type' />\n";
-    }
-
     return $returnstring;
 }
index 8f8a255..2b51b1b 100644 (file)
@@ -816,12 +816,14 @@ if (!empty($CFG->customscripts)) {
     }
 }
 
-// in the first case, ip in allowed list will be performed first
-// for example, client IP is 192.168.1.1
-// 192.168 subnet is an entry in allowed list
-// 192.168.1.1 is banned in blocked list
-// This ip will be banned finally
-if (!empty($CFG->allowbeforeblock)) { // allowed list processed before blocked list?
+if (CLI_SCRIPT and !defined('WEB_CRON_EMULATED_CLI') and !PHPUNIT_SCRIPT) {
+    // no ip blocking
+} else if (!empty($CFG->allowbeforeblock)) { // allowed list processed before blocked list?
+    // in this case, ip in allowed list will be performed first
+    // for example, client IP is 192.168.1.1
+    // 192.168 subnet is an entry in allowed list
+    // 192.168.1.1 is banned in blocked list
+    // This ip will be banned finally
     if (!empty($CFG->allowedip)) {
         if (!remoteip_in_list($CFG->allowedip)) {
             die(get_string('ipblocked', 'admin'));
index bdb287d..c206d10 100644 (file)
@@ -657,7 +657,7 @@ function initialise_cfg() {
                 $CFG->{$name} = $value;
             }
         }
-    } catch (dml_read_exception $e) {
+    } catch (dml_exception $e) {
         // most probably empty db, going to install soon
     }
 }
@@ -1077,6 +1077,10 @@ function redirect_if_major_upgrade_required() {
  * files outside of dataroot if you supply custom paths for some settings in config.php.
  * This function does not verify that the directory is writable.
  *
+ * NOTE: this function uses current file stat cache,
+ *       please use clearstatcache() before this if you expect that the
+ *       directories may have been removed recently from a different request.
+ *
  * @param string $dir absolute directory path
  * @param boolean $create directory if does not exist
  * @param boolean $recursive create directory recursively
index d450ca9..fadfe7c 100644 (file)
@@ -900,6 +900,7 @@ function external_update_descriptions($component) {
         $service['enabled'] = empty($service['enabled']) ? 0 : $service['enabled'];
         $service['requiredcapability'] = empty($service['requiredcapability']) ? null : $service['requiredcapability'];
         $service['restrictedusers'] = !isset($service['restrictedusers']) ? 1 : $service['restrictedusers'];
+        $service['downloadfiles'] = !isset($service['downloadfiles']) ? 0 : $service['downloadfiles'];
         $service['shortname'] = !isset($service['shortname']) ? null : $service['shortname'];
 
         $update = false;
@@ -911,6 +912,10 @@ function external_update_descriptions($component) {
             $dbservice->restrictedusers = $service['restrictedusers'];
             $update = true;
         }
+        if ($dbservice->downloadfiles != $service['downloadfiles']) {
+            $dbservice->downloadfiles = $service['downloadfiles'];
+            $update = true;
+        }
         //if shortname is not a PARAM_ALPHANUMEXT, fail (tested here for service update and creation)
         if (isset($service['shortname']) and
                 (clean_param($service['shortname'], PARAM_ALPHANUMEXT) != $service['shortname'])) {
@@ -964,6 +969,7 @@ function external_update_descriptions($component) {
         $dbservice->enabled            = empty($service['enabled']) ? 0 : $service['enabled'];
         $dbservice->requiredcapability = empty($service['requiredcapability']) ? null : $service['requiredcapability'];
         $dbservice->restrictedusers    = !isset($service['restrictedusers']) ? 1 : $service['restrictedusers'];
+        $dbservice->downloadfiles      = !isset($service['downloadfiles']) ? 0 : $service['downloadfiles'];
         $dbservice->shortname          = !isset($service['shortname']) ? null : $service['shortname'];
         $dbservice->component          = $component;
         $dbservice->timecreated        = time();
index 155b2b0..eadfe5d 100644 (file)
@@ -1413,12 +1413,15 @@ function trusttext_active() {
 
 /**
  * Given raw text (eg typed in by a user), this function cleans it up
- * and removes any nasty tags that could mess up Moodle pages.
+ * and removes any nasty tags that could mess up Moodle pages through XSS attacks.
+ *
+ * The result must be used as a HTML text fragment, this function can not cleanup random
+ * parts of html tags such as url or src attributes.
  *
  * NOTE: the format parameter was deprecated because we can safely clean only HTML.
  *
  * @param string $text The text to be cleaned
- * @param int $format deprecated parameter, should always contain FORMAT_HTML or FORMAT_MOODLE
+ * @param int|string $format deprecated parameter, should always contain FORMAT_HTML or FORMAT_MOODLE
  * @param array $options Array of options; currently only option supported is 'allowid' (if true,
  *   does not remove id attributes when cleaning)
  * @return string The cleaned up text
@@ -1439,9 +1442,10 @@ function clean_text($text, $format = FORMAT_HTML, $options = array()) {
 
     $text = purify_html($text, $options);
 
-    // Remove potential script events - some extra protection for undiscovered bugs in our code
-    $text = preg_replace("~([^a-z])language([[:space:]]*)=~i", "$1Xlanguage=", $text);
-    $text = preg_replace("~([^a-z])on([a-z]+)([[:space:]]*)=~i", "$1Xon$2=", $text);
+    // Originally we tried to neutralise some script events here, it was a wrong approach because
+    // it was trivial to work around that (for example using style based XSS exploits).
+    // We must not give false sense of security here - all developers MUST understand how to use
+    // rawurlencode(), htmlentities(), htmlspecialchars(), p(), s(), moodle_url, html_writer and friends!!!
 
     return $text;
 }
@@ -1530,7 +1534,6 @@ function purify_html($text, $options = array()) {
  * @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
  * @return string
  */
-
 function text_to_html($text, $smiley_ignored=null, $para=true, $newlines=true) {
 /// Remove any whitespace that may be between HTML tags
     $text = preg_replace("~>([[:space:]]+)<~i", "><", $text);
index 72af810..edcc829 100644 (file)
@@ -41,6 +41,13 @@ if (is_restored_user($username)) {
 }
 $user = authenticate_user_login($username, $password);
 if (!empty($user)) {
+
+    //Non admin can not authenticate if maintenance mode
+    $hassiteconfig = has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM), $user);
+    if (!empty($CFG->maintenance_enabled) and !$hassiteconfig) {
+        throw new moodle_exception('sitemaintenance', 'admin');
+    }
+
     if (isguestuser($user)) {
         throw new moodle_exception('noguest');
     }
index a9c8fff..8fe66be 100644 (file)
@@ -2013,8 +2013,10 @@ function message_post_message($userfrom, $userto, $message, $format) {
     $eventdata->subject          = get_string_manager()->get_string('unreadnewmessage', 'message', fullname($userfrom), $userto->lang);
 
     if ($format == FORMAT_HTML) {
-        $eventdata->fullmessage      = '';
         $eventdata->fullmessagehtml  = $message;
+        //some message processors may revert to sending plain text even if html is supplied
+        //so we keep both plain and html versions if we're intending to send html
+        $eventdata->fullmessage = html_to_text($eventdata->fullmessagehtml);
     } else {
         $eventdata->fullmessage      = $message;
         $eventdata->fullmessagehtml  = '';
index 0be90a6..012b3b3 100644 (file)
@@ -769,9 +769,16 @@ class assignment_base {
                 $offset = required_param('offset', PARAM_INT);
                 $nextid = required_param('nextid', PARAM_INT);
                 $id = required_param('id', PARAM_INT);
-                $offset = (int)$offset+1;
-                //$this->display_submission($offset+1 , $nextid);
-                redirect('submissions.php?id='.$id.'&userid='. $nextid . '&mode=single&offset='.$offset);
+                $filter = optional_param('filter', self::FILTER_ALL, PARAM_INT);
+
+                if ($mode == 'next' || $filter !== self::FILTER_REQUIRE_GRADING) {
+                    $offset = (int)$offset+1;
+                }
+                $redirect = new moodle_url('submissions.php',
+                        array('id' => $id, 'offset' => $offset, 'userid' => $nextid,
+                        'mode' => 'single', 'filter' => $filter));
+
+                redirect($redirect);
                 break;
 
             case 'singlenosave':
index 58583c3..d88fe52 100644 (file)
@@ -333,5 +333,17 @@ $capabilities = array(
             'student' => CAP_ALLOW,
         )
     ),
+    'mod/forum:addquestion' => array(
+
+        'riskbitmask' => RISK_SPAM,
+
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_MODULE,
+        'archetypes' => array(
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'manager' => CAP_ALLOW
+        )
+    ),
 );
 
index 6ab1c50..19da7f9 100644 (file)
@@ -149,6 +149,7 @@ $string['exportdiscussion'] = 'Export whole discussion';
 $string['forcessubscribe'] = 'This forum forces everyone to be subscribed';
 $string['forum'] = 'Forum';
 $string['forum:addnews'] = 'Add news';
+$string['forum:addquestion'] = 'Add question';
 $string['forumauthorhidden'] = 'Author (hidden)';
 $string['forumblockingalmosttoomanyposts'] = 'You are approaching the posting threshold. You have posted {$a->numposts} times in the last {$a->blockperiod} and the limit is {$a->blockafter} posts.';
 $string['forumbodyhidden'] = 'This post cannot be viewed by you, probably because you have not posted in the discussion or the maximum editing time hasn\'t passed yet.';
index 4a0e2f7..48344e4 100644 (file)
@@ -4824,6 +4824,8 @@ function forum_user_can_post_discussion($forum, $currentgroup=null, $unused=-1,
 
     if ($forum->type == 'news') {
         $capname = 'mod/forum:addnews';
+    } else if ($forum->type == 'qanda') {
+        $capname = 'mod/forum:addquestion';
     } else {
         $capname = 'mod/forum:startdiscussion';
     }
index c45aeb0..197d3c2 100644 (file)
@@ -460,6 +460,8 @@ if (!empty($forum)) {      // User is starting a new discussion in a forum
 
         $course = $DB->get_record('course', array('id' => $forum->course));
 
+        $PAGE->set_cm($cm);
+        $PAGE->set_context($modcontext);
         $PAGE->navbar->add(format_string($post->subject, true), new moodle_url('/mod/forum/discuss.php', array('d'=>$discussion->id)));
         $PAGE->navbar->add(get_string("prune", "forum"));
         $PAGE->set_title(format_string($discussion->name).": ".format_string($post->subject));
index 06d11fa..8ebae36 100644 (file)
@@ -26,7 +26,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$module->version   = 2011110600;
+$module->version   = 2011110601;
 $module->requires  = 2011110200;  // Requires this Moodle version
 $module->cron      = 60;
 $module->component = 'mod_forum';
\ No newline at end of file
index fa1cf36..fe1cf25 100644 (file)
@@ -158,6 +158,7 @@ $string['endoflesson'] = 'End of lesson';
 $string['enteredthis'] = 'entered this.';
 $string['entername'] = 'Enter a nickname for the high scores list';
 $string['enterpassword'] = 'Please enter the password:';
+$string['emptypassword'] = 'Password cannot be empty';
 $string['eolstudentoutoftime'] = 'Attention:  You ran out of time for this lesson.  Your last answer may not have counted if it was answered after the time was up.';
 $string['eolstudentoutoftimenoanswers'] = 'You did not answer any questions.  You have received a 0 for this lesson.';
 $string['essay'] = 'Essay';
index b0dbfa7..3b2f1a2 100644 (file)
@@ -339,6 +339,9 @@ class mod_lesson_mod_form extends moodleform_mod {
         if (empty($data['maxtime']) and !empty($data['timed'])) {
             $errors['timedgrp'] = get_string('err_numeric', 'form');
         }
+        if (!empty($data['usepassword']) && empty($data['password'])) {
+            $errors['password'] = get_string('emptypassword', 'lesson');
+        }
 
         return $errors;
     }
index a7d8c30..ab75cd3 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * MRTODO: Brief description of this file
+ * AJAX service used when adding an External Tool to provide immediate feedback
+ * of which tool provider is to be used based on the Launch URL.
  *
  * @package    mod
  * @subpackage xml
- * @copyright  2011 onwards MRTODO
+ * @copyright Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @author     Chris Scribner
  */
 
 require_once(dirname(__FILE__) . "/../../config.php");
@@ -37,13 +39,37 @@ $response = new stdClass();
 switch ($action) {
     case 'find_tool_config':
         $toolurl = required_param('toolurl', PARAM_RAW);
+        $toolid = optional_param('toolid', 0, PARAM_INT);
 
-        $tool = lti_get_tool_by_url_match($toolurl, $courseid);
+        if(empty($toolid) && !empty($toolurl)){
+            $tool = lti_get_tool_by_url_match($toolurl, $courseid);
 
-        if (!empty($tool)) {
-            $response->toolid = $tool->id;
-            $response->toolname = htmlspecialchars($tool->name);
-            $response->tooldomain = htmlspecialchars($tool->tooldomain);
+            if(!empty($tool)){
+                $toolid = $tool->id;
+
+                $response->toolid = $tool->id;
+                $response->toolname = htmlspecialchars($tool->name);
+                $response->tooldomain = htmlspecialchars($tool->tooldomain);
+            }
+        } else {
+            $response->toolid = $toolid;
+        }
+
+        if (!empty($toolid)) {
+            // Look up privacy settings
+            $query = '
+                SELECT name, value
+                FROM {lti_types_config}
+                WHERE
+                    typeid = :typeid
+                AND name IN (\'sendname\', \'sendemailaddr\', \'acceptgrades\')
+            ';
+
+            $privacyconfigs = $DB->get_records_sql($query, array('typeid' => $toolid));
+            foreach($privacyconfigs as $config){
+                $configname = $config->name;
+                $response->$configname = $config->value;
+            }
         }
         break;
 }
index 97f1655..b798f46 100644 (file)
@@ -1,4 +1,28 @@
 <?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * 1.9 to 2.0 backup format converter. (Also currently used in common cartridge import process)
+ *
+ * @package    mod
+ * @subpackage lti
+ * @copyright  Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @author     Darko Miletic
+ */
 
 defined('MOODLE_INTERNAL') || die();
 
index 3be2514..c9c2a24 100644 (file)
@@ -77,8 +77,8 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step
             'instructorchoiceallowsetting',
             'grade',
             'instructorcustomparameters',
-            'showtitle',
-            'showdescription'
+            'showtitlelaunch',
+            'showdescriptionlaunch'
             )
         );
 
index 8a5a3ac..0c28a0c 100644 (file)
@@ -25,6 +25,7 @@
  * @author     Marc Alier
  * @author     Jordi Piguillem
  * @author     Nikolas Galanis
+ * @author     Chris Scribner
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
index 4d62cb0..2df3df5 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="mod/lti/db" VERSION="20111018" COMMENT="XMLDB file for Moodle mod/lti"
+<XMLDB PATH="mod/lti/db" VERSION="20111116" COMMENT="XMLDB file for Moodle mod/lti"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
 >
         <FIELD NAME="launchcontainer" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" COMMENT="Launch external tool in a pop-up" PREVIOUS="grade" NEXT="resourcekey"/>
         <FIELD NAME="resourcekey" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" PREVIOUS="launchcontainer" NEXT="password"/>
         <FIELD NAME="password" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" PREVIOUS="resourcekey" NEXT="debuglaunch"/>
-        <FIELD NAME="debuglaunch" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Enable the debug-style launch which pauses before auto-submit" PREVIOUS="password" NEXT="showtitle"/>
-        <FIELD NAME="showtitle" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="debuglaunch" NEXT="showdescription"/>
-        <FIELD NAME="showdescription" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="showtitle" NEXT="servicesalt"/>
-        <FIELD NAME="servicesalt" TYPE="char" LENGTH="40" NOTNULL="false" SEQUENCE="false" PREVIOUS="showdescription" NEXT="icon"/>
+        <FIELD NAME="debuglaunch" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Enable the debug-style launch which pauses before auto-submit" PREVIOUS="password" NEXT="showtitlelaunch"/>
+        <FIELD NAME="showtitlelaunch" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="debuglaunch" NEXT="showdescriptionlaunch"/>
+        <FIELD NAME="showdescriptionlaunch" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="showtitlelaunch" NEXT="servicesalt"/>
+        <FIELD NAME="servicesalt" TYPE="char" LENGTH="40" NOTNULL="false" SEQUENCE="false" PREVIOUS="showdescriptionlaunch" NEXT="icon"/>
         <FIELD NAME="icon" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="servicesalt" NEXT="secureicon"/>
         <FIELD NAME="secureicon" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="icon"/>
       </FIELDS>
@@ -95,4 +95,4 @@
       </INDEXES>
     </TABLE>
   </TABLES>
-</XMLDB>
+</XMLDB>
\ No newline at end of file
diff --git a/mod/lti/db/log.php b/mod/lti/db/log.php
new file mode 100644 (file)
index 0000000..e043978
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * LTI web service endpoints
+ *
+ * @package    mod
+ * @subpackage lti
+ * @copyright  Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @author     Chris Scribner
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$logs = array(
+    array('module'=>'lti', 'action'=>'view', 'mtable'=>'lti', 'field'=>'name'),
+    array('module'=>'lti', 'action'=>'launch', 'mtable'=>'lti', 'field'=>'name'),
+    array('module'=>'lti', 'action'=>'view all', 'mtable'=>'lti', 'field'=>'name')
+);
\ No newline at end of file
index 99f9deb..33aa23c 100644 (file)
@@ -64,6 +64,20 @@ function xmldb_lti_upgrade($oldversion) {
 
     $dbman = $DB->get_manager();
 
+    if ($oldversion < 2011111600){
+        // Rename showdescription to showdescriptionlaunch to not conflict with setting from core Moodle
+        // Rename showtitle as well to be consistent
+        $table = new xmldb_table('lti');
+        $field = new xmldb_field('showtitle', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'debuglaunch');
+
+        $dbman->rename_field($table, $field, 'showtitlelaunch');
+
+        $field = new xmldb_field('showdescription', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'showtitlelaunch');
+        $dbman->rename_field($table, $field, 'showdescriptionlaunch');
+
+        upgrade_mod_savepoint(true, 2011111600, 'lti');
+    }
+
     return true;
 }
 
index 907ce56..3cf2ef8 100644 (file)
@@ -44,6 +44,7 @@
  * @author     Jordi Piguillem
  * @author     Nikolas Galanis
  * @author     Charles Severance
+ * @author     Chris Scribner
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
@@ -125,9 +126,9 @@ class mod_lti_edit_types_form extends moodleform{
         $mform->addHelpButton('lti_acceptgrades', 'accept_grades_admin', 'lti');
 
         // Add grading preferences fieldset where the tool is allowed to retrieve rosters
-        $mform->addElement('select', 'lti_allowroster', get_string('share_roster_admin', 'lti'), $options);
-        $mform->setDefault('lti_allowroster', '2');
-        $mform->addHelpButton('lti_allowroster', 'share_roster_admin', 'lti');
+        //$mform->addElement('select', 'lti_allowroster', get_string('share_roster_admin', 'lti'), $options);
+        //$mform->setDefault('lti_allowroster', '2');
+        //$mform->addHelpButton('lti_allowroster', 'share_roster_admin', 'lti');
 
         $mform->addElement('checkbox', 'lti_forcessl', '&nbsp;', ' ' . get_string('force_ssl', 'lti'), $options);
         $mform->setDefault('lti_forcessl', '0');
index 4e38cee..e8c19a1 100644 (file)
@@ -43,6 +43,7 @@
  * @author     Marc Alier
  * @author     Jordi Piguillem
  * @author     Nikolas Galanis
+ * @author     Chris Scribner
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
index 5c2dff4..65f5989 100644 (file)
@@ -43,6 +43,7 @@
  * @author     Marc Alier
  * @author     Jordi Piguillem
  * @author     Nikolas Galanis
+ * @author     Chris Scribner
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 require_once("../../config.php");
index 36c56b5..b0fe496 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * MRTODO: Brief description of this file
+ * This page allows instructors to configure course level tool providers.
  *
  * @package    mod
  * @subpackage lti
- * @copyright  2011 onwards MRTODO
+ * @copyright  Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @author     Chris Scribner
  */
 
 require_once('../../config.php');
index f37d160..d7e166e 100644 (file)
  * @author     Marc Alier
  * @author     Jordi Piguillem
  * @author     Nikolas Galanis
+ * @author     Chris Scribner
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 defined('MOODLE_INTERNAL') || die;
 
-//Permissions
-$string['lti:view'] = 'View LTI activities';
-$string['lti:grade'] = 'Grade LTI activities';
-$string['lti:addcoursetool'] = 'Grade LTI activities';
-$string['lti:requesttooladd'] = 'Submit a tool to admins for configuration';
-
 $string['accept'] = 'Accept';
+$string['accept_grades'] = 'Accept grades from the tool';
+$string['accept_grades_admin'] = 'Accept grades from the tool';
+$string['accept_grades_admin_help'] = 'Specify whether the tool provider can add, update, read, and delete grades associated with instances of this tool type.
+
+Some tool providers support reporting grades back to Moodle based on actions taken within the tool, creating a more integrated
+experience.';
+$string['accept_grades_help'] = 'Specify whether the tool provider can add, update, read, and delete grades associated only with this external tool instance.
+
+Some tool providers support reporting grades back to Moodle based on actions taken within the tool, creating a more integrated
+experience.
+
+Note that this setting may be overriden in the tool configuration.';
+$string['action'] = 'Action';
+$string['active'] = 'Active';
 $string['activity'] = 'Activity';
 $string['addnewapp'] = 'Enable External Application';
 $string['addserver'] = 'Add new trusted server';
 $string['addtype'] = 'Add external tool configuration';
 $string['allow'] = 'Allow';
 $string['allowinstructorcustom'] = 'Allow instructors to add custom parameters';
-$string['share_roster_admin'] = 'Tool may access course roster';
 $string['allowsetting'] = 'Allow tool to store 8K of settings in Moodle';
 $string['always'] = 'Always';
-$string['lti'] = 'LTI';
+$string['automatic'] = 'Automatic, based on Launch URL';
+$string['baseurl'] = 'Base URL';
 $string['basiclti'] = 'LTI';
 $string['basiclti_base_string'] = 'LTI OAuth Base String';
-$string['basiclti_in_new_window'] = 'Your activity has opened in a new window';
 $string['basiclti_endpoint'] = 'LTI Launch Endpoint';
+$string['basiclti_in_new_window'] = 'Your activity has opened in a new window';
 $string['basiclti_parameters'] = 'LTI Launch Parameters';
 $string['basicltiactivities'] = 'LTI Activities';
 $string['basicltifieldset'] = 'Custom example fieldset';
 $string['basicltiintro'] = 'Activity Description';
 $string['basicltiname'] = 'Activity Name';
 $string['basicltisettings'] = 'Basic Learning Tool Interoperability Settings';
+$string['cannot_delete'] = 'You may not delete this tool configuration.';
+$string['cannot_edit'] = 'You may not edit this tool configuration.';
 $string['comment'] = 'Comment';
 $string['configpassword'] = 'Default Remote Tool Password';
 $string['configpreferheight'] = 'Default preferred height';
@@ -83,31 +94,105 @@ $string['configpreferwidth'] = 'Default preferred width';
 $string['configresourceurl'] = 'Default Resource URL';
 $string['configtoolurl'] = 'Default Remote Tool URL';
 $string['configtypes'] = 'Enable LTI Applications';
+$string['course_tool_types'] = 'Course tool types';
 $string['courseid'] = 'Course id number';
 $string['coursemisconf'] = 'Course is misconfigured';
+$string['createdon'] = 'Created On';
 $string['curllibrarymissing'] = 'PHP Curl library must be installed to use LTI';
 $string['custom'] = 'Custom parameters';
+$string['custom_config'] = 'Using custom tool configuration.';
+$string['custom_help'] = 'Custom parameters are settings used by the tool provider. For example, a custom parameter may be used to display
+a specific resource from the provider.
+
+It is safe to leave this field unchanged unless directed by the tool provider.';
 $string['custominstr'] = 'Custom parameters';
 $string['debuglaunch'] = 'Debug Option';
 $string['debuglaunchoff'] = 'Normal launch';
 $string['debuglaunchon'] = 'Debug launch';
+$string['default'] = 'Default';
+$string['default_launch_container'] = 'Default Launch Container';
+$string['default_launch_container_help'] = 'The launch container affects the display of the tool when launched from the course. Some launch containers provide more screen
+real estate to the tool, and others provide a more integrated feel with the Moodle environemnt.
+
+* **Default** - Use the launch container specified by the tool configuration.
+* **Embed** - The tool is displayed within the existing Moodle window, in a manner similar to most other Activity types.
+* **Embed, without blocks** - The tool is displayed within the existing Moodle window, with just the neavigation controls
+        at the top of the page.
+* **New window** - The tool opens in a new window, occupying all the available space.
+        Depending on the browser, it will open in a new tab or a popup window.
+        It is possible that browsers will prevent the new window from opening.';
+$string['delegate'] = 'Delegate to Instructor';
+$string['delete'] = 'Delete';
+$string['delete_confirmation'] = 'Are you sure you want to delete this external tool configuration?';
+$string['deletetype'] = 'Delete external tool configuration';
+$string['display_description'] = 'Display activity description when launched';
+$string['display_description_help'] = 'If selected, the activity description (specified above) will display above the tool provider\'s content.
+
+The description may be used to provide additional instructions for launchers of the tool, but it is not required.
+
+The description is never displayed when the tool\'s launch container is in a new window.';
+$string['display_name'] = 'Display activity name when launched';
+$string['display_name_help'] = 'If selected, the activity name (specified above) will display above the tool provider\'s content.
+
+It is possible that the tool provider may also display the title. This option can prevent the activity title from
+being displayed twice.
+
+The title is never displayed when the tool\'s launch container is in a new window.';
+$string['domain_mismatch'] = 'Launch URL\'s domain does not match tool configuration.';
 $string['donot'] = 'Do not send';
 $string['donotaccept'] = 'Do not accept';
 $string['donotallow'] = 'Do not allow';
+$string['edittype'] = 'Edit external tool configuration';
+$string['embed'] = 'Embed';
+$string['embed_no_blocks'] = 'Embed, without blocks';
 $string['enableemailnotification'] = 'Send notification emails';
 $string['enableemailnotification_help'] = 'If enabled, students will receive email notification when their tool submissions are graded.';
 $string['errormisconfig'] = 'Misconfigured tool. Please ask your Moodle administrator to fix the configuration of the tool.';
 $string['extensions'] = 'LTI Extension Services';
+$string['external_tool_type'] = 'External tool type';
+$string['external_tool_type_help'] = 'The main purpose of a tool configuration is to set up a secure communication channel between Moodle and the tool provider.
+It also provides an opportunity for configuration defaults and setting up additional services provided by the tool.
+
+* **Automatic, based on Launch URL** - This setting should be used in almost all cases. Moodle will select the most appropriate tool configuration
+       based on the Launch URL. Tools configured by both an administrator or within this course will be used.
+       When the Launch URL is specified, Moodle will provide feedback on whether it recognizes it or not. If Moodle does not recognize the Launch URL,
+       you may need to enter the tool configuration details manually.
+* **A specific tool type** - By selecting a specific tool type, you can force Moodle to use that tool configuration when communicating with the
+       external tool provider. If the Launch URL does not appear to belong to the tool provider, a warning will appear. In some cases, it is not necessary
+       to enter a Launch URL when providing a specific tool type (if not launching to a particular resource within the tool provider).
+* **Custom configuration** - To setup custom tool configuration on just this instance, show Advanced options, and enter the consumer key and
+       shared secret yourself. If you do not have a consumer key and shared secret, you may be able to request them from the tool provider.
+       Not all tools require a consumer key and shared secret, in which case the fields may be left blank.
+
+### Tool type editing
+
+Three icons are available after the External tool type dropdown list:
+
+* **Add** - Create a course level tool configuration. All External Tool instances in this course may use the tool configuration.
+* **Edit** - Select a course level tool type from the dropdown, then click this icon. The details of the tool configuration may be edited.
+* **Delete** - Remove the selected course level tool type.';
+$string['external_tool_types'] = 'External Tool Types';
 $string['failedtoconnect'] = 'Moodle was unable to communicate with the \"$a\" system';
-$string['filterconfig'] = 'LTI administration';
-$string['filtername'] = 'LTI';
 $string['filter_basiclti_configlink'] = 'Configure your preferred sites and their passwords';
 $string['filter_basiclti_password'] = 'Password is mandatory';
+$string['filterconfig'] = 'LTI administration';
+$string['filtername'] = 'LTI';
 $string['fixexistingconf'] = 'Use an existing configuration for the misconfigured instance';
 $string['fixnew'] = 'New Configuration';
 $string['fixnewconf'] = 'Define a new configuration for the misconfigured instance';
 $string['fixold'] = 'Use Existing';
+$string['forced_help'] = 'This setting has been forced in a course or site level tool configuration. You may not change it from this interface.';
+$string['force_ssl'] = 'Force SSL';
+$string['force_ssl_help'] = 'Selecting this option forces all launches to this tool provider to use SSL.
+
+In addition, all web service requests from the tool provider will use SSL.
+
+If using this option, confirm that this Moodle site and the tool provider support SSL.';
+$string['global_tool_types'] = 'Global tool types';
 $string['grading'] = 'Grade Routing';
+$string['icon_url'] = 'Icon URL';
+$string['icon_url_help'] = 'The icon URL allows the icon that shows up in the course listing for this activity to be modified. Instead of using the default
+LTI icon, an icon which conveys the type of activity may be specified.';
 $string['id'] = 'id';
 $string['imsroleadmin'] = 'Instructor,Administrator';
 $string['imsroleinstructor'] = 'Instructor';
@@ -115,16 +200,85 @@ $string['imsrolelearner'] = 'Learner';
 $string['invalidid'] = 'LTI ID was incorrect';
 $string['launch_in_moodle'] = 'Launch tool in moodle';
 $string['launch_in_popup'] = 'Launch tool in a pop-up';
+$string['launch_url'] = 'Launch URL';
+$string['launch_url_help'] = 'The Launch URL indicates the web address of the External Tool, and may contain additional information, such as the resource to show.
+If you are unsure what to enter for the Launch URL, please check with the tool provider for more information.
+
+If you have selected a specific tool type, you may not need to enter a Launch URL. If the tool link is used to just launch
+into the tool provider\'s system, and not go to a specific resource, this will likely be the case.';
 $string['launchinpopup'] = 'Launch Container';
+$string['launchinpopup_help'] = 'The launch container affects the display of the tool when launched from the course. Some launch containers provide more screen
+real estate to the tool, and others provide a more integrated feel with the Moodle environemnt.
+
+* **Default** - Use the launch container specified by the tool configuration.
+* **Embed** - The tool is displayed within the existing Moodle window, in a manner similar to most other Activity types.
+* **Embed, without blocks** - The tool is displayed within the existing Moodle window, with just the neavigation controls
+        at the top of the page.
+* **New window** - The tool opens in a new window, occupying all the available space.
+        Depending on the browser, it will open in a new tab or a popup window.
+        It is possible that browsers will prevent the new window from opening.';
 $string['launchoptions'] = 'Launch Options';
+$string['lti'] = 'LTI';
+$string['lti:addcoursetool'] = 'Grade LTI activities';
+$string['lti:grade'] = 'Grade LTI activities';
+$string['lti:manage'] = 'Edit LTI activities';
+$string['lti:requesttooladd'] = 'Submit a tool to admins for configuration';
+$string['lti:view'] = 'View LTI activities';
+$string['lti_administration'] = 'LTI Administration';
 $string['lti_errormsg'] = 'The tool returned the following error message: \"$a\"';
+$string['lti_launch_error'] = 'An error occured when launching the external tool: ';
+$string['lti_launch_error_tool_request'] = '<p>
+To submit a request for an administrator to complete the tool configuration, click <a href="{$a->admin_request_url}" target="_top">here</a>.
+</p>';
+$string['lti_launch_error_unsigned_help'] = '   <p>
+        This error may be a result of a missing consumer key and shared secret for the tool provider.
+    </p>
+    <p>
+        If you have a consumer key and shared secret, you may enter it when editing the external tool instance (make sure advanced options are visible).<br />
+        Alternatively, you may create a course level tool provider configuration <a href="{$a->course_tool_editor}">here</a>.
+    </p>';
+$string['lti_tool_request_added'] = 'Tool configuration request successfully submitted. You may need to contact an administrator to complete the tool configuration.';
+$string['lti_tool_request_existing'] = 'A tool configuration for the tool domain has already been submitted.';
+$string['main_admin'] = 'General help';
+$string['main_admin_help'] = 'External tools allow Moodle users to seamlessly interact with learning resources hosted remotely. Through a special
+launch protocol, the remote tool will have access to some general information about the launching user. For example,
+the institution name, course id, user id, and other information such as the user\'s name or e-mail address.
+
+Tool types listed on this page are separated into three categories:
+
+* **Active** - These tool providers have been approved and configured by an administrator. They can be used from within any
+        course on this Moodle instance. If a consumer key and shared secret are entered, a trust relationship is established
+        between this Moodle instance and the remote tool, providing a secure communication channel.
+* **Pending** - These tool providers came in through a package import, but have not been configured by an administrator.
+        Instructors may still use tools from these providers if they have a consumer key and shared secret, or if none is required.
+* **Rejected** - These tools providers are flagged as ones which an administrator has no intention of making available to the entire
+        Moodle instance. Instructors may still use tools from these providers if they have a consumer key and shared secret, or if none is required.';
+$string['miscellaneous'] = 'Miscellaneous';
 $string['misconfiguredtools'] = 'Misconfigured tool instances were detected';
 $string['missingparameterserror'] = 'The page is misconfigured: \"$a\"';
 $string['module_class_type'] = 'Moodle module type';
 $string['modulename'] = 'External Tool';
+$string['modulename_help'] = 'External tools allow Moodle users to interact with learning resources and activities on other web sites. For instance, an
+external tool could provide access to a new activity type or learning materials from a publisher.
+
+To setup an external tool instance a tool provider which supports LTI (Learning Tools Interoperability) is required.
+If you find a tool provider which supports LTI, they should be able to provide instructions on how to configure the
+external tool instance. Additionally, tool types configured by a site administrator will also be available for use.
+
+External tools differ from URL resources in a few ways:
+* **Context aware** - External tools have access to information about the user who launched the tool, such as
+        insitution, course, name, and other information.
+* **Deep integration** - External tools support reading, updating, and deleting grades associated with the activity instance. More integration points
+        are planned for future releases.
+* **Security** - External tool configurations create a trust relationship between Moodle and the tool provider, allowing secure communication
+        between them.';
 $string['modulenameplural'] = 'basicltis';
 $string['modulenamepluralformatted'] = 'LTI Instances';
 $string['never'] = 'Never';
+$string['new_window'] = 'New window';
+$string['no_lti_configured'] = 'There are no active External Tools configured.';
+$string['no_lti_pending'] = 'There are no pending External Tools.';
+$string['no_lti_rejected'] = 'There are no rejected External Tools.';
 $string['noattempts'] = 'No attempts have been made on this tool instance';
 $string['noltis'] = 'There are no lti instances';
 $string['noservers'] = 'No servers found';
@@ -134,10 +288,33 @@ $string['optionalsettings'] = 'Optional settings';
 $string['organization'] ='Organization details';
 $string['organizationdescr'] ='Organization Description';
 $string['organizationid'] ='Organization ID';
+$string['organizationid_help'] = 'A unique identifier for this Moodle instance. Typically, the DNS name of the organization is used.
+
+If this field is left blank, the host name of this Moodle site will be used as the default value.';
 $string['organizationurl'] ='Organization URL';
+$string['organizationurl_help'] = 'The base URL of this Moodle instance.
+
+If this field is left blank, a default value will be used based on the site configuration.';
 $string['pagesize'] = 'Submissions shown per page';
 $string['password'] = 'Shared Secret';
 $string['password_admin'] = 'Shared Secret';
+$string['password_admin_help'] = 'The shared secret can be thought of as a password used to authenticate access to the tool. It should be provided
+along with the consumer key from the tool provider.
+
+Tools which do not require secure communication from Moodle and do not provide additional services (such as grade reporting)
+may not require a shared secret.';
+$string['password_help'] = 'For pre-configured tools, it is not necessary to enter a shared secret here, as the shared secret will be
+provided as part of the configuration process.
+
+This field should be entered if creating a link to a tool provider which is not already configured.
+If the tool provider is to be used more than once in this course, adding a course tool configuration is a good idea.
+
+The shared secret can be thought of as a password used to authenticate access to the tool. It should be provided
+along with the consumer key from the tool provider.
+
+Tools which do not require secure communication from Moodle and do not provide additional services (such as grade reporting)
+may not require a shared secret.';
+$string['pending'] = 'Pending';
 $string['pluginadministration'] = 'LTI administration';
 $string['pluginname'] = 'LTI';
 $string['preferheight'] = 'Preferred Height';
@@ -148,203 +325,20 @@ $string['privacy'] = 'Privacy';
 $string['quickgrade'] = 'Allow quick grading';
 $string['quickgrade_help'] = 'If enabled, multiple tools can be graded on one page. Add grades and comments then click the "Save all my feedback" button to save all changes for that page.';
 $string['redirect'] = 'You will be redirected in few seconds. If you are not, press the button.';
+$string['reject'] = 'Reject';
+$string['rejected'] = 'Rejected';
 $string['resource'] = 'Resource';
 $string['resourcekey'] = 'Consumer Key';
 $string['resourcekey_admin'] = 'Consumer Key';
-$string['resourceurl'] = 'Resource URL';
-$string['saveallfeedback'] = 'Save all my feedback';
-$string['send'] = 'Send';
-$string['share_email_admin'] = 'Share launcher\'s email with tool';
-$string['share_name_admin'] = 'Share launcher\'s name with tool';
-$string['setdefault'] = 'Set a default value for the professor if delegating';
-$string['setupbox'] = 'LTI Tool Setup Box';
-$string['setupoptions'] = 'Setup Options';
-$string['size'] = 'Size parameters';
-$string['submission'] = 'Submission';
-$string['toggle_debug_data'] = 'Toggle Debug Data';
-$string['toolsetup'] = 'External Tool Configuration';
-$string['toolurl'] = 'Tool Base URL';
-$string['typename'] = 'Tool Name';
-$string['types'] = 'Types';
-$string['validurl'] = 'A valid URL must start with http(s)://';
-$string['viewsubmissions'] = 'View submissions and grading screen';
-
-//New admin strings
-$string['show_in_course'] = 'Show tool type when creating tool instances';
-$string['delegate'] = 'Delegate to Instructor';
-$string['tool_settings'] = 'Tool Settings';
-$string['miscellaneous'] = 'Miscellaneous';
-$string['embed'] = 'Embed';
-$string['embed_no_blocks'] = 'Embed, without blocks';
-$string['new_window'] = 'New window';
-$string['default_launch_container'] = 'Default Launch Container';
-$string['active'] = 'Active';
-$string['pending'] = 'Pending';
-$string['rejected'] = 'Rejected';
-$string['baseurl'] = 'Base URL';
-$string['action'] = 'Action';
-$string['createdon'] = 'Created On';
-$string['accept'] = 'Accept';
-$string['update'] = 'Update';
-$string['delete'] = 'Delete';
-$string['reject'] = 'Reject';
-$string['external_tool_types'] = 'External Tool Types';
-$string['no_lti_configured'] = 'There are no active External Tools configured.';
-$string['no_lti_pending'] = 'There are no pending External Tools.';
-$string['no_lti_rejected'] = 'There are no rejected External Tools.';
-$string['accept_grades_admin'] = 'Accept grades from the tool';
-$string['force_ssl'] = 'Force SSL';
-$string['lti_administration'] = 'LTI Administration';
-
-//New instructor strings
-$string['display_name'] = 'Display activity name when launched';
-$string['display_description'] = 'Display activity description when launched';
-$string['external_tool_type'] = 'External tool type';
-$string['launch_url'] = 'Launch URL';
-$string['secure_launch_url'] = 'Secure Launch URL';
-$string['share_name'] = 'Share launcher\'s name with the tool';
-$string['share_email'] = 'Share launcher\'s email with the tool';
-$string['accept_grades'] = 'Accept grades from the tool';
-$string['share_roster'] = 'Allow the tool to access this course\'s roster';
-$string['automatic'] = 'Automatic, based on Launch URL';
-$string['default'] = 'Default';
-
-$string['edittype'] = 'Edit external tool configuration';
-$string['deletetype'] = 'Delete external tool configuration';
-$string['delete_confirmation'] = 'Are you sure you want to delete this external tool configuration?';
-$string['cannot_edit'] = 'You may not edit this tool configuration.';
-$string['cannot_delete'] = 'You may not delete this tool configuration.';
-$string['global_tool_types'] = 'Global tool types';
-$string['course_tool_types'] = 'Course tool types';
-
-$string['using_tool_configuration'] = 'Using tool configuration: ';
-$string['domain_mismatch'] = 'Launch URL\'s domain does not match tool configuration.';
-$string['custom_config'] = 'Using custom tool configuration.';
-$string['tool_config_not_found'] = 'Tool configuration not found for this URL.';
-
-$string['icon_url'] = 'Icon URL';
-$string['secure_icon_url'] = 'Secure Icon URL';
-
-$string['return_to_course'] = 'Click <a href="{$a->link}" target="_top">here</a> to return to the course.';
-
-$string['lti_launch_error'] = 'An error occured when launching the external tool: ';
-$string['lti_launch_error_unsigned_help'] = '
-    <p>
-        This error may be a result of a missing consumer key and shared secret for the tool provider.
-    </p>
-    <p>
-        If you have a consumer key and shared secret, you may enter it when editing the external tool instance (make sure advanced options are visible).<br />
-        Alternatively, you may create a course level tool provider configuration <a href="{$a->course_tool_editor}">here</a>.
-    </p>
-';
-
-$string['lti_launch_error_tool_request'] = '
-    <p>
-        To submit a request for an administrator to complete the tool configuration, click <a href="{$a->admin_request_url}" target="_top">here</a>.
-    </p>
-';
-
-$string['lti_tool_request_added'] = '
-    Tool configuration request successfully submitted. You may need to contact an administrator to complete the tool configuration.
-';
-
-$string['lti_tool_request_existing'] = '
-    A tool configuration for the tool domain has already been submitted.
-';
-
-//Instance help
-
-$string['external_tool_type_help'] = '
-The main purpose of a tool configuration is to set up a secure communication channel between Moodle and the tool provider.
-It also provides an opportunity for configuration defaults and setting up additional services provided by the tool.
-
-<ul>
-   <li>
-       <b>Automatic, based on Launch URL</b> - This setting should be used in almost all cases. Moodle will select the most appropriate tool configuration
-       based on the Launch URL. Tools configured by both an administrator or within this course will be used.
-       When the Launch URL is specified, Moodle will provide feedback on whether it recognizes it or not. If Moodle does not recognize the Launch URL,
-       you may need to enter the tool configuration details manually.
-   </li>
-   <li>
-       <b>A specific tool type</b> - By selecting a specific tool type, you can force Moodle to use that tool configuration when communicating with the
-       external tool provider. If the Launch URL does not appear to belong to the tool provider, a warning will appear. In some cases, it is not necessary
-       to enter a Launch URL when providing a specific tool type (if not launching to a particular resource within the tool provider).
-   </li>
-   <li>
-       <b>Custom configuration</b> - To setup custom tool configuration on just this instance, show Advanced options, and enter the consumer key and
-       shared secret yourself. If you do not have a consumer key and shared secret, you may be able to request them from the tool provider.
-       Not all tools require a consumer key and shared secret, in which case the fields may be left blank.
-   </li>
-</ul>
-
-<b>Tool type editing:</b><br />
-
-Three icons are available after the External tool type dropdown list:
-
-<ul>
-    <li>
-        <b>Add</b> - Create a course level tool configuration. All External Tool instances in this course may use the tool configuration.
-    </li>
-    <li>
-        <b>Edit</b> - Select a course level tool type from the dropdown, then click this icon. The details of the tool configuration may be edited.
-    </li>
-    <li>
-        <b>Delete</b> - Remove the selected course level tool type.
-    </li>
-</ul>
-';
-
-$string['launch_url_help'] = '
-The Launch URL indicates the web address of the External Tool, and may contain additional information, such as the resource to show.
-If you are unsure what to enter for the Launch URL, please check with the tool provider for more information.
-
-If you have selected a specific tool type, you may not need to enter a Launch URL. If the tool link is used to just launch
-into the tool provider\'s system, and not go to a specific resource, this will likely be the case.
-';
-
-$string['secure_launch_url_help'] = '
-Similar to Launch URL, but used instead of the launch url if high security is required. Moodle will use the
-secure launch URL instead of the launch URL if the Moodle site is accessed through SSL, or if the tool configuration
-is set to always launch through SSL.
-
-The Launch URL may also be set to an https address to force launching through SSL, and this field may be left blank.
-';
-
-$string['icon_url_help'] = '
-The icon URL allows the icon that shows up in the course listing for this activity to be modified. Instead of using the default
-LTI icon, an icon which conveys the type of activity may be specified.
-';
-
-$string['secure_icon_url_help'] = '
-Similar to the icon URL, but used if the user accessing Moodle securely through SSL. The main purpose for this field is to prevent
-the browser from warning the user if the underlying page was accessed over SSL, but requesting to show an unsecure image.
-';
-
-$string['launchinpopup_help'] = '
-The launch container affects the display of the tool when launched from the course. Some launch containers provide more screen
-real estate to the tool, and others provide a more integrated feel with the Moodle environemnt.
+$string['resourcekey_admin_help'] = 'The consumer key can be thought of as a username used to authenticate access to the tool.
+It can be used by the tool provider to uniquely identify the Moodle site from which users launch into the tool.
 
-<ul>
-    <li>
-        <b>Default</b>  - Use the launch container specified by the tool configuration.
-    </li>
-    <li>
-        <b>Embed</b> - The tool is displayed within the existing Moodle window, in a manner similar to most other Activity types.
-    </li>
-    <li>
-        <b>Embed, without blocks</b> - The tool is displayed within the existing Moodle window, with just the neavigation controls
-        at the top of the page.
-    </li>
-    <li>
-        <b>New window</b> - The tool opens in a new window, occupying all the available space.
-        Depending on the browser, it will open in a new tab or a popup window.
-        It is possible that browsers will prevent the new window from opening.
-    </li>
-</ul>
-';
+The consumer key must be provided by the tool provider. The method of obtaining a consumer key varies between
+tool providers. It may be an automated process, or it may require a dialogue with the tool provider.
 
-$string['resourcekey_help'] = '
-For pre-configured tools, it is not necessary to enter a resource key here, as the consumer key will be
+Tools which do not require secure communication from Moodle and do not provide additional services (such as grade reporting)
+may not require a resource key.';
+$string['resourcekey_help'] = 'For pre-configured tools, it is not necessary to enter a resource key here, as the consumer key will be
 provided as part of the configuration process.
 
 This field should be entered if creating a link to a tool provider which is not already configured.
@@ -357,85 +351,64 @@ The consumer key must be provided by the tool provider. The method of obtaining
 tool providers. It may be an automated process, or it may require a dialogue with the tool provider.
 
 Tools which do not require secure communication from Moodle and do not provide additional services (such as grade reporting)
-may not require a resource key.
-';
-
-$string['password_help'] = '
-For pre-configured tools, it is not necessary to enter a shared secret here, as the shared secret will be
-provided as part of the configuration process.
-
-This field should be entered if creating a link to a tool provider which is not already configured.
-If the tool provider is to be used more than once in this course, adding a course tool configuration is a good idea.
-
-The shared secret can be thought of as a password used to authenticate access to the tool. It should be provided
-along with the consumer key from the tool provider.
-
-Tools which do not require secure communication from Moodle and do not provide additional services (such as grade reporting)
-may not require a shared secret.
-';
-
-$string['custom_help'] = '
-Custom parameters are settings used by the tool provider. For example, a custom parameter may be used to display
-a specific resource from the provider.
-
-It is safe to leave this field unchanged unless directed by the tool provider.
-';
-
-$string['share_name_help'] = '
-Specify whether the full name of the user launching the tool should be shared with the tool provider.
-The tool provider may need launchers\' names to show meaningful information within the tool.
-
-Note that this setting may be overriden in the tool configuration.
-';
+may not require a resource key.';
+$string['resourceurl'] = 'Resource URL';
+$string['return_to_course'] = 'Click <a href="{$a->link}" target="_top">here</a> to return to the course.';
+$string['saveallfeedback'] = 'Save all my feedback';
+$string['secure_icon_url'] = 'Secure Icon URL';
+$string['secure_icon_url_help'] = 'Similar to the icon URL, but used if the user accessing Moodle securely through SSL. The main purpose for this field is to prevent
+the browser from warning the user if the underlying page was accessed over SSL, but requesting to show an unsecure image.';
+$string['secure_launch_url'] = 'Secure Launch URL';
+$string['secure_launch_url_help'] = 'Similar to Launch URL, but used instead of the launch url if high security is required. Moodle will use the
+secure launch URL instead of the launch URL if the Moodle site is accessed through SSL, or if the tool configuration
+is set to always launch through SSL.
 
-$string['share_email_help'] = '
-Specify whether the e-mail address of the user launching the tool will be shared with the tool provider.
+The Launch URL may also be set to an https address to force launching through SSL, and this field may be left blank.';
+$string['send'] = 'Send';
+$string['setdefault'] = 'Set a default value for the professor if delegating';
+$string['setupbox'] = 'LTI Tool Setup Box';
+$string['setupoptions'] = 'Setup Options';
+$string['share_email'] = 'Share launcher\'s email with the tool';
+$string['share_email_admin'] = 'Share launcher\'s email with tool';
+$string['share_email_admin_help'] = 'Specify whether the e-mail address of the user launching the tool will be shared with the tool provider.
+The tool provider may need launcher\'s e-mail addresses to distinguish users with the same name in the UI, or send e-mails
+to users based on actions within the tool.';
+$string['share_email_help'] = 'Specify whether the e-mail address of the user launching the tool will be shared with the tool provider.
 The tool provider may need launcher\'s e-mail addresses to distinguish users with the same name, or send e-mails
 to users based on actions within the tool.
 
-Note that this setting may be overriden in the tool configuration.
-';
-
-$string['accept_grades_help'] = '
-Specify whether the tool provider can add, update, read, and delete grades associated only with this external tool instance.
-
-Some tool providers support reporting grades back to Moodle based on actions taken within the tool, creating a more integrated
-experience.
-
-Note that this setting may be overriden in the tool configuration.
-';
-
-$string['share_roster_help'] = '
-Specify whether the tool can access the list of users enrolled in this course.
-
-Note that this setting may be overriden in the tool configuration.
-';
-
-$string['display_name_help'] = '
-If selected, the activity name (specified above) will display above the tool provider\'s content.
-
-It is possible that the tool provider may also display the title. This option can prevent the activity title from
-being displayed twice.
-
-The title is never displayed when the tool\'s launch container is in a new window.
-';
-
-$string['display_description_help'] = '
-If selected, the activity description (specified above) will display above the tool provider\'s content.
+Note that this setting may be overriden in the tool configuration.';
+$string['share_name'] = 'Share launcher\'s name with the tool';
+$string['share_name_admin'] = 'Share launcher\'s name with tool';
+$string['share_name_admin_help'] = 'Specify whether the full name of the user launching the tool should be shared with the tool provider.
+The tool provider may need launchers\' names to show meaningful information within the tool.';
+$string['share_name_help'] = 'Specify whether the full name of the user launching the tool should be shared with the tool provider.
+The tool provider may need launchers\' names to show meaningful information within the tool.
 
-The description may be used to provide additional instructions for launchers of the tool, but it is not required.
+Note that this setting may be overriden in the tool configuration.';
+$string['share_roster'] = 'Allow the tool to access this course\'s roster';
+$string['share_roster_admin'] = 'Tool may access course roster';
+$string['share_roster_admin_help'] = 'Specify whether the tool can access the list of users enrolled in courses from which this tool type is launched.';
+$string['share_roster_help'] = 'Specify whether the tool can access the list of users enrolled in this course.
 
-The description is never displayed when the tool\'s launch container is in a new window.
-';
+Note that this setting may be overriden in the tool configuration.';
+$string['show_in_course'] = 'Show tool type when creating tool instances';
+$string['show_in_course_help'] = 'If selected, this tool configuration will appear in the "External tool type" dropdown when instructors
+configure external tools within courses.
 
-//Admin help
-$string['typename_help'] = '
-The tool name is used to identify the tool provider within Moodle. The name entered will be visible
-to instructors when adding external tools within courses.
-';
+In most cases, this option does not need to be selected. Instructors can use this tool configuration
+based on the Launch URL matching the Tool base URL, which is the preferred method.
 
-$string['toolurl_help'] = '
-The tool base URL is used to match tool launch URLs to the correct tool configuration. Prefxing the URL with http(s) is optional.
+The only case in which this option should be selected is if the tool configuration is just intended for single sign on.
+For example, if all launches to the tool provider just take the user to a landing page instead of to a specific resource.';
+$string['size'] = 'Size parameters';
+$string['submission'] = 'Submission';
+$string['toggle_debug_data'] = 'Toggle Debug Data';
+$string['tool_config_not_found'] = 'Tool configuration not found for this URL.';
+$string['tool_settings'] = 'Tool Settings';
+$string['toolsetup'] = 'External Tool Configuration';
+$string['toolurl'] = 'Tool Base URL';
+$string['toolurl_help'] = 'The tool base URL is used to match tool launch URLs to the correct tool configuration. Prefxing the URL with http(s) is optional.
 
 Additionally, the base URL is used as the launch URL if a launch URL is not specified in the external tool instance.
 
@@ -478,151 +451,12 @@ Additionally, the base URL is used as the launch URL if a launch URL is not spec
     </tbody>
 </table>
 
-If two different tool configurations are for the same domain, the most specific match will be used.
-';
-
-$string['resourcekey_admin_help'] = '
-The consumer key can be thought of as a username used to authenticate access to the tool.
-It can be used by the tool provider to uniquely identify the Moodle site from which users launch into the tool.
-
-The consumer key must be provided by the tool provider. The method of obtaining a consumer key varies between
-tool providers. It may be an automated process, or it may require a dialogue with the tool provider.
-
-Tools which do not require secure communication from Moodle and do not provide additional services (such as grade reporting)
-may not require a resource key.
-';
-
-$string['password_admin_help'] = '
-The shared secret can be thought of as a password used to authenticate access to the tool. It should be provided
-along with the consumer key from the tool provider.
-
-Tools which do not require secure communication from Moodle and do not provide additional services (such as grade reporting)
-may not require a shared secret.
-';
-
-$string['show_in_course_help'] = '
-If selected, this tool configuration will appear in the "External tool type" dropdown when instructors
-configure external tools within courses.
-
-In most cases, this option does not need to be selected. Instructors can use this tool configuration
-based on the Launch URL matching the Tool base URL, which is the preferred method.
-
-The only case in which this option should be selected is if the tool configuration is just intended for single sign on.
-For example, if all launches to the tool provider just take the user to a landing page instead of to a specific resource.
-';
-
-$string['default_launch_container_help'] = '
-The launch container affects the display of the tool when launched from the course. Some launch containers provide more screen
-real estate to the tool, and others provide a more integrated feel with the Moodle environemnt.
-
-<ul>
-    <li>
-        <b>Default</b>  - Use the launch container specified by the tool configuration.
-    </li>
-    <li>
-        <b>Embed</b> - The tool is displayed within the existing Moodle window, in a manner similar to most other Activity types.
-    </li>
-    <li>
-        <b>Embed, without blocks</b> - The tool is displayed within the existing Moodle window, with just the neavigation controls
-        at the top of the page.
-    </li>
-    <li>
-        <b>New window</b> - The tool opens in a new window, occupying all the available space.
-        Depending on the browser, it will open in a new tab or a popup window.
-        It is possible that browsers will prevent the new window from opening.
-    </li>
-</ul>
-';
-
-$string['share_name_admin_help'] = '
-Specify whether the full name of the user launching the tool should be shared with the tool provider.
-The tool provider may need launchers\' names to show meaningful information within the tool.
-';
-
-$string['share_email_admin_help'] = '
-Specify whether the e-mail address of the user launching the tool will be shared with the tool provider.
-The tool provider may need launcher\'s e-mail addresses to distinguish users with the same name in the UI, or send e-mails
-to users based on actions within the tool.
-';
-
-$string['accept_grades_admin_help'] = '
-Specify whether the tool provider can add, update, read, and delete grades associated with instances of this tool type.
-
-Some tool providers support reporting grades back to Moodle based on actions taken within the tool, creating a more integrated
-experience.
-';
-
-$string['share_roster_admin_help'] = '
-Specify whether the tool can access the list of users enrolled in courses from which this tool type is launched.
-';
-
-$string['main_admin'] = 'General help';
-
-$string['main_admin_help'] = '
-External tools allow Moodle users to seamlessly interact with learning resources hosted remotely. Through a special
-launch protocol, the remote tool will have access to some general information about the launching user. For example,
-the institution name, course id, user id, and other information such as the user\'s name or e-mail address.
-
-Tool types listed on this page are separated into three categories:
-
-<ul>
-    <li>
-        <b>Active</b> - These tool providers have been approved and configured by an administrator. They can be used from within any
-        course on this Moodle instance. If a consumer key and shared secret are entered, a trust relationship is established
-        between this Moodle instance and the remote tool, providing a secure communication channel.
-    </li>
-    <li>
-        <b>Pending</b> - These tool providers came in through a package import, but have not been configured by an administrator.
-        Instructors may still use tools from these providers if they have a consumer key and shared secret, or if none is required.
-    </li>
-    <li>
-        <b>Rejected</b> - These tools providers are flagged as ones which an administrator has no intention of making available to the entire
-        Moodle instance. Instructors may still use tools from these providers if they have a consumer key and shared secret, or if none is required.
-    </li>
-</ul>
-';
-
-$string['modulename_help'] = '
-External tools allow Moodle users to interact with learning resources and activities on other web sites. For instance, an
-external tool could provide access to a new activity type or learning materials from a publisher.
-
-To setup an external tool instance a tool provider which supports LTI (Learning Tools Interoperability) is required.
-If you find a tool provider which supports LTI, they should be able to provide instructions on how to configure the
-external tool instance. Additionally, tool types configured by a site administrator will also be available for use.
-
-External tools differ from URL resources in a few ways:
-<ul>
-    <li>
-        <b>Context aware</b> - External tools have access to information about the user who launched the tool, such as
-        insitution, course, name, and other information.
-    </li>
-    <li>
-        <b>Deep integration</b> - External tools support reading, updating, and deleting grades associated with the activity instance. More integration points
-        are planned for future releases.
-    </li>
-    <li>
-        <b>Security</b> - External tool configurations create a trust relationship between Moodle and the tool provider, allowing secure communication
-        between them.
-    </li>
-</ul>
-';
-
-$string['force_ssl_help'] = '
-Selecting this option forces all launches to this tool provider to use SSL.
-
-In addition, all web service requests from the tool provider will use SSL.
-
-If using this option, confirm that this Moodle site and the tool provider support SSL.
-';
-
-$string['organizationid_help'] = '
-A unique identifier for this Moodle instance. Typically, the DNS name of the organization is used.
-
-If this field is left blank, the host name of this Moodle site will be used as the default value.
-';
-
-$string['organizationurl_help'] = '
-The base URL of this Moodle instance.
-
-If this field is left blank, a default value will be used based on the site configuration.
-';
+If two different tool configurations are for the same domain, the most specific match will be used.';
+$string['typename'] = 'Tool Name';
+$string['typename_help'] = 'The tool name is used to identify the tool provider within Moodle. The name entered will be visible
+to instructors when adding external tools within courses.';
+$string['types'] = 'Types';
+$string['update'] = 'Update';
+$string['using_tool_configuration'] = 'Using tool configuration: ';
+$string['validurl'] = 'A valid URL must start with http(s)://';
+$string['viewsubmissions'] = 'View submissions and grading screen';
index 3afd2e3..fced3b4 100644 (file)
@@ -43,6 +43,7 @@
  * @author     Marc Alier
  * @author     Jordi Piguillem
  * @author     Nikolas Galanis
+ * @author     Chris Scribner
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
index 5853162..97d2d5c 100644 (file)
@@ -43,6 +43,7 @@
  * @author     Marc Alier
  * @author     Jordi Piguillem
  * @author     Nikolas Galanis
+ * @author     Chris Scribner
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
@@ -63,6 +64,7 @@ function lti_supports($feature) {
         case FEATURE_GRADE_HAS_GRADE:         return true;
         case FEATURE_GRADE_OUTCOMES:          return true;
         case FEATURE_BACKUP_MOODLE2:          return true;
+        case FEATURE_SHOW_DESCRIPTION:        return true;
 
         default: return null;
     }
@@ -117,12 +119,12 @@ function lti_update_instance($lti, $mform) {
     $lti->timemodified = time();
     $lti->id = $lti->instance;
 
-    if (!isset($lti->showtitle)) {
-        $lti->showtitle = 0;
+    if (!isset($lti->showtitlelaunch)) {
+        $lti->showtitlelaunch = 0;
     }
 
-    if (!isset($lti->showdescription)) {
-        $lti->showdescription = 0;
+    if (!isset($lti->showdescriptionlaunch)) {
+        $lti->showdescriptionlaunch = 0;
     }
 
     if (!isset($lti->grade)) {
@@ -175,7 +177,7 @@ function lti_get_coursemodule_info($coursemodule) {
     require_once($CFG->dirroot.'/mod/lti/locallib.php');
 
     if (!$lti = $DB->get_record('lti', array('id' => $coursemodule->instance),
-            'icon, secureicon')) {
+            'icon, secureicon, intro, introformat, name')) {
         return null;
     }
 
@@ -189,6 +191,13 @@ function lti_get_coursemodule_info($coursemodule) {
         $info->iconurl = new moodle_url($lti->icon);
     }
 
+    if ($coursemodule->showdescription) {
+        // Convert intro to html. Do not filter cached version, filters run at display time.
+        $info->content = format_module_intro('lti', $lti, $coursemodule->id, false);
+    }
+
+    $info->name = $lti->name;
+
     return $info;
 }
 
index 128c345..7aee7db 100644 (file)
@@ -44,6 +44,7 @@
  * @author     Marc Alier
  * @author     Jordi Piguillem
  * @author     Nikolas Galanis
+ * @author     Chris Scribner
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
index df870bd..53425a3 100644 (file)
@@ -43,6 +43,7 @@
  * @author     Marc Alier
  * @author     Jordi Piguillem
  * @author     Nikolas Galanis
+ * @author     Chris Scribner
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
@@ -233,15 +234,15 @@ function lti_build_request($instance, $typeconfig, $course) {
     }
 
     $requestparams = array(
-        "resource_link_id" => $instance->id,
-        "resource_link_title" => $instance->name,
-        "resource_link_description" => $instance->intro,
-        "user_id" => $USER->id,
-        "roles" => $role,
-        "context_id" => $course->id,
-        "context_label" => $course->shortname,
-        "context_title" => $course->fullname,
-        "launch_presentation_locale" => $locale,
+        'resource_link_id' => $instance->id,
+        'resource_link_title' => $instance->name,
+        'resource_link_description' => $instance->intro,
+        'user_id' => $USER->id,
+        'roles' => $role,
+        'context_id' => $course->id,
+        'context_label' => $course->shortname,
+        'context_title' => $course->fullname,
+        'launch_presentation_locale' => $locale,
     );
 
     $placementsecret = $instance->servicesalt;
@@ -253,40 +254,32 @@ function lti_build_request($instance, $typeconfig, $course) {
     if ( isset($placementsecret) &&
          ( $typeconfig['acceptgrades'] == LTI_SETTING_ALWAYS ||
          ( $typeconfig['acceptgrades'] == LTI_SETTING_DELEGATE && $instance->instructorchoiceacceptgrades == LTI_SETTING_ALWAYS ) ) ) {
-        $requestparams["lis_result_sourcedid"] = $sourcedid;
+        $requestparams['lis_result_sourcedid'] = $sourcedid;
+
+        //Add outcome service URL
+        $serviceurl = new moodle_url('/mod/lti/service.php');
+        $serviceurl = $serviceurl->out();
 
-        $serviceurl = $CFG->wwwroot . '/mod/lti/service.php';
         if ($typeconfig['forcessl'] == '1') {
             $serviceurl = lti_ensure_url_is_https($serviceurl);
         }
 
-        $requestparams["ext_ims_lis_basic_outcome_url"] = $serviceurl;
+        $requestparams['lis_outcome_service_url'] = $serviceurl;
     }
 
-    /*if ( isset($placementsecret) &&
-         ( $typeconfig['allowroster'] == LTI_SETTING_ALWAYS ||
-         ( $typeconfig['allowroster'] == LTI_SETTING_DELEGATE && $instance->instructorchoiceallowroster == LTI_SETTING_ALWAYS ) ) ) {
-        $requestparams["ext_ims_lis_memberships_id"] = $sourcedid;
-        $requestparams["ext_ims_lis_memberships_url"] = $CFG->wwwroot.'/mod/lti/service.php';
-    }*/
-
     // Send user's name and email data if appropriate
     if ( $typeconfig['sendname'] == LTI_SETTING_ALWAYS ||
          ( $typeconfig['sendname'] == LTI_SETTING_DELEGATE && $instance->instructorchoicesendname == LTI_SETTING_ALWAYS ) ) {
-        $requestparams["lis_person_name_given"] =  $USER->firstname;
-        $requestparams["lis_person_name_family"] =  $USER->lastname;
-        $requestparams["lis_person_name_full"] =  $USER->firstname." ".$USER->lastname;
+        $requestparams['lis_person_name_given'] =  $USER->firstname;
+        $requestparams['lis_person_name_family'] =  $USER->lastname;
+        $requestparams['lis_person_name_full'] =  $USER->firstname." ".$USER->lastname;
     }
 
     if ( $typeconfig['sendemailaddr'] == LTI_SETTING_ALWAYS ||
          ( $typeconfig['sendemailaddr'] == LTI_SETTING_DELEGATE && $instance->instructorchoicesendemailaddr == LTI_SETTING_ALWAYS ) ) {
-        $requestparams["lis_person_contact_email_primary"] = $USER->email;
+        $requestparams['lis_person_contact_email_primary'] = $USER->email;
     }
 
-    //Add outcome service URL
-    $url = new moodle_url('/mod/lti/service.php');
-    $requestparams['lis_outcome_service_url'] = $url->out();
-
     // Concatenate the custom parameters from the administrator and the instructor
     // Instructor parameters are only taken into consideration if the administrator
     // has giver permission
@@ -314,20 +307,19 @@ function lti_build_request($instance, $typeconfig, $course) {
 
     // Make sure we let the tool know what LMS they are being called from
     $requestparams["ext_lms"] = "moodle-2";
+    $requestparams['tool_consumer_info_product_family_code'] = 'moodle';
+    $requestparams['tool_consumer_info_version'] = strval($CFG->version);
 
     // Add oauth_callback to be compliant with the 1.0A spec
-    $requestparams["oauth_callback"] = "about:blank";
+    $requestparams['oauth_callback'] = 'about:blank';
 
     //The submit button needs to be part of the signature as it gets posted with the form.
     //This needs to be here to support launching without javascript.
     $submittext = get_string('press_to_submit', 'lti');
-    $requestparams["ext_submit"] = $submittext;
+    $requestparams['ext_submit'] = $submittext;
 
-    $requestparams["lti_version"] = "LTI-1p0";
-    $requestparams["lti_message_type"] = "basic-lti-launch-request";
-    /* Suppress this for now - Chuck
-    if ( $orgdesc ) $requestparams["tool_consumer_instance_description"] = $orgdesc;
-    */
+    $requestparams['lti_version'] = 'LTI-1p0';
+    $requestparams['lti_message_type'] = 'basic-lti-launch-request';
 
     return $requestparams;
 }
@@ -431,7 +423,7 @@ function lti_split_custom_parameters($customstr) {
             continue;
         }
         $key = trim($textlib->substr($line, 0, $pos));
-        $val = trim($textlib->substr($line, $pos+1));
+        $val = trim($textlib->substr($line, $pos+1, strlen($line)));
         $key = lti_map_keyname($key);
         $retval['custom_'.$key] = $val;
     }
@@ -654,7 +646,7 @@ function lti_get_best_tool_by_url($url, $tools, $courseid = null) {
 
         //Prefer course tools over site tools
         if (!empty($courseid)) {
-            //Minus 25 points for not matching the course id (global tools)
+            //Minus 10 points for not matching the course id (global tools)
             if ($tool->course != $courseid) {
                 $tool->_matchscore -= 10;
             }
@@ -707,38 +699,6 @@ function lti_get_shared_secrets_by_key($key) {
     return $values;
 }
 
-/**
- * Prints the various configured tool types
- *
- */
-function lti_filter_print_types() {
-    global $CFG;
-
-    $types = lti_filter_get_types();
-    if (!empty($types)) {
-        echo '<ul>';
-        foreach ($types as $type) {
-            echo '<li>'.
-            $type->name.
-            '<span class="commands">'.
-            '<a class="editing_update" href="typessettings.php?action=update&amp;id='.$type->id.'&amp;sesskey='.sesskey().'" title="Update">'.
-            '<img class="iconsmall" alt="Update" src="'.$CFG->wwwroot.'/pix/t/edit.gif"/>'.
-            '</a>'.
-            '<a class="editing_delete" href="typessettings.php?action=delete&amp;id='.$type->id.'&amp;sesskey='.sesskey().'" title="Delete">'.
-            '<img class="iconsmall" alt="Delete" src="'.$CFG->wwwroot.'/pix/t/delete.gif"/>'.
-            '</a>'.
-            '</span>'.
-            '</li>';
-
-        }
-        echo '</ul>';
-    } else {
-        echo '<div class="message">';
-        echo get_string('notypes', 'lti');
-        echo '</div>';
-    }
-}
-
 /**
  * Delete a Basic LTI configuration
  *
@@ -832,6 +792,8 @@ function lti_get_type_type_config($id) {
     $basicltitype = $DB->get_record('lti_types', array('id' => $id));
     $config = lti_get_type_config($id);
 
+    $type = new stdClass();
+
     $type->lti_typename = $basicltitype->name;
 
     $type->typeid = $basicltitype->id;
@@ -900,7 +862,7 @@ function lti_get_type_type_config($id) {
     }
 
     if (isset($config['module_class_type'])) {
-            $type->lti_module_class_type = $config['module_class_type'];
+        $type->lti_module_class_type = $config['module_class_type'];
     }
 
     return $type;
@@ -1061,8 +1023,6 @@ function lti_sign_parameters($oldparms, $endpoint, $method, $oauthconsumerkey, $
  * @param $debug        Debug (true/false)
  */
 function lti_post_launch_html($newparms, $endpoint, $debug=false) {
-    //global $lastbasestring;
-
     $r = "<form action=\"".$endpoint."\" name=\"ltiLaunchForm\" id=\"ltiLaunchForm\" method=\"post\" encType=\"application/x-www-form-urlencoded\">\n";
 
     $submittext = $newparms['ext_submit'];
@@ -1167,8 +1127,8 @@ function lti_get_launch_container($lti, $toolconfig) {
 }
 
 function lti_request_is_using_ssl() {
-    global $ME;
-    return (stripos($ME, 'https://') === 0);
+    global $FULLME;
+    return (stripos($FULLME, 'https://') === 0);
 }
 
 function lti_ensure_url_is_https($url) {
index 46b8718..117dbf4 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * MRTODO: Brief description of this file
+ * Javascript extensions for the External Tool activity editor.
  *
  * @package    mod
  * @subpackage lti
- * @copyright  2011 onwards MRTODO
+ * @copyright  Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 (function(){
 
     M.mod_lti = M.mod_lti || {};
 
+    M.mod_lti.LTI_SETTING_NEVER = 0;
+    M.mod_lti.LTI_SETTING_ALWAYS = 1;
+    M.mod_lti.LTI_SETTING_DELEGATE = 2;
+
     M.mod_lti.editor = {
         init: function(yui3, settings){
             if(yui3){
@@ -36,6 +40,7 @@
             this.settings = Y.JSON.parse(settings);
 
             this.urlCache = {};
+            this.toolTypeCache = {};
 
             this.addOptGroups();
 
@@ -66,7 +71,7 @@
             textAreas.on('keyup', function(e){
                 clearTimeout(debounce);
 
-                //If no more changes within 2 seconds, look up the matching tool URL
+                // If no more changes within 2 seconds, look up the matching tool URL
                 debounce = setTimeout(function(){
                     updateToolMatches();
                 }, 2000);
@@ -77,6 +82,7 @@
 
         clearToolCache: function(){
             this.urlCache = {};
+            this.toolTypeCache = {};
         },
 
         updateAutomaticToolMatch: function(field){
 
             var url = toolurl.get('value');
 
-            //Hide the display if the url box is empty
+            // Hide the display if the url box is empty
             if(!url){
                 automatchToolDisplay.setStyle('display', 'none');
             } else {
                 automatchToolDisplay.setStyle('display', '');
             }
 
-            var selectedToolType = typeSelector.get('value');
+            var selectedToolType = parseInt(typeSelector.get('value'));
             var selectedOption = typeSelector.one('option[value="' + selectedToolType + '"]');
 
-            //A specific tool type is selected (not "auto")
+            // A specific tool type is selected (not "auto")
+            // We still need to check with the server to get privacy settings
             if(selectedToolType > 0){
-                //If the entered domain matches the domain of the tool configuration...
+                // If the entered domain matches the domain of the tool configuration...
                 var domainRegex = /(?:https?:\/\/)?(?:www\.)?([^\/]+)(?:\/|$)/i;
                 var match = domainRegex.exec(url);
                 if(match && match[1] && match[1].toLowerCase() === selectedOption.getAttribute('domain').toLowerCase()){
                     automatchToolDisplay.set('innerHTML',  '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.str.lti.using_tool_configuration + selectedOption.get('text'));
                 } else {
-                    //The entered URL does not match the domain of the tool configuration
+                    // The entered URL does not match the domain of the tool configuration
                     automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.warning_icon_url + '" />' + M.str.lti.domain_mismatch);
                 }
-
-                return;
             }
 
             var key = Y.one('#id_resourcekey');
             var secret = Y.one('#id_password');
 
-            //We don't care what tool type this tool is associated with if it's manually configured'
+            // Indicate the tool is manually configured
+            // We still check the Launch URL with the server as course/site tools may override privacy settings
             if(key.get('value') !== '' && secret.get('value') !== ''){
                 automatchToolDisplay.set('innerHTML',  '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.str.lti.custom_config);
+            }
+
+            var continuation = function(toolInfo){
+                self.updatePrivacySettings(toolInfo);
+
+                if(toolInfo.toolname){
+                    automatchToolDisplay.set('innerHTML',  '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.str.lti.using_tool_configuration + toolInfo.toolname);
+                } else if(!selectedToolType) {
+                    // Inform them custom configuration is in use
+                    if(key.get('value') === '' || secret.get('value') === ''){
+                        automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.warning_icon_url + '" />' + M.str.lti.tool_config_not_found);
+                    }
+                }
+            };
+
+            // Cache urls which have already been checked to increase performance
+            // Don't use URL cache if tool type manually selected
+            if(selectedToolType && self.toolTypeCache[selectedToolType]){
+                return continuation(self.toolTypeCache[selectedToolType]);
+            } else if(self.urlCache[url] && !selectedToolType){
+                return continuation(self.urlCache[url]);
+            } else if(!selectedToolType && !url) {
+                // No tool type or url set
+                return continuation({});
             } else {
-                var continuation = function(toolInfo){
-                    if(toolInfo.toolname){
-                        automatchToolDisplay.set('innerHTML',  '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.str.lti.using_tool_configuration + toolInfo.toolname);
-                    } else {
-                        //Inform them custom configuration is in use
-                        if(key.get('value') === '' || secret.get('value') === ''){
-                            automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.warning_icon_url + '" />' + M.str.lti.tool_config_not_found);
+                self.findToolByUrl(url, selectedToolType, function(toolInfo){
+                    if(toolInfo){
+                        // Cache the result based on whether the URL or tool type was used to look up the tool
+                        if(!selectedToolType){
+                            self.urlCache[url] = toolInfo;
+                        } else {
+                            self.toolTypeCache[selectedToolType] = toolInfo;
                         }
+
+                        continuation(toolInfo);
                     }
-                };
+                });
+            }
+        },
 
-                //Cache urls which have already been checked to increaes performance
-                if(self.urlCache[url]){
-                    continuation(self.urlCache[url]);
-                } else {
-                    self.findToolByUrl(url, function(toolInfo){
-                        self.urlCache[url] = toolInfo;
+        /**
+         * Updates display of privacy settings to show course / site tool configuration settings.
+         */
+        updatePrivacySettings: function(toolInfo){
+            if(!toolInfo || !toolInfo.toolid){
+                toolInfo = {
+                    sendname: M.mod_lti.LTI_SETTING_DELEGATE,
+                    sendemailaddr: M.mod_lti.LTI_SETTING_DELEGATE,
+                    acceptgrades: M.mod_lti.LTI_SETTING_DELEGATE
+                }
+            }
 
-                        continuation(toolInfo);
-                    });
+            var setting, control;
+
+            // Can't look these up by ID as they seem to get random IDs.
+            // Setting an id manually from mod_form made them turn into text boxes.
+            var privacyControls = {
+                sendname: Y.one('input[name=instructorchoicesendname]'),
+                sendemailaddr: Y.one('input[name=instructorchoicesendemailaddr]'),
+                acceptgrades: Y.one('input[name=instructorchoiceacceptgrades]')
+            };
+
+            // Store a copy of user entered privacy settings as we may overwrite them
+            if(!this.userPrivacySettings){
+                this.userPrivacySettings = {};
+            }
+
+            for(setting in privacyControls){
+                if(privacyControls.hasOwnProperty(setting)){
+                    control = privacyControls[setting];
+
+                    // Only store the value if it hasn't been forced by the editor
+                    if(!control.get('disabled')){
+                        this.userPrivacySettings[setting] = control.get('checked');
+                    }
+                }
+            }
+
+            // Update UI based on course / site tool configuration
+            for(setting in privacyControls){
+                if(privacyControls.hasOwnProperty(setting)){
+                    var settingValue = toolInfo[setting];
+                    control = privacyControls[setting];
+
+                    if(settingValue == M.mod_lti.LTI_SETTING_NEVER){
+                        control.set('disabled', true);
+                        control.set('checked', false);
+                        control.set('title', M.str.lti.forced_help);
+                    } else if(settingValue == M.mod_lti.LTI_SETTING_ALWAYS){
+                        control.set('disabled', true);
+                        control.set('checked', true);
+                        control.set('title', M.str.lti.forced_help);
+                    } else if(settingValue == M.mod_lti.LTI_SETTING_DELEGATE){
+                        control.set('disabled', false);
+
+                        // Get the value out of the stored copy
+                        control.set('checked', this.userPrivacySettings[setting]);
+                        control.set('title', '');
+                    }
                 }
             }
         },
             var typeSelector = Y.one('#id_typeid');
 
             if(typeSelector.one('option[courseTool=1]')){
-                //One ore more course tools exist
+                // One ore more course tools exist
 
                 var globalGroup = Y.Node.create('<optgroup />')
                                     .set('id', 'global_tool_group')
             var lti_edit_tool_type = Y.one('#lti_edit_tool_type');
             var lti_delete_tool_type = Y.one('#lti_delete_tool_type');
 
-            //Make the edit / delete icons look enabled / disabled.
-            //Does not work in older browsers, but alerts will catch those cases.
+            // Make the edit / delete icons look enabled / disabled.
+            // Does not work in older browsers, but alerts will catch those cases.
             if(this.getSelectedToolTypeOption().getAttribute('editable')){
                 lti_edit_tool_type.setStyle('opacity', '1');
                 lti_delete_tool_type.setStyle('opacity', '1');
                 typeSelector.append(option);
             }
 
-            //Adding the new tool may affect which tool gets matched automatically
+            // Adding the new tool may affect which tool gets matched automatically
             this.clearToolCache();
             this.updateAutomaticToolMatch(Y.one('#id_toolurl'));
             this.updateAutomaticToolMatch(Y.one('#id_securetoolurl'));
             option.set('text', toolType.name)
                   .set('domain', toolType.tooldomain);
 
-            //Editing the tool may affect which tool gets matched automatically
+            // Editing the tool may affect which tool gets matched automatically
             this.clearToolCache();
             this.updateAutomaticToolMatch(Y.one('#id_toolurl'));
             this.updateAutomaticToolMatch(Y.one('#id_securetoolurl'));
                     success: function(){
                         self.getSelectedToolTypeOption().remove();
 
-                        //Editing the tool may affect which tool gets matched automatically
+                        // Editing the tool may affect which tool gets matched automatically
                         self.clearToolCache();
                         self.updateAutomaticToolMatch(Y.one('#id_toolurl'));
                         self.updateAutomaticToolMatch(Y.one('#id_securetoolurl'));
             });
         },
 
-        findToolByUrl: function(url, callback){
+        findToolByUrl: function(url, toolId, callback){
             var self = this;
 
             Y.io(self.settings.ajax_url, {
                 data: {action: 'find_tool_config',
                         course: self.settings.courseId,
-                        toolurl: url
+                        toolurl: url,
+                        toolid: toolId || 0
                 },
 
                 on: {
index c588e39..ec1b5bf 100644 (file)
@@ -43,6 +43,7 @@
  * @author     Marc Alier
  * @author     Jordi Piguillem
  * @author     Nikolas Galanis
+ * @author     Chris Scribner
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
@@ -70,13 +71,22 @@ class mod_lti_mod_form extends moodleform_mod {
         $this->add_intro_editor(false, get_string('basicltiintro', 'lti'));
         $mform->setAdvanced('introeditor');
 
-        $mform->addElement('checkbox', 'showtitle', '&nbsp;', ' ' . get_string('display_name', 'lti'));
-        $mform->setAdvanced('showtitle');
-        $mform->addHelpButton('showtitle', 'display_name', 'lti');
+        // Display the label to the right of the checkbox so it&nb