diff --git a/mod/forum/discuss.php b/mod/forum/discuss.php index 88e36cb84ab..dcded7a05eb 100644 --- a/mod/forum/discuss.php +++ b/mod/forum/discuss.php @@ -225,6 +225,8 @@ $PAGE->set_title("$course->shortname: ".format_string($discussion->name)); $PAGE->set_heading($course->fullname); $PAGE->set_button($searchform); + $renderer = $PAGE->get_renderer('mod_forum'); + echo $OUTPUT->header(); $headingvalue = format_string($forum->name); @@ -256,6 +258,10 @@ } } + // Output the links to neighbour discussions. + $neighbours = forum_get_discussion_neighbours($cm, $discussion); + echo $renderer->neighbouring_discussion_navigation($neighbours['prev'], $neighbours['next']); + /// Print the controls across the top echo '
Your post was successfully added.
You have {$a} to edit it if you want to make any changes.
'; diff --git a/mod/forum/lib.php b/mod/forum/lib.php index 024db759cb7..232c3ede509 100644 --- a/mod/forum/lib.php +++ b/mod/forum/lib.php @@ -2638,6 +2638,95 @@ function forum_get_discussions($cm, $forumsort="d.timemodified DESC", $fullpost= return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum); } +/** + * Gets the neighbours (previous and next) of a discussion. + * + * The calculation is based on the timemodified of the discussion and does not handle + * the neighbours having an identical timemodified. The reason is that we do not have any + * other mean to sort the records, e.g. we cannot use IDs as a greater ID can have a lower + * timemodified. + * + * Please note that this does not check whether or not the discussion passed is accessible + * by the user, it simply uses it as a reference to find the neighbours. On the other hand, + * the returned neighbours are checked and are accessible to the current user. + * + * @param object $cm The CM record. + * @param object $discussion The discussion record. + * @return array That always contains the keys 'prev' and 'next'. When there is a result + * they contain the record with minimal information such as 'id' and 'name'. + * When the neighbour is not found the value is false. + */ +function forum_get_discussion_neighbours($cm, $discussion) { + global $CFG, $DB, $USER; + + if ($cm->instance != $discussion->forum) { + throw new coding_exception('Discussion is not part of the same forum.'); + } + + $neighbours = array('prev' => false, 'next' => false); + $now = round(time(), -2); + $params = array(); + + $modcontext = context_module::instance($cm->id); + $groupmode = groups_get_activity_groupmode($cm); + $currentgroup = groups_get_activity_group($cm); + + // Users must fulfill timed posts. + $timelimit = ''; + if (!empty($CFG->forum_enabletimedposts)) { + if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) { + $timelimit = ' AND ((d.timestart <= :tltimestart AND (d.timeend = 0 OR d.timeend > :tltimeend))'; + $params['tltimestart'] = $now; + $params['tltimeend'] = $now; + if (isloggedin()) { + $timelimit .= ' OR d.userid = :tluserid'; + $params['tluserid'] = $USER->id; + } + $timelimit .= ')'; + } + } + + // Limiting to posts accessible according to groups. + $groupselect = ''; + if ($groupmode) { + if ($groupmode == VISIBLEGROUPS || has_capability('moodle/site:accessallgroups', $modcontext)) { + if ($currentgroup) { + $groupselect = 'AND (d.groupid = :groupid OR d.groupid = -1)'; + $params['groupid'] = $currentgroup; + } + } else { + if ($currentgroup) { + $groupselect = 'AND (d.groupid = :groupid OR d.groupid = -1)'; + $params['groupid'] = $currentgroup; + } else { + $groupselect = 'AND d.groupid = -1'; + } + } + } + + $params['forumid'] = $cm->instance; + $params['discid'] = $discussion->id; + $params['disctimemodified'] = $discussion->timemodified; + + $sql = "SELECT d.id, d.name, d.timemodified, d.groupid, d.timestart, d.timeend + FROM {forum_discussions} d + WHERE d.forum = :forumid + AND d.id <> :discid + $timelimit + $groupselect"; + + $prevsql = $sql . " AND d.timemodified < :disctimemodified + ORDER BY d.timemodified DESC"; + + $nextsql = $sql . " AND d.timemodified > :disctimemodified + ORDER BY d.timemodified ASC"; + + $neighbours['prev'] = $DB->get_record_sql($prevsql, $params, IGNORE_MULTIPLE); + $neighbours['next'] = $DB->get_record_sql($nextsql, $params, IGNORE_MULTIPLE); + + return $neighbours; +} + /** * * @global object diff --git a/mod/forum/renderer.php b/mod/forum/renderer.php index f479e776602..237ee7788c5 100644 --- a/mod/forum/renderer.php +++ b/mod/forum/renderer.php @@ -32,6 +32,39 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later **/ class mod_forum_renderer extends plugin_renderer_base { + + /** + * Returns the navigation to the previous and next discussion. + * + * @param mixed $prev Previous discussion record, or false. + * @param mixed $next Next discussion record, or false. + * @return string The output. + */ + public function neighbouring_discussion_navigation($prev, $next) { + $html = ''; + if ($prev || $next) { + $html .= html_writer::start_tag('div', array('class' => 'discussion-nav clearfix')); + $html .= html_writer::start_tag('ul'); + 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::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::end_tag('li'); + } + $html .= html_writer::end_tag('ul'); + $html .= html_writer::end_tag('div'); + } + return $html; + } + /** * This method is used to generate HTML for a subscriber selection form that * uses two user_selector controls diff --git a/mod/forum/styles.css b/mod/forum/styles.css index aefeb13a420..3c239bb0a89 100644 --- a/mod/forum/styles.css +++ b/mod/forum/styles.css @@ -112,3 +112,28 @@ span.unread { .forumpost.unread .row.header { border-bottom: 1px solid #DDD; } + +/* Discussion navigation */ +.path-mod-forum .discussion-nav { + margin: .5em 0; +} +.path-mod-forum .discussion-nav ul { + margin: 0; + list-style: none; +} +.dir-rtl.path-mod-forum .discussion-nav .next-discussion:after, +.path-mod-forum .discussion-nav .prev-discussion:before { + content: ' ◄ '; +} +.dir-rtl.path-mod-forum .discussion-nav .prev-discussion:before, +.path-mod-forum .discussion-nav .next-discussion:after { + content: ' ► '; +} +.dir-rtl.path-mod-forum .discussion-nav .prev-discussion, +.path-mod-forum .discussion-nav .next-discussion { + float: right; +} +.dir-rtl.path-mod-forum .discussion-nav .next-discussion, +.path-mod-forum .discussion-nav .prev-discussion { + float: left; +} diff --git a/mod/forum/tests/behat/discussion_navigation.feature b/mod/forum/tests/behat/discussion_navigation.feature new file mode 100644 index 00000000000..4491b6c33e8 --- /dev/null +++ b/mod/forum/tests/behat/discussion_navigation.feature @@ -0,0 +1,165 @@ +@mod @mod_forum +Feature: A user can navigate to previous and next discussions + In order to get go the previous discussion + As a user + I need to click on the previous discussion link + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | student1 | Student | 1 | student1@asd.com | + | student2 | Student | 2 | student2@asd.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | student1 | C1 | student | + | student2 | C1 | student | + And I log in as "admin" + And I follow "Course 1" + And I navigate to "Groups" node in "Users" + And I press "Create group" + And I set the following fields to these values: + | Group name | Group 1 | + And I press "Save changes" + And I press "Create group" + And I set the following fields to these values: + | Group name | Group 2 | + And I press "Save changes" + And I add "Student 1" user to "Group 1" group members + And I add "Student 2" user to "Group 2" group members + And I am on homepage + And I follow "Course 1" + And I turn editing mode on + + @javascript + Scenario: A user can navigate between discussions + Given I add a "Forum" to section "1" and I fill the form with: + | Forum name | Test forum name | + | Description | Test forum description | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 1 | + | Message | Test post message | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 2 | + | Message | Test post message | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 3 | + | Message | Test post message | + When I follow "Discussion 3" + Then I should not see "Discussion 1" + And I should see "Discussion 2" + And I follow "Discussion 2" + And I should see "Discussion 1" + And I should see "Discussion 3" + And I follow "Discussion 1" + And I should see "Discussion 2" + And I should not see "Discussion 3" + And I follow "Reply" + And I set the following fields to these values: + | Message | Answer to discussion | + And I press "Post to forum" + And I should not see "Discussion 2" + And I should see "Discussion 3" + And I follow "Discussion 3" + And I should see "Discussion 1" + And I should see "Discussion 2" + And I follow "Discussion 2" + And I should not see "Discussion 1" + And I should see "Discussion 3" + + @javascript + Scenario: A user can navigate between discussions with visible groups + Given I add a "Forum" to section "1" and I fill the form with: + | Forum name | Test forum name | + | Description | Test forum description | + | Group mode | Visible groups | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 1 Group 0 | + | Message | Test post message | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 2 Group 0 | + | Message | Test post message | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 1 Group 1 | + | Message | Test post message | + | Group | Group 1 | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 2 Group 1 | + | Message | Test post message | + | Group | Group 1 | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 1 Group 2 | + | Message | Test post message | + | Group | Group 2 | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 2 Group 2 | + | Message | Test post message | + | Group | Group 2 | + And I log out + When I log in as "student1" + And I follow "Course 1" + And I follow "Test forum name" + And I set the field "Visible groups" to "All participants" + And I follow "Discussion 1 Group 0" + Then I should see "Discussion 2 Group 0" + And I should not see "Group 1" + And I should not see "Group 2" + And I follow "Discussion 2 Group 0" + And I should see "Discussion 1 Group 0" + And I should see "Discussion 1 Group 1" + And I follow "Discussion 1 Group 1" + And I should see "Discussion 2 Group 0" + And I should see "Discussion 2 Group 1" + And I follow "Test forum name" + And I follow "Discussion 1 Group 2" + And I should see "Discussion 2 Group 1" + And I should see "Discussion 2 Group 2" + And I follow "Test forum name" + And I set the field "Visible groups" to "Group 1" + And I follow "Discussion 1 Group 1" + Then I should see "Discussion 2 Group 0" + And I should see "Discussion 2 Group 1" + And I follow "Discussion 2 Group 1" + And I should see "Discussion 1 Group 1" + And I should not see "Group 2" + + @javascript + Scenario: A user can navigate between discussions with separate groups + Given I add a "Forum" to section "1" and I fill the form with: + | Forum name | Test forum name | + | Description | Test forum description | + | Group mode | Separate groups | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 1 Group 0 | + | Message | Test post message | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 2 Group 0 | + | Message | Test post message | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 1 Group 1 | + | Message | Test post message | + | Group | Group 1 | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 2 Group 1 | + | Message | Test post message | + | Group | Group 1 | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 1 Group 2 | + | Message | Test post message | + | Group | Group 2 | + And I add a new discussion to "Test forum name" forum with: + | Subject | Discussion 2 Group 2 | + | Message | Test post message | + | Group | Group 2 | + And I log out + When I log in as "student1" + And I follow "Course 1" + And I follow "Test forum name" + And I follow "Discussion 1 Group 1" + Then I should see "Discussion 2 Group 0" + And I should see "Discussion 2 Group 1" + And I follow "Discussion 2 Group 1" + And I should see "Discussion 1 Group 1" + And I should not see "Group 2" diff --git a/mod/forum/tests/lib_test.php b/mod/forum/tests/lib_test.php index b955a2cdf5f..e7417b537e0 100644 --- a/mod/forum/tests/lib_test.php +++ b/mod/forum/tests/lib_test.php @@ -788,4 +788,389 @@ class mod_forum_lib_testcase extends advanced_testcase { $this->assertEquals($forumcontext, $result); $this->assertEquals(1, $aftercount - $startcount); } + + /** + * Test getting the neighbour threads of a discussion. + */ + public function test_forum_get_neighbours() { + global $CFG, $DB; + $this->resetAfterTest(); + + // Setup test data. + $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum'); + $course = $this->getDataGenerator()->create_course(); + $user = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); + $cm = get_coursemodule_from_instance('forum', $forum->id); + $context = context_module::instance($cm->id); + + $record = new stdClass(); + $record->course = $course->id; + $record->userid = $user->id; + $record->forum = $forum->id; + $disc1 = $forumgen->create_discussion($record); + sleep(1); + $disc2 = $forumgen->create_discussion($record); + sleep(1); + $disc3 = $forumgen->create_discussion($record); + sleep(1); + $disc4 = $forumgen->create_discussion($record); + sleep(1); + $disc5 = $forumgen->create_discussion($record); + + // Getting the neighbours. + $neighbours = forum_get_discussion_neighbours($cm, $disc1); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc2->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc2); + $this->assertEquals($disc1->id, $neighbours['prev']->id); + $this->assertEquals($disc3->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc3); + $this->assertEquals($disc2->id, $neighbours['prev']->id); + $this->assertEquals($disc4->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc4); + $this->assertEquals($disc3->id, $neighbours['prev']->id); + $this->assertEquals($disc5->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc5); + $this->assertEquals($disc4->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + // Post in some discussions. We manually update the discussion record because + // the data generator plays with timemodified in a way that would break this test. + sleep(1); + $disc1->timemodified = time(); + $DB->update_record('forum_discussions', $disc1); + + $neighbours = forum_get_discussion_neighbours($cm, $disc5); + $this->assertEquals($disc4->id, $neighbours['prev']->id); + $this->assertEquals($disc1->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc2); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc3->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc1); + $this->assertEquals($disc5->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + // After some discussions were created. + sleep(1); + $disc6 = $forumgen->create_discussion($record); + $neighbours = forum_get_discussion_neighbours($cm, $disc6); + $this->assertEquals($disc1->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + sleep(1); + $disc7 = $forumgen->create_discussion($record); + $neighbours = forum_get_discussion_neighbours($cm, $disc7); + $this->assertEquals($disc6->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + // Adding timed discussions. + $CFG->forum_enabletimedposts = true; + $now = time(); + $past = $now - 60; + $future = $now + 60; + + $record = new stdClass(); + $record->course = $course->id; + $record->userid = $user->id; + $record->forum = $forum->id; + $record->timestart = $past; + $record->timeend = $future; + sleep(1); + $disc8 = $forumgen->create_discussion($record); + sleep(1); + $record->timestart = $future; + $record->timeend = 0; + $disc9 = $forumgen->create_discussion($record); + sleep(1); + $record->timestart = 0; + $record->timeend = 0; + $disc10 = $forumgen->create_discussion($record); + sleep(1); + $record->timestart = 0; + $record->timeend = $past; + $disc11 = $forumgen->create_discussion($record); + sleep(1); + $record->timestart = $past; + $record->timeend = $future; + $disc12 = $forumgen->create_discussion($record); + + // Admin user ignores the timed settings of discussions. + $this->setAdminUser(); + $neighbours = forum_get_discussion_neighbours($cm, $disc8); + $this->assertEquals($disc7->id, $neighbours['prev']->id); + $this->assertEquals($disc9->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc9); + $this->assertEquals($disc8->id, $neighbours['prev']->id); + $this->assertEquals($disc10->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc10); + $this->assertEquals($disc9->id, $neighbours['prev']->id); + $this->assertEquals($disc11->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc11); + $this->assertEquals($disc10->id, $neighbours['prev']->id); + $this->assertEquals($disc12->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc12); + $this->assertEquals($disc11->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + // Normal user can see their own timed discussions. + $this->setUser($user); + $neighbours = forum_get_discussion_neighbours($cm, $disc8); + $this->assertEquals($disc7->id, $neighbours['prev']->id); + $this->assertEquals($disc9->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc9); + $this->assertEquals($disc8->id, $neighbours['prev']->id); + $this->assertEquals($disc10->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc10); + $this->assertEquals($disc9->id, $neighbours['prev']->id); + $this->assertEquals($disc11->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc11); + $this->assertEquals($disc10->id, $neighbours['prev']->id); + $this->assertEquals($disc12->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc12); + $this->assertEquals($disc11->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + // Normal user does not ignore timed settings. + $this->setUser($user2); + $neighbours = forum_get_discussion_neighbours($cm, $disc8); + $this->assertEquals($disc7->id, $neighbours['prev']->id); + $this->assertEquals($disc10->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc10); + $this->assertEquals($disc8->id, $neighbours['prev']->id); + $this->assertEquals($disc12->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc12); + $this->assertEquals($disc10->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + // Reset to normal mode. + $CFG->forum_enabletimedposts = false; + $this->setAdminUser(); + + // Two discussions with identical timemodified ignore each other. + sleep(1); + $now = time(); + $DB->update_record('forum_discussions', (object) array('id' => $disc3->id, 'timemodified' => $now)); + $DB->update_record('forum_discussions', (object) array('id' => $disc2->id, 'timemodified' => $now)); + $disc2 = $DB->get_record('forum_discussions', array('id' => $disc2->id)); + $disc3 = $DB->get_record('forum_discussions', array('id' => $disc3->id)); + + $neighbours = forum_get_discussion_neighbours($cm, $disc2); + $this->assertEquals($disc12->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + $neighbours = forum_get_discussion_neighbours($cm, $disc3); + $this->assertEquals($disc12->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + } + + /** + * Test getting the neighbour threads of a discussion. + */ + public function test_forum_get_neighbours_with_groups() { + $this->resetAfterTest(); + + // Setup test data. + $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_forum'); + $course = $this->getDataGenerator()->create_course(); + $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user1->id, $course->id); + $this->getDataGenerator()->enrol_user($user2->id, $course->id); + $this->getDataGenerator()->create_group_member(array('userid' => $user1->id, 'groupid' => $group1->id)); + + $forum1 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'groupmode' => VISIBLEGROUPS)); + $forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'groupmode' => SEPARATEGROUPS)); + $cm1 = get_coursemodule_from_instance('forum', $forum1->id); + $cm2 = get_coursemodule_from_instance('forum', $forum2->id); + $context1 = context_module::instance($cm1->id); + $context2 = context_module::instance($cm2->id); + + // Creating discussions in both forums. + $record = new stdClass(); + $record->course = $course->id; + $record->userid = $user1->id; + $record->forum = $forum1->id; + $record->groupid = $group1->id; + $disc11 = $forumgen->create_discussion($record); + $record->forum = $forum2->id; + $disc21 = $forumgen->create_discussion($record); + + sleep(1); + $record->userid = $user2->id; + $record->forum = $forum1->id; + $record->groupid = $group2->id; + $disc12 = $forumgen->create_discussion($record); + $record->forum = $forum2->id; + $disc22 = $forumgen->create_discussion($record); + + sleep(1); + $record->userid = $user1->id; + $record->forum = $forum1->id; + $record->groupid = null; + $disc13 = $forumgen->create_discussion($record); + $record->forum = $forum2->id; + $disc23 = $forumgen->create_discussion($record); + + sleep(1); + $record->userid = $user2->id; + $record->forum = $forum1->id; + $record->groupid = $group2->id; + $disc14 = $forumgen->create_discussion($record); + $record->forum = $forum2->id; + $disc24 = $forumgen->create_discussion($record); + + sleep(1); + $record->userid = $user1->id; + $record->forum = $forum1->id; + $record->groupid = $group1->id; + $disc15 = $forumgen->create_discussion($record); + $record->forum = $forum2->id; + $disc25 = $forumgen->create_discussion($record); + + // Admin user can see all groups. + $this->setAdminUser(); + $neighbours = forum_get_discussion_neighbours($cm1, $disc11); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc12->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc21); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc22->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc12); + $this->assertEquals($disc11->id, $neighbours['prev']->id); + $this->assertEquals($disc13->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc22); + $this->assertEquals($disc21->id, $neighbours['prev']->id); + $this->assertEquals($disc23->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc13); + $this->assertEquals($disc12->id, $neighbours['prev']->id); + $this->assertEquals($disc14->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc23); + $this->assertEquals($disc22->id, $neighbours['prev']->id); + $this->assertEquals($disc24->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc14); + $this->assertEquals($disc13->id, $neighbours['prev']->id); + $this->assertEquals($disc15->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc24); + $this->assertEquals($disc23->id, $neighbours['prev']->id); + $this->assertEquals($disc25->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc15); + $this->assertEquals($disc14->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + $neighbours = forum_get_discussion_neighbours($cm2, $disc25); + $this->assertEquals($disc24->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + // Admin user is only viewing group 1. + $_POST['group'] = $group1->id; + $this->assertEquals($group1->id, groups_get_activity_group($cm1, true)); + $this->assertEquals($group1->id, groups_get_activity_group($cm2, true)); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc11); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc13->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc21); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc23->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc13); + $this->assertEquals($disc11->id, $neighbours['prev']->id); + $this->assertEquals($disc15->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc23); + $this->assertEquals($disc21->id, $neighbours['prev']->id); + $this->assertEquals($disc25->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc15); + $this->assertEquals($disc13->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + $neighbours = forum_get_discussion_neighbours($cm2, $disc25); + $this->assertEquals($disc23->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + // Normal user viewing non-grouped posts (this is only possible in visible groups). + $this->setUser($user1); + $_POST['group'] = 0; + $this->assertEquals(0, groups_get_activity_group($cm1, true)); + + // They can see anything in visible groups. + $neighbours = forum_get_discussion_neighbours($cm1, $disc12); + $this->assertEquals($disc11->id, $neighbours['prev']->id); + $this->assertEquals($disc13->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm1, $disc13); + $this->assertEquals($disc12->id, $neighbours['prev']->id); + $this->assertEquals($disc14->id, $neighbours['next']->id); + + // Normal user, orphan of groups, can only see non-grouped posts in separate groups. + $this->setUser($user2); + $_POST['group'] = 0; + $this->assertEquals(0, groups_get_activity_group($cm2, true)); + + $neighbours = forum_get_discussion_neighbours($cm2, $disc23); + $this->assertEmpty($neighbours['prev']); + $this->assertEmpty($neighbours['next']); + + $neighbours = forum_get_discussion_neighbours($cm2, $disc22); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc23->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm2, $disc24); + $this->assertEquals($disc23->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + // Switching to viewing group 1. + $this->setUser($user1); + $_POST['group'] = $group1->id; + $this->assertEquals($group1->id, groups_get_activity_group($cm1, true)); + $this->assertEquals($group1->id, groups_get_activity_group($cm2, true)); + + // They can see non-grouped or same group. + $neighbours = forum_get_discussion_neighbours($cm1, $disc11); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc13->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc21); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc23->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc13); + $this->assertEquals($disc11->id, $neighbours['prev']->id); + $this->assertEquals($disc15->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc23); + $this->assertEquals($disc21->id, $neighbours['prev']->id); + $this->assertEquals($disc25->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc15); + $this->assertEquals($disc13->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + $neighbours = forum_get_discussion_neighbours($cm2, $disc25); + $this->assertEquals($disc23->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + // Querying the neighbours of a discussion passing the wrong CM. + $this->setExpectedException('coding_exception'); + forum_get_discussion_neighbours($cm2, $disc11); + } }