You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
619 lines
25 KiB
619 lines
25 KiB
<?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/>.
|
|
|
|
/**
|
|
* Post exporter class.
|
|
*
|
|
* @package mod_forum
|
|
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
namespace mod_forum\local\exporters;
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
use mod_forum\local\entities\post as post_entity;
|
|
use mod_forum\local\exporters\author as author_exporter;
|
|
use mod_forum\local\factories\exporter as exporter_factory;
|
|
use core\external\exporter;
|
|
use core_files\external\stored_file_exporter;
|
|
use context;
|
|
use core_tag_tag;
|
|
use renderer_base;
|
|
use stdClass;
|
|
|
|
require_once($CFG->dirroot . '/mod/forum/lib.php');
|
|
|
|
/**
|
|
* Post exporter class.
|
|
*
|
|
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class post extends exporter {
|
|
/** @var post_entity $post The post to export */
|
|
private $post;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param post_entity $post The post to export
|
|
* @param array $related List of related data
|
|
*/
|
|
public function __construct(post_entity $post, array $related = []) {
|
|
$this->post = $post;
|
|
return parent::__construct([], $related);
|
|
}
|
|
|
|
/**
|
|
* Return the list of additional properties.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function define_other_properties() {
|
|
$attachmentdefinition = stored_file_exporter::read_properties_definition();
|
|
$attachmentdefinition['urls'] = [
|
|
'type' => [
|
|
'export' => [
|
|
'type' => PARAM_URL,
|
|
'description' => 'The URL used to export the attachment',
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
]
|
|
]
|
|
];
|
|
$attachmentdefinition['html'] = [
|
|
'type' => [
|
|
'plagiarism' => [
|
|
'type' => PARAM_RAW,
|
|
'description' => 'The HTML source for the Plagiarism Response',
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
]
|
|
];
|
|
|
|
return [
|
|
'id' => ['type' => PARAM_INT],
|
|
'subject' => ['type' => PARAM_TEXT],
|
|
'message' => ['type' => PARAM_RAW],
|
|
'messageformat' => ['type' => PARAM_INT],
|
|
'author' => ['type' => author_exporter::read_properties_definition()],
|
|
'discussionid' => ['type' => PARAM_INT],
|
|
'hasparent' => ['type' => PARAM_BOOL],
|
|
'parentid' => [
|
|
'type' => PARAM_INT,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'timecreated' => ['type' => PARAM_INT],
|
|
'unread' => [
|
|
'type' => PARAM_BOOL,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'isdeleted' => ['type' => PARAM_BOOL],
|
|
'isprivatereply' => ['type' => PARAM_BOOL],
|
|
'haswordcount' => ['type' => PARAM_BOOL],
|
|
'wordcount' => [
|
|
'type' => PARAM_INT,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'capabilities' => [
|
|
'type' => [
|
|
'view' => [
|
|
'type' => PARAM_BOOL,
|
|
'null' => NULL_ALLOWED,
|
|
'description' => 'Whether the user can view the post',
|
|
],
|
|
'edit' => [
|
|
'type' => PARAM_BOOL,
|
|
'null' => NULL_ALLOWED,
|
|
'description' => 'Whether the user can edit the post',
|
|
],
|
|
'delete' => [
|
|
'type' => PARAM_BOOL,
|
|
'null' => NULL_ALLOWED,
|
|
'description' => 'Whether the user can delete the post',
|
|
],
|
|
'split' => [
|
|
'type' => PARAM_BOOL,
|
|
'null' => NULL_ALLOWED,
|
|
'description' => 'Whether the user can split the post',
|
|
],
|
|
'reply' => [
|
|
'type' => PARAM_BOOL,
|
|
'null' => NULL_ALLOWED,
|
|
'description' => 'Whether the user can reply to the post',
|
|
],
|
|
'export' => [
|
|
'type' => PARAM_BOOL,
|
|
'null' => NULL_ALLOWED,
|
|
'description' => 'Whether the user can export the post',
|
|
],
|
|
'controlreadstatus' => [
|
|
'type' => PARAM_BOOL,
|
|
'null' => NULL_ALLOWED,
|
|
'description' => 'Whether the user can control the read status of the post',
|
|
],
|
|
'canreplyprivately' => [
|
|
'type' => PARAM_BOOL,
|
|
'null' => NULL_ALLOWED,
|
|
'description' => 'Whether the user can post a private reply',
|
|
]
|
|
]
|
|
],
|
|
'urls' => [
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED,
|
|
'type' => [
|
|
'view' => [
|
|
'description' => 'The URL used to view the post',
|
|
'type' => PARAM_URL,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'viewisolated' => [
|
|
'description' => 'The URL used to view the post in isolation',
|
|
'type' => PARAM_URL,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'viewparent' => [
|
|
'description' => 'The URL used to view the parent of the post',
|
|
'type' => PARAM_URL,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'edit' => [
|
|
'description' => 'The URL used to edit the post',
|
|
'type' => PARAM_URL,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'delete' => [
|
|
'description' => 'The URL used to delete the post',
|
|
'type' => PARAM_URL,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'split' => [
|
|
'description' => 'The URL used to split the discussion ' .
|
|
'with the selected post being the first post in the new discussion',
|
|
'type' => PARAM_URL,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'reply' => [
|
|
'description' => 'The URL used to reply to the post',
|
|
'type' => PARAM_URL,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'export' => [
|
|
'description' => 'The URL used to export the post',
|
|
'type' => PARAM_URL,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'markasread' => [
|
|
'description' => 'The URL used to mark the post as read',
|
|
'type' => PARAM_URL,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'markasunread' => [
|
|
'description' => 'The URL used to mark the post as unread',
|
|
'type' => PARAM_URL,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
],
|
|
'discuss' => [
|
|
'type' => PARAM_URL,
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED
|
|
]
|
|
]
|
|
],
|
|
'attachments' => [
|
|
'multiple' => true,
|
|
'type' => $attachmentdefinition
|
|
],
|
|
'tags' => [
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED,
|
|
'multiple' => true,
|
|
'type' => [
|
|
'id' => [
|
|
'type' => PARAM_INT,
|
|
'description' => 'The ID of the Tag',
|
|
'null' => NULL_NOT_ALLOWED,
|
|
],
|
|
'tagid' => [
|
|
'type' => PARAM_INT,
|
|
'description' => 'The tagid',
|
|
'null' => NULL_NOT_ALLOWED,
|
|
],
|
|
'isstandard' => [
|
|
'type' => PARAM_BOOL,
|
|
'description' => 'Whether this is a standard tag',
|
|
'null' => NULL_NOT_ALLOWED,
|
|
],
|
|
'displayname' => [
|
|
'type' => PARAM_TEXT,
|
|
'description' => 'The display name of the tag',
|
|
'null' => NULL_NOT_ALLOWED,
|
|
],
|
|
'flag' => [
|
|
'type' => PARAM_BOOL,
|
|
'description' => 'Wehther this tag is flagged',
|
|
'null' => NULL_NOT_ALLOWED,
|
|
],
|
|
'urls' => [
|
|
'description' => 'URLs associated with the tag',
|
|
'null' => NULL_NOT_ALLOWED,
|
|
'type' => [
|
|
'view' => [
|
|
'type' => PARAM_URL,
|
|
'description' => 'The URL to view the tag',
|
|
'null' => NULL_NOT_ALLOWED,
|
|
],
|
|
]
|
|
]
|
|
]
|
|
],
|
|
'html' => [
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED,
|
|
'type' => [
|
|
'rating' => [
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED,
|
|
'type' => PARAM_RAW,
|
|
'description' => 'The HTML source to rate the post',
|
|
],
|
|
'taglist' => [
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED,
|
|
'type' => PARAM_RAW,
|
|
'description' => 'The HTML source to view the list of tags',
|
|
],
|
|
'authorsubheading' => [
|
|
'optional' => true,
|
|
'default' => null,
|
|
'null' => NULL_ALLOWED,
|
|
'type' => PARAM_RAW,
|
|
'description' => 'The HTML source to view the author details',
|
|
],
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get the additional values to inject while exporting.
|
|
*
|
|
* @param renderer_base $output The renderer.
|
|
* @return array Keys are the property names, values are their values.
|
|
*/
|
|
protected function get_other_values(renderer_base $output) {
|
|
$post = $this->post;
|
|
$authorgroups = $this->related['authorgroups'];
|
|
$forum = $this->related['forum'];
|
|
$discussion = $this->related['discussion'];
|
|
$author = $this->related['author'];
|
|
$authorcontextid = $this->related['authorcontextid'];
|
|
$user = $this->related['user'];
|
|
$readreceiptcollection = $this->related['readreceiptcollection'];
|
|
$rating = $this->related['rating'];
|
|
$tags = $this->related['tags'];
|
|
$attachments = $this->related['attachments'];
|
|
$includehtml = $this->related['includehtml'];
|
|
$isdeleted = $post->is_deleted();
|
|
$isprivatereply = $post->is_private_reply();
|
|
$hasrating = $rating != null;
|
|
$hastags = !empty($tags);
|
|
$discussionid = $post->get_discussion_id();
|
|
$parentid = $post->get_parent_id();
|
|
|
|
$capabilitymanager = $this->related['capabilitymanager'];
|
|
$canview = $capabilitymanager->can_view_post($user, $discussion, $post);
|
|
$canedit = $capabilitymanager->can_edit_post($user, $discussion, $post);
|
|
$candelete = $capabilitymanager->can_delete_post($user, $discussion, $post);
|
|
$cansplit = $capabilitymanager->can_split_post($user, $discussion, $post);
|
|
$canreply = $capabilitymanager->can_reply_to_post($user, $discussion, $post);
|
|
$canexport = $capabilitymanager->can_export_post($user, $post);
|
|
$cancontrolreadstatus = $capabilitymanager->can_manually_control_post_read_status($user);
|
|
$canreplyprivately = $capabilitymanager->can_reply_privately_to_post($user, $post);
|
|
|
|
$urlfactory = $this->related['urlfactory'];
|
|
$viewurl = $canview ? $urlfactory->get_view_post_url_from_post($post) : null;
|
|
$viewisolatedurl = $canview ? $urlfactory->get_view_isolated_post_url_from_post($post) : null;
|
|
$viewparenturl = $post->has_parent() ? $urlfactory->get_view_post_url_from_post_id($discussionid, $parentid) : null;
|
|
$editurl = $canedit ? $urlfactory->get_edit_post_url_from_post($forum, $post) : null;
|
|
$deleteurl = $candelete ? $urlfactory->get_delete_post_url_from_post($post) : null;
|
|
$spliturl = $cansplit ? $urlfactory->get_split_discussion_at_post_url_from_post($post) : null;
|
|
$replyurl = $canreply ? $urlfactory->get_reply_to_post_url_from_post($post) : null;
|
|
$exporturl = $canexport ? $urlfactory->get_export_post_url_from_post($post) : null;
|
|
$markasreadurl = $cancontrolreadstatus ? $urlfactory->get_mark_post_as_read_url_from_post($post) : null;
|
|
$markasunreadurl = $cancontrolreadstatus ? $urlfactory->get_mark_post_as_unread_url_from_post($post) : null;
|
|
$discussurl = $canview ? $urlfactory->get_discussion_view_url_from_post($post) : null;
|
|
|
|
$authorexporter = new author_exporter(
|
|
$author,
|
|
$authorcontextid,
|
|
$authorgroups,
|
|
($canview && !$isdeleted),
|
|
$this->related
|
|
);
|
|
$exportedauthor = $authorexporter->export($output);
|
|
// Only bother loading the content if the user can see it.
|
|
$loadcontent = $canview && !$isdeleted;
|
|
$exportattachments = $loadcontent && !empty($attachments);
|
|
|
|
if ($loadcontent) {
|
|
$subject = $post->get_subject();
|
|
$timecreated = $post->get_time_created();
|
|
$message = $this->get_message($post);
|
|
} else {
|
|
$subject = $isdeleted ? get_string('forumsubjectdeleted', 'forum') : get_string('forumsubjecthidden', 'forum');
|
|
$message = $isdeleted ? get_string('forumbodydeleted', 'forum') : get_string('forumbodyhidden', 'forum');
|
|
$timecreated = null;
|
|
|
|
if ($isdeleted) {
|
|
$exportedauthor->fullname = null;
|
|
}
|
|
}
|
|
|
|
return [
|
|
'id' => $post->get_id(),
|
|
'subject' => $subject,
|
|
'message' => $message,
|
|
'messageformat' => $post->get_message_format(),
|
|
'author' => $exportedauthor,
|
|
'discussionid' => $post->get_discussion_id(),
|
|
'hasparent' => $post->has_parent(),
|
|
'parentid' => $post->has_parent() ? $post->get_parent_id() : null,
|
|
'timecreated' => $timecreated,
|
|
'unread' => ($loadcontent && $readreceiptcollection) ? !$readreceiptcollection->has_user_read_post($user, $post) : null,
|
|
'isdeleted' => $isdeleted,
|
|
'isprivatereply' => $isprivatereply,
|
|
'haswordcount' => $forum->should_display_word_count(),
|
|
'wordcount' => $forum->should_display_word_count() ? count_words($message) : null,
|
|
'capabilities' => [
|
|
'view' => $canview,
|
|
'edit' => $canedit,
|
|
'delete' => $candelete,
|
|
'split' => $cansplit,
|
|
'reply' => $canreply,
|
|
'export' => $canexport,
|
|
'controlreadstatus' => $cancontrolreadstatus,
|
|
'canreplyprivately' => $canreplyprivately
|
|
],
|
|
'urls' => [
|
|
'view' => $viewurl ? $viewurl->out(false) : null,
|
|
'viewisolated' => $viewisolatedurl ? $viewisolatedurl->out(false) : null,
|
|
'viewparent' => $viewparenturl ? $viewparenturl->out(false) : null,
|
|
'edit' => $editurl ? $editurl->out(false) : null,
|
|
'delete' => $deleteurl ? $deleteurl->out(false) : null,
|
|
'split' => $spliturl ? $spliturl->out(false) : null,
|
|
'reply' => $replyurl ? $replyurl->out(false) : null,
|
|
'export' => $exporturl && $exporturl ? $exporturl->out(false) : null,
|
|
'markasread' => $markasreadurl ? $markasreadurl->out(false) : null,
|
|
'markasunread' => $markasunreadurl ? $markasunreadurl->out(false) : null,
|
|
'discuss' => $discussurl ? $discussurl->out(false) : null,
|
|
],
|
|
'attachments' => ($exportattachments) ? $this->export_attachments($attachments, $post, $output, $canexport) : [],
|
|
'tags' => ($loadcontent && $hastags) ? $this->export_tags($tags) : [],
|
|
'html' => $includehtml ? [
|
|
'rating' => ($loadcontent && $hasrating) ? $output->render($rating) : null,
|
|
'taglist' => ($loadcontent && $hastags) ? $output->tag_list($tags) : null,
|
|
'authorsubheading' => ($loadcontent) ? $this->get_author_subheading_html($exportedauthor, $timecreated) : null
|
|
] : null
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Returns a list of objects that are related.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function define_related() {
|
|
return [
|
|
'capabilitymanager' => 'mod_forum\local\managers\capability',
|
|
'readreceiptcollection' => 'mod_forum\local\entities\post_read_receipt_collection?',
|
|
'urlfactory' => 'mod_forum\local\factories\url',
|
|
'forum' => 'mod_forum\local\entities\forum',
|
|
'discussion' => 'mod_forum\local\entities\discussion',
|
|
'author' => 'mod_forum\local\entities\author',
|
|
'authorcontextid' => 'int?',
|
|
'user' => 'stdClass',
|
|
'context' => 'context',
|
|
'authorgroups' => 'stdClass[]',
|
|
'attachments' => '\stored_file[]?',
|
|
'tags' => '\core_tag_tag[]?',
|
|
'rating' => 'rating?',
|
|
'includehtml' => 'bool'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* This method returns the parameters for the post's message to
|
|
* use with the function external_format_text().
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function get_format_parameters_for_message() {
|
|
return [
|
|
'component' => 'mod_forum',
|
|
'filearea' => 'post',
|
|
'itemid' => $this->post->get_id(),
|
|
'options' => [
|
|
'para' => false,
|
|
'trusted' => $this->post->is_message_trusted()
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get the message text from a post.
|
|
*
|
|
* @param post_entity $post The post
|
|
* @return string
|
|
*/
|
|
private function get_message(post_entity $post) : string {
|
|
global $CFG;
|
|
|
|
$message = $post->get_message();
|
|
|
|
if (!empty($CFG->enableplagiarism)) {
|
|
require_once($CFG->libdir . '/plagiarismlib.php');
|
|
$forum = $this->related['forum'];
|
|
$message .= plagiarism_get_links([
|
|
'userid' => $post->get_author_id(),
|
|
'content' => $message,
|
|
'cmid' => $forum->get_course_module_record()->id,
|
|
'course' => $forum->get_course_id(),
|
|
'forum' => $forum->get_id()
|
|
]);
|
|
}
|
|
|
|
return $message;
|
|
}
|
|
|
|
/**
|
|
* Get the exported attachments for a post.
|
|
*
|
|
* @param stored_file[] $attachments The list of attachments for the post
|
|
* @param post_entity $post The post being exported
|
|
* @param renderer_base $output Renderer base
|
|
* @param bool $canexport If the user can export the post (relates to portfolios not exporters like this class)
|
|
* @return array
|
|
*/
|
|
private function export_attachments(array $attachments, post_entity $post, renderer_base $output, bool $canexport) : array {
|
|
global $CFG;
|
|
|
|
$urlfactory = $this->related['urlfactory'];
|
|
$enableplagiarism = $CFG->enableplagiarism;
|
|
$forum = $this->related['forum'];
|
|
$context = $this->related['context'];
|
|
|
|
if ($enableplagiarism) {
|
|
require_once($CFG->libdir . '/plagiarismlib.php' );
|
|
}
|
|
|
|
return array_map(function($attachment) use (
|
|
$output,
|
|
$enableplagiarism,
|
|
$canexport,
|
|
$context,
|
|
$forum,
|
|
$post,
|
|
$urlfactory
|
|
) {
|
|
$exporter = new stored_file_exporter($attachment, ['context' => $context]);
|
|
$exportedattachment = $exporter->export($output);
|
|
$exporturl = $canexport ? $urlfactory->get_export_attachment_url_from_post_and_attachment($post, $attachment) : null;
|
|
|
|
if ($enableplagiarism) {
|
|
$plagiarismhtml = plagiarism_get_links([
|
|
'userid' => $post->get_author_id(),
|
|
'file' => $attachment,
|
|
'cmid' => $forum->get_course_module_record()->id,
|
|
'course' => $forum->get_course_id(),
|
|
'forum' => $forum->get_id()
|
|
]);
|
|
} else {
|
|
$plagiarismhtml = null;
|
|
}
|
|
|
|
$exportedattachment->urls = [
|
|
'export' => $exporturl ? $exporturl->out(false) : null
|
|
];
|
|
$exportedattachment->html = [
|
|
'plagiarism' => $plagiarismhtml
|
|
];
|
|
|
|
return $exportedattachment;
|
|
}, $attachments);
|
|
}
|
|
|
|
/**
|
|
* Export the list of tags.
|
|
*
|
|
* @param core_tag_tag[] $tags List of tags to export
|
|
* @return array
|
|
*/
|
|
private function export_tags(array $tags) : array {
|
|
$user = $this->related['user'];
|
|
$context = $this->related['context'];
|
|
$capabilitymanager = $this->related['capabilitymanager'];
|
|
$canmanagetags = $capabilitymanager->can_manage_tags($user);
|
|
|
|
return array_values(array_map(function($tag) use ($context, $canmanagetags) {
|
|
$viewurl = core_tag_tag::make_url($tag->tagcollid, $tag->rawname, 0, $context->id);
|
|
return [
|
|
'id' => $tag->taginstanceid,
|
|
'tagid' => $tag->id,
|
|
'isstandard' => $tag->isstandard,
|
|
'displayname' => $tag->get_display_name(),
|
|
'flag' => $canmanagetags && !empty($tag->flag),
|
|
'urls' => [
|
|
'view' => $viewurl->out(false)
|
|
]
|
|
];
|
|
}, $tags));
|
|
}
|
|
|
|
/**
|
|
* Get the HTML to display as a subheading in a post.
|
|
*
|
|
* @param stdClass $exportedauthor The exported author object
|
|
* @param int $timecreated The post time created timestamp if it's to be displayed
|
|
* @return string
|
|
*/
|
|
private function get_author_subheading_html(stdClass $exportedauthor, int $timecreated) : string {
|
|
$fullname = $exportedauthor->fullname;
|
|
$profileurl = $exportedauthor->urls['profile'] ?? null;
|
|
$formatteddate = userdate($timecreated, get_string('strftimedaydatetime', 'core_langconfig'));
|
|
$name = $profileurl ? "<a href=\"{$profileurl}\">{$fullname}</a>" : $fullname;
|
|
$date = "<time>{$formatteddate}</time>";
|
|
return get_string('bynameondate', 'mod_forum', ['name' => $name, 'date' => $date]);
|
|
}
|
|
}
|
|
|