Merge branch 'MDL-30192' of git://github.com/stronk7/moodle
authorSam Hemelryk <sam@moodle.com>
Wed, 23 Nov 2011 01:11:23 +0000 (14:11 +1300)
committerSam Hemelryk <sam@moodle.com>
Wed, 23 Nov 2011 01:11:23 +0000 (14:11 +1300)
118 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
backup/moodle2/restore_stepslib.php
blocks/section_links/block_section_links.php
filter/mediaplugin/filter.php
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
install.php
install/lang/az/langconfig.php
install/lang/cy/moodle.php
install/stringnames.txt
lang/en/admin.php
lang/en/install.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/dmllib.php
lib/editor/tinymce/lib.php
lib/editor/tinymce/settings.php
lib/installlib.php
lib/moodlelib.php
lib/outputrequirementslib.php
lib/plagiarismlib.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/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/wiki/pagelib.php
plagiarism/lib.php
question/format/xml/format.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
theme/anomaly/config.php
theme/anomaly/javascript/navigation.js [deleted file]
theme/anomaly/style/general.css
theme/base/style/core.css
theme/base/style/course.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
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 c9705a9..6486489 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';
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 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
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 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 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 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..8806653 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)) {
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 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 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..26fc43c 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();
 }
 
 /**
@@ -6213,7 +6211,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]]";
             }
         }
@@ -9867,9 +9875,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 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 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 looks better & matches rest of the form
+        $coursedesc = $mform->getElement('showdescription');
+        if(!empty($coursedesc)){
+            $coursedesc->setText(' ' . $coursedesc->getLabel());
+            $coursedesc->setLabel('&nbsp');
+        }
 
-        $mform->addElement('checkbox', 'showdescription', '&nbsp;', ' ' . get_string('display_description', 'lti'));
         $mform->setAdvanced('showdescription');
-        $mform->addHelpButton('showdescription', 'display_description', 'lti');
+
+        $mform->addElement('checkbox', 'showtitlelaunch', '&nbsp;', ' ' . get_string('display_name', 'lti'));
+        $mform->setAdvanced('showtitlelaunch');
+        $mform->addHelpButton('showtitlelaunch', 'display_name', 'lti');
+
+        $mform->addElement('checkbox', 'showdescriptionlaunch', '&nbsp;', ' ' . get_string('display_description', 'lti'));
+        $mform->setAdvanced('showdescriptionlaunch');
+        $mform->addHelpButton('showdescriptionlaunch', 'display_description', 'lti');
 
         // Tool settings
         $tooltypes = $mform->addElement('select', 'typeid', get_string('external_tool_type', 'lti'), array());
@@ -154,9 +164,9 @@ class mod_lti_mod_form extends moodleform_mod {
         $mform->setDefault('instructorchoiceacceptgrades', '1');
         $mform->addHelpButton('instructorchoiceacceptgrades', 'accept_grades', 'lti');
 
-        $mform->addElement('checkbox', 'instructorchoiceallowroster', '&nbsp;', ' ' . get_string('share_roster', 'lti'));
-        $mform->setDefault('instructorchoiceallowroster', '1');
-        $mform->addHelpButton('instructorchoiceallowroster', 'share_roster', 'lti');
+        //$mform->addElement('checkbox', 'instructorchoiceallowroster', '&nbsp;', ' ' . get_string('share_roster', 'lti'));
+        //$mform->setDefault('instructorchoiceallowroster', '1');
+        //$mform->addHelpButton('instructorchoiceallowroster', 'share_roster', 'lti');
 
         //-------------------------------------------------------------------------------
 
@@ -214,7 +224,8 @@ class mod_lti_mod_form extends moodleform_mod {
                 array('using_tool_configuration', 'lti'),
                 array('domain_mismatch', 'lti'),
                 array('custom_config', 'lti'),
-                array('tool_config_not_found', 'lti')
+                array('tool_config_not_found', 'lti'),
+                array('forced_help', 'lti')
             ),
         );
 
index 985f8d4..d5c8c99 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * MRTODO: Brief description of this file
+ * Submits a request to administrators to add a tool configuration for the requested site.
  *
  * @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 7f54286..7b2ed8c 100644 (file)
@@ -19,8 +19,9 @@
  *
  * @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 9099e10..f3c9aca 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * MRTODO: Brief description of this file
+ * LTI web service endpoints
  *
  * @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(dirname(__FILE__) . "/../../config.php");
@@ -61,7 +62,18 @@ foreach ($body->children() as $child) {
 
 switch ($messagetype) {
     case 'replaceResultRequest':
-        $parsed = lti_parse_grade_replace_message($xml);
+        try {
+            $parsed = lti_parse_grade_replace_message($xml);
+        } catch (Exception $e) {
+            $responsexml = lti_get_response_xml(
+                'failure',
+                $e->getMessage(),
+                uniqid(),
+                'replaceResultResponse');
+
+            echo $responsexml->asXML();
+            break;
+        }
 
         $ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
 
@@ -70,7 +82,7 @@ switch ($messagetype) {
         $gradestatus = lti_update_grade($ltiinstance, $parsed->userid, $parsed->launchid, $parsed->gradeval);
 
         $responsexml = lti_get_response_xml(
-                $gradestatus ? 'success' : 'error',
+                $gradestatus ? 'success' : 'failure',
                 'Grade replace response',
                 $parsed->messageid,
                 'replaceResultResponse'
@@ -94,14 +106,16 @@ switch ($messagetype) {
         $grade = lti_read_grade($ltiinstance, $parsed->userid);
 
         $responsexml = lti_get_response_xml(
-                isset($grade) ? 'success' : 'error',
+                isset($grade) ? 'success' : 'failure',
                 'Result read',
                 $parsed->messageid,
                 'readResultResponse'
         );
 
         $node = $responsexml->imsx_POXBody->readResultResponse;
-        $node->addChild('result')->addChild('resultScore')->addChild('textString', isset($grade) ? $grade : '');
+        $node = $node->addChild('result')->addChild('resultScore');
+        $node->addChild('language', 'en');
+        $node->addChild('textString', isset($grade) ? $grade : '');
 
         echo $responsexml->asXML();
 
@@ -117,7 +131,7 @@ switch ($messagetype) {
         $gradestatus = lti_delete_grade($ltiinstance, $parsed->userid);
 
         $responsexml = lti_get_response_xml(
-                $gradestatus ? 'success' : 'error',
+                $gradestatus ? 'success' : 'failure',
                 'Grade delete request',
                 $parsed->messageid,
                 'deleteResultResponse'
index 0dde18c..05acae5 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * MRTODO: Brief description of this file
+ * Utility code for LTI service handling.
  *
  * @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
  */
 
 defined('MOODLE_INTERNAL') || die;
@@ -66,7 +67,15 @@ function lti_parse_grade_replace_message($xml) {
     $resultjson = json_decode((string)$node);
 
     $node = $xml->imsx_POXBody->replaceResultRequest->resultRecord->result->resultScore->textString;
-    $grade = floatval((string)$node);
+
+    $score = (string) $node;
+    if ( ! is_numeric($score) ) {
+        throw new Exception('Score must be numeric');
+    }
+    $grade = floatval($score);
+    if ( $grade < 0.0 || $grade > 1.0 ) {
+        throw new Exception('Score not between 0.0 and 1.0');
+    }
 
     $parsed = new stdClass();
     $parsed->gradeval = $grade * 100;
@@ -163,6 +172,7 @@ function lti_read_grade($ltiinstance, $userid) {
     if (isset($grades) && isset($grades->items[0]) && is_array($grades->items[0]->grades)) {
         foreach ($grades->items[0]->grades as $agrade) {
             $grade = $agrade->grade;
+            $grade = $grade / 100.0;
             break;
         }
     }
index b90a31c..b7d4f2c 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 990dbf4..34a2674 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 470390b..0b252f2 100644 (file)
@@ -31,5 +31,9 @@
 /* Styles for admin */
 .path-admin-mod-lti .mform .fitem .fitemtitle { min-width:18em;padding-right:1em } /* Prevent setting titles from wrapping */
 
+/* Styles for instructor editing an external tool */
+
+.path-mod-lti .mform .fitem .fitemtitle { min-width:14em;padding-right:1em } /* Prevent setting titles from wrapping */
+
 /* Styles for instructor_edit_tool_type.php */
 #page-mod-lti-instructor_edit_tool_type .mform .fitem .fitemtitle { min-width:18em;padding-right:1em } /* Prevent setting titles from wrapping */
index 80e1b0d..9689138 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * MRTODO: Brief description of this file
+ * Javascript extensions for LTI submission viewer.
  *
  * @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
  */
 (function(){
     var Y;
index 0a2f856..dbf7418 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 9eb1e6d..a9d5760 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;
 
-$module->version   = 2011111400;;
+$module->version   = 2011111601;;
 $module->requires  = 2011110200;  // Requires this Moodle version
 $module->cron      = 0;
 $module->component = 'mod_lti';
index 0697df6..7f43092 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
  */
 
@@ -100,12 +101,12 @@ $PAGE->set_heading($course->fullname);
 // Print the page header
 echo $OUTPUT->header();
 
-if ($lti->showtitle) {
+if ($lti->showtitlelaunch) {
     // Print the main part of the page
     echo $OUTPUT->heading(format_string($lti->name));
 }
 
-if ($lti->showdescription && $lti->intro) {
+if ($lti->showdescriptionlaunch && $lti->intro) {
     echo $OUTPUT->box($lti->intro, 'generalbox description', 'intro');
 }
 
index de43634..c926c12 100644 (file)
@@ -252,7 +252,8 @@ function scorm_parse_aicc($scorm) {
             if ($ss = $DB->get_record('scorm_scoes', array('scorm'=>$scorm->id,
                                                            'identifier'=>$sco->identifier))) {
                 $id = $ss->id;
-                $DB->update_record('scorm_scoes', $sco);
+                $sco->id = $id;
+                $DB->update_record('scorm_scoes',$sco);
                 unset($oldscoes[$id]);
             } else {
                 $id = $DB->insert_record('scorm_scoes', $sco);
index aa36d43..ac21684 100644 (file)
@@ -1260,20 +1260,13 @@ class page_wiki_history extends page_wiki {
             } else {
 
                 $checked = $vcount - $offset;
-                $lastdate = '';
                 $rowclass = array();
 
                 foreach ($versions as $version) {
                     $user = wiki_get_user_info($version->userid);
                     $picture = $OUTPUT->user_picture($user, array('popup' => true));
                     $date = userdate($version->timecreated, get_string('strftimedate'));
-                    if ($date == $lastdate) {
-                        $date = '';
-                        $rowclass[] = '';
-                    } else {
-                        $lastdate = $date;
-                        $rowclass[] = 'wiki_histnewdate';
-                    }
+                    $rowclass[] = 'wiki_histnewdate';
                     $time = userdate($version->timecreated, get_string('strftimetime', 'langconfig'));
                     $versionid = wiki_get_version($version->id);
                     if ($versionid) {
index ea58f52..6f47313 100644 (file)
@@ -40,6 +40,19 @@ class plagiarism_plugin {
     public function get_links($linkarray) {
         return '';
     }
+    /**
+     * hook to allow plagiarism specific information to be returned unformatted
+     * @param int $cmid
+     * @param int $userid
+     * @param object $file moodle file object
+     * @return array containing at least:
+     *   - 'analyzed' - whether the file has been successfully analyzed
+     *   - 'score' - similarity score - ('' if not known)
+     *   - 'reporturl' - url of originality report - '' if unavailable
+     */
+    public function get_file_results($cmid, $userid, $file) {
+        return array('analyzed' => '', 'score' => '', 'reporturl' => '');
+    }
     /**
      * hook to add plagiarism specific settings to a module settings page
      * @param object $mform  - Moodle form
index 80ff9c1..b3f8858 100644 (file)
@@ -1446,10 +1446,10 @@ class qformat_xml extends qformat_default {
     }
 
     /**
-     * @param unknown_type $format a FORMAT_... constant.
+     * @param int $format a FORMAT_... constant.
      * @return string the attribute to add to an XML tag.
      */
-    protected function format($format) {
+    public function format($format) {
         return 'format="' . $this->get_format($format) . '"';
     }
 
index 936035c..9e6f930 100644 (file)
@@ -49,11 +49,11 @@ class qtype_truefalse_edit_form extends question_edit_form {
                 1 => get_string('true', 'qtype_truefalse')));
 
         $mform->addElement('editor', 'feedbacktrue',
-                get_string('feedbacktrue', 'qtype_truefalse'), null, $this->editoroptions);
+                get_string('feedbacktrue', 'qtype_truefalse'), array('rows' => 10), $this->editoroptions);
         $mform->setType('feedbacktrue', PARAM_RAW);
 
         $mform->addElement('editor', 'feedbackfalse',
-                get_string('feedbackfalse', 'qtype_truefalse'), null, $this->editoroptions);
+                get_string('feedbackfalse', 'qtype_truefalse'), array('rows' => 10), $this->editoroptions);
         $mform->setType('feedbackfalse', PARAM_RAW);
 
         $mform->addElement('header', 'multitriesheader',
index f127277..5a70587 100644 (file)
@@ -27,7 +27,7 @@ require_once('../../config.php');
 require_once($CFG->libdir.'/adminlib.php');
 require_once($CFG->dirroot.'/backup/lib.php');
 
-admin_externalpage_setup('reportbackups');
+admin_externalpage_setup('reportbackups', '', null, '', array('pagelayout'=>'report'));
 
 $table = new html_table;
 $table->head = array(
index 20fd5c6..8372727 100644 (file)
@@ -32,7 +32,7 @@ $perpage = optional_param('perpage', 30, PARAM_INT);    // how many per page
 $sort    = optional_param('sort', 'timemodified', PARAM_ALPHA);
 $dir     = optional_param('dir', 'DESC', PARAM_ALPHA);
 
-admin_externalpage_setup('reportconfiglog');
+admin_externalpage_setup('reportconfiglog', '', null, '', array('pagelayout'=>'report'));
 echo $OUTPUT->header();
 
 echo $OUTPUT->heading(get_string('configlog', 'report_configlog'));
index 04faf7c..9fe5c13 100644 (file)
@@ -39,7 +39,7 @@ if (empty($CFG->enablestats)) {
     }
 }
 
-admin_externalpage_setup('reportcourseoverview');
+admin_externalpage_setup('reportcourseoverview', '', null, '', array('pagelayout'=>'report'));
 echo $OUTPUT->header();
 
 $course = get_site();
index d7cd8f6..08434eb 100644 (file)
@@ -186,7 +186,7 @@ if (!empty($chooselog)) {
 
 } else {
     if ($hostid != $CFG->mnet_localhost_id || $course->id == SITEID) {
-        admin_externalpage_setup('reportlog');
+        admin_externalpage_setup('reportlog', '', null, '', array('pagelayout'=>'report'));
         echo $OUTPUT->header();
     } else {
         $PAGE->set_title($course->shortname .': '. $strlogs);
index ba9fc30..57ae10f 100644 (file)
@@ -72,7 +72,7 @@ if ($inpopup) {
 
 
 if ($course->id == SITEID) {
-    admin_externalpage_setup('reportloglive');
+    admin_externalpage_setup('reportloglive', '', null, '', array('pagelayout'=>'report'));
     echo $OUTPUT->header();
 
 } else {
index 0273c83..9f8a3f7 100644 (file)
@@ -31,7 +31,7 @@ require_once($CFG->libdir.'/questionlib.php');
 $requestedqtype = optional_param('qtype', '', PARAM_PLUGIN);
 
 // Print the header & check permissions.
-admin_externalpage_setup('reportquestioninstances');
+admin_externalpage_setup('reportquestioninstances', '', null, '', array('pagelayout'=>'report'));
 echo $OUTPUT->header();
 
 // Log.
index 863db1c..339ddaa 100644 (file)
@@ -45,7 +45,7 @@ raise_memory_limit(MEMORY_EXTRA);
 @set_time_limit(0);
 
 // Print the header.
-admin_externalpage_setup('reportsecurity');
+admin_externalpage_setup('reportsecurity', '', null, '', array('pagelayout'=>'report'));
 echo $OUTPUT->header();
 
 echo $OUTPUT->heading(get_string('pluginname', 'report_security'));
index 9675fdc..8238cde 100644 (file)
@@ -71,7 +71,7 @@ add_to_log($course->id, "course", "report stats", "report/stats/index.php?course
 stats_check_uptodate($course->id);
 
 if ($course->id == SITEID) {
-    admin_externalpage_setup('reportstats');
+    admin_externalpage_setup('reportstats', '', null, '', array('pagelayout'=>'report'));
     echo $OUTPUT->header();
 } else {
     $strreports = get_string("reports");
index 6f9abb0..644d13d 100644 (file)
@@ -9,11 +9,27 @@ API changes:
 * new support for report settings
 
 
-How to migrate old admin reports:
-# copy all files to new /report/yourplugin/ location
+How to migrate existing admin reports:
+# move all files to new /report/yourplugin/ location
 # if settings.php exists add $settings=null;
 # if settings.php does not exist create it and link the report, index.php is not linked automatically any more
 # update require('../../config.php'); - remove one ../
+# update all others includes and requires
 # update all links to report pages by removing /admin/ or /$CFG->admin/
-# add language pack with at least pluginname string
+# add language pack with at least 'pluginname' string
+# update CSS selectors
 
+How to migrate existing course reports (optional):
+# move all files to new /report/yourplugin/ location
+# update require('../../config.php'); - remove one ../
+# update all others includes and requires
+# update all links to report pages by removing /course/ part
+# update all language strings (use 'report_yourplugin' instead of 'coursereport_yourplugin') - use AMOS hints in commit message
+# update all capability names
+# grep the plugin codebase and look for any remaining 'coursereport' occurrences
+# add new navigation hooks in lib.php - ex: report_stats_extend_navigation_course(), report_stats_extend_navigation_user()
+# add new page types in lib.php
+# create db/install.php migration script - delete old settings and capabilities (see converted plugins for examples)
+# update CSS selectors
+
+See http://docs.moodle.org/dev/General_report_plugins for more details and explanation.
index 664985e..a83a595 100644 (file)
@@ -124,7 +124,7 @@ $THEME->layouts = array(
 );
 
 $THEME->rendererfactory = 'theme_overridden_renderer_factory';
+
 $THEME->enable_dock = true;
-//$THEME->javascripts_footer = array('navigation');
 
 $THEME->editor_sheets = array('editor');
diff --git a/theme/anomaly/javascript/navigation.js b/theme/anomaly/javascript/navigation.js
deleted file mode 100644 (file)
index bb52d72..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/**
- * Customises the dock for the anomaly theme and does some other cool stuff
- */
-function customise_dock_for_theme() {
-    // If we don't have M.core_dock or Y then bail
-    if (!M.core_dock) {
-        return false;
-    }
-    // Change the defautl remove all icon to work with our black dock
-    M.core_dock.cfg.display.removeallicon = M.util.image_url('dock_removeall', 'theme');
-
-    // On draw completed add the ability to move the dock to from the left to the right
-    M.core_dock.on('dock:drawcompleted', anomaly.dock.enable_side_switching, anomaly.dock);
-    // When the dock is first drawn check to see if it should be moved
-    M.core_dock.on('dock:drawstarted', anomaly.dock.check_initial_position, anomaly.dock);
-    // Corrects the panel x position for the theme
-    M.core_dock.on('dock:itemadded', function(item) {
-        item.on('dockeditem:showstart', anomaly.dock.correct_panel_x_position, anomaly.dock, item);
-        item.on('dockeditem:resizecomplete', anomaly.dock.correct_panel_x_position, anomaly.dock, item);
-    });
-
-    // Override the default fix_title_orientation method with our anomaly method
-    // this will use SVG and rotate the text if possible.
-    M.core_dock.genericblock.prototype.fix_title_orientation = anomaly.dock.fix_title_orientation;
-    M.core_dock.genericblock.prototype.resize_block_space = anomaly.dock.resize_block_space;
-    return true;
-}
-
-var anomaly = (function(){
-    return {
-        namespaces : {
-            svg : 'http://www.w3.org/2000/svg'
-        },
-        dock : {
-            enable_side_switching : function() {
-                var movedock = M.core_dock.Y.Node.create('<img src="'+M.util.image_url('movedock', 'theme')+'" />');
-                var c = M.core_dock.node.one('.controls');
-                c.insertBefore(M.core_dock.Y.Node.create('<br />'), c.one('img'));
-                c.insertBefore(movedock, c.one('br'));
-                movedock.on('click', this.switch_dock_side);
-            },
-            correct_panel_x_position : function(item) {
-                var dockoffset = M.core_dock.Y.one('#dock_item_'+item.id+'_title').get('offsetWidth');
-                var panelwidth = M.core_dock.Y.one(item.panel.body).get('offsetWidth');
-                var screenwidth = parseInt(M.core_dock.Y.get(document.body).get('winWidth'));
-                switch (M.core_dock.cfg.position) {
-                    case 'left':
-                        item.panel.cfg.setProperty('x', dockoffset);
-                        break;
-                    case 'right':
-                        item.panel.cfg.setProperty('x', (screenwidth-panelwidth-dockoffset-5));
-                        break;
-                }
-            },
-            switch_dock_side : function () {
-                var oldorientation = M.core_dock.cfg.orientation;
-                var oldclass = M.core_dock.cfg.css.dock+'_'+M.core_dock.cfg.position+'_'+oldorientation;
-                switch (M.core_dock.cfg.position) {
-                    case 'right':
-                        M.core_dock.cfg.position = 'left';
-                        M.core_dock.cfg.orientation = 'vertical';
-                        break;
-                    case 'left':
-                        M.core_dock.cfg.position = 'right';
-                        M.core_dock.cfg.orientation = 'vertical';
-                        break;
-                }
-                var newclass = M.core_dock.cfg.css.dock+'_'+M.core_dock.cfg.position+'_'+M.core_dock.cfg.orientation;
-                M.core_dock.node.replaceClass(oldclass, newclass);
-                M.core_dock.Y.Cookie.set('dock_position', M.core_dock.cfg.position);
-            },
-            check_initial_position : function () {
-                var cookieposition = M.core_dock.Y.Cookie.get('dock_position');
-                if (cookieposition && cookieposition != 'null' && cookieposition !== M.core_dock.cfg.position) {
-                    var oldclass = M.core_dock.cfg.css.dock+'_'+M.core_dock.cfg.position+'_'+M.core_dock.cfg.orientation;
-                    M.core_dock.cfg.position = cookieposition;
-                    if (M.core_dock.node) {
-                        var newclass = M.core_dock.cfg.css.dock+'_'+M.core_dock.cfg.position+'_'+M.core_dock.cfg.orientation;
-                        M.core_dock.node.replaceClass(oldclass, newclass);
-                    }
-                }
-            },
-            fix_title_orientation : function (node) {
-                if (M.core_dock.cfg.orientation == 'vertical') {
-                    return anomaly.transform.make_vertical_text(node);
-                }
-                return node;
-            },
-            resize_block_space : function (node) {
-                var blockregions = {
-                    pre: {hasblocks:true,c:'side-pre-only'},
-                    post: {hasblocks:true,c:'side-post-only'},
-                    noblocksc:'noblocks'
-                };
-                M.core_dock.Y.all('div.block-region').each(function(blockregion){
-                    if (blockregion.hasClass('side-pre') && blockregion.all('.block').size() == 0) {
-                        blockregions.pre.hasblocks = false;
-                    } else if (blockregion.hasClass('side-post') && blockregion.all('.block').size() == 0) {
-                        blockregions.post.hasblocks = false;
-                    }
-                });
-                if (blockregions.pre.hasblocks && blockregions.post.hasblocks) {
-                    // No classes required both regions have blocks
-                    M.core_dock.Y.one(document.body).removeClass(blockregions.pre.c).removeClass(blockregions.post.c).removeClass(blockregions.noblocksc);
-                } else if (blockregions.pre.hasblocks) {
-                    // side-pre-only required: remove any other classes
-                    M.core_dock.Y.one(document.body).addClass(blockregions.pre.c).removeClass(blockregions.post.c).removeClass(blockregions.noblocksc);
-                } else if (blockregions.post.hasblocks) {
-                    // side-post-only required: remove any other classes
-                    M.core_dock.Y.one(document.body).removeClass(blockregions.pre.c).addClass(blockregions.post.c).removeClass(blockregions.noblocksc);
-                } else {
-                    // All blocks have been docked: add noblocks remove side-xxx-only's if set
-                    M.core_dock.Y.one(document.body).removeClass(blockregions.pre.c).removeClass(blockregions.post.c).addClass(blockregions.noblocksc);
-                }
-                return '200px';
-            }
-        },
-        transform : {
-            make_vertical_text : function(node) {
-
-                if (YAHOO.env.ua.ie > 0) {
-                    if (YAHOO.env.ua.ie > 7) {
-                        node.setAttribute('style', 'writing-mode: tb-rl; filter: flipV flipH;');
-                    } else {
-                        node.innerHTML = node.innerHTML.replace(/(.)/g, "$1<br />");
-                    }
-                    return node;
-                }
-
-                var test = M.core_dock.Y.Node.create('<div><span>'+node.firstChild.nodeValue+'</span></div>');
-                M.core_dock.Y.one(document.body).append(test);
-                var height = test.one('span').get('offsetWidth');
-                test.remove();
-
-                var txt = document.createElementNS(anomaly.namespaces.svg, 'text');
-                txt.setAttribute('x', '0');
-                txt.setAttribute('y', '0');
-                txt.setAttribute('transform','rotate(90, 5, 5)');
-                txt.appendChild(document.createTextNode(node.firstChild.nodeValue));
-
-                var svg = document.createElementNS(anomaly.namespaces.svg, 'svg');
-                svg.setAttribute('version', '1.1');
-                svg.setAttribute('height', height);
-                svg.setAttribute('width', 30);
-                svg.appendChild(txt);
-
-                var div = document.createElement(node.nodeName);
-                div.appendChild(svg);
-
-                return div;
-            }
-        }
-    }
-})();
\ No newline at end of file
index 188bce1..df2b251 100644 (file)
@@ -125,7 +125,7 @@ html, body {
 }
 
 #page-report-outline-user .section {
-    border:i 1px solid #DDD;
+    border: 1px solid #DDD;
     margin: 0 5% 1.5em 5%;
 }
 
index 3abeb99..4ff7ea4 100644 (file)
@@ -77,7 +77,7 @@ a.autolink.glossary:hover {cursor: help;}
 img.resize {height: 1em;width: 1em;}
 .block img.resize,
 .breadcrumb img.resize {height: 0.9em;width: 0.8em;}
-img.icon {height:16px;vertical-align:middle;width:16px;}
+img.icon {height:16px;vertical-align:middle;width:16px;padding-right:4px;}
 img.iconsmall {height:11px;margin-right:1px;vertical-align:middle;width:11px;}
 img.iconhelp {height:17px;margin-right:4px;vertical-align:middle;width:17px;}
 img.icontoggle {height:17px;vertical-align:middle;width:50px;}
@@ -652,6 +652,7 @@ body.tag .managelink {padding: 5px;}
 #page-enrol-users .enrol_user_buttons .enrolusersbutton div,
 #page-enrol-users .enrol_user_buttons .enrolusersbutton form {display:inline;}
 #page-enrol-users .enrol_user_buttons .enrolusersbutton input {padding-left:6px;padding-right:6px;}
+#page-enrol-users.dir-rtl .col_userdetails .subfield_picture {float: right;}
 
 /**
 * Overide for RTL layout
@@ -664,6 +665,9 @@ body.tag .managelink {padding: 5px;}
 .dir-rtl .mform .fitem .fitemtitle {float:right;}
 .dir-rtl .loginbox .loginform .form-label {float:right;text-align:left;}
 .dir-rtl .loginbox .loginform .form-input {text-align: right;}
+.dir-rtl .yui3-menu-hidden {left: 0px;}
+#page-admin-roles-define.dir-rtl #rolesform .felement {margin-right: 180px;}
+#page-message-edit.dir-rtl table.generaltable th.c0 {text-align: right;}
 
 /**
  * Backup
@@ -872,4 +876,4 @@ ol li,
 
 #page-admin-setting-enrolsettingsflatfile.dir-rtl .informationbox {direction: ltr;text-align: left;}
 
-#page-admin-grade-edit-scale-edit.dir-rtl .error input#id_name {margin-right: 170px;}
\ No newline at end of file
+#page-admin-grade-edit-scale-edit.dir-rtl .error input#id_name {margin-right: 170px;}
index 37f9792..3062ac6 100644 (file)
@@ -27,7 +27,7 @@
 .path-course-view li.activity form.togglecompletion .ajaxworking {position:absolute;top:0; left:20px;width: 20px; height: 20px;background: url([[pix:i/ajaxloader]]) no-repeat;}
 .dir-rtl.path-course-view li.activity {margin-right:0px;margin-left:20px;}
 .dir-rtl.path-course-view li.activity form.togglecompletion,
-.dir-rtl.path-course-view li.activity span.autocompletion {right:auto;right:-20px;}
+.dir-rtl.path-course-view li.activity span.autocompletion {right:auto;left:-20px;}
 
 .section img.movetarget {height:16px;width:80px;}
 
index e00f559..63acfaf 100644 (file)
  */
 
 
+// disable moodle specific debug messages and any errors in output,
+// comment out when debugging or better look into error log!
+define('NO_DEBUG_DISPLAY', true);
+
 // we need just the values from config.php and minlib.php
 define('ABORT_AFTER_CONFIG', true);
 require('../config.php'); // this stops immediately at the beginning of lib/setup.php
@@ -115,6 +119,9 @@ if ($rev > -1) {
     $pathinfo = pathinfo($imagefile);
     $cacheimage = "$candidatelocation/$image.".$pathinfo['extension'];
     if (!file_exists($cacheimage)) {
+        // note: cache reset might have purged our cache dir structure,
+        //       make sure we do not use stale file stat cache in the next check_dir_exists()
+        clearstatcache();
         check_dir_exists(dirname($cacheimage));
         copy($imagefile, $cacheimage);
     }
index 6e8a79d..3ebba8b 100644 (file)
  */
 
 
+// disable moodle specific debug messages and any errors in output,
+// comment out when debugging or better look into error log!
+define('NO_DEBUG_DISPLAY', true);
+
 // we need just the values from config.php and minlib.php
 define('ABORT_AFTER_CONFIG', true);
 require('../config.php'); // this stops immediately at the beginning of lib/setup.php
@@ -77,6 +81,9 @@ require_once('Minify.php');
 $theme = theme_config::load($themename);
 
 if ($rev > -1) {
+    // note: cache reset might have purged our cache dir structure,
+    //       make sure we do not use stale file stat cache in the next check_dir_exists()
+    clearstatcache();
     check_dir_exists(dirname($candidate));
     $fp = fopen($candidate, 'w');
     fwrite($fp, minify($theme->javascript_files($type)));
index 9b3d0cd..77465ef 100644 (file)
@@ -27,7 +27,7 @@ $string['choosereadme'] = '<div class="clearfix"><div class="theme_screenshot"><
 $string['colourswatch'] = 'Mobile theme color swatch';
 $string['colourswatch_desc'] = 'Toggle the color swatch of the theme between grey and light/blue.';
 $string['grey'] = 'Grey';
-$string['light'] = 'Light';
+$string['light'] = 'Light/Blue';
 $string['mtoggle'] = 'Toggle 2nd Column';
 $string['no'] = 'No';
 $string['pluginname'] = 'MyMobile';
index 5cdaa76..88f543b 100644 (file)
@@ -26,7 +26,7 @@
 $string['pluginname'] = 'Sky High';
 $string['region-side-post'] = 'Right';
 $string['region-side-pre'] = 'Left';
-$string['choosereadme'] = '<div class="clearfix"><div class="theme_screenshot"><h2>Sky High</h2><img src="sky_high/pix/screenshot.png" /><h3>Theme Discussion Forum:</h3><p><a href="http://moodle.org/mod/forum/view.php?id=46">http://moodle.org/mod/forum/view.php?id=46</a></p><h3>Theme Credits</h3><p><a href="http://docs.moodle.org/en/Theme_credits">http://docs.moodle.org/en/Theme_credits</a></p><h3>Theme Documentation:</h3><p><a href="http://docs.moodle.org/en/Themes">http://docs.moodle.org/en/Themes</a></p><h3>Report a bug:</h3><p><a href="http://tracker.moodle.org">http://tracker.moodle.org</a></p></div><div class="theme_description"><h2>About</h2><p>Serenity is a fluid-width, three-column theme for Moodle 2.0.<h2>Tweaks</h2><p>This theme is built upon both Base and Canvas, two parent themes included in the Moodle core. If you want to modify this theme, we recommend that you first duplicate it then rename it before making your changes. This will prevent your customized theme from being overwritten by future Moodle upgrades, and you\'ll still have the original files if you make a mess. More information on modifying themes can be found in the <a href="http://docs.moodle.org/en/Theme">MoodleDocs</a>.</p><h2>Credits</h2>  <p>This theme was designed by Julian Ridden (julian@moodle.com.au). It was coded and is maintained by John Stabinger of NewSchool Learning (contact@newschoollearning.com). </p><h2>License</h2><p>This, and all other themes included in the Moodle core, are licensed under the <a href="http://www.gnu.org/licenses/gpl.html">GNU General Public License</a>.</div></div>';
+$string['choosereadme'] = '<div class="clearfix"><div class="theme_screenshot"><h2>Sky High</h2><img src="sky_high/pix/screenshot.png" /><h3>Theme Discussion Forum:</h3><p><a href="http://moodle.org/mod/forum/view.php?id=46">http://moodle.org/mod/forum/view.php?id=46</a></p><h3>Theme Credits</h3><p><a href="http://docs.moodle.org/en/Theme_credits">http://docs.moodle.org/en/Theme_credits</a></p><h3>Theme Documentation:</h3><p><a href="http://docs.moodle.org/en/Themes">http://docs.moodle.org/en/Themes</a></p><h3>Report a bug:</h3><p><a href="http://tracker.moodle.org">http://tracker.moodle.org</a></p></div><div class="theme_description"><h2>About</h2><p>Sky High is a fluid-width, three-column (blog style) theme for Moodle 2.0.<h2>Tweaks</h2><p>This theme is built upon both Base and Canvas, two parent themes included in the Moodle core. If you want to modify this theme, we recommend that you first duplicate it then rename it before making your changes. This will prevent your customized theme from being overwritten by future Moodle upgrades, and you\'ll still have the original files if you make a mess. More information on modifying themes can be found in the <a href="http://docs.moodle.org/en/Theme">MoodleDocs</a>.</p><h2>Credits</h2>     <p>This theme was designed by Julian Ridden (julian@moodle.com.au). It was coded and is maintained by John Stabinger of NewSchool Learning (contact@newschoollearning.com). </p><h2>License</h2><p>This, and all other themes included in the Moodle core, are licensed under the <a href="http://www.gnu.org/licenses/gpl.html">GNU General Public License</a>.</div></div>';
 $string['configtitle'] = 'Sky High settings';
 $string['customcss'] = 'Custom CSS';
 $string['customcssdesc'] = 'Any CSS you enter here will be added to every page allowing your to easily customise this theme.';
index 32677ad..568791c 100644 (file)
@@ -51,7 +51,8 @@ table.minicalendar td,
   border-radius: 3px;
 }
 
-.noticebox {
+.noticebox,
+.gradingform_rubric_editform .status {
   -moz-border-radius: 5px;
   -webkit-border-radius: 5px;
   border-radius: 5px;
index 6fcde59..67385e3 100644 (file)
  */
 
 
+// disable moodle specific debug messages and any errors in output,
+// comment out when debugging or better look into error log!
+define('NO_DEBUG_DISPLAY', true);
+
 // we need just the values from config.php and minlib.php
 define('ABORT_AFTER_CONFIG', true);
 require('../config.php'); // this stops immediately at the beginning of lib/setup.php
@@ -113,6 +117,9 @@ send_cached_css($candidatesheet, $rev);
 
 function store_css(theme_config $theme, $csspath, $cssfiles) {
     $css = $theme->post_process(minify($cssfiles));
+    // note: cache reset might have purged our cache dir structure,
+    //       make sure we do not use stale file stat cache in the next check_dir_exists()
+    clearstatcache();
     check_dir_exists(dirname($csspath));
     $fp = fopen($csspath, 'w');
     fwrite($fp, $css);
index 9642f62..2719a1a 100644 (file)
 defined('MOODLE_INTERNAL') || die();
 
 
-$version  = 2011111800.00;              // YYYYMMDD      = weekly release date of this DEV branch
+$version  = 2011112100.00;              // YYYYMMDD      = weekly release date of this DEV branch
                                         //         RR    = release increments - 00 in DEV branches
                                         //           .XX = incremental changes
 
-$release  = '2.2beta (Build: 20111118)';// Human-friendly version name
+$release  = '2.2beta+ (Build: 20111121)';// Human-friendly version name
 
 $maturity = MATURITY_BETA;              // this version's maturity level
index f1c0e32..0c86f3c 100644 (file)
@@ -38,8 +38,7 @@ class core_webservice_external extends external_api {
                 new external_value(
                     PARAM_ALPHANUMEXT,
                     'service shortname'),
-                    'service shortnames - by default, if the list is empty and mobile web services are enabled,
-                    we return the mobile service functions',
+                    'DEPRECATED PARAMETER - it was a design error in the original implementation. It is ignored now. (parameter kept for backward compatibility)',
                     VALUE_DEFAULT,
                     array()
                 ),
@@ -51,11 +50,11 @@ class core_webservice_external extends external_api {
      * Return user information including profile picture + basic site information
      * Note:
      * - no capability checking because we return just known information by logged user
-     * @param array $serviceshortnames of service shortnames - the functions of these services will be returned
+     * @param array $serviceshortnames - DEPRECATED PARAMETER - values will be ignored - it was an original design error, we keep for backward compatibility.
      * @return array
      */
     public function get_site_info($serviceshortnames = array()) {
-        global $USER, $SITE, $CFG;
+        global $USER, $SITE, $CFG, $DB;
 
         $params = self::validate_parameters(self::get_site_info_parameters(),
                       array('serviceshortnames'=>$serviceshortnames));
@@ -63,20 +62,47 @@ class core_webservice_external extends external_api {
         $profileimageurl = moodle_url::make_pluginfile_url(
                 get_context_instance(CONTEXT_USER, $USER->id)->id, 'user', 'icon', NULL, '/', 'f1');
 
-        require_once($CFG->dirroot . "/webservice/lib.php");
-        $webservice = new webservice();
+        //site information
+        $siteinfo =  array(
+            'sitename' => $SITE->fullname,
+            'siteurl' => $CFG->wwwroot,
+            'username' => $USER->username,
+            'firstname' => $USER->firstname,
+            'lastname' => $USER->lastname,
+            'fullname' => fullname($USER),
+            'userid' => $USER->id,
+            'userpictureurl' => $profileimageurl->out(false)
+        );
 
-        //If no service listed always return the mobile one by default
-        if (empty($params['serviceshortnames']) and $CFG->enablewebservices) {
-           $mobileservice = $webservice->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
-           if ($mobileservice->enabled) {
-               $params['serviceshortnames'] = array(MOODLE_OFFICIAL_MOBILE_SERVICE); //return mobile service by default
-           }
+        //Retrieve the service and functions from the web service linked to the token
+        //If you call this function directly from external (not a web service call),
+        //then it will still return site info without information about a service
+        //Note: wsusername/wspassword ws authentication is not supported.
+        $functions = array();
+        if ($CFG->enablewebservices) { //no need to check token if web service are disabled and not a ws call
+            $token = optional_param('wstoken', '', PARAM_ALPHANUM);
+
+            if (!empty($token)) { //no need to run if not a ws call
+                //retrieve service shortname
+                $servicesql = 'SELECT s.*
+                               FROM {external_services} s, {external_tokens} t
+                               WHERE t.externalserviceid = s.id AND token = ? AND t.userid = ? AND s.enabled = 1';
+                $service = $DB->get_record_sql($servicesql, array($token, $USER->id));
+
+                $siteinfo['downloadfiles'] = $service->downloadfiles;
+
+                if (!empty($service)) {
+                    //retrieve the functions
+                    $functionssql = "SELECT f.*
+                            FROM {external_functions} f, {external_services_functions} sf
+                            WHERE f.name = sf.functionname AND sf.externalserviceid = ?";
+                    $functions = $DB->get_records_sql($functionssql, array($service->id));
+                } else {
+                    throw new coding_exception('No service found in get_site_info: something is buggy, it should have fail at the ws server authentication layer.');
+                }
+            }
         }
 
-        //retrieve the functions related to the services
-        $functions = $webservice->get_external_functions_by_enabled_services($params['serviceshortnames']);
-
         //built up the returned values of the list of functions
         $componentversions = array();
         $avalaiblefunctions = array();
@@ -106,17 +132,9 @@ class core_webservice_external extends external_api {
             $avalaiblefunctions[] = $functioninfo;
         }
 
-        return array(
-            'sitename' => $SITE->fullname,
-            'siteurl' => $CFG->wwwroot,
-            'username' => $USER->username,
-            'firstname' => $USER->firstname,
-            'lastname' => $USER->lastname,
-            'fullname' => fullname($USER),
-            'userid' => $USER->id,
-            'userpictureurl' => $profileimageurl->out(false),
-            'functions' => $avalaiblefunctions
-        );
+        $siteinfo['functions'] = $avalaiblefunctions;
+
+        return $siteinfo;
     }
 
     /**
@@ -141,6 +159,7 @@ class core_webservice_external extends external_api {
                             'version' => new external_value(PARAM_FLOAT, 'The version number of moodle site/local plugin linked to the function')
                         ), 'functions that are available')
                     ),
+                'downloadfiles'  => new external_value(PARAM_INT, '1 if users are allowed to download files, 0 if not', VALUE_OPTIONAL),
             )
         );
     }
index bf6f85f..86a3f4f 100644 (file)
@@ -645,7 +645,7 @@ abstract class webservice_server implements webservice_server_interface {
                 throw new webservice_access_exception(get_string('wrongusernamepassword', 'webservice'));
             }
 
-            $user = $DB->get_record('user', array('username'=>$this->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'deleted'=>0), '*', MUST_EXIST);
+            $user = $DB->get_record('user', array('username'=>$this->username, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST);
 
         } else if ($this->authmethod == WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN){
             $user = $this->authenticate_by_token(EXTERNAL_TOKEN_PERMANENT);
@@ -653,6 +653,50 @@ abstract class webservice_server implements webservice_server_interface {
             $user = $this->authenticate_by_token(EXTERNAL_TOKEN_EMBEDDED);
         }
 
+        //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 webservice_access_exception(get_string('sitemaintenance', 'admin'));
+        }
+
+        //only confirmed user should be able to call web service
+        if (!empty($user->deleted)) {
+            add_to_log(SITEID, '', '', '', get_string('wsaccessuserdeleted', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id);
+            throw new webservice_access_exception(get_string('wsaccessuserdeleted', 'webservice', $user->username));
+        }
+
+        //only confirmed user should be able to call web service
+        if (empty($user->confirmed)) {
+            add_to_log(SITEID, '', '', '', get_string('wsaccessuserunconfirmed', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id);
+            throw new webservice_access_exception(get_string('wsaccessuserunconfirmed', 'webservice', $user->username));
+        }
+
+        //check the user is suspended
+        if (!empty($user->suspended)) {
+            add_to_log(SITEID, '', '', '', get_string('wsaccessusersuspended', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id);
+            throw new webservice_access_exception(get_string('wsaccessusersuspended', 'webservice', $user->username));
+        }
+
+        //retrieve the authentication plugin if no previously done
+        if (empty($auth)) {
+          $auth  = get_auth_plugin($user->auth);
+        }
+
+        // check if credentials have expired
+        if (!empty($auth->config->expiration) and $auth->config->expiration == 1) {
+            $days2expire = $auth->password_expire($user->username);
+            if (intval($days2expire) < 0 ) {
+                add_to_log(SITEID, '', '', '', get_string('wsaccessuserexpired', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id);
+                throw new webservice_access_exception(get_string('wsaccessuserexpired', 'webservice', $user->username));
+            }
+        }
+
+        //check if the auth method is nologin (in this case refuse connection)
+        if ($user->auth=='nologin') {
+            add_to_log(SITEID, '', '', '', get_string('wsaccessusernologin', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id);
+            throw new webservice_access_exception(get_string('wsaccessusernologin', 'webservice', $user->username));
+        }
+
         // now fake user login, the session is completely empty too
         enrol_check_plugins($user);
         session_set_user($user);
@@ -694,7 +738,7 @@ abstract class webservice_server implements webservice_server_interface {
         $this->restricted_context = get_context_instance_by_id($token->contextid);
         $this->restricted_serviceid = $token->externalserviceid;
 
-        $user = $DB->get_record('user', array('id'=>$token->userid, 'deleted'=>0), '*', MUST_EXIST);
+        $user = $DB->get_record('user', array('id'=>$token->userid), '*', MUST_EXIST);
 
         // log token access
         $DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id));