Merge branch 'MDL-55611-master' of git://github.com/danpoltawski/moodle
authorDan Poltawski <dan@moodle.com>
Tue, 4 Apr 2017 16:07:41 +0000 (17:07 +0100)
committerDan Poltawski <dan@moodle.com>
Tue, 4 Apr 2017 16:07:41 +0000 (17:07 +0100)
28 files changed:
1  2 
blocks/recent_activity/tests/behat/structural_changes.feature
blocks/social_activities/tests/behat/edit_activities.feature
course/moodleform_mod.php
course/tests/behat/activities_visibility_icons.feature
course/tests/behat/section_visibility.feature
lang/en/calendar.php
lib/blocklib.php
lib/classes/plugin_manager.php
lib/db/install.xml
lib/db/services.php
lib/db/upgrade.php
lib/upgrade.txt
mod/assign/lang/en/assign.php
mod/assign/lib.php
mod/assign/locallib.php
mod/data/lib.php
mod/data/tests/lib_test.php
mod/feedback/lib.php
mod/forum/lib.php
mod/forum/tests/behat/forum_subscriptions_availability.feature
mod/label/tests/behat/label_visibility.feature
mod/lesson/lib.php
mod/lti/lib.php
mod/quiz/lib.php
mod/scorm/lib.php
mod/survey/lib.php
theme/bootstrapbase/style/moodle.css
version.php

Simple merge
@@@ -43,8 -42,8 +42,9 @@@ Feature: Show/hide course section
      And I add a "Forum" to section "3" and I fill the form with:
        | Forum name | Test hidden forum 32 name |
        | Description | Test hidden forum 32 description |
-     And I follow "Course 1"
 +      | Availability | Show on course page |
+       | Visible | Show |
+     And I am on "Course 1" course homepage
      When I hide section "1"
      Then section "1" should be hidden
      And section "2" should be visible
Simple merge
Simple merge
Simple merge
index 2fb5b85,2915e60..3b9525d
mode 100755,100644..100755
Simple merge
@@@ -2611,126 -2611,76 +2611,197 @@@ function xmldb_main_upgrade($oldversion
          upgrade_main_savepoint(true, 2017031400.00);
      }
  
 -    if ($oldversion < 2017040300.04) {
 +    if ($oldversion < 2017033100.01) {
 +
 +        // Define table oauth2_issuer to be created.
 +        $table = new xmldb_table('oauth2_issuer');
 +
 +        // Adding fields to table oauth2_issuer.
 +        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 +        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('image', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('baseurl', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('clientid', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('clientsecret', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('loginscopes', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('loginscopesoffline', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('loginparams', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('loginparamsoffline', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('alloweddomains', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('scopessupported', XMLDB_TYPE_TEXT, null, null, null, null, null);
 +        $table->add_field('showonloginpage', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1');
 +        $table->add_field('enabled', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1');
 +        $table->add_field('sortorder', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +
 +        // Adding keys to table oauth2_issuer.
 +        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 +
 +        // Conditionally launch create table for oauth2_issuer.
 +        if (!$dbman->table_exists($table)) {
 +            $dbman->create_table($table);
 +        }
 +
 +        // Main savepoint reached.
 +        upgrade_main_savepoint(true, 2017033100.01);
 +    }
 +
 +    if ($oldversion < 2017033100.02) {
 +
 +        // Define table oauth2_endpoint to be created.
 +        $table = new xmldb_table('oauth2_endpoint');
 +
 +        // Adding fields to table oauth2_endpoint.
 +        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 +        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('url', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('issuerid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +
 +        // Adding keys to table oauth2_endpoint.
 +        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 +        $table->add_key('issuer_id_key', XMLDB_KEY_FOREIGN, array('issuerid'), 'oauth2_issuer', array('id'));
 +
 +        // Conditionally launch create table for oauth2_endpoint.
 +        if (!$dbman->table_exists($table)) {
 +            $dbman->create_table($table);
 +        }
 +
 +        // Main savepoint reached.
 +        upgrade_main_savepoint(true, 2017033100.02);
 +    }
 +
 +    if ($oldversion < 2017033100.03) {
 +
 +        // Define table oauth2_system_account to be created.
 +        $table = new xmldb_table('oauth2_system_account');
 +
 +        // Adding fields to table oauth2_system_account.
 +        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 +        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('issuerid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('refreshtoken', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('grantedscopes', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('username', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('email', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 +
 +        // Adding keys to table oauth2_system_account.
 +        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 +        $table->add_key('issueridkey', XMLDB_KEY_FOREIGN_UNIQUE, array('issuerid'), 'oauth2_issuer', array('id'));
 +
 +        // Conditionally launch create table for oauth2_system_account.
 +        if (!$dbman->table_exists($table)) {
 +            $dbman->create_table($table);
 +        }
 +
 +        // Main savepoint reached.
 +        upgrade_main_savepoint(true, 2017033100.03);
 +    }
 +
 +    if ($oldversion < 2017033100.04) {
 +
 +        // Define table oauth2_user_field_mapping to be created.
 +        $table = new xmldb_table('oauth2_user_field_mapping');
 +
 +        // Adding fields to table oauth2_user_field_mapping.
 +        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 +        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('issuerid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('externalfield', XMLDB_TYPE_CHAR, '64', null, XMLDB_NOTNULL, null, null);
 +        $table->add_field('internalfield', XMLDB_TYPE_CHAR, '64', null, XMLDB_NOTNULL, null, null);
 +
 +        // Adding keys to table oauth2_user_field_mapping.
 +        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 +        $table->add_key('issuerkey', XMLDB_KEY_FOREIGN, array('issuerid'), 'oauth2_issuer', array('id'));
 +        $table->add_key('uniqinternal', XMLDB_KEY_UNIQUE, array('issuerid', 'internalfield'));
 +
 +        // Conditionally launch create table for oauth2_user_field_mapping.
 +        if (!$dbman->table_exists($table)) {
 +            $dbman->create_table($table);
 +        }
 +
 +        // Main savepoint reached.
 +        upgrade_main_savepoint(true, 2017033100.04);
 +    }
 +
++    if ($oldversion < 2017040400.00) {
+         // If the 'Course overview' block is no longer present, replace with the 'My overview' block.
+         if (!file_exists($CFG->dirroot . '/blocks/course_overview/block_course_overview.php')) {
+             $DB->set_field('block_instances', 'blockname', 'myoverview', array('blockname' => 'course_overview'));
+         }
 -        upgrade_main_savepoint(true, 2017040300.04);
++        upgrade_main_savepoint(true, 2017040400.00);
+     }
 -    if ($oldversion < 2017040300.05) {
++    if ($oldversion < 2017040401.00) {
+         // If the 'Course overview' block is no longer present, remove it.
+         // Note - we do not need to completely remove the block context etc because we
+         // have replaced all occurrences of the 'Course overview' block with the 'My overview'
+         // block in the upgrade step above.
+         if (!file_exists($CFG->dirroot . '/blocks/course_overview/block_course_overview.php')) {
+             // Delete the block from the block table.
+             $DB->delete_records('block', array('name' => 'course_overview'));
+             // Remove capabilities.
+             capabilities_cleanup('block_course_overview');
+             // Clean config.
+             unset_all_config_for_plugin('block_course_overview');
+         }
 -        upgrade_main_savepoint(true, 2017040300.05);
++        upgrade_main_savepoint(true, 2017040401.00);
+     }
 -    if ($oldversion < 2017040300.06) {
++    if ($oldversion < 2017040402.00) {
+         // Define fields to be added to the 'event' table.
+         $table = new xmldb_table('event');
+         $fieldtype = new xmldb_field('type', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, 0, 'instance');
+         $fieldtimesort = new xmldb_field('timesort', XMLDB_TYPE_INTEGER, '10', null, false, null, null, 'timeduration');
+         // Conditionally launch add field.
+         if (!$dbman->field_exists($table, $fieldtype)) {
+             $dbman->add_field($table, $fieldtype);
+         }
+         // Conditionally launch add field.
+         if (!$dbman->field_exists($table, $fieldtimesort)) {
+             $dbman->add_field($table, $fieldtimesort);
+         }
+         // Now, define the index we will be adding.
+         $index = new xmldb_index('type-timesort', XMLDB_INDEX_NOTUNIQUE, array('type', 'timesort'));
+         // Conditionally launch add index.
+         if (!$dbman->index_exists($table, $index)) {
+             $dbman->add_index($table, $index);
+         }
 -        upgrade_main_savepoint(true, 2017040300.06);
++        upgrade_main_savepoint(true, 2017040402.00);
+     }
 -    if ($oldversion < 2017040300.12) {
++    if ($oldversion < 2017040403.00) {
+         // Create adhoc task for upgrading of existing calendar events.
+         $record = new \stdClass();
+         $record->classname = "\\core\\task\\refresh_mod_calendar_events_task";
+         $record->component = 'core';
+         // Next run time based from nextruntime computation in \core\task\manager::queue_adhoc_task().
+         $nextruntime = time() - 1;
+         $record->nextruntime = $nextruntime;
+         $DB->insert_record('task_adhoc', $record);
+         // Main savepoint reached.
 -        upgrade_main_savepoint(true, 2017040300.12);
++        upgrade_main_savepoint(true, 2017040403.00);
+     }
      return true;
  }
diff --cc lib/upgrade.txt
Simple merge
Simple merge
@@@ -1692,35 -1710,105 +1710,135 @@@ function assign_check_updates_since(cm_
          $updates->grades->itemids = array_keys($grades);
      }
  
 +    // Now, teachers should see other students updates.
 +    if (has_capability('mod/assign:viewgrades', $cm->context)) {
 +        $params = array('id' => $cm->instance, 'since1' => $from, 'since2' => $from);
 +        $select = 'assignment = :id AND (timecreated > :since1 OR timemodified > :since2)';
 +
 +        if (groups_get_activity_groupmode($cm) == SEPARATEGROUPS) {
 +            $groupusers = array_keys(groups_get_activity_shared_group_members($cm));
 +            if (empty($groupusers)) {
 +                return $updates;
 +            }
 +            list($insql, $inparams) = $DB->get_in_or_equal($groupusers, SQL_PARAMS_NAMED);
 +            $select .= ' AND userid ' . $insql;
 +            $params = array_merge($params, $inparams);
 +        }
 +
 +        $updates->usersubmissions = (object) array('updated' => false);
 +        $submissions = $DB->get_records_select('assign_submission', $select, $params, '', 'id');
 +        if (!empty($submissions)) {
 +            $updates->usersubmissions->updated = true;
 +            $updates->usersubmissions->itemids = array_keys($submissions);
 +        }
 +
 +        $updates->usergrades = (object) array('updated' => false);
 +        $grades = $DB->get_records_select('assign_grades', $select, $params, '', 'id');
 +        if (!empty($grades)) {
 +            $updates->usergrades->updated = true;
 +            $updates->usergrades->itemids = array_keys($grades);
 +        }
 +    }
 +
      return $updates;
  }
+ /**
+  * Is the event visible?
+  *
+  * This is used to determine global visibility of an event in all places throughout Moodle. For example,
+  * the ASSIGN_EVENT_TYPE_GRADINGDUE event will not be shown to students on their calendar, and
+  * ASSIGN_EVENT_TYPE_DUE events will not be shown to teachers.
+  *
+  * @param calendar_event $event
+  * @return bool Returns true if the event is visible to the current user, false otherwise.
+  */
+ function mod_assign_core_calendar_is_event_visible(calendar_event $event) {
+     global $CFG, $USER;
+     require_once($CFG->dirroot . '/mod/assign/locallib.php');
+     $cm = get_fast_modinfo($event->courseid)->instances['assign'][$event->instance];
+     $context = context_module::instance($cm->id);
+     $assign = new assign($context, $cm, null);
+     if ($event->eventtype == ASSIGN_EVENT_TYPE_GRADINGDUE) {
+         return $assign->can_grade();
+     } else {
+         return !$assign->can_grade() && $assign->can_view_submission($USER->id);
+     }
+ }
+ /**
+  * Handles creating actions for events.
+  *
+  * @param calendar_event $event
+  * @param \core_calendar\action_factory $factory
+  * @return \core_calendar\local\event\value_objects\action|\core_calendar\local\interfaces\action_interface|null
+  */
+ function mod_assign_core_calendar_provide_event_action(calendar_event $event,
+                                                        \core_calendar\action_factory $factory) {
+     global $CFG, $USER;
+     require_once($CFG->dirroot . '/mod/assign/locallib.php');
+     $cm = get_fast_modinfo($event->courseid)->instances['assign'][$event->instance];
+     $context = context_module::instance($cm->id);
+     $assign = new assign($context, $cm, null);
+     // Apply overrides.
+     $assign->update_effective_access($USER->id);
+     if ($event->eventtype == ASSIGN_EVENT_TYPE_GRADINGDUE) {
+         $name = get_string('grade');
+         $url = new \moodle_url('/mod/assign/view.php', [
+             'id' => $cm->id,
+             'action' => 'grader'
+         ]);
+         $itemcount = $assign->count_submissions_need_grading();
+         $actionable = $assign->can_grade() && (time() >= $assign->get_instance()->allowsubmissionsfromdate);
+     } else {
+         $usersubmission = $assign->get_user_submission($USER->id, false);
+         if ($usersubmission && $usersubmission->status === ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
+             // The user has already submitted.
+             // We do not want to change the text to edit the submission, we want to remove the event from the Dashboard entirely.
+             return null;
+         }
+         // The user has not yet submitted anything. Show the addsubmission link.
+         $name = get_string('addsubmission', 'assign');
+         $url = new \moodle_url('/mod/assign/view.php', [
+             'id' => $cm->id,
+             'action' => 'editsubmission'
+         ]);
+         $itemcount = 1;
+         $actionable = $assign->is_any_submission_plugin_enabled() && $assign->can_edit_submission($USER->id);
+     }
+     return $factory->create_instance(
+         $name,
+         $url,
+         $itemcount,
+         $actionable
+     );
+ }
+ /**
+  * Callback function that determines whether an action event should be showing its item count
+  * based on the event type and the item count.
+  *
+  * @param calendar_event $event The calendar event.
+  * @param int $itemcount The item count associated with the action event.
+  * @return bool
+  */
+ function mod_assign_core_calendar_event_action_shows_item_count(calendar_event $event, $itemcount = 0) {
+     // List of event types where the action event's item count should be shown.
+     $eventtypesshowingitemcount = [
+         ASSIGN_EVENT_TYPE_GRADINGDUE
+     ];
+     // For mod_assign, item count should be shown if the event type is 'gradingdue' and there is one or more item count.
+     return in_array($event->eventtype, $eventtypesshowingitemcount) && $itemcount > 0;
+ }
Simple merge
Simple merge
Simple merge
@@@ -3357,35 -3366,39 +3366,69 @@@ function feedback_check_updates_since(c
          $updates->attemptsunfinished->itemids = array_keys($attemptsunfinished);
      }
  
 +    // Now, teachers should see other students updates.
 +    if (has_capability('mod/feedback:viewreports', $cm->context)) {
 +        $select = 'feedback = ? AND timemodified > ?';
 +        $params = array($cm->instance, $from);
 +
 +        if (groups_get_activity_groupmode($cm) == SEPARATEGROUPS) {
 +            $groupusers = array_keys(groups_get_activity_shared_group_members($cm));
 +            if (empty($groupusers)) {
 +                return $updates;
 +            }
 +            list($insql, $inparams) = $DB->get_in_or_equal($groupusers);
 +            $select .= ' AND userid ' . $insql;
 +            $params = array_merge($params, $inparams);
 +        }
 +
 +        $updates->userattemptsfinished = (object) array('updated' => false);
 +        $attemptsfinished = $DB->get_records_select('feedback_completed', $select, $params, '', 'id');
 +        if (!empty($attemptsfinished)) {
 +            $updates->userattemptsfinished->updated = true;
 +            $updates->userattemptsfinished->itemids = array_keys($attemptsfinished);
 +        }
 +
 +        $updates->userattemptsunfinished = (object) array('updated' => false);
 +        $attemptsunfinished = $DB->get_records_select('feedback_completedtmp', $select, $params, '', 'id');
 +        if (!empty($attemptsunfinished)) {
 +            $updates->userattemptsunfinished->updated = true;
 +            $updates->userattemptsunfinished->itemids = array_keys($attemptsunfinished);
 +        }
 +    }
 +
      return $updates;
  }
+ /**
+  * Handles creating actions for events.
+  *
+  * @param calendar_event $event
+  * @param \core_calendar\action_factory $factory
+  * @return \core_calendar\local\event\value_objects\action|\core_calendar\local\interfaces\action_interface|null
+  */
+ function mod_feedback_core_calendar_provide_event_action(calendar_event $event,
+                                                          \core_calendar\action_factory $factory) {
+     global $DB;
+     $cm = get_fast_modinfo($event->courseid)->instances['feedback'][$event->instance];
+     $feedback = $DB->get_record('feedback', ['id' => $event->instance], 'id, timeopen, timeclose');
+     $now = time();
+     if ($feedback->timeopen && $feedback->timeclose) {
+         $actionable = ($now >= $feedback->timeopen) && ($now <= $feedback->timeclose);
+     } else if ($feedback->timeclose) {
+         $actionable = $now < $feedback->timeclose;
+     } else if ($feedback->timeopen) {
+         $actionable = $now >= $feedback->timeopen;
+     } else {
+         $actionable = true;
+     }
+     return $factory->create_instance(
+         get_string('answerquestions', 'feedback'),
+         new \moodle_url('/mod/feedback/view.php', ['id' => $cm->id]),
+         1,
+         $actionable
+     );
+ }
Simple merge
@@@ -19,11 -19,10 +19,10 @@@ Feature: Check label visibility work
        | teacher | C1 | editingteacher |
        | student | C1 | student |
      Given I log in as "teacher"
-     And I follow "Test"
-     And I turn editing mode on
+     And I am on "Test" course homepage with editing mode on
      When I add a "label" to section "1" and I fill the form with:
        | Label text | Swanky label |
 -      | Visible | Hide |
 +      | Availability | Hide from students |
      Then "Swanky label" activity should be hidden
      And I turn editing mode off
      And "Swanky label" activity should be hidden
        | teacher | C1 | editingteacher |
        | student | C1 | student |
      Given I log in as "teacher"
-     And I follow "Test"
-     And I turn editing mode on
+     And I am on "Test" course homepage with editing mode on
      When I add a "label" to section "1" and I fill the form with:
        | Label text | Swanky label |
 -      | Visible | Show |
 +      | Availability | Show on course page |
      Then "Swanky label" activity should be visible
      And I log out
      And I log in as "student"
        | teacher | C1 | editingteacher |
        | student | C1 | student |
      Given I log in as "teacher"
-     And I follow "Test"
-     And I turn editing mode on
+     And I am on "Test" course homepage with editing mode on
      When I add a "label" to section "1" and I fill the form with:
        | Label text | Swanky label |
 -      | Visible | Show |
 +      | Availability | Show on course page |
      And I hide section "1"
      Then "Swanky label" activity should be dimmed
      And I open "Swanky label" actions menu
      And "Swanky label" actions menu should not have "Make unavailable" item
      And I click on "Edit settings" "link" in the "Swanky label" activity
      And I expand all fieldsets
 -    And the "Visible" select box should contain "Hidden from students"
 -    And the "Visible" select box should not contain "Available but not displayed on course page"
 -    And the "Visible" select box should not contain "Hide"
 -    And the "Visible" select box should not contain "Show"
 +    And the "Availability" select box should contain "Hide from students"
 +    And the "Availability" select box should not contain "Make available but not shown on course page"
 +    And the "Availability" select box should not contain "Show on course page"
      And I log out
      And I log in as "student"
-     And I follow "Test"
+     And I am on "Test" course homepage
      And I should not see "Swanky label"
      And I log out
@@@ -1556,54 -1564,31 +1564,80 @@@ function lesson_check_updates_since(cm_
          $updates->timers->itemids = array_keys($timers);
      }
  
 +    // Now, teachers should see other students updates.
 +    if (has_capability('mod/lesson:viewreports', $cm->context)) {
 +        $select = 'lessonid = ? AND timeseen > ?';
 +        $params = array($cm->instance, $from);
 +
 +        $insql = '';
 +        $inparams = [];
 +        if (groups_get_activity_groupmode($cm) == SEPARATEGROUPS) {
 +            $groupusers = array_keys(groups_get_activity_shared_group_members($cm));
 +            if (empty($groupusers)) {
 +                return $updates;
 +            }
 +            list($insql, $inparams) = $DB->get_in_or_equal($groupusers);
 +            $select .= ' AND userid ' . $insql;
 +            $params = array_merge($params, $inparams);
 +        }
 +
 +        $updates->userquestionattempts = (object) array('updated' => false);
 +        $updates->usergrades = (object) array('updated' => false);
 +        $updates->userpagesviewed = (object) array('updated' => false);
 +        $updates->usertimers = (object) array('updated' => false);
 +
 +        $questionattempts = $DB->get_records_select('lesson_attempts', $select, $params, '', 'id');
 +        if (!empty($questionattempts)) {
 +            $updates->userquestionattempts->updated = true;
 +            $updates->userquestionattempts->itemids = array_keys($questionattempts);
 +        }
 +        $pagesviewed = $DB->get_records_select('lesson_branch', $select, $params, '', 'id');
 +        if (!empty($pagesviewed)) {
 +            $updates->userpagesviewed->updated = true;
 +            $updates->userpagesviewed->itemids = array_keys($pagesviewed);
 +        }
 +
 +        $select = 'lessonid = ? AND completed > ? ' . $insql;
 +        $grades = $DB->get_records_select('lesson_grades', $select, $params, '', 'id');
 +        if (!empty($grades)) {
 +            $updates->usergrades->updated = true;
 +            $updates->usergrades->itemids = array_keys($grades);
 +        }
 +
 +        $select = 'lessonid = ? AND (starttime > ? OR lessontime > ? OR timemodifiedoffline > ?) ' . $insql;
 +        $params = array($cm->instance, $from, $from, $from);
 +        $params = array_merge($params, $inparams);
 +        $timers = $DB->get_records_select('lesson_timer', $select, $params, '', 'id');
 +        if (!empty($timers)) {
 +            $updates->usertimers->updated = true;
 +            $updates->usertimers->itemids = array_keys($timers);
 +        }
 +    }
      return $updates;
  }
+ /**
+  * Handles creating actions for events.
+  *
+  * @param calendar_event $event
+  * @param \core_calendar\action_factory $factory
+  * @return \core_calendar\local\event\value_objects\action|\core_calendar\local\interfaces\action_interface|null
+  */
+ function mod_lesson_core_calendar_provide_event_action(calendar_event $event,
+                                                        \core_calendar\action_factory $factory) {
+     global $DB, $CFG, $USER;
+     require_once($CFG->dirroot . '/mod/lesson/locallib.php');
+     $cm = get_fast_modinfo($event->courseid)->instances['lesson'][$event->instance];
+     $lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
+     // Apply overrides.
+     $lesson->update_effective_access($USER->id);
+     return $factory->create_instance(
+         get_string('startlesson', 'lesson'),
+         new \moodle_url('/mod/lesson/view.php', ['id' => $cm->id]),
+         1,
+         $lesson->is_accessible()
+     );
+ }
diff --cc mod/lti/lib.php
Simple merge
Simple merge
Simple merge
@@@ -1072,28 -1083,39 +1083,62 @@@ function survey_check_updates_since(cm_
          $updates->answers->updated = true;
          $updates->answers->itemids = array_keys($answers);
      }
 +
 +    // Now, teachers should see other students updates.
 +    if (has_capability('mod/survey:readresponses', $cm->context)) {
 +        $select = 'survey = ? AND time > ?';
 +        $params = array($cm->instance, $from);
 +
 +        if (groups_get_activity_groupmode($cm) == SEPARATEGROUPS) {
 +            $groupusers = array_keys(groups_get_activity_shared_group_members($cm));
 +            if (empty($groupusers)) {
 +                return $updates;
 +            }
 +            list($insql, $inparams) = $DB->get_in_or_equal($groupusers);
 +            $select .= ' AND userid ' . $insql;
 +            $params = array_merge($params, $inparams);
 +        }
 +
 +        $updates->useranswers = (object) array('updated' => false);
 +        $answers = $DB->get_records_select('survey_answers', $select, $params, '', 'id');
 +        if (!empty($answers)) {
 +            $updates->useranswers->updated = true;
 +            $updates->useranswers->itemids = array_keys($answers);
 +        }
 +    }
      return $updates;
  }
+ /**
+  * Handles creating actions for events.
+  *
+  * @param calendar_event $event
+  * @param \core_calendar\action_factory $factory
+  * @return \core_calendar\local\event\value_objects\action|\core_calendar\local\interfaces\action_interface|null
+  */
+ function mod_survey_core_calendar_provide_event_action(calendar_event $event,
+                                                       \core_calendar\action_factory $factory) {
+     $cm = get_fast_modinfo($event->courseid)->instances['survey'][$event->instance];
+     $context = context_module::instance($cm->id);
+     if (!has_capability('mod/survey:participate', $context)) {
+         return null;
+     }
+     $course = new stdClass();
+     $course->id = $event->courseid;
+     $completion = new \completion_info($course);
+     $completiondata = $completion->get_data($cm, false);
+     if ($completiondata->completionstate != COMPLETION_INCOMPLETE) {
+         return null;
+     }
+     return $factory->create_instance(
+         get_string('view'),
+         new \moodle_url('/mod/survey/view.php', ['id' => $cm->id]),
+         1,
+         true
+     );
+ }
Simple merge
diff --cc version.php
@@@ -29,7 -29,7 +29,7 @@@
  
  defined('MOODLE_INTERNAL') || die();
  
- $version  = 2017040301.00;              // YYYYMMDD      = weekly release date of this DEV branch.
 -$version  = 2017040300.12;              // YYYYMMDD      = weekly release date of this DEV branch.
++$version  = 2017040403.00;              // YYYYMMDD      = weekly release date of this DEV branch.
                                          //         RR    = release increments - 00 in DEV branches.
                                          //           .XX = incremental changes.