1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Exported discussion summaries builder class. 19 * 20 * @package mod_forum 21 * @copyright 2019 Mihail Geshoski <mihail@moodle.com> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace mod_forum\local\builders; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 use mod_forum\local\entities\discussion as discussion_entity; 30 use mod_forum\local\entities\forum as forum_entity; 31 use mod_forum\local\entities\post as post_entity; 32 use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory; 33 use mod_forum\local\factories\exporter as exporter_factory; 34 use mod_forum\local\factories\vault as vault_factory; 35 use mod_forum\local\factories\manager as manager_factory; 36 use rating_manager; 37 use renderer_base; 38 use stdClass; 39 40 /** 41 * Exported discussion summaries builder class. 42 * 43 * This class is an implementation of the builder pattern (loosely). It is responsible 44 * for taking a set of related forums, discussions, and posts and generate the exported 45 * version of the discussion summaries. 46 * 47 * It encapsulates the complexity involved with exporting discussions summaries. All of the relevant 48 * additional resources will be loaded by this class in order to ensure the exporting 49 * process can happen. 50 * 51 * See this doc for more information on the builder pattern: 52 * https://designpatternsphp.readthedocs.io/en/latest/Creational/Builder/README.html 53 * 54 * @package mod_forum 55 * @copyright 2019 Mihail Geshoski <mihail@moodle.com> 56 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 57 */ 58 class exported_discussion_summaries { 59 /** @var renderer_base $renderer Core renderer */ 60 private $renderer; 61 62 /** @var legacy_data_mapper_factory $legacydatamapperfactory Data mapper factory */ 63 private $legacydatamapperfactory; 64 65 /** @var exporter_factory $exporterfactory Exporter factory */ 66 private $exporterfactory; 67 68 /** @var vault_factory $vaultfactory Vault factory */ 69 private $vaultfactory; 70 71 /** @var manager_factory $managerfactory Manager factory */ 72 private $managerfactory; 73 74 /** @var rating_manager $ratingmanager Rating manager */ 75 private $ratingmanager; 76 77 /** 78 * Constructor. 79 * 80 * @param renderer_base $renderer Core renderer 81 * @param legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory 82 * @param exporter_factory $exporterfactory Exporter factory 83 * @param vault_factory $vaultfactory Vault factory 84 * @param manager_factory $managerfactory Manager factory 85 */ 86 public function __construct( 87 renderer_base $renderer, 88 legacy_data_mapper_factory $legacydatamapperfactory, 89 exporter_factory $exporterfactory, 90 vault_factory $vaultfactory, 91 manager_factory $managerfactory 92 ) { 93 $this->renderer = $renderer; 94 $this->legacydatamapperfactory = $legacydatamapperfactory; 95 $this->exporterfactory = $exporterfactory; 96 $this->vaultfactory = $vaultfactory; 97 $this->managerfactory = $managerfactory; 98 $this->ratingmanager = $managerfactory->get_rating_manager(); 99 } 100 101 /** 102 * Build the exported discussion summaries for a given set of discussions. 103 * 104 * This will typically be used for a list of discussions in the same forum. 105 * 106 * @param stdClass $user The user to export the posts for. 107 * @param forum_entity $forum The forum that each of the $discussions belong to 108 * @param discussion_summary_entity[] $discussions A list of all discussion summaries to export 109 * @return stdClass[] List of exported posts in the same order as the $posts array. 110 */ 111 public function build( 112 stdClass $user, 113 forum_entity $forum, 114 array $discussions 115 ) : array { 116 $capabilitymanager = $this->managerfactory->get_capability_manager($forum); 117 $canseeanyprivatereply = $capabilitymanager->can_view_any_private_reply($user); 118 119 $discussionids = array_keys($discussions); 120 121 $postvault = $this->vaultfactory->get_post_vault(); 122 $posts = $postvault->get_from_discussion_ids($user, $discussionids, $canseeanyprivatereply); 123 $groupsbyid = $this->get_groups_available_in_forum($forum); 124 $groupsbyauthorid = $this->get_author_groups_from_posts($posts, $forum); 125 126 $replycounts = $postvault->get_reply_count_for_discussion_ids($user, $discussionids, $canseeanyprivatereply); 127 $latestposts = $postvault->get_latest_posts_for_discussion_ids($user, $discussionids, $canseeanyprivatereply); 128 $latestauthors = $this->get_latest_posts_authors($latestposts); 129 $latestpostsids = array_map(function($post) { 130 return $post->get_id(); 131 }, $latestposts); 132 133 $postauthorids = array_unique(array_reduce($discussions, function($carry, $summary) use ($latestposts){ 134 $firstpostauthorid = $summary->get_first_post_author()->get_id(); 135 $discussion = $summary->get_discussion(); 136 $lastpostauthorid = $latestposts[$discussion->get_id()]->get_author_id(); 137 return array_merge($carry, [$firstpostauthorid, $lastpostauthorid]); 138 }, [])); 139 $postauthorcontextids = $this->get_author_context_ids($postauthorids); 140 141 $unreadcounts = []; 142 $favourites = $this->get_favourites($user); 143 $forumdatamapper = $this->legacydatamapperfactory->get_forum_data_mapper(); 144 $forumrecord = $forumdatamapper->to_legacy_object($forum); 145 146 if (forum_tp_can_track_forums($forumrecord)) { 147 $unreadcounts = $postvault->get_unread_count_for_discussion_ids($user, $discussionids, $canseeanyprivatereply); 148 } 149 150 $summaryexporter = $this->exporterfactory->get_discussion_summaries_exporter( 151 $user, 152 $forum, 153 $discussions, 154 $groupsbyid, 155 $groupsbyauthorid, 156 $replycounts, 157 $unreadcounts, 158 $latestpostsids, 159 $postauthorcontextids, 160 $favourites, 161 $latestauthors 162 ); 163 164 $exportedposts = (array) $summaryexporter->export($this->renderer); 165 $firstposts = $postvault->get_first_post_for_discussion_ids($discussionids); 166 167 array_walk($exportedposts['summaries'], function($summary) use ($firstposts, $latestposts) { 168 $summary->discussion->times['created'] = (int) $firstposts[$summary->discussion->firstpostid]->get_time_created(); 169 $summary->discussion->times['modified'] = (int) $latestposts[$summary->discussion->id]->get_time_created(); 170 }); 171 172 // Pass the current, preferred sort order for the discussions list. 173 $discussionlistvault = $this->vaultfactory->get_discussions_in_forum_vault(); 174 $sortorder = get_user_preferences('forum_discussionlistsortorder', 175 $discussionlistvault::SORTORDER_LASTPOST_DESC); 176 177 $sortoptions = array( 178 'islastpostdesc' => $sortorder == $discussionlistvault::SORTORDER_LASTPOST_DESC, 179 'islastpostasc' => $sortorder == $discussionlistvault::SORTORDER_LASTPOST_ASC, 180 'isrepliesdesc' => $sortorder == $discussionlistvault::SORTORDER_REPLIES_DESC, 181 'isrepliesasc' => $sortorder == $discussionlistvault::SORTORDER_REPLIES_ASC, 182 'iscreateddesc' => $sortorder == $discussionlistvault::SORTORDER_CREATED_DESC, 183 'iscreatedasc' => $sortorder == $discussionlistvault::SORTORDER_CREATED_ASC, 184 'isdiscussiondesc' => $sortorder == $discussionlistvault::SORTORDER_DISCUSSION_DESC, 185 'isdiscussionasc' => $sortorder == $discussionlistvault::SORTORDER_DISCUSSION_ASC, 186 'isstarterdesc' => $sortorder == $discussionlistvault::SORTORDER_STARTER_DESC, 187 'isstarterasc' => $sortorder == $discussionlistvault::SORTORDER_STARTER_ASC, 188 'isgroupdesc' => $sortorder == $discussionlistvault::SORTORDER_GROUP_DESC, 189 'isgroupasc' => $sortorder == $discussionlistvault::SORTORDER_GROUP_ASC, 190 ); 191 192 $exportedposts['state']['sortorder'] = $sortoptions; 193 194 return $exportedposts; 195 } 196 197 /** 198 * Get a list of all favourited discussions. 199 * 200 * @param stdClass $user The user we are getting favourites for 201 * @return int[] A list of favourited itemids 202 */ 203 private function get_favourites(stdClass $user) : array { 204 $ids = []; 205 206 if (isloggedin()) { 207 $usercontext = \context_user::instance($user->id); 208 $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext); 209 $favourites = $ufservice->find_favourites_by_type('mod_forum', 'discussions'); 210 foreach ($favourites as $favourite) { 211 $ids[] = $favourite->itemid; 212 } 213 } 214 215 return $ids; 216 } 217 218 /** 219 * Returns a mapped array of discussionid to the authors of the latest post 220 * 221 * @param array $latestposts Mapped array of discussion to latest posts. 222 * @return array Array of authors mapped to the discussion 223 */ 224 private function get_latest_posts_authors($latestposts) { 225 $authors = $this->vaultfactory->get_author_vault()->get_authors_for_posts($latestposts); 226 227 $mappedauthors = array_reduce($latestposts, function($carry, $item) use ($authors) { 228 $carry[$item->get_discussion_id()] = $authors[$item->get_author_id()]; 229 230 return $carry; 231 }, []); 232 return $mappedauthors; 233 } 234 235 /** 236 * Get the groups details for all groups available to the forum. 237 * @param forum_entity $forum The forum entity 238 * @return stdClass[] 239 */ 240 private function get_groups_available_in_forum($forum) : array { 241 $course = $forum->get_course_record(); 242 $coursemodule = $forum->get_course_module_record(); 243 244 return groups_get_all_groups($course->id, 0, $coursemodule->groupingid); 245 } 246 247 /** 248 * Get the author's groups for a list of posts. 249 * 250 * @param post_entity[] $posts The list of posts 251 * @param forum_entity $forum The forum entity 252 * @return array Author groups indexed by author id 253 */ 254 private function get_author_groups_from_posts(array $posts, $forum) : array { 255 $course = $forum->get_course_record(); 256 $coursemodule = $forum->get_course_module_record(); 257 $authorids = array_reduce($posts, function($carry, $post) { 258 $carry[$post->get_author_id()] = true; 259 return $carry; 260 }, []); 261 $authorgroups = groups_get_all_groups($course->id, array_keys($authorids), $coursemodule->groupingid, 262 'g.*, gm.id, gm.groupid, gm.userid'); 263 264 $authorgroups = array_reduce($authorgroups, function($carry, $group) { 265 // Clean up data returned from groups_get_all_groups. 266 $userid = $group->userid; 267 $groupid = $group->groupid; 268 269 unset($group->userid); 270 unset($group->groupid); 271 $group->id = $groupid; 272 273 if (!isset($carry[$userid])) { 274 $carry[$userid] = [$group]; 275 } else { 276 $carry[$userid][] = $group; 277 } 278 279 return $carry; 280 }, []); 281 282 foreach (array_diff(array_keys($authorids), array_keys($authorgroups)) as $authorid) { 283 $authorgroups[$authorid] = []; 284 } 285 286 return $authorgroups; 287 } 288 289 /** 290 * Get the user context ids for each of the authors. 291 * 292 * @param int[] $authorids The list of author ids to fetch context ids for. 293 * @return int[] Context ids indexed by author id 294 */ 295 private function get_author_context_ids(array $authorids) : array { 296 $authorvault = $this->vaultfactory->get_author_vault(); 297 return $authorvault->get_context_ids_for_author_ids($authorids); 298 } 299 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body