2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Discussion list renderer.
21 * @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 namespace mod_forum\local\renderers;
27 defined('MOODLE_INTERNAL') || die();
29 use mod_forum\grades\forum_gradeitem;
30 use mod_forum\local\entities\forum as forum_entity;
31 use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
32 use mod_forum\local\factories\exporter as exporter_factory;
33 use mod_forum\local\factories\vault as vault_factory;
34 use mod_forum\local\factories\url as url_factory;
35 use mod_forum\local\managers\capability as capability_manager;
36 use mod_forum\local\vaults\discussion_list as discussion_list_vault;
39 use core\output\notification;
40 use mod_forum\local\factories\builder as builder_factory;
42 require_once($CFG->dirroot . '/mod/forum/lib.php');
45 * The discussion list renderer.
48 * @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
49 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
51 class discussion_list {
52 /** @var forum_entity The forum being rendered */
55 /** @var stdClass The DB record for the forum being rendered */
58 /** @var renderer_base The renderer used to render the view */
61 /** @var legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory */
62 private $legacydatamapperfactory;
64 /** @var exporter_factory $exporterfactory Exporter factory */
65 private $exporterfactory;
67 /** @var vault_factory $vaultfactory Vault factory */
68 private $vaultfactory;
70 /** @var capability_manager $capabilitymanager Capability manager */
71 private $capabilitymanager;
73 /** @var url_factory $urlfactory URL factory */
76 /** @var array $notifications List of notification HTML */
77 private $notifications;
79 /** @var builder_factory $builderfactory Builder factory */
80 private $builderfactory;
82 /** @var callable $postprocessfortemplate Function to process exported posts before template rendering */
83 private $postprocessfortemplate;
85 /** @var string $template The template to use when displaying */
88 /** @var gradeitem The gradeitem instance associated with this forum */
89 private $forumgradeitem;
92 * Constructor for a new discussion list renderer.
94 * @param forum_entity $forum The forum entity to be rendered
95 * @param renderer_base $renderer The renderer used to render the view
96 * @param legacy_data_mapper_factory $legacydatamapperfactory The factory used to fetch a legacy record
97 * @param exporter_factory $exporterfactory The factory used to fetch exporter instances
98 * @param vault_factory $vaultfactory The factory used to fetch the vault instances
99 * @param builder_factory $builderfactory The factory used to fetch the builder instances
100 * @param capability_manager $capabilitymanager The managed used to check capabilities on the forum
101 * @param url_factory $urlfactory The factory used to create URLs in the forum
102 * @param string $template
103 * @param notification[] $notifications A list of any notifications to be displayed within the page
104 * @param callable|null $postprocessfortemplate Callback function to process discussion lists for templates
106 public function __construct(
108 renderer_base $renderer,
109 legacy_data_mapper_factory $legacydatamapperfactory,
110 exporter_factory $exporterfactory,
111 vault_factory $vaultfactory,
112 builder_factory $builderfactory,
113 capability_manager $capabilitymanager,
114 url_factory $urlfactory,
115 forum_gradeitem $forumgradeitem,
117 array $notifications = [],
118 callable $postprocessfortemplate = null
120 $this->forum = $forum;
121 $this->renderer = $renderer;
122 $this->legacydatamapperfactory = $legacydatamapperfactory;
123 $this->exporterfactory = $exporterfactory;
124 $this->vaultfactory = $vaultfactory;
125 $this->builderfactory = $builderfactory;
126 $this->capabilitymanager = $capabilitymanager;
128 $this->urlfactory = $urlfactory;
129 $this->notifications = $notifications;
130 $this->postprocessfortemplate = $postprocessfortemplate;
131 $this->template = $template;
132 $this->forumgradeitem = $forumgradeitem;
134 $forumdatamapper = $this->legacydatamapperfactory->get_forum_data_mapper();
135 $this->forumrecord = $forumdatamapper->to_legacy_object($forum);
139 * Render for the specified user.
141 * @param stdClass $user The user to render for
142 * @param cm_info $cm The course module info for this discussion list
143 * @param int $groupid The group to render
144 * @param int $sortorder The sort order to use when selecting the discussions in the list
145 * @param int $pageno The zero-indexed page number to use
146 * @param int $pagesize The number of discussions to show on the page
147 * @param int $displaymode The discussion display mode
148 * @return string The rendered content for display
150 public function render(
157 int $displaymode = null
161 $forum = $this->forum;
162 $course = $forum->get_course_record();
164 $forumexporter = $this->exporterfactory->get_forum_exporter(
170 $pagesize = $this->get_page_size($pagesize);
171 $pageno = $this->get_page_number($pageno);
173 // Count all forum discussion posts.
174 $alldiscussionscount = mod_forum_count_all_discussions($forum, $user, $groupid);
176 // Get all forum discussion summaries.
177 $discussions = mod_forum_get_discussion_summaries($forum, $user, $groupid, $sortorder, $pageno, $pagesize);
179 $capabilitymanager = $this->capabilitymanager;
180 $hasanyactions = false;
181 $hasanyactions = $hasanyactions || $capabilitymanager->can_favourite_discussion($user);
182 $hasanyactions = $hasanyactions || $capabilitymanager->can_pin_discussions($user);
183 $hasanyactions = $hasanyactions || $capabilitymanager->can_manage_forum($user);
186 'forum' => (array) $forumexporter->export($this->renderer),
187 'contextid' => $forum->get_context()->id,
189 'name' => format_string($forum->get_name()),
190 'courseid' => $course->id,
191 'coursename' => format_string($course->shortname),
192 'experimentaldisplaymode' => $displaymode == FORUM_MODE_NESTED_V2,
193 'gradingcomponent' => $this->forumgradeitem->get_grading_component_name(),
194 'gradingcomponentsubtype' => $this->forumgradeitem->get_grading_component_subtype(),
195 'sendstudentnotifications' => $forum->should_notify_students_default_when_grade_for_forum(),
196 'hasanyactions' => $hasanyactions,
197 'groupchangemenu' => groups_print_activity_menu(
199 $this->urlfactory->get_forum_view_url_from_forum($forum),
202 'hasmore' => ($alldiscussionscount > $pagesize),
203 'notifications' => $this->get_notifications($user, $groupid),
205 'excludetext' => true,
206 'togglemoreicon' => true,
207 'excludesubscription' => true
209 'totaldiscussioncount' => $alldiscussionscount,
210 'userid' => $user->id,
211 'visiblediscussioncount' => count($discussions)
214 if ($forumview['forum']['capabilities']['create']) {
215 $forumview['newdiscussionhtml'] = $this->get_discussion_form($user, $cm, $groupid);
219 return $this->renderer->render_from_template($this->template, $forumview);
222 if ($this->postprocessfortemplate !== null) {
223 // We've got some post processing to do!
224 $exportedposts = ($this->postprocessfortemplate) ($discussions, $user, $forum);
227 $baseurl = new \moodle_url($PAGE->url, array('o' => $sortorder));
229 $forumview = array_merge(
232 'pagination' => $this->renderer->render(new \paging_bar($alldiscussionscount, $pageno, $pagesize, $baseurl, 'p')),
237 $firstdiscussion = reset($discussions);
238 $forumview['firstgradeduserid'] = $firstdiscussion->get_latest_post_author()->get_id();
240 return $this->renderer->render_from_template($this->template, $forumview);
244 * Get the mod_forum_post_form. This is the default boiler plate from mod_forum/post_form.php with the inpage flag caveat
246 * @param stdClass $user The user the form is being generated for
247 * @param \cm_info $cm
248 * @param int $groupid The groupid if any
250 * @return string The rendered html
252 private function get_discussion_form(stdClass $user, \cm_info $cm, ?int $groupid) {
253 $forum = $this->forum;
254 $forumrecord = $this->legacydatamapperfactory->get_forum_data_mapper()->to_legacy_object($forum);
255 $modcontext = \context_module::instance($cm->id);
256 $coursecontext = \context_course::instance($forum->get_course_id());
258 'course' => $forum->get_course_id(),
259 'forum' => $forum->get_id(),
260 'discussion' => 0, // Ie discussion # not defined yet.
263 'userid' => $user->id,
265 'messageformat' => editors_get_preferred_format(),
267 'groupid' => $groupid,
269 $thresholdwarning = forum_check_throttling($forumrecord, $cm);
272 'course' => $forum->get_course_record(),
274 'coursecontext' => $coursecontext,
275 'modcontext' => $modcontext,
276 'forum' => $forumrecord,
278 'subscribe' => \mod_forum\subscriptions::is_subscribed($user->id, $forumrecord,
280 'thresholdwarning' => $thresholdwarning,
281 'inpagereply' => true,
284 $posturl = new \moodle_url('/mod/forum/post.php');
285 $mformpost = new \mod_forum_post_form($posturl, $formparams, 'post', '', array('id' => 'mformforum'));
286 $discussionsubscribe = \mod_forum\subscriptions::get_user_default_subscription($forumrecord, $coursecontext, $cm, null);
288 $params = array('reply' => 0, 'forum' => $forumrecord->id, 'edit' => 0) +
289 (isset($post->groupid) ? array('groupid' => $post->groupid) : array()) +
291 'userid' => $post->userid,
292 'parent' => $post->parent,
293 'discussion' => $post->discussion,
294 'course' => $forum->get_course_id(),
295 'discussionsubscribe' => $discussionsubscribe
297 $mformpost->set_data($params);
299 return $mformpost->render();
303 * Fetch the page size to use when displaying the page.
305 * @param int $pagesize The number of discussions to show on the page
306 * @return int The normalised page size
308 private function get_page_size(?int $pagesize) : int {
309 if (null === $pagesize || $pagesize <= 0) {
310 $pagesize = discussion_list_vault::PAGESIZE_DEFAULT;
317 * Fetch the current page number (zero-indexed).
319 * @param int $pageno The zero-indexed page number to use
320 * @return int The normalised page number
322 private function get_page_number(?int $pageno) : int {
323 if (null === $pageno || $pageno < 0) {
331 * Get the list of notification for display.
333 * @param stdClass $user The viewing user
334 * @param int|null $groupid The forum's group id
337 private function get_notifications(stdClass $user, ?int $groupid) : array {
338 $notifications = $this->notifications;
339 $forum = $this->forum;
340 $renderer = $this->renderer;
341 $capabilitymanager = $this->capabilitymanager;
343 if ($forum->is_cutoff_date_reached()) {
344 $notifications[] = (new notification(
345 get_string('cutoffdatereached', 'forum'),
346 notification::NOTIFY_INFO
347 ))->set_show_closebutton();
348 } else if ($forum->is_due_date_reached()) {
349 $notifications[] = (new notification(
350 get_string('thisforumisdue', 'forum', userdate($forum->get_due_date())),
351 notification::NOTIFY_INFO
352 ))->set_show_closebutton();
353 } else if ($forum->has_due_date()) {
354 $notifications[] = (new notification(
355 get_string('thisforumhasduedate', 'forum', userdate($forum->get_due_date())),
356 notification::NOTIFY_INFO
357 ))->set_show_closebutton();
360 if ($forum->has_blocking_enabled()) {
361 $notifications[] = (new notification(
362 get_string('thisforumisthrottled', 'forum', [
363 'blockafter' => $forum->get_block_after(),
364 'blockperiod' => get_string('secondstotime' . $forum->get_block_period())
366 ))->set_show_closebutton();
369 if ($forum->is_in_group_mode() && !$capabilitymanager->can_access_all_groups($user)) {
370 if ($groupid === null) {
371 if (!$capabilitymanager->can_post_to_my_groups($user)) {
372 $notifications[] = (new notification(
373 get_string('cannotadddiscussiongroup', 'mod_forum'),
374 \core\output\notification::NOTIFY_WARNING
375 ))->set_show_closebutton();
377 $notifications[] = (new notification(
378 get_string('cannotadddiscussionall', 'mod_forum'),
379 \core\output\notification::NOTIFY_WARNING
380 ))->set_show_closebutton();
382 } else if (!$capabilitymanager->can_access_group($user, $groupid)) {
383 $notifications[] = (new notification(
384 get_string('cannotadddiscussion', 'mod_forum'),
385 \core\output\notification::NOTIFY_WARNING
386 ))->set_show_closebutton();
390 if ('qanda' === $forum->get_type() && !$capabilitymanager->can_manage_forum($user)) {
391 $notifications[] = (new notification(
392 get_string('qandanotify', 'forum'),
393 notification::NOTIFY_INFO
394 ))->set_show_closebutton();
397 if ('eachuser' === $forum->get_type()) {
398 $notifications[] = (new notification(
399 get_string('allowsdiscussions', 'forum'),
400 notification::NOTIFY_INFO)
401 )->set_show_closebutton();
404 return array_map(function($notification) {
405 return $notification->export_for_template($this->renderer);