Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

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

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