MDL-66078 mod_forum: Add webservice to pull discussion data for user
authorMathew May <mathewm@hotmail.co.nz>
Thu, 11 Jul 2019 03:51:13 +0000 (11:51 +0800)
committerMathew May <mathewm@hotmail.co.nz>
Wed, 30 Oct 2019 02:23:40 +0000 (10:23 +0800)
Part of MDL-66074

mod/forum/amd/build/repository.min.js
mod/forum/amd/build/repository.min.js.map
mod/forum/amd/src/repository.js
mod/forum/classes/local/renderers/discussion_list.php
mod/forum/classes/local/vaults/discussion.php
mod/forum/classes/local/vaults/post.php
mod/forum/db/services.php
mod/forum/externallib.php
mod/forum/tests/externallib_test.php
mod/forum/tests/vaults_discussion_test.php
mod/forum/tests/vaults_post_test.php

index c63ca07..4518127 100644 (file)
Binary files a/mod/forum/amd/build/repository.min.js and b/mod/forum/amd/build/repository.min.js differ
index 8e49556..299b2ef 100644 (file)
Binary files a/mod/forum/amd/build/repository.min.js.map and b/mod/forum/amd/build/repository.min.js.map differ
index 1e8e29d..5cb8cd0 100644 (file)
@@ -112,11 +112,34 @@ define(['core/ajax'], function(Ajax) {
         return Ajax.call([request])[0];
     };
 
+    /**
+     * Get the discussions for the user and cmid provided.
+     *
+     * @param {number} userid
+     * @param {number} cmid
+     * @param {string} sortby
+     * @param {string} sortdirection
+     * @return {*|Promise}
+     */
+    var getDiscussionByUserID = function(userid, cmid, sortby = 'modified', sortdirection = 'DESC') {
+        var request = {
+            methodname: 'mod_forum_get_discussion_posts_by_userid',
+            args: {
+                userid: userid,
+                cmid: cmid,
+                sortby: sortby,
+                sortdirection: sortdirection,
+            },
+        };
+        return Ajax.call([request])[0];
+    };
+
     return {
         setDiscussionSubscriptionState: setDiscussionSubscriptionState,
         addDiscussionPost: addDiscussionPost,
         setDiscussionLockState: setDiscussionLockState,
         setFavouriteDiscussionState: setFavouriteDiscussionState,
-        setPinDiscussionState: setPinDiscussionState
+        setPinDiscussionState: setPinDiscussionState,
+        getDiscussionByUserID: getDiscussionByUserID
     };
 });
index 24879b1..ae01085 100644 (file)
@@ -168,6 +168,7 @@ class discussion_list {
 
         $forumview = [
             'forum' => (array) $forumexporter->export($this->renderer),
+            'cmid' => $cm->id,
             'hasanyactions' => $hasanyactions,
             'groupchangemenu' => groups_print_activity_menu(
                 $cm,
@@ -207,6 +208,9 @@ class discussion_list {
             $exportedposts
         );
 
+        $firstdiscussion = reset($discussions);
+        $forumview['firstgradeduserid'] = $firstdiscussion->get_latest_post_author()->get_id();
+
         return $this->renderer->render_from_template($this->template, $forumview);
     }
 
index 084c612..128c365 100644 (file)
@@ -92,10 +92,10 @@ class discussion extends db_table_vault {
      * @param   forum_entity $forum
      * @return  array
      */
-    public function get_all_discussions_in_forum(forum_entity $forum): ?array {
+    public function get_all_discussions_in_forum(forum_entity $forum, string $sort = null): ?array {
         $records = $this->get_db()->get_records(self::TABLE, [
             'forum' => $forum->get_id(),
-        ]);
+        ], $sort ?? '');
 
         return $this->transform_db_records_to_entities($records);
     }
index fec45d2..b8a7b32 100644 (file)
@@ -26,6 +26,7 @@ namespace mod_forum\local\vaults;
 
 defined('MOODLE_INTERNAL') || die();
 
+use mod_forum\local\entities\forum as forum_entity;
 use mod_forum\local\entities\post as post_entity;
 use mod_forum\local\factories\entity as entity_factory;
 use stdClass;
@@ -511,4 +512,75 @@ class post extends db_table_vault {
         $records = $this->get_db()->get_records_sql($sql, $params);
         return $this->transform_db_records_to_entities($records);
     }
+
+    /**
+     * Get the posts for the given user.
+     *
+     * @param int[] $discussionids The list of discussions to fetch posts for
+     * @param int $userid
+     * @param bool $canseeprivatereplies Whether this user can see all private replies or not
+     * @param string $orderby Order the results
+     * @return post_entity[]
+     */
+    public function get_posts_in_forum_for_user_id(
+        array $discussionids,
+        int $userid,
+        bool $canseeprivatereplies,
+        string $orderby = 'created ASC'
+    ): array {
+        $user = $this->get_db()->get_record('user', ['id' => (int)$userid], '*', IGNORE_MISSING);
+        list($insql, $params) = $this->get_db()->get_in_or_equal($discussionids, SQL_PARAMS_NAMED);
+
+        $alias = $this->get_table_alias();
+        [
+            'where' => $privatewhere,
+            'params' => $privateparams,
+        ] = $this->get_private_reply_sql($user, $canseeprivatereplies);
+
+        $wheresql = "{$alias}.userid = :authorid {$privatewhere}";
+        $orderbysql = $alias . '.' . $orderby;
+
+        $sql = $this->generate_get_records_sql($wheresql, $orderbysql);
+        $records = $this->get_db()->get_records_sql($sql, array_merge([
+            'authorid' => $userid,
+        ], $privateparams));
+
+        return $this->transform_db_records_to_entities($records);
+    }
+
+    /**
+     * Get the posts for the given user.
+     *
+     * @param int $discussionid The discussion to fetch posts for
+     * @param int $userid The user to fetch posts for
+     * @param bool $canseeprivatereplies Whether this user can see all private replies or not
+     * @param string $orderby Order the results
+     * @return post_entity[]
+     */
+    public function get_posts_in_discussion_for_user_id(
+        int $discussionid,
+        int $userid,
+        bool $canseeprivatereplies,
+        string $orderby = 'created ASC'
+    ): array {
+        $user = $this->get_db()->get_record('user', ['id' => (int)$userid], '*', IGNORE_MISSING);
+
+        $alias = $this->get_table_alias();
+        [
+            'where' => $privatewhere,
+            'params' => $privateparams,
+        ] = $this->get_private_reply_sql($user, $canseeprivatereplies);
+
+        $wheresql = "{$alias}.userid = :authorid AND
+                     {$alias}.discussion = :discussionid {$privatewhere}";
+        $orderbysql = $alias . '.' . $orderby;
+
+        $sql = $this->generate_get_records_sql($wheresql, $orderbysql);
+        $records = $this->get_db()->get_records_sql($sql, array_merge([
+            'authorid' => $userid,
+            'discussionid' => $discussionid
+        ], $privateparams));
+
+        return $this->transform_db_records_to_entities($records);
+    }
 }
index f6cee74..78c4e9f 100644 (file)
@@ -175,6 +175,7 @@ $functions = array(
         'ajax' => true,
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
     ),
+
     'mod_forum_delete_post' => array(
         'classname' => 'mod_forum_external',
         'methodname' => 'delete_post',
@@ -183,4 +184,14 @@ $functions = array(
         'type' => 'write',
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
     ),
+
+    'mod_forum_get_discussion_posts_by_userid' => array(
+        'classname' => 'mod_forum_external',
+        'methodname' => 'get_discussion_posts_by_userid',
+        'classpath' => 'mod/forum/externallib.php',
+        'description' => 'Returns a list of forum posts for a discussion for a user.',
+        'type' => 'read',
+        'ajax' => true,
+        'capabilities' => 'mod/forum:viewdiscussion, mod/forum:viewqandawithoutposting',
+    ),
 );
index 624e723..fab94fa 100644 (file)
@@ -2152,4 +2152,141 @@ class mod_forum_external extends external_api {
             )
         );
     }
+
+    /**
+     * Get the forum posts in the specified forum instance.
+     *
+     * @param   int $userid
+     * @param   int $cmid
+     * @param   string $sortby
+     * @param   string $sortdirection
+     * @return  array
+     */
+    public static function get_discussion_posts_by_userid(int $userid = 0, int $cmid, ?string $sortby, ?string $sortdirection) {
+        global $USER, $DB;
+        // Validate the parameter.
+        $params = self::validate_parameters(self::get_discussion_posts_by_userid_parameters(), [
+                'userid' => $userid,
+                'cmid' => $cmid,
+                'sortby' => $sortby,
+                'sortdirection' => $sortdirection,
+        ]);
+        $warnings = [];
+
+        $user = $DB->get_record('user', ['id' => (int)$params['userid']], '*', IGNORE_MISSING);
+
+        $vaultfactory = mod_forum\local\container::get_vault_factory();
+
+        $forumvault = $vaultfactory->get_forum_vault();
+        $forum = $forumvault->get_from_course_module_id($params['cmid']);
+
+        // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
+        self::validate_context($forum->get_context());
+
+        $sortby = $params['sortby'];
+        $sortdirection = $params['sortdirection'];
+        $sortallowedvalues = ['id', 'created', 'modified'];
+        $directionallowedvalues = ['ASC', 'DESC'];
+
+        if (!in_array(strtolower($sortby), $sortallowedvalues)) {
+            throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
+                    'allowed values are: ' . implode(', ', $sortallowedvalues));
+        }
+
+        $sortdirection = strtoupper($sortdirection);
+        if (!in_array($sortdirection, $directionallowedvalues)) {
+            throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
+                    'allowed values are: ' . implode(',', $directionallowedvalues));
+        }
+
+        $managerfactory = mod_forum\local\container::get_manager_factory();
+        $capabilitymanager = $managerfactory->get_capability_manager($forum);
+
+        $discussionvault = $vaultfactory->get_discussion_vault();
+        $discussions = $discussionvault->get_all_discussions_in_forum($forum, 'timemodified ASC, id ASC');
+
+        $postvault = $vaultfactory->get_post_vault();
+
+        $builderfactory = mod_forum\local\container::get_builder_factory();
+        $postbuilder = $builderfactory->get_exported_posts_builder();
+
+        $builtdiscussions = [];
+        foreach ($discussions as $id => $discussion) {
+            $posts = $postvault->get_posts_in_discussion_for_user_id(
+                    $discussion->get_id(),
+                    $user->id,
+                    $capabilitymanager->can_view_any_private_reply($USER),
+                    "{$sortby} {$sortdirection}"
+            );
+            if (empty($posts)) {
+                continue;
+            }
+
+            $parentids = array_filter(array_map(function($post) {
+                return $post->has_parent() ? $post->get_parent_id() : null;
+            }, $posts));
+
+            $parentposts = [];
+            if ($parentids) {
+                $parentposts = $postbuilder->build(
+                    $user,
+                    [$forum],
+                    [$discussion],
+                    $postvault->get_from_ids(array_values($parentids))
+                );
+            }
+
+            $builtdiscussions[] = [
+                'name' => $discussion->get_name(),
+                'id' => $discussion->get_id(),
+                'posts' => [
+                    'userposts' => $postbuilder->build($user, [$forum], [$discussion], $posts),
+                    'parentposts' => $parentposts,
+                ],
+            ];
+        }
+
+        return [
+                'discussions' => $builtdiscussions,
+                'warnings' => $warnings,
+        ];
+    }
+
+    /**
+     * Describe the post parameters.
+     *
+     * @return external_function_parameters
+     */
+    public static function get_discussion_posts_by_userid_parameters() {
+        return new external_function_parameters ([
+                'userid' => new external_value(
+                        PARAM_INT, 'The ID of the user of whom to fetch posts.', VALUE_REQUIRED),
+                'cmid' => new external_value(
+                        PARAM_INT, 'The ID of the module of which to fetch items.', VALUE_REQUIRED),
+                'sortby' => new external_value(
+                        PARAM_ALPHA, 'Sort by this element: id, created or modified', VALUE_DEFAULT, 'created'),
+                'sortdirection' => new external_value(
+                        PARAM_ALPHA, 'Sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC')
+        ]);
+    }
+
+    /**
+     * Describe the post return format.
+     *
+     * @return external_single_structure
+     */
+    public static function get_discussion_posts_by_userid_returns() {
+        return new external_single_structure([
+                'discussions' => new external_multiple_structure(
+                    new external_single_structure([
+                        'name' => new external_value(PARAM_RAW, 'Name of the discussion'),
+                        'id' => new external_value(PARAM_INT, 'ID of the discussion'),
+                        'posts' => new external_single_structure([
+                            'userposts' => new external_multiple_structure(\mod_forum\local\exporters\post::get_read_structure()),
+                            'parentposts' => new external_multiple_structure(\mod_forum\local\exporters\post::get_read_structure()),
+                        ]),
+                    ])),
+                'warnings' => new external_warnings(),
+        ]);
+    }
 }
index 7ee3275..840c8e0 100644 (file)
@@ -2465,12 +2465,12 @@ class mod_forum_external_testcase extends externallib_advanced_testcase {
         $discussionrecord->userid = $user1->id;
         $discussionrecord->forum = $forum->id;
         $discussionrecord->message = $dangeroustext;
-        $discussionrecord->messagetrust  = trusttext_trusted($context);
+        $discussionrecord->messagetrust = trusttext_trusted($context);
         $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($discussionrecord);
 
         self::setAdminUser();
         $discussionrecord->userid = $USER->id;
-        $discussionrecord->messagetrust  = trusttext_trusted($context);
+        $discussionrecord->messagetrust = trusttext_trusted($context);
         $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($discussionrecord);
 
         $discussions = mod_forum_external::get_forum_discussions($forum->id);
@@ -2604,4 +2604,397 @@ class mod_forum_external_testcase extends externallib_advanced_testcase {
         $this->expectExceptionMessage(get_string('cannotdeletepost', 'forum'));
         mod_forum_external::delete_post($post->id);
     }
+
+    /*
+     * Test get forum posts by user id.
+     */
+    public function test_mod_forum_get_discussion_posts_by_userid() {
+        $this->resetAfterTest(true);
+
+        $urlfactory = mod_forum\local\container::get_url_factory();
+        $entityfactory = mod_forum\local\container::get_entity_factory();
+        $vaultfactory = mod_forum\local\container::get_vault_factory();
+        $postvault = $vaultfactory->get_post_vault();
+        $legacydatamapper = mod_forum\local\container::get_legacy_data_mapper_factory();
+        $legacypostmapper = $legacydatamapper->get_post_data_mapper();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user1entity = $entityfactory->get_author_from_stdclass($user1);
+        $exporteduser1 = [
+            'id' => (int) $user1->id,
+            'fullname' => fullname($user1),
+            'groups' => [],
+            'urls' => [
+                'profile' => $urlfactory->get_author_profile_url($user1entity),
+                'profileimage' => $urlfactory->get_author_profile_image_url($user1entity),
+            ],
+            'isdeleted' => false,
+        ];
+        // Create a bunch of other users to post.
+        $user2 = self::getDataGenerator()->create_user();
+        $user2entity = $entityfactory->get_author_from_stdclass($user2);
+        $exporteduser2 = [
+            'id' => (int) $user2->id,
+            'fullname' => fullname($user2),
+            'groups' => [],
+            'urls' => [
+                'profile' => $urlfactory->get_author_profile_url($user2entity),
+                'profileimage' => $urlfactory->get_author_profile_image_url($user2entity),
+            ],
+            'isdeleted' => false,
+        ];
+        $user2->fullname = $exporteduser2['fullname'];
+
+        $forumgenerator = self::getDataGenerator()->get_plugin_generator('mod_forum');
+
+        // Set the first created user to the test user.
+        self::setUser($user1);
+
+        // Create course to add the module.
+        $course1 = self::getDataGenerator()->create_course();
+
+        // Forum with tracking off.
+        $record = new stdClass();
+        $record->course = $course1->id;
+        $forum1 = self::getDataGenerator()->create_module('forum', $record);
+        $forum1context = context_module::instance($forum1->cmid);
+
+        // Add discussions to the forums.
+        $record = new stdClass();
+        $record->course = $course1->id;
+        $record->userid = $user1->id;
+        $record->forum = $forum1->id;
+        $discussion1 = $forumgenerator->create_discussion($record);
+        $discussion1firstpost = $postvault->get_first_post_for_discussion_ids([$discussion1->id]);
+        $discussion1firstpostobject = $legacypostmapper->to_legacy_object($discussion1firstpost[$discussion1->firstpost]);
+
+        $record = new stdClass();
+        $record->course = $course1->id;
+        $record->userid = $user1->id;
+        $record->forum = $forum1->id;
+        $discussion2 = $forumgenerator->create_discussion($record);
+        $discussion2firstpost = $postvault->get_first_post_for_discussion_ids([$discussion2->id]);
+        $discussion2firstpostobject = $legacypostmapper->to_legacy_object($discussion2firstpost[$discussion2->firstpost]);
+
+        // Add 1 reply to the discussion 1 from a different user.
+        $record = new stdClass();
+        $record->discussion = $discussion1->id;
+        $record->parent = $discussion1->firstpost;
+        $record->userid = $user2->id;
+        $discussion1reply1 = $forumgenerator->create_post($record);
+        $filename = 'shouldbeanimage.jpg';
+        // Add a fake inline image to the post.
+        $filerecordinline = array(
+                'contextid' => $forum1context->id,
+                'component' => 'mod_forum',
+                'filearea'  => 'post',
+                'itemid'    => $discussion1reply1->id,
+                'filepath'  => '/',
+                'filename'  => $filename,
+        );
+        $fs = get_file_storage();
+        $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
+
+        // Add 1 reply to the discussion 2 from a different user.
+        $record = new stdClass();
+        $record->discussion = $discussion2->id;
+        $record->parent = $discussion2->firstpost;
+        $record->userid = $user2->id;
+        $discussion2reply1 = $forumgenerator->create_post($record);
+        $filename = 'shouldbeanimage.jpg';
+        // Add a fake inline image to the post.
+        $filerecordinline = array(
+                'contextid' => $forum1context->id,
+                'component' => 'mod_forum',
+                'filearea'  => 'post',
+                'itemid'    => $discussion2reply1->id,
+                'filepath'  => '/',
+                'filename'  => $filename,
+        );
+        $fs = get_file_storage();
+        $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
+
+        // Following line enrol and assign default role id to the user.
+        // So the user automatically gets mod/forum:viewdiscussion on all forums of the course.
+        $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
+        $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
+
+        // Create what we expect to be returned when querying the discussion.
+        $expectedposts = array(
+            'discussions' => array(),
+            'warnings' => array(),
+        );
+
+        $isolatedurluser = $urlfactory->get_discussion_view_url_from_discussion_id($discussion1reply1->discussion);
+        $isolatedurluser->params(['parent' => $discussion1reply1->id]);
+        $isolatedurlparent = $urlfactory->get_discussion_view_url_from_discussion_id($discussion1firstpostobject->discussion);
+        $isolatedurlparent->params(['parent' => $discussion1firstpostobject->id]);
+
+        $expectedposts['discussions'][0] = [
+            'name' => $discussion1->name,
+            'id' => $discussion1->id,
+            'posts' => [
+                'userposts' => [
+                    [
+                        'id' => $discussion1reply1->id,
+                        'discussionid' => $discussion1reply1->discussion,
+                        'parentid' => $discussion1reply1->parent,
+                        'hasparent' => true,
+                        'timecreated' => $discussion1reply1->created,
+                        'subject' => $discussion1reply1->subject,
+                        'replysubject' => get_string('re', 'mod_forum') . " {$discussion1reply1->subject}",
+                        'message' => file_rewrite_pluginfile_urls($discussion1reply1->message, 'pluginfile.php',
+                        $forum1context->id, 'mod_forum', 'post', $discussion1reply1->id),
+                        'messageformat' => 1,   // This value is usually changed by external_format_text() function.
+                        'unread' => null,
+                        'isdeleted' => false,
+                        'isprivatereply' => false,
+                        'haswordcount' => false,
+                        'wordcount' => null,
+                        'author' => $exporteduser2,
+                        'attachments' => [],
+                        'tags' => [],
+                        'html' => [
+                            'rating' => null,
+                            'taglist' => null,
+                            'authorsubheading' => $forumgenerator->get_author_subheading_html(
+                                (object)$exporteduser2, $discussion1reply1->created)
+                        ],
+                        'charcount' => null,
+                        'capabilities' => [
+                            'view' => true,
+                            'edit' => true,
+                            'delete' => true,
+                            'split' => false,
+                            'reply' => true,
+                            'export' => false,
+                            'controlreadstatus' => false,
+                            'canreplyprivately' => false,
+                            'selfenrol' => false
+                        ],
+                        'urls' => [
+                            'view' => $urlfactory->get_view_post_url_from_post_id(
+                                    $discussion1reply1->discussion, $discussion1reply1->id)->out(false),
+                            'viewisolated' => $isolatedurluser->out(false),
+                            'viewparent' => $urlfactory->get_view_post_url_from_post_id(
+                                    $discussion1reply1->discussion, $discussion1reply1->parent)->out(false),
+                            'edit' => (new moodle_url('/mod/forum/post.php', [
+                                    'edit' => $discussion1reply1->id
+                            ]))->out(false),
+                            'delete' => (new moodle_url('/mod/forum/post.php', [
+                                    'delete' => $discussion1reply1->id
+                            ]))->out(false),
+                            'split' => null,
+                            'reply' => (new moodle_url('/mod/forum/post.php#mformforum', [
+                                    'reply' => $discussion1reply1->id
+                            ]))->out(false),
+                            'export' => null,
+                            'markasread' => null,
+                            'markasunread' => null,
+                            'discuss' => $urlfactory->get_discussion_view_url_from_discussion_id(
+                                    $discussion1reply1->discussion)->out(false),
+                        ],
+                    ]
+                ],
+                'parentposts' => [
+                    [
+                        'id' => $discussion1firstpostobject->id,
+                        'discussionid' => $discussion1firstpostobject->discussion,
+                        'parentid' => null,
+                        'hasparent' => false,
+                        'timecreated' => $discussion1firstpostobject->created,
+                        'subject' => $discussion1firstpostobject->subject,
+                        'replysubject' => get_string('re', 'mod_forum') . " {$discussion1firstpostobject->subject}",
+                        'message' => file_rewrite_pluginfile_urls($discussion1firstpostobject->message, 'pluginfile.php',
+                            $forum1context->id, 'mod_forum', 'post', $discussion1firstpostobject->id),
+                        'messageformat' => 1,   // This value is usually changed by external_format_text() function.
+                        'unread' => null,
+                        'isdeleted' => false,
+                        'isprivatereply' => false,
+                        'haswordcount' => false,
+                        'wordcount' => null,
+                        'author' => $exporteduser1,
+                        'attachments' => [],
+                        'tags' => [],
+                        'html' => [
+                            'rating' => null,
+                            'taglist' => null,
+                            'authorsubheading' => $forumgenerator->get_author_subheading_html(
+                                (object)$exporteduser1, $discussion1firstpostobject->created)
+                        ],
+                        'charcount' => null,
+                        'capabilities' => [
+                            'view' => true,
+                            'edit' => false,
+                            'delete' => false,
+                            'split' => false,
+                            'reply' => true,
+                            'export' => false,
+                            'controlreadstatus' => false,
+                            'canreplyprivately' => false,
+                            'selfenrol' => false
+                        ],
+                        'urls' => [
+                            'view' => $urlfactory->get_view_post_url_from_post_id(
+                                $discussion1firstpostobject->discussion, $discussion1firstpostobject->id)->out(false),
+                            'viewisolated' => $isolatedurlparent->out(false),
+                            'viewparent' => null,
+                            'edit' => null,
+                            'delete' => null,
+                            'split' => null,
+                            'reply' => (new moodle_url('/mod/forum/post.php#mformforum', [
+                                'reply' => $discussion1firstpostobject->id
+                            ]))->out(false),
+                            'export' => null,
+                            'markasread' => null,
+                            'markasunread' => null,
+                            'discuss' => $urlfactory->get_discussion_view_url_from_discussion_id(
+                                $discussion1firstpostobject->discussion)->out(false),
+                        ],
+                    ]
+                ],
+            ],
+        ];
+
+        $isolatedurluser = $urlfactory->get_discussion_view_url_from_discussion_id($discussion2reply1->discussion);
+        $isolatedurluser->params(['parent' => $discussion2reply1->id]);
+        $isolatedurlparent = $urlfactory->get_discussion_view_url_from_discussion_id($discussion2firstpostobject->discussion);
+        $isolatedurlparent->params(['parent' => $discussion2firstpostobject->id]);
+
+        $expectedposts['discussions'][1] = [
+            'name' => $discussion2->name,
+            'id' => $discussion2->id,
+            'posts' => [
+                'userposts' => [
+                    [
+                        'id' => $discussion2reply1->id,
+                        'discussionid' => $discussion2reply1->discussion,
+                        'parentid' => $discussion2reply1->parent,
+                        'hasparent' => true,
+                        'timecreated' => $discussion2reply1->created,
+                        'subject' => $discussion2reply1->subject,
+                        'replysubject' => get_string('re', 'mod_forum') . " {$discussion2reply1->subject}",
+                        'message' => file_rewrite_pluginfile_urls($discussion2reply1->message, 'pluginfile.php',
+                            $forum1context->id, 'mod_forum', 'post', $discussion2reply1->id),
+                        'messageformat' => 1,   // This value is usually changed by external_format_text() function.
+                        'unread' => null,
+                        'isdeleted' => false,
+                        'isprivatereply' => false,
+                        'haswordcount' => false,
+                        'wordcount' => null,
+                        'author' => $exporteduser2,
+                        'attachments' => [],
+                        'tags' => [],
+                        'html' => [
+                            'rating' => null,
+                            'taglist' => null,
+                            'authorsubheading' => $forumgenerator->get_author_subheading_html(
+                                (object)$exporteduser2, $discussion2reply1->created)
+                        ],
+                        'charcount' => null,
+                        'capabilities' => [
+                            'view' => true,
+                            'edit' => true,
+                            'delete' => true,
+                            'split' => false,
+                            'reply' => true,
+                            'export' => false,
+                            'controlreadstatus' => false,
+                            'canreplyprivately' => false,
+                            'selfenrol' => false
+                        ],
+                        'urls' => [
+                            'view' => $urlfactory->get_view_post_url_from_post_id(
+                                $discussion2reply1->discussion, $discussion2reply1->id)->out(false),
+                            'viewisolated' => $isolatedurluser->out(false),
+                            'viewparent' => $urlfactory->get_view_post_url_from_post_id(
+                                $discussion2reply1->discussion, $discussion2reply1->parent)->out(false),
+                            'edit' => (new moodle_url('/mod/forum/post.php', [
+                                'edit' => $discussion2reply1->id
+                            ]))->out(false),
+                            'delete' => (new moodle_url('/mod/forum/post.php', [
+                                'delete' => $discussion2reply1->id
+                            ]))->out(false),
+                            'split' => null,
+                            'reply' => (new moodle_url('/mod/forum/post.php#mformforum', [
+                                'reply' => $discussion2reply1->id
+                            ]))->out(false),
+                            'export' => null,
+                            'markasread' => null,
+                            'markasunread' => null,
+                            'discuss' => $urlfactory->get_discussion_view_url_from_discussion_id(
+                                $discussion2reply1->discussion)->out(false),
+                        ],
+                    ]
+                ],
+                'parentposts' => [
+                    [
+                        'id' => $discussion2firstpostobject->id,
+                        'discussionid' => $discussion2firstpostobject->discussion,
+                        'parentid' => null,
+                        'hasparent' => false,
+                        'timecreated' => $discussion2firstpostobject->created,
+                        'subject' => $discussion2firstpostobject->subject,
+                        'replysubject' => get_string('re', 'mod_forum') . " {$discussion2firstpostobject->subject}",
+                        'message' => file_rewrite_pluginfile_urls($discussion2firstpostobject->message, 'pluginfile.php',
+                            $forum1context->id, 'mod_forum', 'post', $discussion2firstpostobject->id),
+                        'messageformat' => 1,   // This value is usually changed by external_format_text() function.
+                        'unread' => null,
+                        'isdeleted' => false,
+                        'isprivatereply' => false,
+                        'haswordcount' => false,
+                        'wordcount' => null,
+                        'author' => $exporteduser1,
+                        'attachments' => [],
+                        'tags' => [],
+                        'html' => [
+                            'rating' => null,
+                            'taglist' => null,
+                            'authorsubheading' => $forumgenerator->get_author_subheading_html(
+                                (object)$exporteduser1, $discussion2firstpostobject->created)
+                        ],
+                        'charcount' => null,
+                        'capabilities' => [
+                            'view' => true,
+                            'edit' => false,
+                            'delete' => false,
+                            'split' => false,
+                            'reply' => true,
+                            'export' => false,
+                            'controlreadstatus' => false,
+                            'canreplyprivately' => false,
+                            'selfenrol' => false
+                        ],
+                        'urls' => [
+                            'view' => $urlfactory->get_view_post_url_from_post_id(
+                                $discussion2firstpostobject->discussion, $discussion2firstpostobject->id)->out(false),
+                            'viewisolated' => $isolatedurlparent->out(false),
+                            'viewparent' => null,
+                            'edit' => null,
+                            'delete' => null,
+                            'split' => null,
+                            'reply' => (new moodle_url('/mod/forum/post.php#mformforum', [
+                                'reply' => $discussion2firstpostobject->id
+                            ]))->out(false),
+                            'export' => null,
+                            'markasread' => null,
+                            'markasunread' => null,
+                            'discuss' => $urlfactory->get_discussion_view_url_from_discussion_id(
+                                $discussion2firstpostobject->discussion)->out(false),
+
+                        ]
+                    ],
+                ]
+            ],
+        ];
+
+        // Test discussions with one additional post each (total 2 posts).
+        // Also testing that we get the parent posts too.
+        $discussions = mod_forum_external::get_discussion_posts_by_userid($user2->id, $forum1->cmid, 'modified', 'DESC');
+        $discussions = external_api::clean_returnvalue(mod_forum_external::get_discussion_posts_by_userid_returns(), $discussions);
+
+        $this->assertEquals(2, count($discussions['discussions']));
+
+        $this->assertEquals($expectedposts, $discussions);
+    }
 }
index d3c5fa1..3a575d9 100644 (file)
@@ -91,4 +91,32 @@ class mod_forum_vaults_discussion_testcase extends advanced_testcase {
         $discussionentity = $vault->get_first_discussion_in_forum($forumentity);
         $this->assertEquals($discussion2->id, $discussionentity->get_id());
     }
+
+    /**
+     * Test get_all_discussions_in_forum
+     */
+    public function test_get_all_discussions_in_forum() {
+        $this->resetAfterTest();
+
+        $vault = $this->vault;
+        $entityfactory = \mod_forum\local\container::get_entity_factory();
+        $datagenerator = $this->getDataGenerator();
+        $user = $datagenerator->create_user();
+        $course = $datagenerator->create_course();
+        $forum = $datagenerator->create_module('forum', ['course' => $course->id]);
+        $coursemodule = get_coursemodule_from_instance('forum', $forum->id);
+        $context = context_module::instance($coursemodule->id);
+        $forumentity = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
+
+        $this->assertEquals([], $vault->get_all_discussions_in_forum($forumentity));
+
+        [$discussion1, $post] = $this->helper_post_to_forum($forum, $user, ['timemodified' => 2]);
+        [$discussion2, $post] = $this->helper_post_to_forum($forum, $user, ['timemodified' => 1]);
+        [$discussion3, $post] = $this->helper_post_to_forum($forum, $user, ['timemodified' => 3]);
+
+        $discussionentity = $vault->get_all_discussions_in_forum($forumentity);
+        $this->assertArrayHasKey($discussion1->id, $discussionentity); // Order is not guaranteed, so just verify element existence.
+        $this->assertArrayHasKey($discussion2->id, $discussionentity);
+        $this->assertArrayHasKey($discussion3->id, $discussionentity);
+    }
 }
index e9b8a95..f3c096a 100644 (file)
@@ -1127,5 +1127,41 @@ class mod_forum_vaults_post_testcase extends advanced_testcase {
                 ['discussionids' => $discussionids, 'userids' => $userids], false);
         $this->assertCount(1, $entities);
         $this->assertArrayHasKey($otherpost->id, $entities);
+
+    }
+
+    /**
+     * Test get_from_user_id.
+     *
+     * @covers ::get_posts_in_forum_for_user_id
+     */
+    public function test_get_from_user_id() {
+        $this->resetAfterTest();
+
+        $datagenerator = $this->getDataGenerator();
+        $user = $datagenerator->create_user();
+        $course = $datagenerator->create_course();
+        $forum = $datagenerator->create_module('forum', ['course' => $course->id]);
+
+        $vaultfactory = mod_forum\local\container::get_vault_factory();
+
+        $forumvault = $vaultfactory->get_forum_vault();
+        $forumentity = $forumvault->get_from_course_module_id($forum->cmid);
+
+        $managerfactory = mod_forum\local\container::get_manager_factory();
+        $capabilitymanager = $managerfactory->get_capability_manager($forumentity);
+        [$discussion1, $post1] = $this->helper_post_to_forum($forum, $user);
+        $post2 = $this->helper_reply_to_post($post1, $user);
+        $post3 = $this->helper_reply_to_post($post1, $user);
+        [$discussion2, $post4] = $this->helper_post_to_forum($forum, $user);
+        $discussionkeys = [$discussion1->id, $discussion2->id];
+
+        $viewhidden = $capabilitymanager->can_view_any_private_reply($user);
+        $entities = $this->vault->get_posts_in_forum_for_user_id($discussionkeys, $user->id, $viewhidden, 'modified DESC');
+        $this->assertCount(4, $entities);
+        $this->assertArrayHasKey($post1->id, $entities); // Order is not guaranteed, so just verify element existence.
+        $this->assertArrayHasKey($post2->id, $entities);
+        $this->assertArrayHasKey($post3->id, $entities);
+        $this->assertArrayHasKey($post4->id, $entities);
     }
 }