Merge branch 'MDL-46232-master' of git://github.com/FMCorz/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Mon, 27 Jul 2015 07:53:59 +0000 (15:53 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Mon, 27 Jul 2015 07:53:59 +0000 (15:53 +0800)
56 files changed:
admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-debug.js
admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-min.js
admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search.js
admin/tool/capability/yui/src/search/js/search.js
availability/classes/info.php
availability/classes/multiple_messages.php [new file with mode: 0644]
availability/classes/tree.php
availability/condition/completion/tests/behat/conditional_bug.feature [new file with mode: 0644]
availability/renderer.php
availability/tests/tree_test.php
blocks/navigation/renderer.php
course/moodleform_mod.php
grade/grading/form/guide/js/guideeditor.js
grade/grading/form/guide/renderer.php
install/lang/bg/admin.php
install/lang/es_mx/langconfig.php
install/lang/mk/install.php
lang/en/moodle.php
lib/blocklib.php
lib/db/services.php
lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-debug.js
lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-min.js
lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector.js
lib/form/yui/src/dateselector/js/dateselector.js
lib/moodlelib.php
lib/tablelib.php
lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js
lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js
lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop.js
lib/yui/src/dragdrop/js/dragdrop.js
mod/assign/mod_form.php
mod/assign/settings.php
mod/book/styles.css
mod/forum/renderer.php
mod/glossary/view.php
mod/lesson/mod_form.php
mod/scorm/classes/external.php [new file with mode: 0644]
mod/scorm/db/services.php [new file with mode: 0644]
mod/scorm/lib.php
mod/scorm/locallib.php
mod/scorm/tests/externallib_test.php [new file with mode: 0644]
mod/scorm/tests/lib_test.php [new file with mode: 0644]
mod/scorm/version.php
mod/scorm/view.php
mod/upgrade.txt
mod/workshop/lang/en/workshop.php
mod/workshop/locallib.php
mod/workshop/renderer.php
mod/workshop/styles.css
mod/workshop/tests/behat/workshop_assessment.feature
mod/workshop/view.php
my/index.php
my/lib.php
tag/manage.php
theme/upgrade.txt
version.php

index 3beb4f7..ca1f505 100644 (file)
Binary files a/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-debug.js and b/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-debug.js differ
index 8b04092..7e1baef 100644 (file)
Binary files a/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-min.js and b/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-min.js differ
index 3beb4f7..ca1f505 100644 (file)
Binary files a/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search.js and b/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search.js differ
index 6fbaf67..785263a 100644 (file)
@@ -81,7 +81,6 @@ SEARCH.prototype = {
         div.append(label).append(this.input);
 
         this.select.insert(div, 'before');
-        this.select.one('option').setStyle('display', 'none');
 
         this.input.on('keyup', this.typed, this);
         this.select.on('change', this.validate, this);
index afd1d40..05d82fa 100644 (file)
@@ -713,11 +713,21 @@ abstract class info {
      * that we can guarantee does not happen from within building the modinfo
      * object.
      *
-     * @param string $info Info string
+     * @param \renderable|string $inforenderable Info string or renderable
      * @param int|\stdClass $courseorid
      * @return string Correctly formatted info string
      */
-    public static function format_info($info, $courseorid) {
+    public static function format_info($inforenderable, $courseorid) {
+        global $PAGE;
+
+        // Use renderer if required.
+        if (is_string($inforenderable)) {
+            $info = $inforenderable;
+        } else {
+            $renderer = $PAGE->get_renderer('core', 'availability');
+            $info = $renderer->render($inforenderable);
+        }
+
         // Don't waste time if there are no special tags.
         if (strpos($info, '<AVAILABILITY_') === false) {
             return $info;
diff --git a/availability/classes/multiple_messages.php b/availability/classes/multiple_messages.php
new file mode 100644 (file)
index 0000000..0ba70a7
--- /dev/null
@@ -0,0 +1,70 @@
+<?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/>.
+
+/**
+ * Represents multiple availability messages.
+ *
+ * These are messages like 'Not available until <date>'. This class includes
+ * multiple messages so that they can be rendered into a combined display, e.g.
+ * using bulleted lists.
+ *
+ * The tree structure of this object matches that of the availability
+ * restrictions.
+ *
+ * @package core_availability
+ * @copyright 2015 Andrew Nicols <andrew@nicols.co.uk>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Represents multiple availability messages.
+ *
+ * These are messages like 'Not available until <date>'. This class includes
+ * multiple messages so that they can be rendered into a combined display, e.g.
+ * using bulleted lists.
+ *
+ * The tree structure of this object matches that of the availability
+ * restrictions.
+ *
+ * @package core_availability
+ * @copyright 2015 Andrew Nicols <andrew@nicols.co.uk>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_availability_multiple_messages implements renderable {
+    /** @var bool True if this object represents the root of the tree */
+    public $root;
+    /** @var bool True if items use the AND operator (false = OR) */
+    public $andoperator;
+    /** @var bool True if this part of the tree is marked 'hide entirely' for non-matching users */
+    public $treehidden;
+    /** @var array Array of child items (may be string or this type) */
+    public $items;
+
+    /**
+     * Constructor.
+     *
+     * @param bool $root True if this object represents the root of the tree
+     * @param bool $andoperator True if items use the AND operator (false = OR)
+     * @param bool $treehidden True if this part of the tree is marked 'hide entirely' for non-matching users
+     * @param array $items Array of items (may be string or this type)
+     */
+    public function __construct($root, $andoperator, $treehidden, array $items) {
+        $this->root = $root;
+        $this->andoperator = $andoperator;
+        $this->treehidden = $treehidden;
+        $this->items = $items;
+    }
+}
index 8849320..ef15c58 100644 (file)
@@ -471,11 +471,10 @@ class tree extends tree_node {
      * @param result $result Result object if this is a student display, else null
      * @param bool $root True if this is the root item
      * @param bool $hidden Staff display; true if this tree has show=false (from parent)
+     * @return string|renderable Information to render
      */
     protected function get_full_information_recursive(
             $not, info $info, result $result = null, $root, $hidden = false) {
-        global $PAGE;
-
         // Get list of children - either full list, or those which are shown.
         $children = $this->children;
         $staff = true;
@@ -550,8 +549,7 @@ class tree extends tree_node {
         }
 
         // Format output for display.
-        $renderer = $PAGE->get_renderer('core', 'availability');
-        return $renderer->multiple_messages($root, $andoperator, $treehidden, $items);
+        return new \core_availability_multiple_messages($root, $andoperator, $treehidden, $items);
     }
 
     /**
diff --git a/availability/condition/completion/tests/behat/conditional_bug.feature b/availability/condition/completion/tests/behat/conditional_bug.feature
new file mode 100644 (file)
index 0000000..b7f6fec
--- /dev/null
@@ -0,0 +1,60 @@
+@availability @availability_completion
+Feature: Confirm that conditions on completion no longer cause a bug
+  In order to use completion conditions
+  As a teacher
+  I need it to not break when I set up certain conditions on some modules
+
+  Background:
+    Given the following "courses" exist:
+      | fullname | shortname | format |
+      | Course 1 | C1        | topics |
+    And the following "users" exist:
+      | username |
+      | teacher1 |
+    And the following "course enrolments" exist:
+      | user     | course | role           |
+      | teacher1 | C1     | editingteacher |
+    And the following config values are set as admin:
+      | enableavailability | 1 |
+      | enablecompletion   | 1 |
+
+  @javascript
+  Scenario: Multiple completion conditions on glossary
+    # Set up course.
+    Given I log in as "teacher1"
+    And I follow "Course 1"
+    And I navigate to "Edit settings" node in "Course administration"
+    And I expand all fieldsets
+    And I set the field "Enable completion tracking" to "Yes"
+    And I press "Save and display"
+    And I turn editing mode on
+
+    # Add a couple of Pages with manual completion.
+    And I add a "Page" to section "1" and I fill the form with:
+      | Name         | Page1 |
+      | Page content | x     |
+    And I add a "Page" to section "1" and I fill the form with:
+      | Name         | Page2 |
+      | Page content | x     |
+
+    # Add a Glossary.
+    When I add a "Glossary" to section "1"
+    And I set the following fields to these values:
+      | Name | TestGlossary |
+    And I expand all fieldsets
+
+    # Add restrictions to the previous Pages being complete.
+    And I press "Add restriction..."
+    And I click on "Activity completion" "button" in the "Add restriction..." "dialogue"
+    And I set the field "Activity or resource" to "Page1"
+    And I press "Add restriction..."
+    And I click on "Activity completion" "button" in the "Add restriction..." "dialogue"
+    And I set the field with xpath "//div[@class='availability-item'][preceding-sibling::div]//select[@name='cm']" to "Page2"
+    And I press "Save and return to course"
+    And I should see "Not available unless:" in the ".activity.glossary" "css_element"
+    And I should see "The activity Page1 is marked complete" in the ".activity.glossary" "css_element"
+    And I should see "The activity Page2 is marked complete" in the ".activity.glossary" "css_element"
+    And I follow "TestGlossary"
+
+    # Behat will automatically check there is no error on this page.
+    Then I should see "TestGlossary"
index b098e81..20fff45 100644 (file)
@@ -40,22 +40,25 @@ class core_availability_renderer extends plugin_renderer_base {
      *
      * This function will not be called unless there are at least two messages.
      *
-     * @param bool $root True if this is a root-level list for an activity
-     * @param bool $andoperator True if the messages are being combined as AND
-     * @param bool $roothidden True if the root level should use 'hidden' message
-     * @param array $messages Messages to render
+     * @param core_availability_multiple_messages $renderable Multiple messages
      * @return string Combined HTML
      */
-    public function multiple_messages($root, $andoperator, $roothidden, array $messages) {
+    public function render_core_availability_multiple_messages(
+            core_availability_multiple_messages $renderable) {
         // Get initial message.
-        $out = get_string('list_' . ($root ? 'root_' : '') .
-                ($andoperator ? 'and' : 'or') . ($roothidden ? '_hidden' : ''),
+        $out = get_string('list_' . ($renderable->root ? 'root_' : '') .
+                ($renderable->andoperator ? 'and' : 'or') . ($renderable->treehidden ? '_hidden' : ''),
                 'availability');
 
         // Make the list.
         $out .= html_writer::start_tag('ul');
-        foreach ($messages as $message) {
-            $out .= html_writer::tag('li', $message);
+        foreach ($renderable->items as $item) {
+            if (is_string($item)) {
+                $str = $item;
+            } else {
+                $str = $this->render($item);
+            }
+            $out .= html_writer::tag('li', $str);
         }
         $out .= html_writer::end_tag('ul');
         return $out;
index b19d62c..ebabc08 100644 (file)
@@ -341,9 +341,15 @@ class tree_testcase extends \advanced_testcase {
      * @param int $userid User id
      */
     protected function get_available_results($structure, \core_availability\info $info, $userid) {
+        global $PAGE;
         $tree = new tree($structure);
         $result = $tree->check_available(false, $info, true, $userid);
-        return array($result->is_available(), $tree->get_result_information($info, $result));
+        $information = $tree->get_result_information($info, $result);
+        if (!is_string($information)) {
+            $renderer = $PAGE->get_renderer('core', 'availability');
+            $information = $renderer->render($information);
+        }
+        return array($result->is_available(), $information);
     }
 
     /**
@@ -389,6 +395,8 @@ class tree_testcase extends \advanced_testcase {
      * Tests the get_full_information() function.
      */
     public function test_get_full_information() {
+        global $PAGE;
+        $renderer = $PAGE->get_renderer('core', 'availability');
         // Setup.
         $info = new \core_availability\mock_info();
 
@@ -417,7 +425,7 @@ class tree_testcase extends \advanced_testcase {
                 self::mock(array('m' => 3)));
         $tree = new tree($structure);
         $this->assertRegExp('~<ul.*<ul.*<li.*1.*<li.*2.*</ul>.*<li.*3~',
-                $tree->get_full_information($info));
+                $renderer->render($tree->get_full_information($info)));
 
         // Test intro messages before list. First, OR message.
         $structure->c = array(
@@ -426,13 +434,13 @@ class tree_testcase extends \advanced_testcase {
         );
         $tree = new tree($structure);
         $this->assertRegExp('~Not available unless any of:.*<ul>~',
-                $tree->get_full_information($info));
+                $renderer->render($tree->get_full_information($info)));
 
         // Now, OR message when not shown.
         $structure->show = false;
         $tree = new tree($structure);
         $this->assertRegExp('~hidden.*<ul>~',
-                $tree->get_full_information($info));
+                $renderer->render($tree->get_full_information($info)));
 
         // AND message.
         $structure->op = '&';
@@ -440,11 +448,11 @@ class tree_testcase extends \advanced_testcase {
         $structure->showc = array(false, false);
         $tree = new tree($structure);
         $this->assertRegExp('~Not available unless:.*<ul>~',
-                $tree->get_full_information($info));
+                $renderer->render($tree->get_full_information($info)));
 
         // Hidden markers on items.
         $this->assertRegExp('~1.*hidden.*2.*hidden~',
-                $tree->get_full_information($info));
+                $renderer->render($tree->get_full_information($info)));
 
         // Hidden markers on child tree and items.
         $structure->c[1] = tree::get_nested_json(array(
@@ -452,11 +460,11 @@ class tree_testcase extends \advanced_testcase {
                 self::mock(array('m' => '3'))), tree::OP_AND);
         $tree = new tree($structure);
         $this->assertRegExp('~1.*hidden.*All of \(hidden.*2.*3~',
-                $tree->get_full_information($info));
+                $renderer->render($tree->get_full_information($info)));
         $structure->c[1]->op = '|';
         $tree = new tree($structure);
         $this->assertRegExp('~1.*hidden.*Any of \(hidden.*2.*3~',
-                $tree->get_full_information($info));
+                $renderer->render($tree->get_full_information($info)));
 
         // Hidden markers on single-item display, AND and OR.
         $structure->showc = array(false);
@@ -480,7 +488,7 @@ class tree_testcase extends \advanced_testcase {
                 self::mock(array('m' => '2'))), tree::OP_AND);
         $tree = new tree($structure);
         $this->assertRegExp('~Not available \(hidden.*1.*2~',
-                $tree->get_full_information($info));
+                $renderer->render($tree->get_full_information($info)));
 
         // Single item tree containing single item.
         unset($structure->c[0]->c[1]);
index 74cc088..2a92c9f 100644 (file)
@@ -115,7 +115,7 @@ class block_navigation_renderer extends plugin_renderer_base {
             } else if ($item->action instanceof action_link) {
                 //TODO: to be replaced with something else
                 $link = $item->action;
-                $link->text = $icon.$link->text;
+                $link->text = $icon.html_writer::span($link->text, 'item-content-wrap');
                 $link->attributes = array_merge($link->attributes, $attributes);
                 $content = $this->output->render($link);
             } else if ($item->action instanceof moodle_url) {
index 0f7ab3f..8a13aa8 100644 (file)
@@ -142,6 +142,7 @@ abstract class moodleform_mod extends moodleform {
                     }
                 }
 
+                $hasgradeitems = false;
                 $items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename,'iteminstance'=>$instance, 'courseid'=>$COURSE->id));
                 //will be no items if, for example, this activity supports ratings but rating aggregate type == no ratings
                 if (!empty($items)) {
@@ -151,6 +152,8 @@ abstract class moodleform_mod extends moodleform {
                             if ($mform->elementExists($elname)) {
                                 $mform->hardFreeze($elname); // prevent removing of existing outcomes
                             }
+                        } else {
+                            $hasgradeitems = true;
                         }
                     }
 
@@ -167,12 +170,17 @@ abstract class moodleform_mod extends moodleform {
                     }
                 }
 
+                if (!$hasgradeitems && $mform->elementExists('gradepass')) {
+                    // Remove form element 'Grade to pass' since there are no grade items (when rating not selected).
+                    $mform->removeElement('gradepass');
+                }
+
                 if ($gradecat === false) {
                     // items and outcomes in different categories - remove the option
                     // TODO: add a "Mixed categories" text instead of removing elements with no explanation
                     if ($mform->elementExists('gradecat')) {
                         $mform->removeElement('gradecat');
-                        if ($this->_features->rating) {
+                        if ($this->_features->rating  && !$mform->elementExists('gradepass')) {
                             //if supports ratings then the max grade dropdown wasnt added so the grade box can be removed entirely
                             $mform->removeElement('modstandardgrade');
                         }
index bd7229d..d298b15 100644 (file)
@@ -111,7 +111,8 @@ M.gradingform_guideeditor.editmode = function(el, editmode) {
             value = M.util.get_string('clicktoedit', 'gradingform_guide')
             taplain.addClass('empty')
         }
-        taplain.one('.textvalue').set('innerHTML', Y.Escape.html(value))
+        // Replace newlines with <br> tags, when displaying in the page.
+        taplain.one('.textvalue').set('innerHTML', Y.Escape.html(value).replace(/(?:\r\n|\r|\n)/g, '<br>'))
         if (tb) {
             tbplain.one('.textvalue').set('innerHTML', Y.Escape.html(tb.get('value')))
         }
index 87e94de..c99219d 100644 (file)
@@ -145,6 +145,10 @@ class gradingform_guide_renderer extends plugin_renderer_base {
                       'name' => '{NAME}[criteria][{CRITERION-id}][descriptionmarkers]'));
             $maxscore   = html_writer::tag('div', s($criterion['maxscore']),
                 array('class'=>'criteriondescriptionscore', 'name' => '{NAME}[criteria][{CRITERION-id}][maxscore]'));
+
+            // Retain newlines as <br> tags when displaying the marking guide.
+            $description = nl2br($description);
+            $descriptionmarkers = nl2br($descriptionmarkers);
         }
 
         if (isset($criterion['error_description'])) {
@@ -290,6 +294,8 @@ class gradingform_guide_renderer extends plugin_renderer_base {
             } else {
                 $description = s($comment['description']);
             }
+            // Retain newlines as <br> tags when displaying 'frequently used comments'.
+            $description = nl2br($description);
         }
         $descriptionclass = 'description';
         if (isset($comment['error_description'])) {
index b7e5729..fb5e988 100644 (file)
@@ -32,7 +32,9 @@ defined('MOODLE_INTERNAL') || die();
 
 $string['clianswerno'] = 'Не';
 $string['cliansweryes'] = 'Да';
+$string['cliincorrectvalueerror'] = 'Грешка, некоректна стойност "{$a->value}" за "{$a->option}"';
 $string['cliincorrectvalueretry'] = 'Неправилна стойност. Моля опитайте отново';
 $string['clitypevalue'] = 'Тип стойност';
+$string['cliyesnoprompt'] = 'Въведете y (означава да) или n (означава не)';
 $string['environmentrequireinstall'] = 'Трябва да бъде инсталиран и разрешен';
 $string['environmentrequireversion'] = 'Необходима е версия {$a->needed} а Вие имате  {$a->current}';
index eea0465..cee0984 100644 (file)
@@ -31,5 +31,5 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['parentlanguage'] = '';
-$string['thisdirection'] = 'ltr';
+$string['thisdirection'] = 'izq-a-der';
 $string['thislanguage'] = 'Español - México';
index c0bc147..334adff 100644 (file)
@@ -37,7 +37,11 @@ $string['chooselanguagesub'] = 'Изберете јазик САМО за инс
 $string['clialreadyconfigured'] = 'Конфигурациската датотека config.php веќе постои. Ве молиме користете ја admin/cli/install_database.php за да го инсталирате Moodle на овој сајт.';
 $string['clialreadyinstalled'] = 'Конфигурациската датотека config.php веќе постои. Ве молиме користете ја admin/cli/install_database.php за да го надградите Moodle за овој сајт.';
 $string['cliinstallheader'] = 'Програма за инсталирање на Moodle {$a} командна линија';
+$string['databasehost'] = 'Домаќин на базата на податоци';
+$string['databasename'] = 'Име на базата на податоци';
+$string['databasetypehead'] = 'Одбери го двигателот на базата за податоци';
 $string['dataroot'] = 'Директориум';
+$string['datarootpermission'] = 'Дозвола за директориумот за податоци';
 $string['dbprefix'] = 'Префикс на табели';
 $string['dirroot'] = 'Moodle директориум';
 $string['environmenthead'] = 'Ја проверувам околината...';
@@ -61,6 +65,7 @@ $string['memorylimithelp'] = '<p>Прагот на меморијата кај P
     <p><blockquote>php_value memory_limit 40M</blockquote></p>
     <p>Како и да е, на некои компјутери, ова може да ги спречи <b>сите </b> PHP страници да работат (ќе забележите грешки на страниците) па ќе треба да ја преместите датотеката .htaccess.</p></li>
 </ol>';
+$string['pathswrongadmindir'] = 'Администраторскиот директориум не постои';
 $string['phpversion'] = 'Верзија на PHP';
 $string['phpversionhelp'] = '<p>На Moodle му е потребна верзија на PHP, и тоа најмалку 4.1.0. </p>
 <p>Моментално работите на верзијата {$a} </p>
index e0f5cd0..78468c7 100644 (file)
@@ -1530,6 +1530,7 @@ $string['resetnotimplemented'] = 'Reset not implemented';
 $string['resetrecordexpired'] = 'The password reset link you used is more than {$a} minutes old and has expired. Please initiate a new password reset.';
 $string['resetstartdate'] = 'Reset start date';
 $string['resetstatus'] = 'Status';
+$string['resettable'] = 'Reset table preferences';
 $string['resettask'] = 'Task';
 $string['resettodefaults'] = 'Reset to defaults';
 $string['resortsubcategoriesby'] = 'Sort subcategories by {$a} ascending';
index a44c185..5d1874e 100644 (file)
@@ -578,7 +578,7 @@ class block_manager {
         }
 
         $context = $this->page->context;
-        $contexttest = 'bi.parentcontextid = :contextid2';
+        $contexttest = 'bi.parentcontextid IN (:contextid2, :contextid3)';
         $parentcontextparams = array();
         $parentcontextids = $context->get_parent_context_ids();
         if ($parentcontextids) {
@@ -594,12 +594,14 @@ class block_manager {
         $ccselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
         $ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = bi.id AND ctx.contextlevel = :contextlevel)";
 
+        $systemcontext = context_system::instance();
         $params = array(
             'contextlevel' => CONTEXT_BLOCK,
             'subpage1' => $this->page->subpage,
             'subpage2' => $this->page->subpage,
             'contextid1' => $context->id,
             'contextid2' => $context->id,
+            'contextid3' => $systemcontext->id,
             'pagetype' => $this->page->pagetype,
         );
         if ($this->page->subpage === '') {
index 7803aa8..d270a75 100644 (file)
@@ -1140,6 +1140,7 @@ $services = array(
             'core_user_get_users_by_field',
             'core_user_add_user_private_files',
             'mod_assign_view_grading_table',
+            'mod_scorm_view_scorm',
             ),
         'enabled' => 0,
         'restrictedusers' => 0,
index 9777cdc..3c6e027 100644 (file)
Binary files a/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-debug.js and b/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-debug.js differ
index 555bcf1..2b4e22a 100644 (file)
Binary files a/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-min.js and b/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-min.js differ
index 9777cdc..3c6e027 100644 (file)
Binary files a/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector.js and b/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector.js differ
index 684f27f..a0b09a4 100644 (file)
@@ -82,7 +82,7 @@ M.form.dateselector = {
             width: "300px",
             showPrevMonth: true,
             showNextMonth: true,
-            firstdayofweek: config.firstdayofweek,
+            firstdayofweek: parseInt(config.firstdayofweek, 10),
             WEEKDAYS_MEDIUM: [
                 config.sun,
                 config.mon,
index e6e0274..049184a 100644 (file)
@@ -2807,15 +2807,6 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
         }
     }
 
-    // Set the global $COURSE.
-    // TODO MDL-49434: setting current course/cm should be after the check $cm->uservisible .
-    if ($cm) {
-        $PAGE->set_cm($cm, $course);
-        $PAGE->set_pagelayout('incourse');
-    } else if (!empty($courseorid)) {
-        $PAGE->set_course($course);
-    }
-
     // Check visibility of activity to current user; includes visible flag, conditional availability, etc.
     if ($cm && !$cm->uservisible) {
         if ($preventredirect) {
@@ -2829,6 +2820,14 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
         redirect($url, get_string('activityiscurrentlyhidden'));
     }
 
+    // Set the global $COURSE.
+    if ($cm) {
+        $PAGE->set_cm($cm, $course);
+        $PAGE->set_pagelayout('incourse');
+    } else if (!empty($courseorid)) {
+        $PAGE->set_course($course);
+    }
+
     // Finally access granted, update lastaccess times.
     user_accesstime_log($course->id);
 }
index cbf66ac..39afa90 100644 (file)
@@ -34,6 +34,7 @@ define('TABLE_VAR_SHOW',   3);
 define('TABLE_VAR_IFIRST', 4);
 define('TABLE_VAR_ILAST',  5);
 define('TABLE_VAR_PAGE',   6);
+define('TABLE_VAR_RESET',  7);
 /**#@-*/
 
 /**#@+
@@ -138,6 +139,7 @@ class flexible_table {
             TABLE_VAR_IFIRST => 'tifirst',
             TABLE_VAR_ILAST  => 'tilast',
             TABLE_VAR_PAGE   => 'page',
+            TABLE_VAR_RESET  => 'treset'
         );
     }
 
@@ -508,6 +510,17 @@ class flexible_table {
             $this->prefs['i_first'] = $ifirst;
         }
 
+        // Allow user to reset table preferences.
+        if (optional_param($this->request[TABLE_VAR_RESET], 0, PARAM_BOOL) === 1) {
+            $this->prefs = array(
+                'collapse' => array(),
+                'sortby'   => array(),
+                'i_first'  => '',
+                'i_last'   => '',
+                'textsort' => $this->column_textsort,
+            );
+        }
+
         // Save user preferences if they have changed.
         if ($this->prefs != $oldprefs) {
             if ($this->persistent) {
@@ -971,6 +984,10 @@ class flexible_table {
      */
     function print_nothing_to_display() {
         global $OUTPUT;
+
+        // Render button to allow user to reset table preferences.
+        echo $this->render_reset_button();
+
         $this->print_initials_bar();
 
         echo $OUTPUT->heading(get_string('nothingtodisplay'));
@@ -1325,6 +1342,10 @@ class flexible_table {
      */
     function start_html() {
         global $OUTPUT;
+
+        // Render button to allow user to reset table preferences.
+        echo $this->render_reset_button();
+
         // Do we need to print initial bars?
         $this->print_initials_bar();
 
@@ -1363,6 +1384,21 @@ class flexible_table {
         }
         return $string;
     }
+
+    /**
+     * Generate the HTML for the table preferences reset button.
+     *
+     * @return string HTML fragment.
+     */
+    private function render_reset_button() {
+        $url = $this->baseurl->out(false, array($this->request[TABLE_VAR_RESET] => 1));
+
+        $html  = html_writer::start_div('mdl-right');
+        $html .= html_writer::link($url, get_string('resettable'));
+        $html .= html_writer::end_div();
+
+        return $html;
+    }
 }
 
 
index 51adebc..47fc0f6 100644 (file)
Binary files a/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js and b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js differ
index bb3367a..27dec90 100644 (file)
Binary files a/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js and b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js differ
index 51adebc..47fc0f6 100644 (file)
Binary files a/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop.js and b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop.js differ
index 3a4bea6..e7700d0 100644 (file)
@@ -207,6 +207,8 @@ Y.extend(DRAGDROP, Y.Base, {
         if (!this.in_group(drag)) {
             return;
         }
+        // Store the nodes current style, so we can restore it later.
+        this.originalstyle = drag.get('node').getAttribute('style');
         // Set some general styles here
         drag.get('node').setStyle('opacity', '.25');
         drag.get('dragNode').setStyles({
@@ -225,10 +227,7 @@ Y.extend(DRAGDROP, Y.Base, {
             return;
         }
         //Put our general styles back
-        drag.get('node').setStyles({
-            visibility: '',
-            opacity: '1'
-        });
+        drag.get('node').setAttribute('style', this.originalstyle);
         this.drag_end(e);
     },
 
index b5c2757..412f72f 100644 (file)
@@ -144,6 +144,7 @@ class mod_assign_mod_form extends moodleform_mod {
             'preventsubmissionnotingroup',
             'assign');
         $mform->setType('preventsubmissionnotingroup', PARAM_BOOL);
+        $mform->disabledIf('preventsubmissionnotingroup', 'teamsubmission', 'eq', 0);
 
         $name = get_string('requireallteammemberssubmit', 'assign');
         $mform->addElement('selectyesno', 'requireallteammemberssubmit', $name);
index d13bd95..4b1b4bc 100644 (file)
@@ -175,6 +175,16 @@ if ($ADMIN->fulltree) {
     $setting->set_locked_flag_options(admin_setting_flag::ENABLED, false);
     $settings->add($setting);
 
+    $name = new lang_string('preventsubmissionnotingroup', 'mod_assign');
+    $description = new lang_string('preventsubmissionnotingroup_help', 'mod_assign');
+    $setting = new admin_setting_configcheckbox('assign/preventsubmissionnotingroup',
+        $name,
+        $description,
+        0);
+    $setting->set_advanced_flag_options(admin_setting_flag::ENABLED, false);
+    $setting->set_locked_flag_options(admin_setting_flag::ENABLED, false);
+    $settings->add($setting);
+
     $name = new lang_string('requireallteammemberssubmit', 'mod_assign');
     $description = new lang_string('requireallteammemberssubmit_help', 'mod_assign');
     $setting = new admin_setting_configcheckbox('assign/requireallteammemberssubmit',
@@ -252,16 +262,6 @@ if ($ADMIN->fulltree) {
     $setting->set_advanced_flag_options(admin_setting_flag::ENABLED, false);
     $setting->set_locked_flag_options(admin_setting_flag::ENABLED, false);
     $settings->add($setting);
-
-    $name = new lang_string('preventsubmissionnotingroup', 'mod_assign');
-    $description = new lang_string('preventsubmissionnotingroup_help', 'mod_assign');
-    $setting = new admin_setting_configcheckbox('assign/preventsubmissionnotingroup',
-                                                    $name,
-                                                    $description,
-                                                    0);
-    $setting->set_advanced_flag_options(admin_setting_flag::ENABLED, false);
-    $setting->set_locked_flag_options(admin_setting_flag::ENABLED, false);
-    $settings->add($setting);
 }
 
 $ADMIN->add('modassignfolder', $settings);
index 4a60767..bbc8e64 100644 (file)
@@ -46,9 +46,6 @@
 }
 
 /* toc style NONE */
-.path-mod-book .book_toc_none {
-  font-size: 0.8em;
-}
 .path-mod-book .book_toc_none ul ul,
 .dir-rtl.path-mod-book .book_toc_none ul ul {
     margin-left: 0;
@@ -56,9 +53,6 @@
 }
 
 /*toc style BULLETS */
-.path-mod-book .book_toc_bullets {
-  font-size: 0.8em;
-}
 .path-mod-book .book_toc_bullets ul ul {
     margin-left: 20px;
 }
     list-style: circle;
 }
 
-/* toc style INDENTED*/
-.path-mod-book .book_toc_indented {
-  font-size: 0.8em;
+.path-mod-book .book_toc_bullets li li:before {
+    display: none;
 }
+
+/* toc style INDENTED*/
 .path-mod-book .book_toc_indented ul {
     margin-left: 5px;
 }
index 237ee77..07fcdd5 100644 (file)
@@ -48,15 +48,15 @@ class mod_forum_renderer extends plugin_renderer_base {
             if ($prev) {
                 $url = new moodle_url('/mod/forum/discuss.php', array('d' => $prev->id));
                 $html .= html_writer::start_tag('li', array('class' => 'prev-discussion'));
-                $html .= html_writer::link($url, $prev->name,
-                    array('aria-label' => get_string('prevdiscussiona', 'mod_forum', $prev->name)));
+                $html .= html_writer::link($url, format_string($prev->name),
+                    array('aria-label' => get_string('prevdiscussiona', 'mod_forum', format_string($prev->name))));
                 $html .= html_writer::end_tag('li');
             }
             if ($next) {
                 $url = new moodle_url('/mod/forum/discuss.php', array('d' => $next->id));
                 $html .= html_writer::start_tag('li', array('class' => 'next-discussion'));
-                $html .= html_writer::link($url, $next->name,
-                    array('aria-label' => get_string('nextdiscussiona', 'mod_forum', $next->name)));
+                $html .= html_writer::link($url, format_string($next->name),
+                    array('aria-label' => get_string('nextdiscussiona', 'mod_forum', format_string($next->name))));
                 $html .= html_writer::end_tag('li');
             }
             $html .= html_writer::end_tag('ul');
index a616df1..a9aee92 100644 (file)
@@ -136,14 +136,6 @@ if ( $show ) {
     $hook = $show;
     $show = '';
 }
-/// Processing standard security processes
-if ($course->id != SITEID) {
-    require_login($course);
-}
-if (!$cm->visible and !has_capability('moodle/course:viewhiddenactivities', $context)) {
-    echo $OUTPUT->header();
-    notice(get_string("activityiscurrentlyhidden"));
-}
 
 /// stablishing flag variables
 if ( $sortorder = strtolower($sortorder) ) {
index d64cfe6..fa70507 100644 (file)
@@ -146,15 +146,15 @@ class mod_lesson_mod_form extends moodleform_mod {
         if ($mods = get_course_mods($COURSE->id)) {
             $modinstances = array();
             foreach ($mods as $mod) {
-
-                // get the module name and then store it in a new array
+                // Get the module name and then store it in a new array.
                 if ($module = get_coursemodule_from_instance($mod->modname, $mod->instance, $COURSE->id)) {
-                    if (isset($this->_cm->id) and $this->_cm->id != $mod->id){
+                    // Exclude this lesson, if it's already been saved.
+                    if (!isset($this->_cm->id) || $this->_cm->id != $mod->id) {
                         $modinstances[$mod->id] = $mod->modname.' - '.$module->name;
                     }
                 }
             }
-            asort($modinstances); // sort by module name
+            asort($modinstances); // Sort by module name.
             $modinstances=array(0=>get_string('none'))+$modinstances;
 
             $mform->addElement('select', 'activitylink', get_string('activitylink', 'lesson'), $modinstances);
diff --git a/mod/scorm/classes/external.php b/mod/scorm/classes/external.php
new file mode 100644 (file)
index 0000000..e6425b4
--- /dev/null
@@ -0,0 +1,105 @@
+<?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/>.
+
+/**
+ * SCORM module external API
+ *
+ * @package    mod_scorm
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once($CFG->libdir . '/externallib.php');
+require_once($CFG->dirroot . '/mod/scorm/lib.php');
+
+/**
+ * SCORM module external functions
+ *
+ * @package    mod_scorm
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+class mod_scorm_external extends external_api {
+
+    /**
+     * Returns description of method parameters
+     *
+     * @return external_function_parameters
+     * @since Moodle 3.0
+     */
+    public static function view_scorm_parameters() {
+        return new external_function_parameters(
+            array(
+                'scormid' => new external_value(PARAM_INT, 'scorm instance id')
+            )
+        );
+    }
+
+    /**
+     * Trigger the course module viewed event.
+     *
+     * @param int $scormid the scorm instance id
+     * @return array of warnings and status result
+     * @since Moodle 3.0
+     * @throws moodle_exception
+     */
+    public static function view_scorm($scormid) {
+        global $DB, $CFG;
+        require_once($CFG->dirroot . '/mod/scorm/lib.php');
+
+        $params = self::validate_parameters(self::view_scorm_parameters(),
+                                            array(
+                                                'scormid' => $scormid
+                                            ));
+        $warnings = array();
+
+        // Request and permission validation.
+        $scorm = $DB->get_record('scorm', array('id' => $params['scormid']), '*', MUST_EXIST);
+        list($course, $cm) = get_course_and_cm_from_instance($scorm, 'scorm');
+
+        $context = context_module::instance($cm->id);
+        self::validate_context($context);
+
+        // Call the scorm/lib API.
+        scorm_view($scorm, $course, $cm, $context);
+
+        $result = array();
+        $result['status'] = true;
+        $result['warnings'] = $warnings;
+        return $result;
+    }
+
+    /**
+     * Returns description of method result value
+     *
+     * @return external_description
+     * @since Moodle 3.0
+     */
+    public static function view_scorm_returns() {
+        return new external_single_structure(
+            array(
+                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
+                'warnings' => new external_warnings()
+            )
+        );
+    }
+}
diff --git a/mod/scorm/db/services.php b/mod/scorm/db/services.php
new file mode 100644 (file)
index 0000000..1a95c3e
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * SCORM external functions and service definitions.
+ *
+ * @package    mod_scorm
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+$functions = array(
+
+    'mod_scorm_view_scorm' => array(
+        'classname'     => 'mod_scorm_external',
+        'methodname'    => 'view_scorm',
+        'description'   => 'Trigger the course module viewed event.',
+        'type'          => 'write',
+        'capabilities'  => ''
+    ),
+);
index bb5bfc2..1e2164f 100644 (file)
@@ -1440,3 +1440,27 @@ function scorm_check_mode($scorm, &$newattempt, &$attempt, $userid, &$mode) {
         }
     }
 }
+
+/**
+ * Trigger the course_module_viewed event.
+ *
+ * @param  stdClass $scorm        scorm object
+ * @param  stdClass $course     course object
+ * @param  stdClass $cm         course module object
+ * @param  stdClass $context    context object
+ * @since Moodle 3.0
+ */
+function scorm_view($scorm, $course, $cm, $context) {
+
+    // Trigger course_module_viewed event.
+    $params = array(
+        'context' => $context,
+        'objectid' => $scorm->id
+    );
+
+    $event = \mod_scorm\event\course_module_viewed::create($params);
+    $event->add_record_snapshot('course_modules', $cm);
+    $event->add_record_snapshot('course', $course);
+    $event->add_record_snapshot('scorm', $scorm);
+    $event->trigger();
+}
index d76312f..7472137 100644 (file)
@@ -840,7 +840,15 @@ function scorm_get_all_attempts($scormid, $userid) {
     return $attemptids;
 }
 
-function scorm_view_display ($user, $scorm, $action, $cm) {
+/**
+ * Displays the entry form and toc if required.
+ *
+ * @param  stdClass $user   user object
+ * @param  stdClass $scorm  scorm object
+ * @param  string   $action base URL for the organizations select box
+ * @param  stdClass $cm     course module object
+ */
+function scorm_print_launch ($user, $scorm, $action, $cm) {
     global $CFG, $DB, $PAGE, $OUTPUT, $COURSE;
 
     if ($scorm->updatefreq == SCORM_UPDATE_EVERYTIME) {
diff --git a/mod/scorm/tests/externallib_test.php b/mod/scorm/tests/externallib_test.php
new file mode 100644 (file)
index 0000000..176fee5
--- /dev/null
@@ -0,0 +1,100 @@
+<?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/>.
+
+/**
+ * SCORM module external functions tests
+ *
+ * @package    mod_scorm
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+require_once($CFG->dirroot . '/webservice/tests/helpers.php');
+require_once($CFG->dirroot . '/mod/scorm/lib.php');
+
+/**
+ * SCORM module external functions tests
+ *
+ * @package    mod_scorm
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+class mod_scorm_external_testcase extends externallib_advanced_testcase {
+
+    /**
+     * Test view_scorm
+     */
+    public function test_view_scorm() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        $this->setAdminUser();
+        // Setup test data.
+        $course = $this->getDataGenerator()->create_course();
+        $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id));
+        $context = context_module::instance($scorm->cmid);
+        $cm = get_coursemodule_from_instance('scorm', $scorm->id);
+
+        // Test invalid instance id.
+        try {
+            mod_scorm_external::view_scorm(0);
+            $this->fail('Exception expected due to invalid mod_scorm instance id.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('invalidrecord', $e->errorcode);
+        }
+
+        // Test not-enrolled user.
+        $user = self::getDataGenerator()->create_user();
+        $this->setUser($user);
+        try {
+            mod_scorm_external::view_scorm($scorm->id);
+            $this->fail('Exception expected due to not enrolled user.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('requireloginerror', $e->errorcode);
+        }
+
+        // Test user with full capabilities.
+        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
+        $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+
+        $result = mod_scorm_external::view_scorm($scorm->id);
+        $result = external_api::clean_returnvalue(mod_scorm_external::view_scorm_returns(), $result);
+
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = array_shift($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\mod_scorm\event\course_module_viewed', $event);
+        $this->assertEquals($context, $event->get_context());
+        $moodleurl = new \moodle_url('/mod/scorm/view.php', array('id' => $cm->id));
+        $this->assertEquals($moodleurl, $event->get_url());
+        $this->assertEventContextNotUsed($event);
+        $this->assertNotEmpty($event->get_name());
+    }
+}
diff --git a/mod/scorm/tests/lib_test.php b/mod/scorm/tests/lib_test.php
new file mode 100644 (file)
index 0000000..a0e4a60
--- /dev/null
@@ -0,0 +1,79 @@
+<?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/>.
+
+/**
+ * SCORM module library functions tests
+ *
+ * @package    mod_scorm
+ * @category   test
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+require_once($CFG->dirroot . '/webservice/tests/helpers.php');
+require_once($CFG->dirroot . '/mod/scorm/lib.php');
+
+/**
+ * SCORM module library functions tests
+ *
+ * @package    mod_scorm
+ * @category   test
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+class mod_scorm_lib_testcase extends externallib_advanced_testcase {
+
+    /**
+     * Test scorm_view
+     * @return void
+     */
+    public function test_scorm_view() {
+        global $CFG;
+
+        $this->resetAfterTest();
+
+        $this->setAdminUser();
+        // Setup test data.
+        $course = $this->getDataGenerator()->create_course();
+        $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id));
+        $context = context_module::instance($scorm->cmid);
+        $cm = get_coursemodule_from_instance('scorm', $scorm->id);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+
+        scorm_view($scorm, $course, $cm, $context);
+
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = array_shift($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\mod_scorm\event\course_module_viewed', $event);
+        $this->assertEquals($context, $event->get_context());
+        $url = new \moodle_url('/mod/scorm/view.php', array('id' => $cm->id));
+        $this->assertEquals($url, $event->get_url());
+        $this->assertEventContextNotUsed($event);
+        $this->assertNotEmpty($event->get_name());
+    }
+
+}
index 13747d8..54f9161 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2015051101;    // The current module version (Date: YYYYMMDDXX).
+$plugin->version   = 2015051102;    // The current module version (Date: YYYYMMDDXX).
 $plugin->requires  = 2015050500;    // Requires this Moodle version.
 $plugin->component = 'mod_scorm';   // Full name of the plugin (used for diagnostics).
 $plugin->cron      = 300;
index f6dc57c..fd98432 100644 (file)
@@ -15,6 +15,7 @@
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 require_once("../../config.php");
+require_once($CFG->dirroot.'/mod/scorm/lib.php');
 require_once($CFG->dirroot.'/mod/scorm/locallib.php');
 require_once($CFG->dirroot.'/course/lib.php');
 
@@ -123,14 +124,7 @@ $shortname = format_string($course->shortname, true, array('context' => $context
 $pagetitle = strip_tags($shortname.': '.format_string($scorm->name));
 
 // Trigger module viewed event.
-$event = \mod_scorm\event\course_module_viewed::create(array(
-    'objectid' => $scorm->id,
-    'context' => $contextmodule,
-));
-$event->add_record_snapshot('course', $course);
-$event->add_record_snapshot('scorm', $scorm);
-$event->add_record_snapshot('course_modules', $cm);
-$event->trigger();
+scorm_view($scorm, $course, $cm, $contextmodule);
 
 if (empty($preventskip) && empty($launch) && (has_capability('mod/scorm:skipview', $contextmodule))) {
     scorm_simple_play($scorm, $USER, $contextmodule, $cm->id);
@@ -179,7 +173,7 @@ if (!empty($scorm->timeclose) && $timenow > $scorm->timeclose) {
     $scormopen = false;
 }
 if ($scormopen && empty($launch)) {
-    scorm_view_display($USER, $scorm, 'view.php?id='.$cm->id, $cm);
+    scorm_print_launch($USER, $scorm, 'view.php?id='.$cm->id, $cm);
 }
 if (!empty($forcejs)) {
     echo $OUTPUT->box(get_string("forcejavascriptmessage", "scorm"), "generalbox boxaligncenter forcejavascriptmessage");
index bff2c99..2eaa8ae 100644 (file)
@@ -1,6 +1,10 @@
 This files describes API changes in /mod/* - activity modules,
 information provided here is intended especially for developers.
 
+=== 3.0 ===
+
+* Function scorm_view_display was renamed to scorm_print_launch to avoid confussion with new function scorm_view.
+
 === 2.9 ===
 
 * Added Grade to pass field to mod_form for activities that support grading.
index a3cda79..e327aab 100644 (file)
@@ -196,6 +196,7 @@ $string['notassessed'] = 'Not assessed yet';
 $string['notoverridden'] = 'Not overridden';
 $string['noworkshops'] = 'There are no workshops in this course';
 $string['noyoursubmission'] = 'You have not submitted your work yet';
+$string['nothingfound'] = 'Nothing to display';
 $string['nullgrade'] = '-';
 $string['overallfeedback'] = 'Overall feedback';
 $string['overallfeedbackfiles'] = 'Maximum number of overall feedback attachments';
@@ -265,11 +266,14 @@ If you enable this feature, it is recommended to set up the scheduled allocation
 $string['submissiongrade'] = 'Grade for submission';
 $string['submissiongrade_help'] = 'This setting specifies the maximum grade that may be obtained for submitted work.';
 $string['submissiongradeof'] = 'Grade for submission (of {$a})';
+$string['submissionlastmodified'] = 'Last modified';
 $string['submissionsettings'] = 'Submission settings';
 $string['submissionstart'] = 'Open for submissions from';
 $string['submissionstartevent'] = '{$a} (opens for submissions)';
 $string['submissionstartdatetime'] = 'Open for submissions from {$a->daydatetime} ({$a->distanceday})';
 $string['submissiontitle'] = 'Title';
+$string['submissionsreport'] = 'Workshop submissions report';
+$string['submittednotsubmitted'] = 'Submitted ({$a->submitted}) / not submitted ({$a->notsubmitted})';
 $string['subplugintype_workshopallocation'] = 'Submissions allocation method';
 $string['subplugintype_workshopallocation_plural'] = 'Submissions allocation methods';
 $string['subplugintype_workshopeval'] = 'Grading evaluation method';
index 55fee5a..4f98583 100644 (file)
@@ -1782,7 +1782,8 @@ class workshop {
             return array();
         }
 
-        if (!in_array($sortby, array('lastname','firstname','submissiontitle','submissiongrade','gradinggrade'))) {
+        if (!in_array($sortby, array('lastname', 'firstname', 'submissiontitle', 'submissionmodified',
+                'submissiongrade', 'gradinggrade'))) {
             $sortby = 'lastname';
         }
 
@@ -1813,7 +1814,8 @@ class workshop {
             }
             $sqlsort = implode(',', $sqlsort);
             $picturefields = user_picture::fields('u', array(), 'userid');
-            $sql = "SELECT $picturefields, s.title AS submissiontitle, s.grade AS submissiongrade, ag.gradinggrade
+            $sql = "SELECT $picturefields, s.title AS submissiontitle, s.timemodified AS submissionmodified,
+                           s.grade AS submissiongrade, ag.gradinggrade
                       FROM {user} u
                  LEFT JOIN {workshop_submissions} s ON (s.authorid = u.id AND s.workshopid = :workshopid1 AND s.example = 0)
                  LEFT JOIN {workshop_aggregations} ag ON (ag.userid = u.id AND ag.workshopid = :workshopid2)
index e5cc7ac..91ac7fa 100644 (file)
@@ -420,21 +420,29 @@ class mod_workshop_renderer extends plugin_renderer_base {
             $sortbyname = $sortbyfirstname . ' / ' . $sortbylastname;
         }
 
+        $sortbysubmisstiontitle = $this->helper_sortable_heading(get_string('submission', 'workshop'), 'submissiontitle',
+                $options->sortby, $options->sorthow);
+        $sortbysubmisstionlastmodified = $this->helper_sortable_heading(get_string('submissionlastmodified', 'workshop'),
+                'submissionmodified', $options->sortby, $options->sorthow);
+        $sortbysubmisstion = $sortbysubmisstiontitle . ' / ' . $sortbysubmisstionlastmodified;
+
         $table->head = array();
         $table->head[] = $sortbyname;
-        $table->head[] = $this->helper_sortable_heading(get_string('submission', 'workshop'), 'submissiontitle',
-                $options->sortby, $options->sorthow);
-        $table->head[] = $this->helper_sortable_heading(get_string('receivedgrades', 'workshop'));
-        if ($options->showsubmissiongrade) {
-            $table->head[] = $this->helper_sortable_heading(get_string('submissiongradeof', 'workshop', $data->maxgrade),
-                    'submissiongrade', $options->sortby, $options->sorthow);
-        }
-        $table->head[] = $this->helper_sortable_heading(get_string('givengrades', 'workshop'));
-        if ($options->showgradinggrade) {
-            $table->head[] = $this->helper_sortable_heading(get_string('gradinggradeof', 'workshop', $data->maxgradinggrade),
-                    'gradinggrade', $options->sortby, $options->sorthow);
+        $table->head[] = $sortbysubmisstion;
+
+        // If we are in submission phase ignore the following headers (columns).
+        if ($options->workshopphase != workshop::PHASE_SUBMISSION) {
+            $table->head[] = $this->helper_sortable_heading(get_string('receivedgrades', 'workshop'));
+            if ($options->showsubmissiongrade) {
+                $table->head[] = $this->helper_sortable_heading(get_string('submissiongradeof', 'workshop', $data->maxgrade),
+                        'submissiongrade', $options->sortby, $options->sorthow);
+            }
+            $table->head[] = $this->helper_sortable_heading(get_string('givengrades', 'workshop'));
+            if ($options->showgradinggrade) {
+                $table->head[] = $this->helper_sortable_heading(get_string('gradinggradeof', 'workshop', $data->maxgradinggrade),
+                        'gradinggrade', $options->sortby, $options->sorthow);
+            }
         }
-
         $table->rowclasses  = array();
         $table->colclasses  = array();
         $table->data        = array();
@@ -484,6 +492,13 @@ class mod_workshop_renderer extends plugin_renderer_base {
                     $cell->attributes['class'] = 'submission';
                     $row->cells[] = $cell;
                 }
+
+                // If we are in submission phase ignore the following columns.
+                if ($options->workshopphase == workshop::PHASE_SUBMISSION) {
+                    $table->data[] = $row;
+                    continue;
+                }
+
                 // column #3 - received grades
                 if ($tr % $spanreceived == 0) {
                     $idx = intval($tr / $spanreceived);
@@ -997,6 +1012,9 @@ class mod_workshop_renderer extends plugin_renderer_base {
             $url = new moodle_url('/mod/workshop/submission.php',
                                   array('cmid' => $this->page->context->instanceid, 'id' => $participant->submissionid));
             $out = html_writer::link($url, format_string($participant->submissiontitle), array('class'=>'title'));
+
+            $lastmodified = get_string('userdatemodified', 'workshop', userdate($participant->submissionmodified));
+            $out .= html_writer::tag('div', $lastmodified, array('class' => 'lastmodified'));
         }
 
         return $out;
index bca77ad..8a0787a 100644 (file)
     text-align: center;
 }
 
+.path-mod-workshop .lastmodified {
+    line-height: 1.0em;
+}
+
+.path-mod-workshop .nothingfound {
+    font-size: 150%;
+    color: #FF4500;
+}
+
 .path-mod-workshop .workshop-risk-dataloss { vertical-align: text-bottom; }
index 53a8c0a..e8b9957 100644 (file)
@@ -64,10 +64,12 @@ Feature: Workshop submission and assessment
     And I follow "TestWorkshop"
     And I should see "to allocate: 3"
     And I should see "There is at least one author who has not yet submitted their work"
-    And I should see "All submissions (3)"
-    And I should see "Submission1"
-    And I should see "Submission2"
-    And I should see "Submission3"
+    Then I should see "Workshop submissions report"
+    And I should see "Submitted (3) / not submitted (1)"
+    And I should see "Submission1" in the "Sam1 Student1" "table_row"
+    And I should see "Submission2" in the "Sam2 Student2" "table_row"
+    And I should see "Submission3" in the "Sam3 Student3" "table_row"
+    And I should see "No submission found for this user" in the "Sam4 Student4" "table_row"
     And I allocate submissions in workshop "TestWorkshop" as:"
       | Participant   | Reviewer      |
       | Sam1 Student1 | Sam2 Student2 |
index 10a26d8..66adc17 100644 (file)
@@ -224,32 +224,44 @@ case workshop::PHASE_SUBMISSION:
             }
         }
 
-        $countsubmissions = $workshop->count_submissions('all', $groupid);
+        print_collapsible_region_start('', 'workshop-viewlet-allsubmissions', get_string('submissionsreport', 'workshop'));
+
         $perpage = get_user_preferences('workshop_perpage', 10);
-        $pagingbar = new paging_bar($countsubmissions, $page, $perpage, $PAGE->url, 'page');
+        $data = $workshop->prepare_grading_report_data($USER->id, $groupid, $page, $perpage, $sortby, $sorthow);
+        if ($data) {
+            $countparticipants = $workshop->count_participants();
+            $countsubmissions = $workshop->count_submissions(array_keys($data->grades), $groupid);
+            $a = new stdClass();
+            $a->submitted = $countsubmissions;
+            $a->notsubmitted = $data->totalcount - $countsubmissions;
 
-        print_collapsible_region_start('', 'workshop-viewlet-allsubmissions', get_string('allsubmissions', 'workshop', $countsubmissions));
-        echo $output->box_start('generalbox allsubmissions');
-        echo $output->container(groups_print_activity_menu($workshop->cm, $PAGE->url, true), 'groupwidget');
+            echo html_writer::tag('div', get_string('submittednotsubmitted', 'workshop', $a));
 
-        if ($countsubmissions == 0) {
-            echo $output->container(get_string('nosubmissions', 'workshop'), 'nosubmissions');
+            echo $output->container(groups_print_activity_menu($workshop->cm, $PAGE->url, true), 'groupwidget');
+
+            // Prepare the paging bar.
+            $baseurl = new moodle_url($PAGE->url, array('sortby' => $sortby, 'sorthow' => $sorthow));
+            $pagingbar = new paging_bar($data->totalcount, $page, $perpage, $baseurl, 'page');
+
+            // Populate the display options for the submissions report.
+            $reportopts                     = new stdclass();
+            $reportopts->showauthornames     = has_capability('mod/workshop:viewauthornames', $workshop->context);
+            $reportopts->showreviewernames   = has_capability('mod/workshop:viewreviewernames', $workshop->context);
+            $reportopts->sortby              = $sortby;
+            $reportopts->sorthow             = $sorthow;
+            $reportopts->showsubmissiongrade = false;
+            $reportopts->showgradinggrade    = false;
+            $reportopts->workshopphase       = $workshop->phase;
 
-        } else {
-            $submissions = $workshop->get_submissions('all', $groupid, $page * $perpage, $perpage);
-            $shownames = has_capability('mod/workshop:viewauthornames', $workshop->context);
             echo $output->render($pagingbar);
-            foreach ($submissions as $submission) {
-                echo $output->render($workshop->prepare_submission_summary($submission, $shownames));
-            }
+            echo $output->render(new workshop_grading_report($data, $reportopts));
             echo $output->render($pagingbar);
             echo $output->perpage_selector($perpage);
+        } else {
+            echo html_writer::tag('div', get_string('nothingfound', 'workshop'), array('class' => 'nothingfound'));
         }
-
-        echo $output->box_end();
         print_collapsible_region_end();
     }
-
     break;
 
 case workshop::PHASE_ASSESSMENT:
@@ -298,6 +310,7 @@ case workshop::PHASE_ASSESSMENT:
             $reportopts->sorthow                = $sorthow;
             $reportopts->showsubmissiongrade    = false;
             $reportopts->showgradinggrade       = false;
+            $reportopts->workshopphase          = $workshop->phase;
 
             print_collapsible_region_start('', 'workshop-viewlet-gradereport', get_string('gradesreport', 'workshop'));
             echo $output->box_start('generalbox gradesreport');
@@ -447,6 +460,7 @@ case workshop::PHASE_EVALUATION:
             $reportopts->sorthow                = $sorthow;
             $reportopts->showsubmissiongrade    = true;
             $reportopts->showgradinggrade       = true;
+            $reportopts->workshopphase          = $workshop->phase;
 
             print_collapsible_region_start('', 'workshop-viewlet-gradereport', get_string('gradesreport', 'workshop'));
             echo $output->box_start('generalbox gradesreport');
@@ -565,6 +579,7 @@ case workshop::PHASE_CLOSED:
             $reportopts->sorthow                = $sorthow;
             $reportopts->showsubmissiongrade    = true;
             $reportopts->showgradinggrade       = true;
+            $reportopts->workshopphase          = $workshop->phase;
 
             print_collapsible_region_start('', 'workshop-viewlet-gradereport', get_string('gradesreport', 'workshop'));
             echo $output->box_start('generalbox gradesreport');
index bb994c8..8bad3d9 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -104,11 +103,11 @@ if (!isguestuser()) {   // Skip default home page for guests
 }
 
 // Toggle the editing state and switches
-if ($PAGE->user_allowed_editing()) {
+if (empty($CFG->forcedefaultmymoodle) && $PAGE->user_allowed_editing()) {
     if ($reset !== null) {
         if (!is_null($userid)) {
             require_sesskey();
-            if(!$currentpage = my_reset_page($userid, MY_PAGE_PRIVATE)){
+            if (!$currentpage = my_reset_page($userid, MY_PAGE_PRIVATE)) {
                 print_error('reseterror', 'my');
             }
             redirect(new moodle_url('/my'));
index b93b29e..017abc2 100644 (file)
@@ -58,7 +58,7 @@ function my_get_page($userid, $private=MY_PAGE_PRIVATE) {
 function my_copy_page($userid, $private=MY_PAGE_PRIVATE, $pagetype='my-index') {
     global $DB;
 
-    if ($customised = $DB->record_exists('my_pages', array('userid' => $userid, 'private' => $private))) {
+    if ($customised = $DB->get_record('my_pages', array('userid' => $userid, 'private' => $private))) {
         return $customised;  // We're done!
     }
 
index 8f26369..deccb47 100644 (file)
@@ -156,10 +156,10 @@ switch($action) {
 }
 
 if ($err_notice) {
-    echo $OUTPUT->notification($err_notice, 'red');
+    echo $OUTPUT->notification($err_notice, 'notifyproblem');
 }
 if ($notice) {
-    echo $OUTPUT->notification($notice, 'green');
+    echo $OUTPUT->notification($notice, 'notifysuccess');
 }
 
 // small form to add an official tag
index b503392..ba34087 100644 (file)
@@ -1,6 +1,12 @@
 This files describes API changes in /theme/* themes,
 information provided here is intended especially for theme designer.
 
+=== 3.0 ===
+
+* The renderer function core_availability_renderer::multiple_messages was changed to
+  render_core_availability_multiple_messages, requiring a parameter of the new
+  \core_availability\multiple_messages type.
+
 === 2.9 ===
 
 * Themes Bootstrapbase, Clean and More have undergone some changes for RTL layouts see - MDL-48160.
index 1228df7..8cd33c1 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2015071600.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2015072300.00;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.
 
-$release  = '3.0dev (Build: 20150716)'; // Human-friendly version name
+$release  = '3.0dev (Build: 20150723)'; // Human-friendly version name
 
 $branch   = '30';                       // This version's branch.
 $maturity = MATURITY_ALPHA;             // This version's maturity level.