Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   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   * Discussion list renderer.
  19   *
  20   * @package    mod_forum
  21   * @copyright  2019 Andrew Nicols <andrew@nicols.co.uk>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace mod_forum\local\renderers;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  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;
  37  use renderer_base;
  38  use stdClass;
  39  use core\output\notification;
  40  use mod_forum\local\factories\builder as builder_factory;
  41  
  42  require_once($CFG->dirroot . '/mod/forum/lib.php');
  43  
  44  /**
  45   * The discussion list renderer.
  46   *
  47   * @package    mod_forum
  48   * @copyright  2019 Andrew Nicols <andrew@nicols.co.uk>
  49   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  50   */
  51  class discussion_list {
  52      /** @var forum_entity The forum being rendered */
  53      private $forum;
  54  
  55      /** @var stdClass The DB record for the forum being rendered */
  56      private $forumrecord;
  57  
  58      /** @var renderer_base The renderer used to render the view */
  59      private $renderer;
  60  
  61      /** @var legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory */
  62      private $legacydatamapperfactory;
  63  
  64      /** @var exporter_factory $exporterfactory Exporter factory */
  65      private $exporterfactory;
  66  
  67      /** @var vault_factory $vaultfactory Vault factory */
  68      private $vaultfactory;
  69  
  70      /** @var capability_manager $capabilitymanager Capability manager */
  71      private $capabilitymanager;
  72  
  73      /** @var url_factory $urlfactory URL factory */
  74      private $urlfactory;
  75  
  76      /** @var array $notifications List of notification HTML */
  77      private $notifications;
  78  
  79      /** @var builder_factory $builderfactory Builder factory */
  80      private $builderfactory;
  81  
  82      /** @var callable $postprocessfortemplate Function to process exported posts before template rendering */
  83      private $postprocessfortemplate;
  84  
  85      /** @var string $template The template to use when displaying */
  86      private $template;
  87  
  88      /** @var gradeitem The gradeitem instance associated with this forum */
  89      private $forumgradeitem;
  90  
  91      /**
  92       * Constructor for a new discussion list renderer.
  93       *
  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
 105       */
 106      public function __construct(
 107          forum_entity $forum,
 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,
 116          string $template,
 117          array $notifications = [],
 118          callable $postprocessfortemplate = null
 119      ) {
 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;
 127  
 128          $this->urlfactory = $urlfactory;
 129          $this->notifications = $notifications;
 130          $this->postprocessfortemplate = $postprocessfortemplate;
 131          $this->template = $template;
 132          $this->forumgradeitem = $forumgradeitem;
 133  
 134          $forumdatamapper = $this->legacydatamapperfactory->get_forum_data_mapper();
 135          $this->forumrecord = $forumdatamapper->to_legacy_object($forum);
 136      }
 137  
 138      /**
 139       * Render for the specified user.
 140       *
 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
 149       */
 150      public function render(
 151          stdClass $user,
 152          \cm_info $cm,
 153          ?int $groupid,
 154          ?int $sortorder,
 155          ?int $pageno,
 156          ?int $pagesize,
 157          int $displaymode = null
 158      ) : string {
 159          global $PAGE;
 160  
 161          $forum = $this->forum;
 162          $course = $forum->get_course_record();
 163  
 164          $forumexporter = $this->exporterfactory->get_forum_exporter(
 165              $user,
 166              $this->forum,
 167              $groupid
 168          );
 169  
 170          $pagesize = $this->get_page_size($pagesize);
 171          $pageno = $this->get_page_number($pageno);
 172  
 173          // Count all forum discussion posts.
 174          $alldiscussionscount = mod_forum_count_all_discussions($forum, $user, $groupid);
 175  
 176          // Get all forum discussion summaries.
 177          $discussions = mod_forum_get_discussion_summaries($forum, $user, $groupid, $sortorder, $pageno, $pagesize);
 178  
 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);
 184  
 185          $forumview = [
 186              'forum' => (array) $forumexporter->export($this->renderer),
 187              'contextid' => $forum->get_context()->id,
 188              'cmid' => $cm->id,
 189              'groupid' => $groupid,
 190              'name' => format_string($forum->get_name()),
 191              'courseid' => $course->id,
 192              'coursename' => format_string($course->shortname),
 193              'experimentaldisplaymode' => $displaymode == FORUM_MODE_NESTED_V2,
 194              'gradingcomponent' => $this->forumgradeitem->get_grading_component_name(),
 195              'gradingcomponentsubtype' => $this->forumgradeitem->get_grading_component_subtype(),
 196              'sendstudentnotifications' => $forum->should_notify_students_default_when_grade_for_forum(),
 197              'gradeonlyactiveusers' => $this->forumgradeitem->should_grade_only_active_users(),
 198              'hasanyactions' => $hasanyactions,
 199              'groupchangemenu' => groups_print_activity_menu(
 200                  $cm,
 201                  $this->urlfactory->get_forum_view_url_from_forum($forum),
 202                  true
 203              ),
 204              'hasmore' => ($alldiscussionscount > $pagesize),
 205              'notifications' => $this->get_notifications($user, $groupid),
 206              'settings' => [
 207                  'excludetext' => true,
 208                  'togglemoreicon' => true,
 209                  'excludesubscription' => true
 210              ],
 211              'totaldiscussioncount' => $alldiscussionscount,
 212              'userid' => $user->id,
 213              'visiblediscussioncount' => count($discussions)
 214          ];
 215  
 216          if ($forumview['forum']['capabilities']['create']) {
 217              $forumview['newdiscussionhtml'] = $this->get_discussion_form($user, $cm, $groupid);
 218          }
 219  
 220          if (!$discussions) {
 221              return $this->renderer->render_from_template($this->template, $forumview);
 222          }
 223  
 224          if ($this->postprocessfortemplate !== null) {
 225              // We've got some post processing to do!
 226              $exportedposts = ($this->postprocessfortemplate) ($discussions, $user, $forum);
 227          }
 228  
 229          $baseurl = new \moodle_url($PAGE->url, array('o' => $sortorder));
 230  
 231          $forumview = array_merge(
 232              $forumview,
 233              [
 234                  'pagination' => $this->renderer->render(new \paging_bar($alldiscussionscount, $pageno, $pagesize, $baseurl, 'p')),
 235              ],
 236              $exportedposts
 237          );
 238  
 239          $firstdiscussion = reset($discussions);
 240          $forumview['firstgradeduserid'] = $firstdiscussion->get_latest_post_author()->get_id();
 241  
 242          return $this->renderer->render_from_template($this->template, $forumview);
 243      }
 244  
 245      /**
 246       * Get the mod_forum_post_form. This is the default boiler plate from mod_forum/post_form.php with the inpage flag caveat
 247       *
 248       * @param stdClass $user The user the form is being generated for
 249       * @param \cm_info $cm
 250       * @param int $groupid The groupid if any
 251       *
 252       * @return string The rendered html
 253       */
 254      private function get_discussion_form(stdClass $user, \cm_info $cm, ?int $groupid) {
 255          $forum = $this->forum;
 256          $forumrecord = $this->legacydatamapperfactory->get_forum_data_mapper()->to_legacy_object($forum);
 257          $modcontext = \context_module::instance($cm->id);
 258          $coursecontext = \context_course::instance($forum->get_course_id());
 259          $post = (object) [
 260              'course' => $forum->get_course_id(),
 261              'forum' => $forum->get_id(),
 262              'discussion' => 0,           // Ie discussion # not defined yet.
 263              'parent' => 0,
 264              'subject' => '',
 265              'userid' => $user->id,
 266              'message' => '',
 267              'messageformat' => editors_get_preferred_format(),
 268              'messagetrust' => 0,
 269              'groupid' => $groupid,
 270          ];
 271          $thresholdwarning = forum_check_throttling($forumrecord, $cm);
 272  
 273          $formparams = array(
 274              'course' => $forum->get_course_record(),
 275              'cm' => $cm,
 276              'coursecontext' => $coursecontext,
 277              'modcontext' => $modcontext,
 278              'forum' => $forumrecord,
 279              'post' => $post,
 280              'subscribe' => \mod_forum\subscriptions::is_subscribed($user->id, $forumrecord,
 281                  null, $cm),
 282              'thresholdwarning' => $thresholdwarning,
 283              'inpagereply' => true,
 284              'edit' => 0
 285          );
 286          $posturl = new \moodle_url('/mod/forum/post.php');
 287          $mformpost = new \mod_forum_post_form($posturl, $formparams, 'post', '', array('id' => 'mformforum'));
 288          $discussionsubscribe = \mod_forum\subscriptions::get_user_default_subscription($forumrecord, $coursecontext, $cm, null);
 289  
 290          $params = array('reply' => 0, 'forum' => $forumrecord->id, 'edit' => 0) +
 291              (isset($post->groupid) ? array('groupid' => $post->groupid) : array()) +
 292              array(
 293                  'userid' => $post->userid,
 294                  'parent' => $post->parent,
 295                  'discussion' => $post->discussion,
 296                  'course' => $forum->get_course_id(),
 297                  'discussionsubscribe' => $discussionsubscribe
 298              );
 299          $mformpost->set_data($params);
 300  
 301          return $mformpost->render();
 302      }
 303  
 304      /**
 305       * Fetch the page size to use when displaying the page.
 306       *
 307       * @param   int         $pagesize The number of discussions to show on the page
 308       * @return  int         The normalised page size
 309       */
 310      private function get_page_size(?int $pagesize) : int {
 311          if (null === $pagesize || $pagesize <= 0) {
 312              $pagesize = discussion_list_vault::PAGESIZE_DEFAULT;
 313          }
 314  
 315          return $pagesize;
 316      }
 317  
 318      /**
 319       * Fetch the current page number (zero-indexed).
 320       *
 321       * @param   int         $pageno The zero-indexed page number to use
 322       * @return  int         The normalised page number
 323       */
 324      private function get_page_number(?int $pageno) : int {
 325          if (null === $pageno || $pageno < 0) {
 326              $pageno = 0;
 327          }
 328  
 329          return $pageno;
 330      }
 331  
 332      /**
 333       * Get the list of notification for display.
 334       *
 335       * @param stdClass $user The viewing user
 336       * @param int|null $groupid The forum's group id
 337       * @return      array
 338       */
 339      private function get_notifications(stdClass $user, ?int $groupid) : array {
 340          $notifications = $this->notifications;
 341          $forum = $this->forum;
 342          $capabilitymanager = $this->capabilitymanager;
 343  
 344          if ($forum->is_cutoff_date_reached()) {
 345              $notifications[] = (new notification(
 346                      get_string('cutoffdatereached', 'forum'),
 347                      notification::NOTIFY_INFO
 348              ))->set_show_closebutton();
 349          }
 350  
 351          if ($forum->has_blocking_enabled()) {
 352              $notifications[] = (new notification(
 353                  get_string('thisforumisthrottled', 'forum', [
 354                      'blockafter' => $forum->get_block_after(),
 355                      'blockperiod' => get_string('secondstotime' . $forum->get_block_period())
 356                  ]),
 357                  notification::NOTIFY_INFO
 358              ))->set_show_closebutton();
 359          }
 360  
 361          if ($forum->is_in_group_mode() && !$capabilitymanager->can_access_all_groups($user)) {
 362              if ($groupid === null) {
 363                  if (!$capabilitymanager->can_post_to_my_groups($user)) {
 364                      $notifications[] = (new notification(
 365                          get_string('cannotadddiscussiongroup', 'mod_forum'),
 366                          \core\output\notification::NOTIFY_WARNING
 367                      ))->set_show_closebutton();
 368                  } else {
 369                      $notifications[] = (new notification(
 370                          get_string('cannotadddiscussionall', 'mod_forum'),
 371                          \core\output\notification::NOTIFY_WARNING
 372                      ))->set_show_closebutton();
 373                  }
 374              } else if (!$capabilitymanager->can_access_group($user, $groupid)) {
 375                  $notifications[] = (new notification(
 376                      get_string('cannotadddiscussion', 'mod_forum'),
 377                      \core\output\notification::NOTIFY_WARNING
 378                  ))->set_show_closebutton();
 379              }
 380          }
 381  
 382          if ('qanda' === $forum->get_type() && !$capabilitymanager->can_manage_forum($user)) {
 383              $notifications[] = (new notification(
 384                  get_string('qandanotify', 'forum'),
 385                  notification::NOTIFY_INFO
 386              ))->set_show_closebutton();
 387          }
 388  
 389          if ('eachuser' === $forum->get_type()) {
 390              $notifications[] = (new notification(
 391                  get_string('allowsdiscussions', 'forum'),
 392                  notification::NOTIFY_INFO)
 393              )->set_show_closebutton();
 394          }
 395  
 396          return array_map(function($notification) {
 397              return $notification->export_for_template($this->renderer);
 398          }, $notifications);
 399      }
 400  }