Merge branch 'MDL-47896-master' of git://github.com/zbdd/moodle
authorDan Poltawski <dan@moodle.com>
Thu, 30 Oct 2014 15:15:28 +0000 (15:15 +0000)
committerDan Poltawski <dan@moodle.com>
Thu, 30 Oct 2014 15:15:28 +0000 (15:15 +0000)
34 files changed:
admin/tool/monitor/classes/output/managesubs/rules.php
admin/tool/monitor/lang/en/tool_monitor.php
admin/tool/monitor/tests/behat/subscription.feature
backup/cc/cc2moodle.php
backup/converter/moodle1/handlerlib.php
grade/edit/tree/lib.php
grade/lib.php
grade/report/grader/lib.php
grade/report/singleview/classes/local/screen/grade.php
grade/report/singleview/classes/local/screen/tablelike.php
grade/report/singleview/classes/local/screen/user.php
grade/report/singleview/lang/en/gradereport_singleview.php
grade/report/singleview/styles.css
grade/report/user/lib.php
grade/tests/behat/grade_calculated_weights.feature
grade/tests/behat/grade_scales.feature
grade/tests/behat/grade_single_item_scales.feature
lang/en/moodle.php
mod/forum/db/upgrade.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/post.php
mod/forum/unsubscribeall.php
mod/forum/version.php
mod/lesson/essay.php
mod/quiz/classes/output/edit_renderer.php
mod/quiz/classes/structure.php
mod/quiz/edit_rest.php
mod/quiz/tests/behat/editing_click_delete_icon.feature
mod/quiz/yui/build/moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes-debug.js
mod/quiz/yui/build/moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes-min.js
mod/quiz/yui/build/moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes.js
mod/quiz/yui/src/toolboxes/js/toolbox.js
mod/scorm/index.php

index debe732..62182cc 100644 (file)
@@ -129,6 +129,7 @@ class rules extends \table_sql implements \renderable {
     public function col_select(\tool_monitor\rule $rule) {
         global $OUTPUT;
         $select = $rule->get_module_select($this->courseid);
+        $select->set_label(get_string('subscribeto', 'tool_monitor', $rule->get_name($this->context)), array('class' => 'accesshide'));
         return is_object($select) ? $OUTPUT->render($select) : $select;
     }
 
index a01dac4..5433d09 100644 (file)
@@ -93,6 +93,7 @@ $string['subcreatesuccess'] = "Subscription successfully created";
 $string['subdeletesuccess'] = "Subscription successfully removed";
 $string['subhelp'] = 'Subscription details';
 $string['subhelp_help'] = 'This subscription listens for when the event \'{$a->eventname}\' has been triggered in \'{$a->moduleinstance}\' {$a->frequency} time(s) in {$a->minutes} minute(s).';
+$string['subscribeto'] = 'Subscribe to rule "{$a}"';
 $string['system'] = "System";
 $string['taskcleanevents'] = 'Removes any unnecessary event monitor events';
 $string['title'] = '{$a->coursename} : {$a->reportname}';
index 0aa1d15..f189a2a 100644 (file)
@@ -44,8 +44,8 @@ Feature: tool_monitor_subscriptions
     Given I log in as "teacher1"
     And   I follow "Course 1"
     And   I navigate to "Event monitoring" node in "My profile settings"
-    And   I set the field "courseid" to "Course 1"
-    When  I set the field "cmid" to "All events"
+    And   I set the field "Select a course" to "Course 1"
+    When  I set the field "Subscribe to rule \"New rule course level\"" to "All events"
     Then  I should see "Subscription successfully created"
     And   "#toolmonitorsubs_r0" "css_element" should exist
 
@@ -53,8 +53,8 @@ Feature: tool_monitor_subscriptions
     Given I log in as "teacher1"
     And   I follow "Course 1"
     And   I navigate to "Event monitoring" node in "My profile settings"
-    And   I set the field "courseid" to "Course 1"
-    And   I set the field "cmid" to "All events"
+    And   I set the field "Select a course" to "Course 1"
+    And   I set the field "Subscribe to rule \"New rule course level\"" to "All events"
     And   I should see "Subscription successfully created"
     When  I click on "Delete subscription" "link" in the "New rule course level" "table_row"
     And   I should see "Are you sure you want to delete this subscription for the rule \"New rule course level\"?"
@@ -65,16 +65,16 @@ Feature: tool_monitor_subscriptions
   Scenario: Subscribe to a rule on site level
     Given I log in as "admin"
     And   I navigate to "Event monitoring" node in "My profile settings"
-    And   I set the field "courseid" to "Site"
-    When  I set the field "cmid" to "All events"
+    And   I set the field "Select a course" to "Site"
+    When  I set the field "Subscribe to rule \"New rule site level\"" to "All events"
     Then  I should see "Subscription successfully created"
     And   "#toolmonitorsubs_r0" "css_element" should exist
 
   Scenario: Delete a subscription on site level
     Given I log in as "admin"
     And   I navigate to "Event monitoring" node in "My profile settings"
-    And   I set the field "courseid" to "Site"
-    And   I set the field "cmid" to "All events"
+    And   I set the field "Select a course" to "Site"
+    And   I set the field "Subscribe to rule \"New rule site level\"" to "All events"
     And   I should see "Subscription successfully created"
     And   "#toolmonitorsubs_r0" "css_element" should exist
     When  I click on "Delete subscription" "link" in the "New rule site level" "table_row"
@@ -91,8 +91,8 @@ Feature: tool_monitor_subscriptions
     And   I am on homepage
     And   I follow "Course 1"
     And   I navigate to "Event monitoring" node in "My profile settings"
-    And   I set the field "courseid" to "Site"
-    And   I set the field "cmid" to "All events"
+    And   I set the field "Select a course" to "Site"
+    And   I set the field "Subscribe to rule \"New rule site level\"" to "All events"
     And   I should see "Subscription successfully created"
     And   "#toolmonitorsubs_r0" "css_element" should exist
     And   I am on homepage
@@ -110,8 +110,8 @@ Feature: tool_monitor_subscriptions
     And   I am on homepage
     And   I follow "Course 1"
     And   I navigate to "Event monitoring" node in "My profile settings"
-    And   I set the field "courseid" to "Course 1"
-    And   I set the field "cmid" to "All events"
+    And   I set the field "Select a course" to "Course 1"
+    And   I set the field "Subscribe to rule \"New rule course level\"" to "All events"
     And   I should see "Subscription successfully created"
     And   "#toolmonitorsubs_r0" "css_element" should exist
     And   I am on homepage
index 332a956..0728dac 100644 (file)
@@ -338,7 +338,7 @@ class cc2moodle {
 
                     $replace_values = array($i,
                                             $i - 1,
-                                            $topic['title'],
+                                            entities::safexml($topic['title']),
                                             $node_node_course_sections_section_mods_mod);
 
                 } else {
index 5265fa0..bbdfd7f 100644 (file)
@@ -1292,7 +1292,9 @@ class moodle1_question_bank_handler extends moodle1_xml_handler {
      * Closes the questions wrapper
      */
     public function on_questions_end() {
-        $this->xmlwriter->end_tag('questions');
+        if ($this->questionswrapperwritten) {
+            $this->xmlwriter->end_tag('questions');
+        }
     }
 
     /**
index d36add4..fe90e41 100644 (file)
@@ -113,7 +113,7 @@ class grade_edit_tree {
 
         $object = $element['object'];
         $eid    = $element['eid'];
-        $object->name = $this->gtree->get_element_header($element, true, true, true, true);
+        $object->name = $this->gtree->get_element_header($element, true, true, true, true, true);
         $object->stripped_name = $this->gtree->get_element_header($element, false, false, false);
 
         $is_category_item = false;
index 40aa1c5..3470a69 100644 (file)
@@ -1334,32 +1334,34 @@ class grade_structure {
      * @param bool  $icon Whether or not to display an icon with this header
      * @param bool  $spacerifnone return spacer if no icon found
      * @param bool  $withdescription Show description if defined by this item.
+     * @param bool  $fulltotal If the item is a category total, returns $categoryname."total"
+     *                         instead of "Category total" or "Course total"
      *
      * @return string header
      */
-    public function get_element_header(&$element, $withlink=false, $icon=true, $spacerifnone=false, $withdescription=false) {
+    public function get_element_header(&$element, $withlink = false, $icon = true, $spacerifnone = false,
+        $withdescription = false, $fulltotal = false) {
         $header = '';
 
         if ($icon) {
             $header .= $this->get_element_icon($element, $spacerifnone);
         }
 
-        $header .= $element['object']->get_name();
+        $header .= $element['object']->get_name($fulltotal);
 
         if ($element['type'] != 'item' and $element['type'] != 'categoryitem' and
             $element['type'] != 'courseitem') {
             return $header;
         }
 
-        if ($withlink) {
-            $url = $this->get_activity_link($element);
-            if ($url) {
-                $a = new stdClass();
-                $a->name = get_string('modulename', $element['object']->itemmodule);
-                $title = get_string('linktoactivity', 'grades', $a);
+        if ($withlink && $url = $this->get_activity_link($element)) {
+            $a = new stdClass();
+            $a->name = get_string('modulename', $element['object']->itemmodule);
+            $title = get_string('linktoactivity', 'grades', $a);
 
-                $header = html_writer::link($url, $header, array('title' => $title));
-            }
+            $header = html_writer::link($url, $header, array('title' => $title));
+        } else {
+            $header = html_writer::span($header);
         }
 
         if ($withdescription) {
index 26b4933..bdd7727 100644 (file)
@@ -816,7 +816,7 @@ class grade_report_grader extends grade_report {
                         $arrow = $this->get_sort_arrow('move', $sortlink);
                     }
 
-                    $headerlink = $this->gtree->get_element_header($element, true, $showactivityicons, false);
+                    $headerlink = $this->gtree->get_element_header($element, true, $showactivityicons, false, false, true);
 
                     $itemcell = new html_table_cell();
                     $itemcell->attributes['class'] = $type . ' ' . $catlevel . ' highlightable'. ' i'. $element['object']->id;
index ad20cdd..87c8cdb 100644 (file)
@@ -160,7 +160,6 @@ class grade extends tablelike implements selectable_items, filterable_items {
     public function original_headers() {
         return array(
             '', // For filter icon.
-            '', // For user picture.
             get_string('firstname') . ' (' . get_string('alternatename') . ') ' . get_string('lastname'),
             get_string('range', 'grades'),
             get_string('grade', 'grades'),
@@ -208,12 +207,31 @@ class grade extends tablelike implements selectable_items, filterable_items {
 
         $line = array(
             $OUTPUT->action_icon($this->format_link('user', $item->id), new pix_icon('t/editstring', $iconstring)),
-            $OUTPUT->user_picture($item),
+            $OUTPUT->user_picture($item, array('visibletoscreenreaders' => false)) .
             html_writer::link($url, $fullname),
             $this->item_range()
         );
+        $lineclasses = array(
+            "action",
+            "user",
+            "range"
+        );
+        $outputline = array();
+        $i = 0;
+        foreach ($line as $key => $value) {
+            $cell = new \html_table_cell($value);
+            if ($isheader = $i == 1) {
+                $cell->header = $isheader;
+                $cell->scope = "row";
+            }
+            if (array_key_exists($key, $lineclasses)) {
+                $cell->attributes['class'] = $lineclasses[$key];
+            }
+            $outputline[] = $cell;
+            $i++;
+        }
 
-        return $this->format_definition($line, $grade);
+        return $this->format_definition($outputline, $grade);
     }
 
     /**
@@ -267,6 +285,15 @@ class grade extends tablelike implements selectable_items, filterable_items {
         return $this->item->get_name();
     }
 
+    /**
+     * Get the summary for this table.
+     *
+     * @return string
+     */
+    public function summary() {
+        return get_string('summarygrade', 'gradereport_singleview');
+    }
+
     /**
      * Process the data from the form.
      *
index 937fad9..c718787 100644 (file)
@@ -59,6 +59,13 @@ abstract class tablelike extends screen {
      */
     public abstract function format_line($item);
 
+    /**
+     * Get the summary for this table.
+     *
+     * @return string
+     */
+    public abstract function summary();
+
     /**
      * Get the table headers
      *
@@ -166,6 +173,11 @@ abstract class tablelike extends screen {
 
         $table->head = $this->headers();
 
+        $summary = $this->summary();
+        if (!empty($summary)) {
+            $table->summary = $summary;
+        }
+
         // To be used for extra formatting.
         $this->index = 0;
         $this->total = count($this->items);
index c588d6e..74743c5 100644 (file)
@@ -139,7 +139,6 @@ class user extends tablelike implements selectable_items {
     public function original_headers() {
         return array(
             '', // For filter icon.
-            '', // For activity icon.
             get_string('assessmentname', 'gradereport_singleview'),
             get_string('gradecategory', 'grades'),
             get_string('range', 'grades'),
@@ -192,14 +191,38 @@ class user extends tablelike implements selectable_items {
         $gradetreeitem['object'] = $item;
         $gradetreeitem['userid'] = $this->item->id;
 
-        $itemlabel = $this->structure->get_element_header($gradetreeitem, true, false);
+        $itemlabel = $this->structure->get_element_header($gradetreeitem, true, false, false, false, true);
         $grade->label = $item->get_name();
 
         $line = array(
             $OUTPUT->action_icon($this->format_link('grade', $item->id), new pix_icon('t/editstring', $iconstring)),
-            $this->format_icon($item) . $lockicon, $itemlabel, $this->category($item), (new range($item))
+            $this->format_icon($item) . $lockicon . $itemlabel,
+            $this->category($item),
+            new range($item)
         );
-        return $this->format_definition($line, $grade);
+        $lineclasses = array(
+            "action",
+            "gradeitem",
+            "category",
+            "range"
+        );
+
+        $outputline = array();
+        $i = 0;
+        foreach ($line as $key => $value) {
+            $cell = new \html_table_cell($value);
+            if ($isheader = $i == 1) {
+                $cell->header = $isheader;
+                $cell->scope = "row";
+            }
+            if (array_key_exists($key, $lineclasses)) {
+                $cell->attributes['class'] = $lineclasses[$key];
+            }
+            $outputline[] = $cell;
+            $i++;
+        }
+
+        return $this->format_definition($outputline, $grade);
     }
 
     /**
@@ -252,6 +275,15 @@ class user extends tablelike implements selectable_items {
         return fullname($this->item);
     }
 
+    /**
+     * Get the summary for this table.
+     *
+     * @return string
+     */
+    public function summary() {
+        return get_string('summaryuser', 'gradereport_singleview');
+    }
+
     /**
      * Default pager
      *
index 2b5bd32..7264b71 100644 (file)
@@ -46,3 +46,5 @@ $string['overridefor'] = 'Override for {$a}';
 $string['overridenone'] = 'Override no grades';
 $string['pluginname'] = 'Single view';
 $string['singleview:view'] = 'View report';
+$string['summarygrade'] = 'A table of users, with columns for range, grade, feedback, and whether to override or exclude a particular grade.';
+$string['summaryuser'] = 'A table of grade items, with columns for grade category, range, grade, feedback, and whether to override or exclude a particular grade.';
index cdfef58..a2a7470 100644 (file)
 .path-grade-report-singleview input[name^="finalgrade"] {
     width: 50px;
 }
+.path-grade-report-singleview .generaltable tbody th {
+  white-space: nowrap;
+}
+.path-grade-report-singleview .generaltable tbody th > * {
+  display: inline-block;
+  vertical-align: middle;
+  margin: 0 2px;
+}
 
 .path-grade-report-singleview #region-main h2, .paging{
    text-align: center;
index e02c99f..a7f8633 100644 (file)
@@ -385,7 +385,7 @@ class grade_report_user extends grade_report {
         $grade_object = $element['object'];
         $eid = $grade_object->id;
         $element['userid'] = $this->user->id;
-        $fullname = $this->gtree->get_element_header($element, true, true, true, true);
+        $fullname = $this->gtree->get_element_header($element, true, true, true, true, true);
         $data = array();
         $hidden = '';
         $excluded = '';
index 13d1177..2b5dde9 100644 (file)
@@ -225,7 +225,7 @@ Feature: We can understand the gradebook user report
       | Test assignment four | 33.33 % | 10.00 | 1.11 % |
       | Test assignment five | 33.33 % | 70.00 | 7.78 % |
       | Test assignment six | 33.33 % | 30.00 | 3.33 % |
-      | Category totalWeighted mean of grades. | 33.33 % | 36.67 | - |
+      | Sub category totalWeighted mean of grades. | 33.33 % | 36.67 | - |
       | Course total | - | 156.67 | - |
 
   @javascript
@@ -244,5 +244,5 @@ Feature: We can understand the gradebook user report
       | Test assignment four | 33.33 % | 10.00 | 2.00 % |
       | Test assignment five | 33.33 % | 70.00 | 14.00 % |
       | Test assignment six | 33.33 % | 30.00 | 6.00 % |
-      | Category total | 60.00 % | 110.00 | - |
+      | Sub category total | 60.00 % | 110.00 | - |
       | Course total | - | 230.00 | - |
index c04f0f8..67e98f4 100644 (file)
@@ -90,13 +90,13 @@ Feature: View gradebook when scales are used
     And the following should exist in the "user-grade" table:
       | Grade item          | Grade | Range | Percentage | Contribution to course total |
       | Test assignment one | C     | F–A   | 50.00 %    | 60.00 %                      |
-      | Category total      | 3.00  | 0–5   | 60.00 %    | -                            |
+      | Sub category 1 total      | 3.00  | 0–5   | 60.00 %    | -                            |
       | Course total        | 3.00  | 0–5   | 60.00 %    | -                            |
     And I set the field "jump" to "Categories and items"
     And the following should exist in the "grade_edit_tree_table" table:
       | Name                | Max grade |
       | Test assignment one | 5.00      |
-      | Category total      | 5.00      |
+      | Sub category 1 total      | 5.00      |
       | Course total        | 5.00      |
     And I log out
     And I log in as "student2"
@@ -105,7 +105,7 @@ Feature: View gradebook when scales are used
     And the following should exist in the "user-grade" table:
       | Grade item          | Grade | Range | Percentage | Contribution to course total |
       | Test assignment one | B     | F–A   | 75.00 %    | 80.00 %                      |
-      | Category total      | 4.00  | 0–5   | 80.00 %    | -                            |
+      | Sub category 1 total      | 4.00  | 0–5   | 80.00 %    | -                            |
       | Course total        | 4.00  | 0–5   | 80.00 %    | -                            |
 
   @javascript
@@ -138,13 +138,13 @@ Feature: View gradebook when scales are used
     And the following should exist in the "user-grade" table:
       | Grade item                   | Grade          | Range | Percentage    | Contribution to course total |
       | Test assignment one          | C              | F–A   | 50.00 %       | <contrib3>                   |
-      | Category total<aggregation>. | 3.00           | 1–5   | 50.00 %       | -                            |
+      | Sub category (<aggregation>) total<aggregation>. | 3.00           | 1–5   | 50.00 %       | -                            |
       | Course total<aggregation>.   | <coursetotal3> | 0–100 | <courseperc3> | -                            |
     And I set the field "jump" to "Categories and items"
     And the following should exist in the "grade_edit_tree_table" table:
       | Name                | Max grade |
       | Test assignment one | A (5)     |
-      | Category total<aggregation>. |           |
+      | Sub category (<aggregation>) total<aggregation>. |           |
       | Course total<aggregation>.   |           |
     And I log out
     And I log in as "student2"
@@ -153,7 +153,7 @@ Feature: View gradebook when scales are used
     And the following should exist in the "user-grade" table:
       | Grade item                   | Grade          | Range | Percentage    | Contribution to course total |
       | Test assignment one          | B              | F–A   | 75.00 %       | <contrib2>                   |
-      | Category total<aggregation>. | 4.00           | 1–5   | 75.00 %       | -                            |
+      | Sub category (<aggregation>) total<aggregation>. | 4.00           | 1–5   | 75.00 %       | -                            |
       | Course total<aggregation>.   | <coursetotal2> | 0–100 | <courseperc2> | -                            |
 
     Examples:
index 5a83d48..200b209 100644 (file)
@@ -71,19 +71,19 @@ Feature: View gradebook when single item scales are used
     And the following should exist in the "user-grade" table:
       | Grade item          | Grade | Range     | Contribution to course total |
       | Test assignment one | Ace!  | Ace!–Ace! | 100.00 %                     |
-      | Category total      | 1.00  | 0–1       | -                            |
+      | Sub category 1 total      | 1.00  | 0–1       | -                            |
       | Course total        | 1.00  | 0–1       | -                            |
     And I set the field "Select all or one user" to "Student 2"
     And the following should exist in the "user-grade" table:
       | Grade item          | Grade | Range     | Contribution to course total |
       | Test assignment one | -     | Ace!–Ace! | -                            |
-      | Category total      | -     | 0–1       | -                            |
+      | Sub category 1 total      | -     | 0–1       | -                            |
       | Course total        | -     | 0–1       | -                            |
     And I set the field "jump" to "Categories and items"
     And the following should exist in the "grade_edit_tree_table" table:
       | Name                | Max grade |
       | Test assignment one | 1.00      |
-      | Category total      | 1.00      |
+      | Sub category 1 total      | 1.00      |
       | Course total        | 1.00      |
 
   @javascript
@@ -113,13 +113,13 @@ Feature: View gradebook when single item scales are used
     And the following should exist in the "user-grade" table:
       | Grade item                        | Grade          | Range       | Contribution to course total |
       | Test assignment one               | Ace!           | Ace!–Ace!   | <contrib1>                   |
-      | Category total<aggregation>.      | <cattotal1>    | 0–100       | -                            |
+      | Sub category (<aggregation>) total<aggregation>.      | <cattotal1>    | 0–100       | -                            |
       | Course total<aggregation>.        | <coursetotal1> | 0–100       | -                            |
     And I set the field "jump" to "Categories and items"
     And the following should exist in the "grade_edit_tree_table" table:
       | Name                         | Max grade |
       | Test assignment one          | Ace! (1)  |
-      | Category total<aggregation>. | 100.00    |
+      | Sub category (<aggregation>) total<aggregation>. | 100.00    |
       | Course total<aggregation>.   | 100.00    |
 
     Examples:
index 5caa783..9d8ee54 100644 (file)
@@ -160,8 +160,8 @@ $string['authenticateduserdescription'] = 'All logged in users.';
 $string['authentication'] = 'Authentication';
 $string['authenticationplugins'] = 'Authentication plugins';
 $string['autosubscribe'] = 'Forum auto-subscribe';
-$string['autosubscribeno'] = 'No: don\'t automatically subscribe me to forums';
-$string['autosubscribeyes'] = 'Yes: when I post, subscribe me to that forum';
+$string['autosubscribeno'] = 'No: don\'t automatically subscribe me to forum discussions';
+$string['autosubscribeyes'] = 'Yes: when I post, subscribe me to that forum discussion';
 $string['availability'] = 'Availability';
 $string['availablecourses'] = 'Available courses';
 $string['back'] = 'Back';
index c241541..06a9377 100644 (file)
@@ -200,5 +200,29 @@ function xmldb_forum_upgrade($oldversion) {
         upgrade_mod_savepoint(true, 2014081900, 'forum');
     }
 
+    if ($oldversion < 2014103000) {
+        // Find records with multiple userid/postid combinations and find the lowest ID.
+        // Later we will remove all those which don't match this ID.
+        $sql = "
+            SELECT MIN(id) as lowid, userid, postid
+            FROM {forum_read}
+            GROUP BY userid, postid
+            HAVING COUNT(id) > 1";
+
+        if ($duplicatedrows = $DB->get_recordset_sql($sql)) {
+            foreach ($duplicatedrows as $row) {
+                $DB->delete_records_select('forum_read', 'userid = ? AND postid = ? AND id <> ?', array(
+                    $row->userid,
+                    $row->postid,
+                    $row->lowid,
+                ));
+            }
+        }
+        $duplicatedrows->close();
+
+        // Forum savepoint reached.
+        upgrade_mod_savepoint(true, 2014103000, 'forum');
+    }
+
     return true;
 }
index 4ef1c54..85ce9cd 100644 (file)
@@ -490,7 +490,9 @@ $string['unreadpostsnumber'] = '{$a} unread posts';
 $string['unreadpostsone'] = '1 unread post';
 $string['unsubscribe'] = 'Unsubscribe from this forum';
 $string['unsubscribeall'] = 'Unsubscribe from all forums';
-$string['unsubscribeallconfirm'] = 'You are subscribed to {$a} forums now. Do you really want to unsubscribe from all forums and disable forum auto-subscribe?';
+$string['unsubscribeallconfirm'] = 'You are currently subscribed to {$a->forums} forums, and {$a->discussions} discussions. Do you really want to unsubscribe from all forums and discussions, and disable discussion auto-subscription?';
+$string['unsubscribeallconfirmforums'] = 'You are currently subscribed to {$a->forums} forums. Do you really want to unsubscribe from all forums and disable discussion auto-subscription?';
+$string['unsubscribeallconfirmdiscussions'] = 'You are currently subscribed to {$a->discussions} discussions. Do you really want to unsubscribe from all discussions and disable discussion auto-subscription?';
 $string['unsubscribealldone'] = 'All optional forum subscriptions were removed. You will still receive notifications from forums with forced subscription. To manage forum notifications go to Messaging in My Profile Settings.';
 $string['unsubscribeallempty'] = 'You are not subscribed to any forums. To disable all notifications from this server go to Messaging in My Profile Settings.';
 $string['unsubscribed'] = 'Unsubscribed';
index 1ecd6c3..54fcac2 100644 (file)
@@ -5962,57 +5962,58 @@ function forum_tp_mark_posts_read($user, $postids) {
         return $status;
     }
 
-    list($usql, $params) = $DB->get_in_or_equal($postids);
-    $params[] = $user->id;
+    list($usql, $postidparams) = $DB->get_in_or_equal($postids, SQL_PARAMS_NAMED, 'postid');
+
+    $insertparams = array(
+        'userid1' => $user->id,
+        'userid2' => $user->id,
+        'userid3' => $user->id,
+        'firstread' => $now,
+        'lastread' => $now,
+        'cutoffdate' => $cutoffdate,
+    );
+    $params = array_merge($postidparams, $insertparams);
 
-    $sql = "SELECT id
-              FROM {forum_read}
-             WHERE postid $usql AND userid = ?";
-    if ($existing = $DB->get_records_sql($sql, $params)) {
-        $existing = array_keys($existing);
+    if ($CFG->forum_allowforcedreadtracking) {
+        $trackingsql = "AND (f.trackingtype = ".FORUM_TRACKING_FORCED."
+                        OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND tf.id IS NULL))";
     } else {
-        $existing = array();
-    }
-
-    $new = array_diff($postids, $existing);
-
-    if ($new) {
-        list($usql, $new_params) = $DB->get_in_or_equal($new);
-        $params = array($user->id, $now, $now, $user->id);
-        $params = array_merge($params, $new_params);
-        $params[] = $cutoffdate;
-
-        if ($CFG->forum_allowforcedreadtracking) {
-            $trackingsql = "AND (f.trackingtype = ".FORUM_TRACKING_FORCED."
-                            OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND tf.id IS NULL))";
-        } else {
-            $trackingsql = "AND ((f.trackingtype = ".FORUM_TRACKING_OPTIONAL."  OR f.trackingtype = ".FORUM_TRACKING_FORCED.")
-                                AND tf.id IS NULL)";
-        }
-
-        $sql = "INSERT INTO {forum_read} (userid, postid, discussionid, forumid, firstread, lastread)
-
-                SELECT ?, p.id, p.discussion, d.forum, ?, ?
-                  FROM {forum_posts} p
-                       JOIN {forum_discussions} d       ON d.id = p.discussion
-                       JOIN {forum} f                   ON f.id = d.forum
-                       LEFT JOIN {forum_track_prefs} tf ON (tf.userid = ? AND tf.forumid = f.id)
-                 WHERE p.id $usql
-                       AND p.modified >= ?
-                       $trackingsql";
-        $status = $DB->execute($sql, $params) && $status;
-    }
-
-    if ($existing) {
-        list($usql, $new_params) = $DB->get_in_or_equal($existing);
-        $params = array($now, $user->id);
-        $params = array_merge($params, $new_params);
-
-        $sql = "UPDATE {forum_read}
-                   SET lastread = ?
-                 WHERE userid = ? AND postid $usql";
-        $status = $DB->execute($sql, $params) && $status;
-    }
+        $trackingsql = "AND ((f.trackingtype = ".FORUM_TRACKING_OPTIONAL."  OR f.trackingtype = ".FORUM_TRACKING_FORCED.")
+                            AND tf.id IS NULL)";
+    }
+
+    // First insert any new entries.
+    $sql = "INSERT INTO {forum_read} (userid, postid, discussionid, forumid, firstread, lastread)
+
+            SELECT :userid1, p.id, p.discussion, d.forum, :firstread, :lastread
+                FROM {forum_posts} p
+                    JOIN {forum_discussions} d       ON d.id = p.discussion
+                    JOIN {forum} f                   ON f.id = d.forum
+                    LEFT JOIN {forum_track_prefs} tf ON (tf.userid = :userid2 AND tf.forumid = f.id)
+                    LEFT JOIN {forum_read} fr        ON (
+                            fr.userid = :userid3
+                        AND fr.postid = p.id
+                        AND fr.discussionid = d.id
+                        AND fr.forumid = f.id
+                    )
+                WHERE p.id $usql
+                    AND p.modified >= :cutoffdate
+                    $trackingsql
+                    AND fr.id IS NULL";
+
+    $status = $DB->execute($sql, $params) && $status;
+
+    // Then update all records.
+    $updateparams = array(
+        'userid' => $user->id,
+        'lastread' => $now,
+    );
+    $params = array_merge($postidparams, $updateparams);
+    $status = $DB->set_field_select('forum_read', 'lastread', $now, '
+                userid      =  :userid
+            AND lastread    <> :lastread
+            AND postid      ' . $usql,
+            $params) && $status;
 
     return $status;
 }
index f9b7454..3464d6b 100644 (file)
@@ -616,9 +616,8 @@ $postid = empty($post->id) ? null : $post->id;
 $draftid_editor = file_get_submitted_draft_itemid('message');
 $currenttext = file_prepare_draft_area($draftid_editor, $modcontext->id, 'mod_forum', 'post', $postid, mod_forum_post_form::editor_options($modcontext, $postid), $post->message);
 
-// Always suggest that the user be subscribed to a discussion that they're posting in unless they've already posted, in
-// which case use their existing preference.
-$discussionsubscribe = true;
+// Respect the user's discussion autosubscribe preference unless they have already posted - in which case, use that preference.
+$discussionsubscribe = $USER->autosubscribe;
 if (isset($discussion) && forum_user_has_posted($forum->id, $discussion->id, $USER->id)) {
     $discussionsubscribe = \mod_forum\subscriptions::is_subscribed($USER->id, $forum, $discussion->id, $cm);
 }
index acecc23..42cfcd2 100644 (file)
@@ -52,6 +52,7 @@ if (data_submitted() and $confirm and confirm_sesskey()) {
     foreach($forums as $forum) {
         \mod_forum\subscriptions::unsubscribe_user($USER->id, $forum, context_module::instance($forum->cm), true);
     }
+    $DB->delete_records('forum_discussion_subs', array('userid' => $USER->id));
     $DB->set_field('user', 'autosubscribe', 0, array('id'=>$USER->id));
 
     echo $OUTPUT->box(get_string('unsubscribealldone', 'forum'));
@@ -60,10 +61,18 @@ if (data_submitted() and $confirm and confirm_sesskey()) {
     die;
 
 } else {
-    $a = count(\mod_forum\subscriptions::get_unsubscribable_forums());
+    $count = new stdClass();
+    $count->forums = count(\mod_forum\subscriptions::get_unsubscribable_forums());
+    $count->discussions = $DB->count_records('forum_discussion_subs', array('userid' => $USER->id));
 
-    if ($a) {
-        $msg = get_string('unsubscribeallconfirm', 'forum', $a);
+    if ($count->forums || $count->discussions) {
+        if ($count->forums && $count->discussions) {
+            $msg = get_string('unsubscribeallconfirm', 'forum', $count);
+        } elseif ($count->forums) {
+            $msg = get_string('unsubscribeallconfirmforums', 'forum', $count);
+        } elseif ($count->discussions) {
+            $msg = get_string('unsubscribeallconfirmdiscussions', 'forum', $count);
+        }
         echo $OUTPUT->confirm($msg, new moodle_url('unsubscribeall.php', array('confirm'=>1)), $return);
         echo $OUTPUT->footer();
         die;
index fc107f3..5cbe482 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2014082101;       // The current module version (Date: YYYYMMDDXX)
+$plugin->version   = 2014103000;       // The current module version (Date: YYYYMMDDXX)
 $plugin->requires  = 2014050800;       // Requires this Moodle version
 $plugin->component = 'mod_forum';      // Full name of the plugin (used for diagnostics)
index 11e58a4..94331dc 100644 (file)
@@ -182,7 +182,7 @@ switch ($mode) {
 
         $pages = $lesson->load_all_pages();
         foreach ($pages as $key=>$page) {
-            if ($page->qtype !== LESSON_PAGE_ESSAY) {
+            if ($page->qtype != LESSON_PAGE_ESSAY) {
                 unset($pages[$key]);
             }
         }
@@ -265,7 +265,7 @@ switch ($mode) {
         // Get lesson pages that are essay
         $pages = $lesson->load_all_pages();
         foreach ($pages as $key=>$page) {
-            if ($page->qtype !== LESSON_PAGE_ESSAY) {
+            if ($page->qtype != LESSON_PAGE_ESSAY) {
                 unset($pages[$key]);
             }
         }
index 3e5574c..b123e19 100644 (file)
@@ -939,6 +939,7 @@ class edit_renderer extends \plugin_renderer_base {
                 'confirmremovequestion',
                 'dragtoafter',
                 'dragtostart',
+                'numquestionsx',
                 'removepagebreak',
         ), 'quiz');
 
index c81f7ae..ac6405b 100644 (file)
@@ -613,6 +613,8 @@ class structure {
             question_delete_question($slot->questionid);
         }
 
+        unset($this->questions[$slot->questionid]);
+
         $this->refresh_page_numbers_and_update_db($quiz);
 
         $trans->allow_commit();
index 1896d34..9bfd958 100644 (file)
@@ -134,7 +134,7 @@ switch($requestmethod) {
                 quiz_delete_previews($quiz);
                 quiz_update_sumgrades($quiz);
                 echo json_encode(array('newsummarks' => quiz_format_grade($quiz, $quiz->sumgrades),
-                            'deleted' => true));
+                            'deleted' => true, 'newnumquestions' => $structure->get_question_count()));
                 break;
         }
         break;
index 60f0bcf..a2b47ae 100644 (file)
@@ -38,6 +38,8 @@ Feature: Edit quiz page - delete
     And I should see "Question B" on quiz page "1"
     And I should see "Question C" on quiz page "2"
     And I should see "Total of marks: 3.00"
+    And I should see "Questions: 3"
+    And I should see "This quiz is open"
 
     # Delete last question in last page. Page contains multiple questions
     When I delete "Question C" in the quiz by clicking the delete icon
@@ -45,6 +47,7 @@ Feature: Edit quiz page - delete
     And I should see "Question B" on quiz page "1"
     And I should not see "Question C" on quiz page "2"
     And I should see "Total of marks: 2.00"
+    And I should see "Questions: 2"
 
     # Delete last question in last page. The page contains multiple questions and there are multiple pages.
     When I click on the "Add" page break icon after question "Question A"
index e005dd6..46637e9 100644 (file)
Binary files a/mod/quiz/yui/build/moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes-debug.js and b/mod/quiz/yui/build/moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes-debug.js differ
index 963665b..ce8eba7 100644 (file)
Binary files a/mod/quiz/yui/build/moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes-min.js and b/mod/quiz/yui/build/moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes-min.js differ
index e005dd6..46637e9 100644 (file)
Binary files a/mod/quiz/yui/build/moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes.js and b/mod/quiz/yui/build/moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes.js differ
index 2e1eb75..d87f4f7 100644 (file)
@@ -51,6 +51,7 @@
         INSTANCEMAXMARK : 'span.instancemaxmark',
         MODINDENTDIV : '.mod-indent',
         MODINDENTOUTER : '.mod-indent-outer',
+        NUMQUESTIONS : '.numberofquestions',
         PAGECONTENT : 'div#page-content',
         PAGELI : 'li.page',
         SECTIONUL : 'ul.section',
@@ -124,6 +125,9 @@ Y.extend(TOOLBOX, Y.Base, {
                     if (responsetext.newsummarks) {
                         Y.one(SELECTOR.SUMMARKS).setHTML(responsetext.newsummarks);
                     }
+                    if (responsetext.newnumquestions) {
+                        Y.one(SELECTOR.NUMQUESTIONS).setHTML(M.util.get_string('numquestionsx', 'quiz', responsetext.newnumquestions));
+                    }
                     if (success_callback) {
                         Y.bind(success_callback, this, responsetext)();
                     }
index e92e8ee..34b8418 100644 (file)
@@ -90,7 +90,7 @@ foreach ($scorms as $scorm) {
         $trackedusers = scorm_get_count_users($scorm->id, $scorm->groupingid);
         if ($trackedusers > 0) {
             $reportshow = html_writer::link('report.php?id='.$scorm->coursemodule,
-                                                get_string('viewallreports', 'scorm', $trackedusers)).html_writer::end_div();
+                                                get_string('viewallreports', 'scorm', $trackedusers));
         } else {
             $reportshow = get_string('noreports', 'scorm');
         }