bc4c7337e1
This patch adds new capabilities: 'mod/forum:postprivatereply' - whether a user is able to post private replies; and 'mod/forum:readprivatereplies' - whether a user is able to read private replies. Private replies are only visible to the intended recipient (the author of the parent post), the author of the private reply, and those with the ability to read private replies. If a post is private then it cannot be replied to further.
585 lines
18 KiB
PHP
585 lines
18 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
/**
|
|
* Forum post renderable.
|
|
*
|
|
* @package mod_forum
|
|
* @copyright 2015 Andrew Nicols <andrew@nicols.co.uk>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
namespace mod_forum\output;
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
/**
|
|
* Forum post renderable.
|
|
*
|
|
* @copyright 2015 Andrew Nicols <andrew@nicols.co.uk>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*
|
|
* @property boolean $viewfullnames Whether to override fullname()
|
|
*/
|
|
class forum_post implements \renderable, \templatable {
|
|
|
|
/**
|
|
* The course that the forum post is in.
|
|
*
|
|
* @var object $course
|
|
*/
|
|
protected $course = null;
|
|
|
|
/**
|
|
* The course module for the forum.
|
|
*
|
|
* @var object $cm
|
|
*/
|
|
protected $cm = null;
|
|
|
|
/**
|
|
* The forum that the post is in.
|
|
*
|
|
* @var object $forum
|
|
*/
|
|
protected $forum = null;
|
|
|
|
/**
|
|
* The discussion that the forum post is in.
|
|
*
|
|
* @var object $discussion
|
|
*/
|
|
protected $discussion = null;
|
|
|
|
/**
|
|
* The forum post being displayed.
|
|
*
|
|
* @var object $post
|
|
*/
|
|
protected $post = null;
|
|
|
|
/**
|
|
* Whether the user can reply to this post.
|
|
*
|
|
* @var boolean $canreply
|
|
*/
|
|
protected $canreply = false;
|
|
|
|
/**
|
|
* Whether to override forum display when displaying usernames.
|
|
* @var boolean $viewfullnames
|
|
*/
|
|
protected $viewfullnames = false;
|
|
|
|
/**
|
|
* The user that is reading the post.
|
|
*
|
|
* @var object $userto
|
|
*/
|
|
protected $userto = null;
|
|
|
|
/**
|
|
* The user that wrote the post.
|
|
*
|
|
* @var object $author
|
|
*/
|
|
protected $author = null;
|
|
|
|
/**
|
|
* An associative array indicating which keys on this object should be writeable.
|
|
*
|
|
* @var array $writablekeys
|
|
*/
|
|
protected $writablekeys = array(
|
|
'viewfullnames' => true,
|
|
);
|
|
|
|
/**
|
|
* Builds a renderable forum post
|
|
*
|
|
* @param object $course Course of the forum
|
|
* @param object $cm Course Module of the forum
|
|
* @param object $forum The forum of the post
|
|
* @param object $discussion Discussion thread in which the post appears
|
|
* @param object $post The post
|
|
* @param object $author Author of the post
|
|
* @param object $recipient Recipient of the email
|
|
* @param bool $canreply True if the user can reply to the post
|
|
*/
|
|
public function __construct($course, $cm, $forum, $discussion, $post, $author, $recipient, $canreply) {
|
|
$this->course = $course;
|
|
$this->cm = $cm;
|
|
$this->forum = $forum;
|
|
$this->discussion = $discussion;
|
|
$this->post = $post;
|
|
$this->author = $author;
|
|
$this->userto = $recipient;
|
|
$this->canreply = $canreply;
|
|
}
|
|
|
|
/**
|
|
* Export this data so it can be used as the context for a mustache template.
|
|
*
|
|
* @param \mod_forum_renderer $renderer The render to be used for formatting the message and attachments
|
|
* @param bool $plaintext Whethe the target is a plaintext target
|
|
* @return array Data ready for use in a mustache template
|
|
*/
|
|
public function export_for_template(\renderer_base $renderer, $plaintext = false) {
|
|
if ($plaintext) {
|
|
return $this->export_for_template_text($renderer);
|
|
} else {
|
|
return $this->export_for_template_html($renderer);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export this data so it can be used as the context for a mustache template.
|
|
*
|
|
* @param \mod_forum_renderer $renderer The render to be used for formatting the message and attachments
|
|
* @return array Data ready for use in a mustache template
|
|
*/
|
|
protected function export_for_template_text(\mod_forum_renderer $renderer) {
|
|
$data = $this->export_for_template_shared($renderer);
|
|
return $data + array(
|
|
'id' => html_entity_decode($this->post->id),
|
|
'coursename' => html_entity_decode($this->get_coursename()),
|
|
'courselink' => html_entity_decode($this->get_courselink()),
|
|
'forumname' => html_entity_decode($this->get_forumname()),
|
|
'showdiscussionname' => html_entity_decode($this->get_showdiscussionname()),
|
|
'discussionname' => html_entity_decode($this->get_discussionname()),
|
|
'subject' => html_entity_decode($this->get_subject()),
|
|
'authorfullname' => html_entity_decode($this->get_author_fullname()),
|
|
'postdate' => html_entity_decode($this->get_postdate()),
|
|
|
|
// Format some components according to the renderer.
|
|
'message' => html_entity_decode($renderer->format_message_text($this->cm, $this->post)),
|
|
'attachments' => html_entity_decode($renderer->format_message_attachments($this->cm, $this->post)),
|
|
|
|
'canreply' => $this->canreply,
|
|
'permalink' => $this->get_permalink(),
|
|
'firstpost' => $this->get_is_firstpost(),
|
|
'replylink' => $this->get_replylink(),
|
|
'unsubscribediscussionlink' => $this->get_unsubscribediscussionlink(),
|
|
'unsubscribeforumlink' => $this->get_unsubscribeforumlink(),
|
|
'parentpostlink' => $this->get_parentpostlink(),
|
|
|
|
'forumindexlink' => $this->get_forumindexlink(),
|
|
'forumviewlink' => $this->get_forumviewlink(),
|
|
'discussionlink' => $this->get_discussionlink(),
|
|
|
|
'authorlink' => $this->get_authorlink(),
|
|
'authorpicture' => $this->get_author_picture($renderer),
|
|
|
|
'grouppicture' => $this->get_group_picture($renderer),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Export this data so it can be used as the context for a mustache template.
|
|
*
|
|
* @param \mod_forum_renderer $renderer The render to be used for formatting the message and attachments
|
|
* @return array Data ready for use in a mustache template
|
|
*/
|
|
protected function export_for_template_html(\mod_forum_renderer $renderer) {
|
|
$data = $this->export_for_template_shared($renderer);
|
|
return $data + array(
|
|
'id' => $this->post->id,
|
|
'coursename' => $this->get_coursename(),
|
|
'courselink' => $this->get_courselink(),
|
|
'forumname' => $this->get_forumname(),
|
|
'showdiscussionname' => $this->get_showdiscussionname(),
|
|
'discussionname' => $this->get_discussionname(),
|
|
'subject' => $this->get_subject(),
|
|
'authorfullname' => $this->get_author_fullname(),
|
|
'postdate' => $this->get_postdate(),
|
|
|
|
// Format some components according to the renderer.
|
|
'message' => $renderer->format_message_text($this->cm, $this->post),
|
|
'attachments' => $renderer->format_message_attachments($this->cm, $this->post),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Export this data so it can be used as the context for a mustache template.
|
|
*
|
|
* @param \mod_forum_renderer $renderer The render to be used for formatting the message and attachments
|
|
* @return stdClass Data ready for use in a mustache template
|
|
*/
|
|
protected function export_for_template_shared(\mod_forum_renderer $renderer) {
|
|
return array(
|
|
'canreply' => $this->canreply,
|
|
'permalink' => $this->get_permalink(),
|
|
'firstpost' => $this->get_is_firstpost(),
|
|
'replylink' => $this->get_replylink(),
|
|
'unsubscribediscussionlink' => $this->get_unsubscribediscussionlink(),
|
|
'unsubscribeforumlink' => $this->get_unsubscribeforumlink(),
|
|
'parentpostlink' => $this->get_parentpostlink(),
|
|
|
|
'forumindexlink' => $this->get_forumindexlink(),
|
|
'forumviewlink' => $this->get_forumviewlink(),
|
|
'discussionlink' => $this->get_discussionlink(),
|
|
|
|
'authorlink' => $this->get_authorlink(),
|
|
'authorpicture' => $this->get_author_picture($renderer),
|
|
|
|
'grouppicture' => $this->get_group_picture($renderer),
|
|
|
|
'isprivatereply' => !empty($this->post->privatereplyto),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Magically sets a property against this object.
|
|
*
|
|
* @param string $key
|
|
* @param mixed $value
|
|
*/
|
|
public function __set($key, $value) {
|
|
// First attempt to use the setter function.
|
|
$methodname = 'set_' . $key;
|
|
if (method_exists($this, $methodname)) {
|
|
return $this->{$methodname}($value);
|
|
}
|
|
|
|
// Fall back to the writable keys list.
|
|
if (isset($this->writablekeys[$key]) && $this->writablekeys[$key]) {
|
|
return $this->{$key} = $value;
|
|
}
|
|
|
|
// Throw an error rather than fail silently.
|
|
throw new \coding_exception('Tried to set unknown property "' . $key . '"');
|
|
}
|
|
|
|
/**
|
|
* Whether this is the first post.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function get_is_firstpost() {
|
|
return empty($this->post->parent);
|
|
}
|
|
|
|
/**
|
|
* Get the link to the course.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_courselink() {
|
|
$link = new \moodle_url(
|
|
// Posts are viewed on the topic.
|
|
'/course/view.php', array(
|
|
'id' => $this->course->id,
|
|
)
|
|
);
|
|
|
|
return $link->out(false);
|
|
}
|
|
|
|
/**
|
|
* Get the link to the forum index for this course.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_forumindexlink() {
|
|
$link = new \moodle_url(
|
|
// Posts are viewed on the topic.
|
|
'/mod/forum/index.php', array(
|
|
'id' => $this->course->id,
|
|
)
|
|
);
|
|
|
|
return $link->out(false);
|
|
}
|
|
|
|
/**
|
|
* Get the link to the view page for this forum.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_forumviewlink() {
|
|
$link = new \moodle_url(
|
|
// Posts are viewed on the topic.
|
|
'/mod/forum/view.php', array(
|
|
'f' => $this->forum->id,
|
|
)
|
|
);
|
|
|
|
return $link->out(false);
|
|
}
|
|
|
|
/**
|
|
* Get the link to the current discussion.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function _get_discussionlink() {
|
|
return new \moodle_url(
|
|
// Posts are viewed on the topic.
|
|
'/mod/forum/discuss.php', array(
|
|
// Within a discussion.
|
|
'd' => $this->discussion->id,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the link to the current discussion.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_discussionlink() {
|
|
$link = $this->_get_discussionlink();
|
|
|
|
return $link->out(false);
|
|
}
|
|
|
|
/**
|
|
* Get the link to the current post, including post anchor.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_permalink() {
|
|
$link = $this->_get_discussionlink();
|
|
$link->set_anchor($this->get_postanchor());
|
|
|
|
return $link->out(false);
|
|
}
|
|
|
|
/**
|
|
* Get the link to the parent post.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_parentpostlink() {
|
|
$link = $this->_get_discussionlink();
|
|
$link->param('parent', $this->post->parent);
|
|
|
|
return $link->out(false);
|
|
}
|
|
|
|
/**
|
|
* Get the link to the author's profile page.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_authorlink() {
|
|
$link = new \moodle_url(
|
|
'/user/view.php', array(
|
|
'id' => $this->post->userid,
|
|
'course' => $this->course->id,
|
|
)
|
|
);
|
|
|
|
return $link->out(false);
|
|
}
|
|
|
|
/**
|
|
* Get the link to unsubscribe from the forum.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_unsubscribeforumlink() {
|
|
if (!\mod_forum\subscriptions::is_subscribable($this->forum)) {
|
|
return null;
|
|
}
|
|
$link = new \moodle_url(
|
|
'/mod/forum/subscribe.php', array(
|
|
'id' => $this->forum->id,
|
|
)
|
|
);
|
|
|
|
return $link->out(false);
|
|
}
|
|
|
|
/**
|
|
* Get the link to unsubscribe from the discussion.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_unsubscribediscussionlink() {
|
|
if (!\mod_forum\subscriptions::is_subscribable($this->forum)) {
|
|
return null;
|
|
}
|
|
$link = new \moodle_url(
|
|
'/mod/forum/subscribe.php', array(
|
|
'id' => $this->forum->id,
|
|
'd' => $this->discussion->id,
|
|
)
|
|
);
|
|
|
|
return $link->out(false);
|
|
}
|
|
|
|
/**
|
|
* Get the link to reply to the current post.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_replylink() {
|
|
return new \moodle_url(
|
|
'/mod/forum/post.php', array(
|
|
'reply' => $this->post->id,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* The formatted subject for the current post.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_subject() {
|
|
return format_string($this->post->subject, true);
|
|
}
|
|
|
|
/**
|
|
* The plaintext anchor id for the current post.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_postanchor() {
|
|
return 'p' . $this->post->id;
|
|
}
|
|
|
|
/**
|
|
* ID number of the course that the forum is in.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_courseidnumber() {
|
|
return s($this->course->idnumber);
|
|
}
|
|
|
|
/**
|
|
* The full name of the course that the forum is in.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_coursefullname() {
|
|
return format_string($this->course->fullname, true, array(
|
|
'context' => \context_course::instance($this->course->id),
|
|
));
|
|
}
|
|
|
|
/**
|
|
* The name of the course that the forum is in.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_coursename() {
|
|
return format_string($this->course->shortname, true, array(
|
|
'context' => \context_course::instance($this->course->id),
|
|
));
|
|
}
|
|
|
|
/**
|
|
* The name of the forum.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_forumname() {
|
|
return format_string($this->forum->name, true);
|
|
}
|
|
|
|
/**
|
|
* The name of the current discussion.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_discussionname() {
|
|
return format_string($this->discussion->name, true);
|
|
}
|
|
|
|
/**
|
|
* Whether to show the discussion name.
|
|
* If the forum name matches the discussion name, the discussion name
|
|
* is not typically displayed.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function get_showdiscussionname() {
|
|
return ($this->forum->name !== $this->discussion->name);
|
|
}
|
|
|
|
/**
|
|
* The fullname of the post author.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_author_fullname() {
|
|
return fullname($this->author, $this->viewfullnames);
|
|
}
|
|
|
|
/**
|
|
* The recipient of the post.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function get_postto() {
|
|
global $USER;
|
|
if (null === $this->userto) {
|
|
return $USER;
|
|
}
|
|
|
|
return $this->userto;
|
|
}
|
|
|
|
/**
|
|
* The date of the post, formatted according to the postto user's
|
|
* preferences.
|
|
*
|
|
* @return string.
|
|
*/
|
|
public function get_postdate() {
|
|
global $CFG;
|
|
|
|
$postmodified = $this->post->modified;
|
|
if (!empty($CFG->forum_enabletimedposts) && ($this->discussion->timestart > $postmodified)) {
|
|
$postmodified = $this->discussion->timestart;
|
|
}
|
|
|
|
return userdate($postmodified, "", \core_date::get_user_timezone($this->get_postto()));
|
|
}
|
|
|
|
/**
|
|
* The HTML for the author's user picture.
|
|
*
|
|
* @param \renderer_base $renderer
|
|
* @return string
|
|
*/
|
|
public function get_author_picture(\renderer_base $renderer) {
|
|
return $renderer->user_picture($this->author, array('courseid' => $this->course->id));
|
|
}
|
|
|
|
/**
|
|
* The HTML for a group picture.
|
|
*
|
|
* @param \renderer_base $renderer
|
|
* @return string
|
|
*/
|
|
public function get_group_picture(\renderer_base $renderer) {
|
|
if (isset($this->userfrom->groups)) {
|
|
$groups = $this->userfrom->groups[$this->forum->id];
|
|
} else {
|
|
$groups = groups_get_all_groups($this->course->id, $this->author->id, $this->cm->groupingid);
|
|
}
|
|
|
|
if ($this->get_is_firstpost()) {
|
|
return print_group_picture($groups, $this->course->id, false, true, true, true);
|
|
}
|
|
}
|
|
}
|