Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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

   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   * Forum post renderable.
  19   *
  20   * @package    mod_forum
  21   * @copyright  2015 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\output;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  /**
  30   * Forum post renderable.
  31   *
  32   * @copyright  2015 Andrew Nicols <andrew@nicols.co.uk>
  33   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   *
  35   * @property boolean $viewfullnames Whether to override fullname()
  36   */
  37  class forum_post implements \renderable, \templatable {
  38  
  39      /**
  40       * The course that the forum post is in.
  41       *
  42       * @var object $course
  43       */
  44      protected $course = null;
  45  
  46      /**
  47       * The course module for the forum.
  48       *
  49       * @var object $cm
  50       */
  51      protected $cm = null;
  52  
  53      /**
  54       * The forum that the post is in.
  55       *
  56       * @var object $forum
  57       */
  58      protected $forum = null;
  59  
  60      /**
  61       * The discussion that the forum post is in.
  62       *
  63       * @var object $discussion
  64       */
  65      protected $discussion = null;
  66  
  67      /**
  68       * The forum post being displayed.
  69       *
  70       * @var object $post
  71       */
  72      protected $post = null;
  73  
  74      /**
  75       * Whether the user can reply to this post.
  76       *
  77       * @var boolean $canreply
  78       */
  79      protected $canreply = false;
  80  
  81      /**
  82       * Whether to override forum display when displaying usernames.
  83       * @var boolean $viewfullnames
  84       */
  85      protected $viewfullnames = false;
  86  
  87      /**
  88       * The user that is reading the post.
  89       *
  90       * @var object $userto
  91       */
  92      protected $userto = null;
  93  
  94      /**
  95       * The user that wrote the post.
  96       *
  97       * @var object $author
  98       */
  99      protected $author = null;
 100  
 101      /**
 102       * An associative array indicating which keys on this object should be writeable.
 103       *
 104       * @var array $writablekeys
 105       */
 106      protected $writablekeys = array(
 107          'viewfullnames'    => true,
 108      );
 109  
 110      /**
 111       * Builds a renderable forum post
 112       *
 113       * @param object $course Course of the forum
 114       * @param object $cm Course Module of the forum
 115       * @param object $forum The forum of the post
 116       * @param object $discussion Discussion thread in which the post appears
 117       * @param object $post The post
 118       * @param object $author Author of the post
 119       * @param object $recipient Recipient of the email
 120       * @param bool $canreply True if the user can reply to the post
 121       */
 122      public function __construct($course, $cm, $forum, $discussion, $post, $author, $recipient, $canreply) {
 123          $this->course = $course;
 124          $this->cm = $cm;
 125          $this->forum = $forum;
 126          $this->discussion = $discussion;
 127          $this->post = $post;
 128          $this->author = $author;
 129          $this->userto = $recipient;
 130          $this->canreply = $canreply;
 131      }
 132  
 133      /**
 134       * Export this data so it can be used as the context for a mustache template.
 135       *
 136       * @param \mod_forum_renderer $renderer The render to be used for formatting the message and attachments
 137       * @param bool $plaintext Whethe the target is a plaintext target
 138       * @return array Data ready for use in a mustache template
 139       */
 140      public function export_for_template(\renderer_base $renderer, $plaintext = false) {
 141          if ($plaintext) {
 142              return $this->export_for_template_text($renderer);
 143          } else {
 144              return $this->export_for_template_html($renderer);
 145          }
 146      }
 147  
 148      /**
 149       * Export this data so it can be used as the context for a mustache template.
 150       *
 151       * @param \mod_forum_renderer $renderer The render to be used for formatting the message and attachments
 152       * @return array Data ready for use in a mustache template
 153       */
 154      protected function export_for_template_text(\mod_forum_renderer $renderer) {
 155          $data = $this->export_for_template_shared($renderer);
 156          return $data + array(
 157              'id'                            => html_entity_decode($this->post->id, ENT_COMPAT),
 158              'coursename'                    => html_entity_decode($this->get_coursename(), ENT_COMPAT),
 159              'courselink'                    => html_entity_decode($this->get_courselink(), ENT_COMPAT),
 160              'forumname'                     => html_entity_decode($this->get_forumname(), ENT_COMPAT),
 161              'showdiscussionname'            => html_entity_decode($this->get_showdiscussionname(), ENT_COMPAT),
 162              'discussionname'                => html_entity_decode($this->get_discussionname(), ENT_COMPAT),
 163              'subject'                       => html_entity_decode($this->get_subject(), ENT_COMPAT),
 164              'authorfullname'                => html_entity_decode($this->get_author_fullname(), ENT_COMPAT),
 165              'postdate'                      => html_entity_decode($this->get_postdate(), ENT_COMPAT),
 166  
 167              // Format some components according to the renderer.
 168              'message'                       => html_entity_decode($renderer->format_message_text($this->cm, $this->post), ENT_COMPAT),
 169              'attachments'                   => html_entity_decode($renderer->format_message_attachments($this->cm, $this->post), ENT_COMPAT),
 170  
 171              'canreply'                      => $this->canreply,
 172              'permalink'                     => $this->get_permalink(),
 173              'firstpost'                     => $this->get_is_firstpost(),
 174              'replylink'                     => $this->get_replylink(),
 175              'unsubscribediscussionlink'     => $this->get_unsubscribediscussionlink(),
 176              'unsubscribeforumlink'          => $this->get_unsubscribeforumlink(),
 177              'parentpostlink'                => $this->get_parentpostlink(),
 178  
 179              'forumindexlink'                => $this->get_forumindexlink(),
 180              'forumviewlink'                 => $this->get_forumviewlink(),
 181              'discussionlink'                => $this->get_discussionlink(),
 182  
 183              'authorlink'                    => $this->get_authorlink(),
 184              'authorpicture'                 => $this->get_author_picture($renderer),
 185  
 186              'grouppicture'                  => $this->get_group_picture($renderer),
 187          );
 188      }
 189  
 190      /**
 191       * Export this data so it can be used as the context for a mustache template.
 192       *
 193       * @param \mod_forum_renderer $renderer The render to be used for formatting the message and attachments
 194       * @return array Data ready for use in a mustache template
 195       */
 196      protected function export_for_template_html(\mod_forum_renderer $renderer) {
 197          $data = $this->export_for_template_shared($renderer);
 198          return $data + array(
 199              'id'                            => $this->post->id,
 200              'coursename'                    => $this->get_coursename(),
 201              'courselink'                    => $this->get_courselink(),
 202              'forumname'                     => $this->get_forumname(),
 203              'showdiscussionname'            => $this->get_showdiscussionname(),
 204              'discussionname'                => $this->get_discussionname(),
 205              'subject'                       => $this->get_subject(),
 206              'authorfullname'                => $this->get_author_fullname(),
 207              'postdate'                      => $this->get_postdate(),
 208  
 209              // Format some components according to the renderer.
 210              'message'                       => $renderer->format_message_text($this->cm, $this->post),
 211              'attachments'                   => $renderer->format_message_attachments($this->cm, $this->post),
 212          );
 213      }
 214  
 215      /**
 216       * Export this data so it can be used as the context for a mustache template.
 217       *
 218       * @param \mod_forum_renderer $renderer The render to be used for formatting the message and attachments
 219       * @return stdClass Data ready for use in a mustache template
 220       */
 221      protected function export_for_template_shared(\mod_forum_renderer $renderer) {
 222          return array(
 223              'canreply'                      => $this->canreply,
 224              'permalink'                     => $this->get_permalink(),
 225              'firstpost'                     => $this->get_is_firstpost(),
 226              'replylink'                     => $this->get_replylink(),
 227              'unsubscribediscussionlink'     => $this->get_unsubscribediscussionlink(),
 228              'unsubscribeforumlink'          => $this->get_unsubscribeforumlink(),
 229              'parentpostlink'                => $this->get_parentpostlink(),
 230  
 231              'forumindexlink'                => $this->get_forumindexlink(),
 232              'forumviewlink'                 => $this->get_forumviewlink(),
 233              'discussionlink'                => $this->get_discussionlink(),
 234  
 235              'authorlink'                    => $this->get_authorlink(),
 236              'authorpicture'                 => $this->get_author_picture($renderer),
 237  
 238              'grouppicture'                  => $this->get_group_picture($renderer),
 239  
 240              'isprivatereply'                => !empty($this->post->privatereplyto),
 241          );
 242      }
 243  
 244      /**
 245       * Magically sets a property against this object.
 246       *
 247       * @param string $key
 248       * @param mixed $value
 249       */
 250      public function __set($key, $value) {
 251          // First attempt to use the setter function.
 252          $methodname = 'set_' . $key;
 253          if (method_exists($this, $methodname)) {
 254              return $this->{$methodname}($value);
 255          }
 256  
 257          // Fall back to the writable keys list.
 258          if (isset($this->writablekeys[$key]) && $this->writablekeys[$key]) {
 259              return $this->{$key} = $value;
 260          }
 261  
 262          // Throw an error rather than fail silently.
 263          throw new \coding_exception('Tried to set unknown property "' . $key . '"');
 264      }
 265  
 266      /**
 267       * Whether this is the first post.
 268       *
 269       * @return boolean
 270       */
 271      public function get_is_firstpost() {
 272          return empty($this->post->parent);
 273      }
 274  
 275      /**
 276       * Get the link to the course.
 277       *
 278       * @return string
 279       */
 280      public function get_courselink() {
 281          $link = new \moodle_url(
 282              // Posts are viewed on the topic.
 283              '/course/view.php', array(
 284                  'id'    => $this->course->id,
 285              )
 286          );
 287  
 288          return $link->out(false);
 289      }
 290  
 291      /**
 292       * Get the link to the forum index for this course.
 293       *
 294       * @return string
 295       */
 296      public function get_forumindexlink() {
 297          $link = new \moodle_url(
 298              // Posts are viewed on the topic.
 299              '/mod/forum/index.php', array(
 300                  'id'    => $this->course->id,
 301              )
 302          );
 303  
 304          return $link->out(false);
 305      }
 306  
 307      /**
 308       * Get the link to the view page for this forum.
 309       *
 310       * @return string
 311       */
 312      public function get_forumviewlink() {
 313          $link = new \moodle_url(
 314              // Posts are viewed on the topic.
 315              '/mod/forum/view.php', array(
 316                  'f' => $this->forum->id,
 317              )
 318          );
 319  
 320          return $link->out(false);
 321      }
 322  
 323      /**
 324       * Get the link to the current discussion.
 325       *
 326       * @return string
 327       */
 328      protected function _get_discussionlink() {
 329          return new \moodle_url(
 330              // Posts are viewed on the topic.
 331              '/mod/forum/discuss.php', array(
 332                  // Within a discussion.
 333                  'd' => $this->discussion->id,
 334              )
 335          );
 336      }
 337  
 338      /**
 339       * Get the link to the current discussion.
 340       *
 341       * @return string
 342       */
 343      public function get_discussionlink() {
 344          $link = $this->_get_discussionlink();
 345  
 346          return $link->out(false);
 347      }
 348  
 349      /**
 350       * Get the link to the current post, including post anchor.
 351       *
 352       * @return string
 353       */
 354      public function get_permalink() {
 355          $link = $this->_get_discussionlink();
 356          $link->set_anchor($this->get_postanchor());
 357  
 358          return $link->out(false);
 359      }
 360  
 361      /**
 362       * Get the link to the parent post.
 363       *
 364       * @return string
 365       */
 366      public function get_parentpostlink() {
 367          $link = $this->_get_discussionlink();
 368          $link->param('parent', $this->post->parent);
 369  
 370          return $link->out(false);
 371      }
 372  
 373      /**
 374       * Get the link to the author's profile page.
 375       *
 376       * @return string
 377       */
 378      public function get_authorlink() {
 379          $link = new \moodle_url(
 380              '/user/view.php', array(
 381                  'id' => $this->post->userid,
 382                  'course' => $this->course->id,
 383              )
 384          );
 385  
 386          return $link->out(false);
 387      }
 388  
 389      /**
 390       * Get the link to unsubscribe from the forum.
 391       *
 392       * @return string
 393       */
 394      public function get_unsubscribeforumlink() {
 395          if (!\mod_forum\subscriptions::is_subscribable($this->forum)) {
 396              return null;
 397          }
 398          $link = new \moodle_url(
 399              '/mod/forum/subscribe.php', array(
 400                  'id' => $this->forum->id,
 401              )
 402          );
 403  
 404          return $link->out(false);
 405      }
 406  
 407      /**
 408       * Get the link to unsubscribe from the discussion.
 409       *
 410       * @return string
 411       */
 412      public function get_unsubscribediscussionlink() {
 413          if (!\mod_forum\subscriptions::is_subscribable($this->forum)) {
 414              return null;
 415          }
 416          $link = new \moodle_url(
 417              '/mod/forum/subscribe.php', array(
 418                  'id'  => $this->forum->id,
 419                  'd'   => $this->discussion->id,
 420              )
 421          );
 422  
 423          return $link->out(false);
 424      }
 425  
 426      /**
 427       * Get the link to reply to the current post.
 428       *
 429       * @return string
 430       */
 431      public function get_replylink() {
 432          return new \moodle_url(
 433              '/mod/forum/post.php', array(
 434                  'reply' => $this->post->id,
 435              )
 436          );
 437      }
 438  
 439      /**
 440       * The formatted subject for the current post.
 441       *
 442       * @return string
 443       */
 444      public function get_subject() {
 445          return format_string($this->post->subject, true);
 446      }
 447  
 448      /**
 449       * The plaintext anchor id for the current post.
 450       *
 451       * @return string
 452       */
 453      public function get_postanchor() {
 454          return 'p' . $this->post->id;
 455      }
 456  
 457      /**
 458       * ID number of the course that the forum is in.
 459       *
 460       * @return string
 461       */
 462      public function get_courseidnumber() {
 463          return s($this->course->idnumber);
 464      }
 465  
 466      /**
 467       * The full name of the course that the forum is in.
 468       *
 469       * @return string
 470       */
 471      public function get_coursefullname() {
 472          return format_string($this->course->fullname, true, array(
 473              'context' => \context_course::instance($this->course->id),
 474          ));
 475      }
 476  
 477      /**
 478       * The name of the course that the forum is in.
 479       *
 480       * @return string
 481       */
 482      public function get_coursename() {
 483          return format_string($this->course->shortname, true, array(
 484              'context' => \context_course::instance($this->course->id),
 485          ));
 486      }
 487  
 488      /**
 489       * The name of the forum.
 490       *
 491       * @return string
 492       */
 493      public function get_forumname() {
 494          return format_string($this->forum->name, true);
 495      }
 496  
 497      /**
 498       * The name of the current discussion.
 499       *
 500       * @return string
 501       */
 502      public function get_discussionname() {
 503          return format_string($this->discussion->name, true);
 504      }
 505  
 506      /**
 507       * Whether to show the discussion name.
 508       * If the forum name matches the discussion name, the discussion name
 509       * is not typically displayed.
 510       *
 511       * @return boolean
 512       */
 513      public function get_showdiscussionname() {
 514          return ($this->forum->name !== $this->discussion->name);
 515      }
 516  
 517      /**
 518       * The fullname of the post author.
 519       *
 520       * @return string
 521       */
 522      public function get_author_fullname() {
 523          return fullname($this->author, $this->viewfullnames);
 524      }
 525  
 526      /**
 527       * The recipient of the post.
 528       *
 529       * @return string
 530       */
 531      protected function get_postto() {
 532          global $USER;
 533          if (null === $this->userto) {
 534              return $USER;
 535          }
 536  
 537          return $this->userto;
 538      }
 539  
 540      /**
 541       * The date of the post, formatted according to the postto user's
 542       * preferences.
 543       *
 544       * @return string.
 545       */
 546      public function get_postdate() {
 547          global $CFG;
 548  
 549          $postmodified = $this->post->modified;
 550          if (!empty($CFG->forum_enabletimedposts) && ($this->discussion->timestart > $postmodified)) {
 551              $postmodified = $this->discussion->timestart;
 552          }
 553  
 554          return userdate($postmodified, "", \core_date::get_user_timezone($this->get_postto()));
 555      }
 556  
 557      /**
 558       * The HTML for the author's user picture.
 559       *
 560       * @param   \renderer_base $renderer
 561       * @return string
 562       */
 563      public function get_author_picture(\renderer_base $renderer) {
 564          return $renderer->user_picture($this->author, array('courseid' => $this->course->id));
 565      }
 566  
 567      /**
 568       * The HTML for a group picture.
 569       *
 570       * @param   \renderer_base $renderer
 571       * @return string
 572       */
 573      public function get_group_picture(\renderer_base $renderer) {
 574          if (isset($this->userfrom->groups)) {
 575              $groups = $this->userfrom->groups[$this->forum->id];
 576          } else {
 577              $groups = groups_get_all_groups($this->course->id, $this->author->id, $this->cm->groupingid);
 578          }
 579  
 580          if ($this->get_is_firstpost()) {
 581              return print_group_picture($groups, $this->course->id, false, true, true, true);
 582          }
 583      }
 584  }