diff --git a/mod/forum/discuss.php b/mod/forum/discuss.php index d5fed0bfd60..0fd6e771a41 100644 --- a/mod/forum/discuss.php +++ b/mod/forum/discuss.php @@ -266,7 +266,7 @@ if (!$canreply and $forum->type !== 'news') { } // Output the links to neighbour discussions. -$neighbours = forum_get_discussion_neighbours($cm, $discussion); +$neighbours = forum_get_discussion_neighbours($cm, $discussion, $forum); $neighbourlinks = $renderer->neighbouring_discussion_navigation($neighbours['prev'], $neighbours['next']); echo $neighbourlinks; diff --git a/mod/forum/lib.php b/mod/forum/lib.php index 42f6db39301..8f1557c8e18 100644 --- a/mod/forum/lib.php +++ b/mod/forum/lib.php @@ -2704,20 +2704,24 @@ function forum_get_discussions($cm, $forumsort="d.timemodified DESC", $fullpost= * other mean to sort the records, e.g. we cannot use IDs as a greater ID can have a lower * timemodified. * + * For blog-style forums, the calculation is based on the original creation time of the + * blog post. + * * 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. + * @param object $forum The forum instance 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) { +function forum_get_discussion_neighbours($cm, $discussion, $forum) { global $CFG, $DB, $USER; - if ($cm->instance != $discussion->forum) { + if ($cm->instance != $discussion->forum or $discussion->forum != $forum->id or $forum->id != $cm->instance) { throw new coding_exception('Discussion is not part of the same forum.'); } @@ -2762,25 +2766,54 @@ function forum_get_discussion_neighbours($cm, $discussion) { } } - $params['forumid'] = $cm->instance; - $params['discid'] = $discussion->id; - $params['disctimemodified'] = $discussion->timemodified; + if ($forum->type === 'blog') { + $params['forumid'] = $cm->instance; + $params['discid1'] = $discussion->id; + $params['discid2'] = $discussion->id; - $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"; + $sql = "SELECT d.id, d.name, d.timemodified, d.groupid, d.timestart, d.timeend + FROM {forum_discussions} d + JOIN {forum_posts} p ON d.firstpost = p.id + WHERE d.forum = :forumid + AND d.id <> :discid1 + $timelimit + $groupselect"; - $prevsql = $sql . " AND d.timemodified < :disctimemodified - ORDER BY d.timemodified DESC"; + $sub = "SELECT pp.created + FROM {forum_discussions} dd + JOIN {forum_posts} pp ON dd.firstpost = pp.id + WHERE dd.id = :discid2"; - $nextsql = $sql . " AND d.timemodified > :disctimemodified - ORDER BY d.timemodified ASC"; + $prevsql = $sql . " AND p.created < ($sub) + ORDER BY p.created DESC"; - $neighbours['prev'] = $DB->get_record_sql($prevsql, $params, IGNORE_MULTIPLE); - $neighbours['next'] = $DB->get_record_sql($nextsql, $params, IGNORE_MULTIPLE); + $nextsql = $sql . " AND p.created > ($sub) + ORDER BY p.created ASC"; + + $neighbours['prev'] = $DB->get_record_sql($prevsql, $params, IGNORE_MULTIPLE); + $neighbours['next'] = $DB->get_record_sql($nextsql, $params, IGNORE_MULTIPLE); + + } else { + $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; } diff --git a/mod/forum/tests/behat/posts_ordering_blog.feature b/mod/forum/tests/behat/posts_ordering_blog.feature new file mode 100644 index 00000000000..8d08a9d4fd0 --- /dev/null +++ b/mod/forum/tests/behat/posts_ordering_blog.feature @@ -0,0 +1,87 @@ +@mod @mod_forum +Feature: Blog posts are always displayed in reverse chronological order + In order to use forum as a blog + As a user + I need to see most recent blog posts first + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + | student1 | Student | 1 | student1@example.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + And I log in as "teacher1" + And I follow "Course 1" + And I turn editing mode on + And I add a "Forum" to section "1" and I fill the form with: + | Forum name | Course blog forum | + | Description | Single discussion forum description | + | Forum type | Standard forum displayed in a blog-like format | + And I log out + + # + # We need javascript/wait to prevent creation of the posts in the same second. The threads + # would then ignore each other in the prev/next navigation as the Forum is unable to compute + # the correct order. + # + @javascript + Scenario: Replying to a blog post or editing it does not affect its display order + Given I log in as "student1" + And I follow "Course 1" + And I follow "Course blog forum" + # + # Add three posts into the blog. + # + When I add a new topic to "Course blog forum" forum with: + | Subject | Blog post 1 | + | Message | This is the first post | + And I wait "1" seconds + And I add a new topic to "Course blog forum" forum with: + | Subject | Blog post 2 | + | Message | This is the second post | + And I wait "1" seconds + And I add a new topic to "Course blog forum" forum with: + | Subject | Blog post 3 | + | Message | This is the third post | + # + # Edit one of the blog posts. + # + And I click on "Edit" "link" in the "//div[@aria-label='Blog post 2 by Student 1']" "xpath_element" + And I set the following fields to these values: + | Subject | Edited blog post 2 | + And I press "Save changes" + And I wait to be redirected + And I log out + # + # Reply to another blog post. + # + And I log in as "teacher1" + And I follow "Course 1" + And I follow "Course blog forum" + And I click on "Discuss this topic" "link" in the "//div[@aria-label='Blog post 1 by Student 1']" "xpath_element" + And I click on "Reply" "link" in the "//div[@aria-label='Blog post 1 by Student 1']" "xpath_element" + And I set the following fields to these values: + | Message | Reply to the first post | + And I press "Post to forum" + And I wait to be redirected + And I am on site homepage + And I follow "Course 1" + And I follow "Course blog forum" + # + # Make sure the order of the blog posts is still reverse chronological. + # + Then I should see "This is the third post" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ')][position()=1]" "xpath_element" + And I should see "This is the second post" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ')][position()=2]" "xpath_element" + And I should see "This is the first post" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ')][position()=3]" "xpath_element" + # + # Make sure the next/prev navigation uses the same order of the posts. + # + And I click on "Discuss this topic" "link" in the "//div[@aria-label='Edited blog post 2 by Student 1']" "xpath_element" + And "//a[@aria-label='Next discussion: Blog post 3']" "xpath_element" should exist + And "//a[@aria-label='Previous discussion: Blog post 1']" "xpath_element" should exist diff --git a/mod/forum/tests/behat/posts_ordering_general.feature b/mod/forum/tests/behat/posts_ordering_general.feature new file mode 100644 index 00000000000..cf5df8aabf7 --- /dev/null +++ b/mod/forum/tests/behat/posts_ordering_general.feature @@ -0,0 +1,88 @@ +@mod @mod_forum +Feature: New discussions and discussions with recently added replies are displayed first + In order to use forum as a discussion tool + As a user + I need to see currently active discussions first + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + | student1 | Student | 1 | student1@example.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + And I log in as "teacher1" + And I follow "Course 1" + And I turn editing mode on + And I add a "Forum" to section "1" and I fill the form with: + | Forum name | Course general forum | + | Description | Single discussion forum description | + | Forum type | Standard forum for general use | + And I log out + + # + # We need javascript/wait to prevent creation of the posts in the same second. The threads + # would then ignore each other in the prev/next navigation as the Forum is unable to compute + # the correct order. + # + @javascript + Scenario: Replying to a forum post or editing it puts the discussion to the front + Given I log in as "student1" + And I follow "Course 1" + And I follow "Course general forum" + # + # Add three posts into the forum. + # + When I add a new discussion to "Course general forum" forum with: + | Subject | Forum post 1 | + | Message | This is the first post | + And I wait "1" seconds + And I add a new discussion to "Course general forum" forum with: + | Subject | Forum post 2 | + | Message | This is the second post | + And I wait "1" seconds + And I add a new discussion to "Course general forum" forum with: + | Subject | Forum post 3 | + | Message | This is the third post | + # + # Edit one of the forum posts. + # + And I follow "Forum post 2" + And I click on "Edit" "link" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ')][contains(., 'Forum post 2')]" "xpath_element" + And I set the following fields to these values: + | Subject | Edited forum post 2 | + And I press "Save changes" + And I wait to be redirected + And I log out + # + # Reply to another forum post. + # + And I log in as "teacher1" + And I follow "Course 1" + And I follow "Course general forum" + And I follow "Forum post 1" + And I click on "Reply" "link" in the "//div[@aria-label='Forum post 1 by Student 1']" "xpath_element" + And I set the following fields to these values: + | Message | Reply to the first post | + And I press "Post to forum" + And I wait to be redirected + And I am on site homepage + And I follow "Course 1" + And I follow "Course general forum" + # + # Make sure the order of the forum posts is as expected (most recently participated first). + # + Then I should see "Forum post 3" in the "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ')][position()=3]" "xpath_element" + And I should see "Edited forum post 2" in the "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ')][position()=2]" "xpath_element" + And I should see "Forum post 1" in the "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ')][position()=1]" "xpath_element" + # + # Make sure the next/prev navigation uses the same order of the posts. + # + And I follow "Edited forum post 2" + And "//a[@aria-label='Next discussion: Forum post 1']" "xpath_element" should exist + And "//a[@aria-label='Previous discussion: Forum post 3']" "xpath_element" should exist diff --git a/mod/forum/tests/lib_test.php b/mod/forum/tests/lib_test.php index bc38397c8e7..d427c85c2da 100644 --- a/mod/forum/tests/lib_test.php +++ b/mod/forum/tests/lib_test.php @@ -822,23 +822,23 @@ class mod_forum_lib_testcase extends advanced_testcase { $disc5 = $forumgen->create_discussion($record); // Getting the neighbours. - $neighbours = forum_get_discussion_neighbours($cm, $disc1); + $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum); $this->assertEmpty($neighbours['prev']); $this->assertEquals($disc2->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc2); + $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum); $this->assertEquals($disc1->id, $neighbours['prev']->id); $this->assertEquals($disc3->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc3); + $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum); $this->assertEquals($disc2->id, $neighbours['prev']->id); $this->assertEquals($disc4->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc4); + $neighbours = forum_get_discussion_neighbours($cm, $disc4, $forum); $this->assertEquals($disc3->id, $neighbours['prev']->id); $this->assertEquals($disc5->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc5); + $neighbours = forum_get_discussion_neighbours($cm, $disc5, $forum); $this->assertEquals($disc4->id, $neighbours['prev']->id); $this->assertEmpty($neighbours['next']); @@ -848,28 +848,28 @@ class mod_forum_lib_testcase extends advanced_testcase { $disc1->timemodified = time(); $DB->update_record('forum_discussions', $disc1); - $neighbours = forum_get_discussion_neighbours($cm, $disc5); + $neighbours = forum_get_discussion_neighbours($cm, $disc5, $forum); $this->assertEquals($disc4->id, $neighbours['prev']->id); $this->assertEquals($disc1->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc2); + $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum); $this->assertEmpty($neighbours['prev']); $this->assertEquals($disc3->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc1); + $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum); $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); + $neighbours = forum_get_discussion_neighbours($cm, $disc6, $forum); $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); + $neighbours = forum_get_discussion_neighbours($cm, $disc7, $forum); $this->assertEquals($disc6->id, $neighbours['prev']->id); $this->assertEmpty($neighbours['next']); @@ -906,59 +906,59 @@ class mod_forum_lib_testcase extends advanced_testcase { // Admin user ignores the timed settings of discussions. $this->setAdminUser(); - $neighbours = forum_get_discussion_neighbours($cm, $disc8); + $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum); $this->assertEquals($disc7->id, $neighbours['prev']->id); $this->assertEquals($disc9->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc9); + $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum); $this->assertEquals($disc8->id, $neighbours['prev']->id); $this->assertEquals($disc10->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc10); + $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum); $this->assertEquals($disc9->id, $neighbours['prev']->id); $this->assertEquals($disc11->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc11); + $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum); $this->assertEquals($disc10->id, $neighbours['prev']->id); $this->assertEquals($disc12->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc12); + $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum); $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); + $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum); $this->assertEquals($disc7->id, $neighbours['prev']->id); $this->assertEquals($disc9->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc9); + $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum); $this->assertEquals($disc8->id, $neighbours['prev']->id); $this->assertEquals($disc10->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc10); + $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum); $this->assertEquals($disc9->id, $neighbours['prev']->id); $this->assertEquals($disc11->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc11); + $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum); $this->assertEquals($disc10->id, $neighbours['prev']->id); $this->assertEquals($disc12->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc12); + $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum); $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); + $neighbours = forum_get_discussion_neighbours($cm, $disc8, $forum); $this->assertEquals($disc7->id, $neighbours['prev']->id); $this->assertEquals($disc10->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc10); + $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum); $this->assertEquals($disc8->id, $neighbours['prev']->id); $this->assertEquals($disc12->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm, $disc12); + $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum); $this->assertEquals($disc10->id, $neighbours['prev']->id); $this->assertEmpty($neighbours['next']); @@ -974,11 +974,197 @@ class mod_forum_lib_testcase extends advanced_testcase { $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); + $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum); $this->assertEquals($disc12->id, $neighbours['prev']->id); $this->assertEmpty($neighbours['next']); - $neighbours = forum_get_discussion_neighbours($cm, $disc3); + $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum); + $this->assertEquals($disc12->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + } + + /** + * Test getting the neighbour threads of a blog-like forum. + */ + public function test_forum_get_neighbours_blog() { + 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, 'type' => 'blog')); + $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, $forum); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc2->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum); + $this->assertEquals($disc1->id, $neighbours['prev']->id); + $this->assertEquals($disc3->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum); + $this->assertEquals($disc2->id, $neighbours['prev']->id); + $this->assertEquals($disc4->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc4, $forum); + $this->assertEquals($disc3->id, $neighbours['prev']->id); + $this->assertEquals($disc5->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc5, $forum); + $this->assertEquals($disc4->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + // Make sure that the thread's timemodified does not affect the order. + sleep(1); + $disc1->timemodified = time(); + $DB->update_record('forum_discussions', $disc1); + + $neighbours = forum_get_discussion_neighbours($cm, $disc1, $forum); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc2->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum); + $this->assertEquals($disc1->id, $neighbours['prev']->id); + $this->assertEquals($disc3->id, $neighbours['next']->id); + + // Add another blog post. + sleep(1); + $disc6 = $forumgen->create_discussion($record); + $neighbours = forum_get_discussion_neighbours($cm, $disc6, $forum); + $this->assertEquals($disc5->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + sleep(1); + $disc7 = $forumgen->create_discussion($record); + $neighbours = forum_get_discussion_neighbours($cm, $disc7, $forum); + $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, $forum); + $this->assertEquals($disc7->id, $neighbours['prev']->id); + $this->assertEquals($disc9->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum); + $this->assertEquals($disc8->id, $neighbours['prev']->id); + $this->assertEquals($disc10->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum); + $this->assertEquals($disc9->id, $neighbours['prev']->id); + $this->assertEquals($disc11->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum); + $this->assertEquals($disc10->id, $neighbours['prev']->id); + $this->assertEquals($disc12->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum); + $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, $forum); + $this->assertEquals($disc7->id, $neighbours['prev']->id); + $this->assertEquals($disc9->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc9, $forum); + $this->assertEquals($disc8->id, $neighbours['prev']->id); + $this->assertEquals($disc10->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum); + $this->assertEquals($disc9->id, $neighbours['prev']->id); + $this->assertEquals($disc11->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc11, $forum); + $this->assertEquals($disc10->id, $neighbours['prev']->id); + $this->assertEquals($disc12->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum); + $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, $forum); + $this->assertEquals($disc7->id, $neighbours['prev']->id); + $this->assertEquals($disc10->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc10, $forum); + $this->assertEquals($disc8->id, $neighbours['prev']->id); + $this->assertEquals($disc12->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm, $disc12, $forum); + $this->assertEquals($disc10->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + // Reset to normal mode. + $CFG->forum_enabletimedposts = false; + $this->setAdminUser(); + + // Two blog posts with identical creation time ignore each other. + sleep(1); + $now = time(); + $DB->update_record('forum_posts', (object) array('id' => $disc2->firstpost, 'created' => $now)); + $DB->update_record('forum_posts', (object) array('id' => $disc3->firstpost, 'created' => $now)); + + $neighbours = forum_get_discussion_neighbours($cm, $disc2, $forum); + $this->assertEquals($disc12->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + + $neighbours = forum_get_discussion_neighbours($cm, $disc3, $forum); $this->assertEquals($disc12->id, $neighbours['prev']->id); $this->assertEmpty($neighbours['next']); } @@ -1051,38 +1237,38 @@ class mod_forum_lib_testcase extends advanced_testcase { // Admin user can see all groups. $this->setAdminUser(); - $neighbours = forum_get_discussion_neighbours($cm1, $disc11); + $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1); $this->assertEmpty($neighbours['prev']); $this->assertEquals($disc12->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm2, $disc21); + $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2); $this->assertEmpty($neighbours['prev']); $this->assertEquals($disc22->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm1, $disc12); + $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1); $this->assertEquals($disc11->id, $neighbours['prev']->id); $this->assertEquals($disc13->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm2, $disc22); + $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2); $this->assertEquals($disc21->id, $neighbours['prev']->id); $this->assertEquals($disc23->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm1, $disc13); + $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); $this->assertEquals($disc12->id, $neighbours['prev']->id); $this->assertEquals($disc14->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm2, $disc23); + $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); $this->assertEquals($disc22->id, $neighbours['prev']->id); $this->assertEquals($disc24->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm1, $disc14); + $neighbours = forum_get_discussion_neighbours($cm1, $disc14, $forum1); $this->assertEquals($disc13->id, $neighbours['prev']->id); $this->assertEquals($disc15->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm2, $disc24); + $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2); $this->assertEquals($disc23->id, $neighbours['prev']->id); $this->assertEquals($disc25->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm1, $disc15); + $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1); $this->assertEquals($disc14->id, $neighbours['prev']->id); $this->assertEmpty($neighbours['next']); - $neighbours = forum_get_discussion_neighbours($cm2, $disc25); + $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2); $this->assertEquals($disc24->id, $neighbours['prev']->id); $this->assertEmpty($neighbours['next']); @@ -1091,24 +1277,24 @@ class mod_forum_lib_testcase extends advanced_testcase { $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); + $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1); $this->assertEmpty($neighbours['prev']); $this->assertEquals($disc13->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm2, $disc21); + $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2); $this->assertEmpty($neighbours['prev']); $this->assertEquals($disc23->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm1, $disc13); + $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); $this->assertEquals($disc11->id, $neighbours['prev']->id); $this->assertEquals($disc15->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm2, $disc23); + $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); $this->assertEquals($disc21->id, $neighbours['prev']->id); $this->assertEquals($disc25->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm1, $disc15); + $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1); $this->assertEquals($disc13->id, $neighbours['prev']->id); $this->assertEmpty($neighbours['next']); - $neighbours = forum_get_discussion_neighbours($cm2, $disc25); + $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2); $this->assertEquals($disc23->id, $neighbours['prev']->id); $this->assertEmpty($neighbours['next']); @@ -1118,10 +1304,10 @@ class mod_forum_lib_testcase extends advanced_testcase { $this->assertEquals(0, groups_get_activity_group($cm1, true)); // They can see anything in visible groups. - $neighbours = forum_get_discussion_neighbours($cm1, $disc12); + $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1); $this->assertEquals($disc11->id, $neighbours['prev']->id); $this->assertEquals($disc13->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm1, $disc13); + $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); $this->assertEquals($disc12->id, $neighbours['prev']->id); $this->assertEquals($disc14->id, $neighbours['next']->id); @@ -1130,15 +1316,15 @@ class mod_forum_lib_testcase extends advanced_testcase { $_POST['group'] = 0; $this->assertEquals(0, groups_get_activity_group($cm2, true)); - $neighbours = forum_get_discussion_neighbours($cm2, $disc23); + $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); $this->assertEmpty($neighbours['prev']); $this->assertEmpty($neighbours['next']); - $neighbours = forum_get_discussion_neighbours($cm2, $disc22); + $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2); $this->assertEmpty($neighbours['prev']); $this->assertEquals($disc23->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm2, $disc24); + $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2); $this->assertEquals($disc23->id, $neighbours['prev']->id); $this->assertEmpty($neighbours['next']); @@ -1149,30 +1335,224 @@ class mod_forum_lib_testcase extends advanced_testcase { $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); + $neighbours = forum_get_discussion_neighbours($cm1, $disc11, $forum1); $this->assertEmpty($neighbours['prev']); $this->assertEquals($disc13->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm2, $disc21); + $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2); $this->assertEmpty($neighbours['prev']); $this->assertEquals($disc23->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm1, $disc13); + $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); $this->assertEquals($disc11->id, $neighbours['prev']->id); $this->assertEquals($disc15->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm2, $disc23); + $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); $this->assertEquals($disc21->id, $neighbours['prev']->id); $this->assertEquals($disc25->id, $neighbours['next']->id); - $neighbours = forum_get_discussion_neighbours($cm1, $disc15); + $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1); $this->assertEquals($disc13->id, $neighbours['prev']->id); $this->assertEmpty($neighbours['next']); - $neighbours = forum_get_discussion_neighbours($cm2, $disc25); + $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2); $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); + forum_get_discussion_neighbours($cm2, $disc11, $forum2); + } + + /** + * Test getting the neighbour threads of a blog-like forum with groups involved. + */ + public function test_forum_get_neighbours_with_groups_blog() { + $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, 'type' => 'blog', + 'groupmode' => VISIBLEGROUPS)); + $forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id, 'type' => 'blog', + '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 blog posts 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, $forum1); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc12->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc22->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc12, $forum1); + $this->assertEquals($disc11->id, $neighbours['prev']->id); + $this->assertEquals($disc13->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2); + $this->assertEquals($disc21->id, $neighbours['prev']->id); + $this->assertEquals($disc23->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); + $this->assertEquals($disc12->id, $neighbours['prev']->id); + $this->assertEquals($disc14->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); + $this->assertEquals($disc22->id, $neighbours['prev']->id); + $this->assertEquals($disc24->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc14, $forum1); + $this->assertEquals($disc13->id, $neighbours['prev']->id); + $this->assertEquals($disc15->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2); + $this->assertEquals($disc23->id, $neighbours['prev']->id); + $this->assertEquals($disc25->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1); + $this->assertEquals($disc14->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2); + $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, $forum1); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc13->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc23->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); + $this->assertEquals($disc11->id, $neighbours['prev']->id); + $this->assertEquals($disc15->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); + $this->assertEquals($disc21->id, $neighbours['prev']->id); + $this->assertEquals($disc25->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1); + $this->assertEquals($disc13->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2); + $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, $forum1); + $this->assertEquals($disc11->id, $neighbours['prev']->id); + $this->assertEquals($disc13->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); + $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, $forum2); + $this->assertEmpty($neighbours['prev']); + $this->assertEmpty($neighbours['next']); + + $neighbours = forum_get_discussion_neighbours($cm2, $disc22, $forum2); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc23->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm2, $disc24, $forum2); + $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, $forum1); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc13->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc21, $forum2); + $this->assertEmpty($neighbours['prev']); + $this->assertEquals($disc23->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc13, $forum1); + $this->assertEquals($disc11->id, $neighbours['prev']->id); + $this->assertEquals($disc15->id, $neighbours['next']->id); + $neighbours = forum_get_discussion_neighbours($cm2, $disc23, $forum2); + $this->assertEquals($disc21->id, $neighbours['prev']->id); + $this->assertEquals($disc25->id, $neighbours['next']->id); + + $neighbours = forum_get_discussion_neighbours($cm1, $disc15, $forum1); + $this->assertEquals($disc13->id, $neighbours['prev']->id); + $this->assertEmpty($neighbours['next']); + $neighbours = forum_get_discussion_neighbours($cm2, $disc25, $forum2); + $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, $forum2); } public function test_count_discussion_replies_basic() { diff --git a/mod/forum/view.php b/mod/forum/view.php index af2c8d7ffb0..483fed79ce9 100644 --- a/mod/forum/view.php +++ b/mod/forum/view.php @@ -207,9 +207,10 @@ case 'blog': echo '
'; if (!empty($showall)) { - forum_print_latest_discussions($course, $forum, 0, 'plain', '', -1, -1, -1, 0, $cm); + forum_print_latest_discussions($course, $forum, 0, 'plain', 'p.created DESC', -1, -1, -1, 0, $cm); } else { - forum_print_latest_discussions($course, $forum, -1, 'plain', '', -1, -1, $page, $CFG->forum_manydiscussions, $cm); + forum_print_latest_discussions($course, $forum, -1, 'plain', 'p.created DESC', -1, -1, $page, + $CFG->forum_manydiscussions, $cm); } break;