Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

   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              'hasanyactions' => $hasanyactions,
 198              'groupchangemenu' => groups_print_activity_menu(
 199                  $cm,
 200                  $this->urlfactory->get_forum_view_url_from_forum($forum),
 201                  true
 202              ),
 203              'hasmore' => ($alldiscussionscount > $pagesize),
 204              'notifications' => $this->get_notifications($user, $groupid),
 205              'settings' => [
 206                  'excludetext' => true,
 207                  'togglemoreicon' => true,
 208                  'excludesubscription' => true
 209              ],
 210              'totaldiscussioncount' => $alldiscussionscount,
 211              'userid' => $user->id,
 212              'visiblediscussioncount' => count($discussions)
 213          ];
 214  
 215          if ($forumview['forum']['capabilities']['create']) {
 216              $forumview['newdiscussionhtml'] = $this->get_discussion_form($user, $cm, $groupid);
 217          }
 218  
 219          if (!$discussions) {
 220              return $this->renderer->render_from_template($this->template, $forumview);
 221          }
 222  
 223          if ($this->postprocessfortemplate !== null) {
 224              // We've got some post processing to do!
 225              $exportedposts = ($this->postprocessfortemplate) ($discussions, $user, $forum);
 226          }
 227  
 228          $baseurl = new \moodle_url($PAGE->url, array('o' => $sortorder));
 229  
 230          $forumview = array_merge(
 231              $forumview,
 232              [
 233                  'pagination' => $this->renderer->render(new \paging_bar($alldiscussionscount, $pageno, $pagesize, $baseurl, 'p')),
 234              ],
 235              $exportedposts
 236          );
 237  
 238          $firstdiscussion = reset($discussions);
 239          $forumview['firstgradeduserid'] = $firstdiscussion->get_latest_post_author()->get_id();
 240  
 241          return $this->renderer->render_from_template($this->template, $forumview);
 242      }
 243  
 244      /**
 245       * Get the mod_forum_post_form. This is the default boiler plate from mod_forum/post_form.php with the inpage flag caveat
 246       *
 247       * @param stdClass $user The user the form is being generated for
 248       * @param \cm_info $cm
 249       * @param int $groupid The groupid if any
 250       *
 251       * @return string The rendered html
 252       */
 253      private function get_discussion_form(stdClass $user, \cm_info $cm, ?int $groupid) {
 254          $forum = $this->forum;
 255          $forumrecord = $this->legacydatamapperfactory->get_forum_data_mapper()->to_legacy_object($forum);
 256          $modcontext = \context_module::instance($cm->id);
 257          $coursecontext = \context_course::instance($forum->get_course_id());
 258          $post = (object) [
 259              'course' => $forum->get_course_id(),
 260              'forum' => $forum->get_id(),
 261              'discussion' => 0,           // Ie discussion # not defined yet.
 262              'parent' => 0,
 263              'subject' => '',
 264              'userid' => $user->id,
 265              'message' => '',
 266              'messageformat' => editors_get_preferred_format(),
 267              'messagetrust' => 0,
 268              'groupid' => $groupid,
 269          ];
 270          $thresholdwarning = forum_check_throttling($forumrecord, $cm);
 271  
 272          $formparams = array(
 273              'course' => $forum->get_course_record(),
 274              'cm' => $cm,
 275              'coursecontext' => $coursecontext,
 276              'modcontext' => $modcontext,
 277              'forum' => $forumrecord,
 278              'post' => $post,
 279              'subscribe' => \mod_forum\subscriptions::is_subscribed($user->id, $forumrecord,
 280                  null, $cm),
 281              'thresholdwarning' => $thresholdwarning,
 282              'inpagereply' => true,
 283              'edit' => 0
 284          );
 285          $posturl = new \moodle_url('/mod/forum/post.php');
 286          $mformpost = new \mod_forum_post_form($posturl, $formparams, 'post', '', array('id' => 'mformforum'));
 287          $discussionsubscribe = \mod_forum\subscriptions::get_user_default_subscription($forumrecord, $coursecontext, $cm, null);
 288  
 289          $params = array('reply' => 0, 'forum' => $forumrecord->id, 'edit' => 0) +
 290              (isset($post->groupid) ? array('groupid' => $post->groupid) : array()) +
 291              array(
 292                  'userid' => $post->userid,
 293                  'parent' => $post->parent,
 294                  'discussion' => $post->discussion,
 295                  'course' => $forum->get_course_id(),
 296                  'discussionsubscribe' => $discussionsubscribe
 297              );
 298          $mformpost->set_data($params);
 299  
 300          return $mformpost->render();
 301      }
 302  
 303      /**
 304       * Fetch the page size to use when displaying the page.
 305       *
 306       * @param   int         $pagesize The number of discussions to show on the page
 307       * @return  int         The normalised page size
 308       */
 309      private function get_page_size(?int $pagesize) : int {
 310          if (null === $pagesize || $pagesize <= 0) {
 311              $pagesize = discussion_list_vault::PAGESIZE_DEFAULT;
 312          }
 313  
 314          return $pagesize;
 315      }
 316  
 317      /**
 318       * Fetch the current page number (zero-indexed).
 319       *
 320       * @param   int         $pageno The zero-indexed page number to use
 321       * @return  int         The normalised page number
 322       */
 323      private function get_page_number(?int $pageno) : int {
 324          if (null === $pageno || $pageno < 0) {
 325              $pageno = 0;
 326          }
 327  
 328          return $pageno;
 329      }
 330  
 331      /**
 332       * Get the list of notification for display.
 333       *
 334       * @param stdClass $user The viewing user
 335       * @param int|null $groupid The forum's group id
 336       * @return      array
 337       */
 338      private function get_notifications(stdClass $user, ?int $groupid) : array {
 339          $notifications = $this->notifications;
 340          $forum = $this->forum;
 341          $renderer = $this->renderer;
 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          } else if ($forum->is_due_date_reached()) {
 350              $notifications[] = (new notification(
 351                      get_string('thisforumisdue', 'forum', userdate($forum->get_due_date())),
 352                      notification::NOTIFY_INFO
 353              ))->set_show_closebutton();
 354          } else if ($forum->has_due_date()) {
 355              $notifications[] = (new notification(
 356                      get_string('thisforumhasduedate', 'forum', userdate($forum->get_due_date())),
 357                      notification::NOTIFY_INFO
 358              ))->set_show_closebutton();
 359          }
 360  
 361          if ($forum->has_blocking_enabled()) {
 362              $notifications[] = (new notification(
 363                  get_string('thisforumisthrottled', 'forum', [
 364                      'blockafter' => $forum->get_block_after(),
 365                      'blockperiod' => get_string('secondstotime' . $forum->get_block_period())
 366                  ])
 367              ))->set_show_closebutton();
 368          }
 369  
 370          if ($forum->is_in_group_mode() && !$capabilitymanager->can_access_all_groups($user)) {
 371              if ($groupid === null) {
 372                  if (!$capabilitymanager->can_post_to_my_groups($user)) {
 373                      $notifications[] = (new notification(
 374                          get_string('cannotadddiscussiongroup', 'mod_forum'),
 375                          \core\output\notification::NOTIFY_WARNING
 376                      ))->set_show_closebutton();
 377                  } else {
 378                      $notifications[] = (new notification(
 379                          get_string('cannotadddiscussionall', 'mod_forum'),
 380                          \core\output\notification::NOTIFY_WARNING
 381                      ))->set_show_closebutton();
 382                  }
 383              } else if (!$capabilitymanager->can_access_group($user, $groupid)) {
 384                  $notifications[] = (new notification(
 385                      get_string('cannotadddiscussion', 'mod_forum'),
 386                      \core\output\notification::NOTIFY_WARNING
 387                  ))->set_show_closebutton();
 388              }
 389          }
 390  
 391          if ('qanda' === $forum->get_type() && !$capabilitymanager->can_manage_forum($user)) {
 392              $notifications[] = (new notification(
 393                  get_string('qandanotify', 'forum'),
 394                  notification::NOTIFY_INFO
 395              ))->set_show_closebutton();
 396          }
 397  
 398          if ('eachuser' === $forum->get_type()) {
 399              $notifications[] = (new notification(
 400                  get_string('allowsdiscussions', 'forum'),
 401                  notification::NOTIFY_INFO)
 402              )->set_show_closebutton();
 403          }
 404  
 405          return array_map(function($notification) {
 406              return $notification->export_for_template($this->renderer);
 407          }, $notifications);
 408      }
 409  }