MDL-64820 forum: add new classes for rendering to local namespace

This commit is contained in:
Ryan Wyllie
2019-03-22 09:24:05 +08:00
parent 2f6f3397fc
commit 47d38303cf
81 changed files with 14232 additions and 49 deletions
@@ -0,0 +1,503 @@
<?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/>.
/**
* Exported post builder class.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\builders;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\entities\post as post_entity;
use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
use mod_forum\local\factories\exporter as exporter_factory;
use mod_forum\local\factories\vault as vault_factory;
use context;
use core_tag_tag;
use moodle_exception;
use rating_manager;
use renderer_base;
use stdClass;
/**
* Exported post builder class.
*
* This class is an implementation of the builder pattern (loosely). It is responsible
* for taking a set of related forums, discussions, and posts and generate the exported
* version of the posts.
*
* It encapsulates the complexity involved with exporting posts. All of the relevant
* additional resources will be loaded by this class in order to ensure the exporting
* process can happen.
*
* See this doc for more information on the builder pattern:
* https://designpatternsphp.readthedocs.io/en/latest/Creational/Builder/README.html
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class exported_posts {
/** @var renderer_base $renderer Core renderer */
private $renderer;
/** @var legacy_data_mapper_factory $legacydatamapperfactory Data mapper factory */
private $legacydatamapperfactory;
/** @var exporter_factory $exporterfactory Exporter factory */
private $exporterfactory;
/** @var vault_factory $vaultfactory Vault factory */
private $vaultfactory;
/** @var rating_manager $ratingmanager Rating manager */
private $ratingmanager;
/**
* Constructor.
*
* @param renderer_base $renderer Core renderer
* @param legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory
* @param exporter_factory $exporterfactory Exporter factory
* @param vault_factory $vaultfactory Vault factory
* @param rating_manager $ratingmanager Rating manager
*/
public function __construct(
renderer_base $renderer,
legacy_data_mapper_factory $legacydatamapperfactory,
exporter_factory $exporterfactory,
vault_factory $vaultfactory,
rating_manager $ratingmanager
) {
$this->renderer = $renderer;
$this->legacydatamapperfactory = $legacydatamapperfactory;
$this->exporterfactory = $exporterfactory;
$this->vaultfactory = $vaultfactory;
$this->ratingmanager = $ratingmanager;
}
/**
* Build the exported posts for a given set of forums, discussions, and posts.
*
* This will typically be used for a list of posts in the same discussion/forum however
* it does support exporting any arbitrary list of posts as long as the caller also provides
* a unique list of all discussions for the list of posts and all forums for the list of discussions.
*
* Increasing the number of different forums being processed will increase the processing time
* due to processing multiple contexts (for things like capabilities, files, etc). The code attempts
* to load the additional resources as efficiently as possible but there is no way around some of
* the additional overhead.
*
* @param stdClass $user The user to export the posts for.
* @param forum_entity[] $forums A list of all forums that each of the $discussions belong to
* @param discussion_entity[] $discussions A list of all discussions that each of the $posts belong to
* @param post_entity[] $posts The list of posts to export.
* @return stdClass[] List of exported posts in the same order as the $posts array.
*/
public function build(
stdClass $user,
array $forums,
array $discussions,
array $posts
) : array {
// Format the forums and discussion to make them more easily accessed later.
$forums = array_reduce($forums, function($carry, $forum) {
$carry[$forum->get_id()] = $forum;
return $carry;
}, []);
$discussions = array_reduce($discussions, function($carry, $discussion) {
$carry[$discussion->get_id()] = $discussion;
return $carry;
}, []);
// Group the posts by discussion and forum so that we can load the resources in
// batches to improve performance.
$groupedposts = $this->group_posts_by_discussion($forums, $discussions, $posts);
// Load all of the resources we need in order to export the posts.
$authorsbyid = $this->get_authors_for_posts($posts);
$attachmentsbypostid = $this->get_attachments_for_posts($groupedposts);
$groupsbycourseandauthorid = $this->get_author_groups_from_posts($groupedposts);
$tagsbypostid = $this->get_tags_from_posts($posts);
$ratingbypostid = $this->get_ratings_from_posts($user, $groupedposts);
$readreceiptcollectionbyforumid = $this->get_read_receipts_from_posts($user, $groupedposts);
$exportedposts = [];
// Export each set of posts per discussion because it's the largest chunks we can
// break them into due to constraints on capability checks.
foreach ($groupedposts as $grouping) {
[
'forum' => $forum,
'discussion' => $discussion,
'posts' => $groupedposts
] = $grouping;
$forumid = $forum->get_id();
$courseid = $forum->get_course_record()->id;
$postsexporter = $this->exporterfactory->get_posts_exporter(
$user,
$forum,
$discussion,
$groupedposts,
$authorsbyid,
$attachmentsbypostid,
$groupsbycourseandauthorid[$courseid],
$readreceiptcollectionbyforumid[$forumid] ?? null,
$tagsbypostid,
$ratingbypostid,
true
);
['posts' => $exportedgroupedposts] = (array) $postsexporter->export($this->renderer);
$exportedposts = array_merge($exportedposts, $exportedgroupedposts);
}
if (count($forums) == 1 && count($discussions) == 1) {
// All of the posts belong to a single discussion in a single forum so
// the exported order will match the given $posts array.
return $exportedposts;
} else {
// Since we grouped the posts by discussion and forum the ordering of the
// exported posts may be different to the given $posts array so we should
// sort it back into the correct order for the caller.
return $this->sort_exported_posts($posts, $exportedposts);
}
}
/**
* Group the posts by which discussion they belong to in order for them to be processed
* in chunks by the exporting.
*
* Returns a list of groups where each group has a forum, discussion, and list of posts.
* E.g.
* [
* [
* 'forum' => <forum_entity>,
* 'discussion' => <discussion_entity>,
* 'posts' => [
* <post_entity in discussion>,
* <post_entity in discussion>,
* <post_entity in discussion>
* ]
* ]
* ]
*
* @param forum_entity[] $forums A list of all forums that each of the $discussions belong to, indexed by id.
* @param discussion_entity[] $discussions A list of all discussions that each of the $posts belong to, indexed by id.
* @param post_entity[] $posts The list of posts to process.
* @return array List of grouped posts. Each group has a discussion, forum, and posts.
*/
private function group_posts_by_discussion(array $forums, array $discussions, array $posts) : array {
return array_reduce($posts, function($carry, $post) use ($forums, $discussions) {
$discussionid = $post->get_discussion_id();
if (!isset($discussions[$discussionid])) {
throw new moodle_exception('Unable to find discussion with id ' . $discussionid);
}
if (isset($carry[$discussionid])) {
$carry[$discussionid]['posts'][] = $post;
} else {
$discussion = $discussions[$discussionid];
$forumid = $discussion->get_forum_id();
if (!isset($forums[$forumid])) {
throw new moodle_exception('Unable to find forum with id ' . $forumid);
}
$carry[$discussionid] = [
'forum' => $forums[$forumid],
'discussion' => $discussions[$discussionid],
'posts' => [$post]
];
}
return $carry;
}, []);
}
/**
* Load the list of authors for the given posts.
*
* The list of authors will be indexed by the author id.
*
* @param post_entity[] $posts The list of posts to process.
* @return author_entity[]
*/
private function get_authors_for_posts(array $posts) : array {
$authorvault = $this->vaultfactory->get_author_vault();
return $authorvault->get_authors_for_posts($posts);
}
/**
* Load the list of all attachments for the posts. The list of attachments will be
* indexed by the post id.
*
* @param array $groupedposts List of posts grouped by discussions.
* @return stored_file[]
*/
private function get_attachments_for_posts(array $groupedposts) : array {
$attachmentsbypostid = [];
$postattachmentvault = $this->vaultfactory->get_post_attachment_vault();
$postsbyforum = array_reduce($groupedposts, function($carry, $grouping) {
['forum' => $forum, 'posts' => $posts] = $grouping;
$forumid = $forum->get_id();
if (!isset($carry[$forumid])) {
$carry[$forumid] = [
'forum' => $forum,
'posts' => []
];
}
$carry[$forumid]['posts'] = array_merge($carry[$forumid]['posts'], $posts);
return $carry;
}, []);
foreach ($postsbyforum as $grouping) {
['forum' => $forum, 'posts' => $posts] = $grouping;
$attachments = $postattachmentvault->get_attachments_for_posts($forum->get_context(), $posts);
// Have to loop in order to maintain the correct indexes since they are numeric.
foreach ($attachments as $postid => $attachment) {
$attachmentsbypostid[$postid] = $attachment;
}
}
return $attachmentsbypostid;
}
/**
* Get the groups for each author of the given posts.
*
* The results are grouped by course and then author id because the groups are
* contextually related to the course, e.g. a single author can be part of two different
* sets of groups in two different courses.
*
* @param array $groupedposts List of posts grouped by discussions.
* @return array List of groups indexed by forum id and then author id.
*/
private function get_author_groups_from_posts(array $groupedposts) : array {
$groupsbyauthorid = [];
$authoridsbycourseid = [];
// Get the unique list of author ids for each course in the grouped
// posts. Grouping by course is the largest grouping we can achieve.
foreach ($groupedposts as $grouping) {
['forum' => $forum, 'posts' => $posts] = $grouping;
$course = $forum->get_course_record();
$courseid = $course->id;
if (!isset($authoridsbycourseid[$courseid])) {
$coursemodule = $forum->get_course_module_record();
$authoridsbycourseid[$courseid] = [
'groupingid' => $coursemodule->groupingid,
'authorids' => []
];
}
$authorids = array_map(function($post) {
return $post->get_author_id();
}, $posts);
foreach ($authorids as $authorid) {
$authoridsbycourseid[$courseid]['authorids'][$authorid] = $authorid;
}
}
// Load each set of groups per course.
foreach ($authoridsbycourseid as $courseid => $values) {
['groupingid' => $groupingid, 'authorids' => $authorids] = $values;
$authorgroups = groups_get_all_groups(
$courseid,
array_keys($authorids),
$groupingid,
'g.*, gm.id, gm.groupid, gm.userid'
);
if (!isset($groupsbyauthorid[$courseid])) {
$groupsbyauthorid[$courseid] = [];
}
foreach ($authorgroups as $group) {
// Clean up data returned from groups_get_all_groups.
$userid = $group->userid;
$groupid = $group->groupid;
unset($group->userid);
unset($group->groupid);
$group->id = $groupid;
if (!isset($groupsbyauthorid[$courseid][$userid])) {
$groupsbyauthorid[$courseid][$userid] = [];
}
$groupsbyauthorid[$courseid][$userid][] = $group;
}
}
return $groupsbyauthorid;
}
/**
* Get the list of tags for each of the posts. The tags will be returned in an
* array indexed by the post id.
*
* @param post_entity[] $posts The list of posts to load tags for.
* @return array Sets of tags indexed by post id.
*/
private function get_tags_from_posts(array $posts) : array {
$postids = array_map(function($post) {
return $post->get_id();
}, $posts);
return core_tag_tag::get_items_tags('mod_forum', 'forum_posts', $postids);
}
/**
* Get the list of ratings for each post. The ratings are returned in an array
* indexed by the post id.
*
* @param stdClass $user The user viewing the ratings.
* @param array $groupedposts List of posts grouped by discussions.
* @return array Sets of ratings indexed by post id.
*/
private function get_ratings_from_posts(stdClass $user, array $groupedposts) {
$ratingsbypostid = [];
$postsdatamapper = $this->legacydatamapperfactory->get_post_data_mapper();
$postsbyforum = array_reduce($groupedposts, function($carry, $grouping) {
['forum' => $forum, 'posts' => $posts] = $grouping;
$forumid = $forum->get_id();
if (!isset($carry[$forumid])) {
$carry[$forumid] = [
'forum' => $forum,
'posts' => []
];
}
$carry[$forumid]['posts'] = array_merge($carry[$forumid]['posts'], $posts);
return $carry;
}, []);
foreach ($postsbyforum as $grouping) {
['forum' => $forum, 'posts' => $posts] = $grouping;
if (!$forum->has_rating_aggregate()) {
continue;
}
$items = $postsdatamapper->to_legacy_objects($posts);
$ratingoptions = (object) [
'context' => $forum->get_context(),
'component' => 'mod_forum',
'ratingarea' => 'post',
'items' => $items,
'aggregate' => $forum->get_rating_aggregate(),
'scaleid' => $forum->get_scale(),
'userid' => $user->id,
'assesstimestart' => $forum->get_assess_time_start(),
'assesstimefinish' => $forum->get_assess_time_finish()
];
$rm = $this->ratingmanager;
$items = $rm->get_ratings($ratingoptions);
foreach ($items as $item) {
$ratingsbypostid[$item->id] = empty($item->rating) ? null : $item->rating;
}
}
return $ratingsbypostid;
}
/**
* Get the read receipt collections for the given viewing user and each forum. The
* receipt collections will only be loaded for posts in forums that the user is tracking.
*
* The receipt collections are returned in an array indexed by the forum ids.
*
* @param stdClass $user The user viewing the posts.
* @param array $groupedposts List of posts grouped by discussions.
*/
private function get_read_receipts_from_posts(stdClass $user, array $groupedposts) {
$forumdatamapper = $this->legacydatamapperfactory->get_forum_data_mapper();
$trackedforums = [];
$trackedpostids = [];
foreach ($groupedposts as $group) {
['forum' => $forum, 'posts' => $posts] = $group;
$forumid = $forum->get_id();
if (!isset($trackedforums[$forumid])) {
$forumrecord = $forumdatamapper->to_legacy_object($forum);
$trackedforums[$forumid] = forum_tp_is_tracked($forumrecord, $user);
}
if ($trackedforums[$forumid]) {
foreach ($posts as $post) {
$trackedpostids[] = $post->get_id();
}
}
}
if (empty($trackedpostids)) {
return [];
}
// We can just load a single receipt collection for all tracked posts.
$receiptvault = $this->vaultfactory->get_post_read_receipt_collection_vault();
$readreceiptcollection = $receiptvault->get_from_user_id_and_post_ids($user->id, $trackedpostids);
$receiptsbyforumid = [];
// Assign the collection to all forums that are tracked.
foreach ($trackedforums as $forumid => $tracked) {
if ($tracked) {
$receiptsbyforumid[$forumid] = $readreceiptcollection;
}
}
return $receiptsbyforumid;
}
/**
* Sort the list of exported posts back into the same order as the given posts.
* The ordering of the exported posts can often deviate from the given posts due
* to the process of exporting them so we need to sort them back into the order
* that the calling code expected.
*
* @param post_entity[] $posts The posts in the expected order.
* @param stdClass[] $exportedposts The list of exported posts in any order.
* @return stdClass[] Sorted exported posts.
*/
private function sort_exported_posts(array $posts, array $exportedposts) {
$postindexes = [];
foreach (array_values($posts) as $index => $post) {
$postindexes[$post->get_id()] = $index;
}
$sortedexportedposts = [];
foreach ($exportedposts as $exportedpost) {
$index = $postindexes[$exportedpost->id];
$sortedexportedposts[$index] = $exportedpost;
}
return $sortedexportedposts;
}
}
+152
View File
@@ -0,0 +1,152 @@
<?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/>.
/**
* Container class.
*
* @package mod_forum
* @copyright 2018 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\factories\renderer as renderer_factory;
use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
use mod_forum\local\factories\entity as entity_factory;
use mod_forum\local\factories\exporter as exporter_factory;
use mod_forum\local\factories\manager as manager_factory;
use mod_forum\local\factories\vault as vault_factory;
use mod_forum\local\factories\builder as builder_factory;
use mod_forum\local\factories\url as url_factory;
/**
* Container class.
*
* This class provides helper methods with static configurations to get any
* of the factories from the "local" namespace.
*
* @copyright 2018 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class container {
/**
* Create the renderer factory.
*
* @return renderer_factory
*/
public static function get_renderer_factory() : renderer_factory {
global $PAGE;
return new renderer_factory(
self::get_legacy_data_mapper_factory(),
self::get_exporter_factory(),
self::get_vault_factory(),
self::get_manager_factory(),
self::get_entity_factory(),
self::get_builder_factory(),
self::get_url_factory(),
$PAGE
);
}
/**
* Create the legacy data mapper factory.
*
* @return legacy_data_mapper_factory
*/
public static function get_legacy_data_mapper_factory() : legacy_data_mapper_factory {
return new legacy_data_mapper_factory();
}
/**
* Create the exporter factory.
*
* @return exporter_factory
*/
public static function get_exporter_factory() : exporter_factory {
return new exporter_factory(
self::get_legacy_data_mapper_factory(),
self::get_manager_factory(),
self::get_url_factory()
);
}
/**
* Create the vault factory.
*
* @return vault_factory
*/
public static function get_vault_factory() : vault_factory {
global $DB;
return new vault_factory(
$DB,
self::get_entity_factory(),
get_file_storage()
);
}
/**
* Create the manager factory.
*
* @return manager_factory
*/
public static function get_manager_factory() : manager_factory {
return new manager_factory(
self::get_legacy_data_mapper_factory()
);
}
/**
* Create the entity factory.
*
* @return entity_factory
*/
public static function get_entity_factory() : entity_factory {
return new entity_factory();
}
/**
* Create the builder factory.
*
* @return builder_factory
*/
public static function get_builder_factory() : builder_factory {
global $PAGE;
return new builder_factory(
self::get_legacy_data_mapper_factory(),
self::get_exporter_factory(),
self::get_vault_factory(),
self::get_manager_factory(),
$PAGE->get_renderer('mod_forum')
);
}
/**
* Create the URL factory.
*
* @return url_factory
*/
public static function get_url_factory() : url_factory {
return new url_factory(
self::get_legacy_data_mapper_factory()
);
}
}
@@ -0,0 +1,72 @@
<?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/>.
/**
* Author data mapper.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\data_mappers\legacy;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\author as author_entity;
use stdClass;
/**
* Convert an author entity into an stdClass.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class author {
/**
* Convert a list of author entities into stdClasses.
*
* @param author_entity[] $authors The authors to convert.
* @return stdClass[]
*/
public function to_legacy_objects(array $authors) : array {
return array_map(function(author_entity $author) {
return (object) [
'id' => $author->get_id(),
'picture' => $author->get_picture_item_id(),
'firstname' => $author->get_first_name(),
'lastname' => $author->get_last_name(),
'fullname' => $author->get_full_name(),
'email' => $author->get_email(),
'middlename' => $author->get_middle_name(),
'firstnamephonetic' => $author->get_first_name_phonetic(),
'lastnamephonetic' => $author->get_last_name_phonetic(),
'alternatename' => $author->get_alternate_name(),
'imagealt' => $author->get_image_alt()
];
}, $authors);
}
/**
* Convert an author entity into an stdClass.
*
* @param author_entity $author The author to convert.
* @return stdClass
*/
public function to_legacy_object(author_entity $author) : stdClass {
return $this->to_legacy_objects([$author])[0];
}
}
@@ -0,0 +1,74 @@
<?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/>.
/**
* Discussion data mapper.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\data_mappers\legacy;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\discussion as discussion_entity;
use stdClass;
/**
* Convert a discussion entity into an stdClass.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class discussion {
/**
* Convert a list of discussion entities into stdClasses.
*
* @param discussion_entity[] $authors The authors to convert.
* @return stdClass[]
*/
public function to_legacy_objects(array $discussions) : array {
return array_map(function(discussion_entity $discussion) {
return (object) [
'id' => $discussion->get_id(),
'course' => $discussion->get_course_id(),
'forum' => $discussion->get_forum_id(),
'name' => $discussion->get_name(),
'firstpost' => $discussion->get_first_post_id(),
'userid' => $discussion->get_user_id(),
'groupid' => $discussion->get_group_id(),
'assessed' => $discussion->is_assessed(),
'timemodified' => $discussion->get_time_modified(),
'usermodified' => $discussion->get_user_modified(),
'timestart' => $discussion->get_time_start(),
'timeend' => $discussion->get_time_end(),
'pinned' => $discussion->is_pinned()
];
}, $discussions);
}
/**
* Convert a discussion entity into an stdClass.
*
* @param discussion_entity $discussion The discussion to convert.
* @return stdClass
*/
public function to_legacy_object(discussion_entity $discussion) : stdClass {
return $this->to_legacy_objects([$discussion])[0];
}
}
@@ -0,0 +1,86 @@
<?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 data mapper.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\data_mappers\legacy;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\forum as forum_entity;
use stdClass;
/**
* Convert a forum entity into an stdClass.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class forum {
/**
* Convert a list of forum entities into stdClasses.
*
* @param forum_entity[] $forums The forums to convert.
* @return stdClass[]
*/
public function to_legacy_objects(array $forums) : array {
return array_map(function(forum_entity $forum) {
return (object) [
'id' => $forum->get_id(),
'course' => $forum->get_course_id(),
'type' => $forum->get_type(),
'name' => $forum->get_name(),
'intro' => $forum->get_intro(),
'introformat' => $forum->get_intro_format(),
'assessed' => $forum->get_rating_aggregate(),
'assesstimestart' => $forum->get_assess_time_start(),
'assesstimefinish' => $forum->get_assess_time_finish(),
'scale' => $forum->get_scale(),
'maxbytes' => $forum->get_max_bytes(),
'maxattachments' => $forum->get_max_attachments(),
'forcesubscribe' => $forum->is_subscription_forced(),
'trackingtype' => $forum->get_tracking_type(),
'rsstype' => $forum->get_rss_type(),
'rssarticles' => $forum->get_rss_articles(),
'timemodified' => $forum->get_time_modified(),
'warnafter' => $forum->get_warn_after(),
'blockafter' => $forum->get_block_after(),
'blockperiod' => $forum->get_block_period(),
'completiondiscussions' => $forum->get_completion_discussions(),
'completionreplies' => $forum->get_completion_replies(),
'completionposts' => $forum->get_completion_posts(),
'displaywordcount' => $forum->should_display_word_count(),
'lockdiscussionafter' => $forum->get_lock_discussions_after()
];
}, $forums);
}
/**
* Convert a forum entity into an stdClass.
*
* @param forum_entity $forum The forum to convert.
* @return stdClass
*/
public function to_legacy_object(forum_entity $forum) : stdClass {
return $this->to_legacy_objects([$forum])[0];
}
}
@@ -0,0 +1,76 @@
<?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 data mapper.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\data_mappers\legacy;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\post as post_entity;
use stdClass;
/**
* Convert a post entity into an stdClass.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class post {
/**
* Convert a list of post entities into stdClasses.
*
* @param post_entity[] $posts The posts to convert.
* @return stdClass[]
*/
public function to_legacy_objects(array $posts) : array {
return array_map(function(post_entity $post) {
return (object) [
'id' => $post->get_id(),
'discussion' => $post->get_discussion_id(),
'parent' => $post->get_parent_id(),
'userid' => $post->get_author_id(),
'created' => $post->get_time_created(),
'modified' => $post->get_time_modified(),
'mailed' => $post->has_been_mailed(),
'subject' => $post->get_subject(),
'message' => $post->get_message(),
'messageformat' => $post->get_message_format(),
'messagetrust' => $post->is_message_trusted(),
'attachment' => $post->has_attachments(),
'totalscore' => $post->get_total_score(),
'mailnow' => $post->should_mail_now(),
'deleted' => $post->is_deleted()
];
}, $posts);
}
/**
* Convert a post entity into an stdClass.
*
* @param post_entity $post The post to convert.
* @return stdClass
*/
public function to_legacy_object(post_entity $post) : stdClass {
return $this->to_legacy_objects([$post])[0];
}
}
+198
View File
@@ -0,0 +1,198 @@
<?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/>.
/**
* Author class.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\entities;
defined('MOODLE_INTERNAL') || die();
/**
* Author class.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class author {
/** @var int $id ID */
private $id;
/** @var int $pictureitemid Picture item id */
private $pictureitemid;
/** @var string $firstname First name */
private $firstname;
/** @var string $lastname Last name */
private $lastname;
/** @var string $fullname Full name */
private $fullname;
/** @var string $email Email */
private $email;
/** @var string $middlename Middle name */
private $middlename;
/** @var string $firstnamephonetic Phonetic spelling of first name */
private $firstnamephonetic;
/** @var string $lastnamephonetic Phonetic spelling of last name */
private $lastnamephonetic;
/** @var string $alternatename Altername name */
private $alternatename;
/** @var string $imagealt Image alt */
private $imagealt;
/**
* Constructor.
*
* @param int $id ID
* @param int $pictureitemid Picture item id
* @param string $firstname First name
* @param string $lastname Last name
* @param string $fullname Full name
* @param string $email Email
* @param string|null $middlename Middle name
* @param string|null $firstnamephonetic Phonetic spelling of first name
* @param string|null $lastnamephonetic Phonetic spelling of last name
* @param string|null $alternatename Altername name
* @param string|null $imagealt Image alt
*/
public function __construct(
int $id,
int $pictureitemid,
string $firstname,
string $lastname,
string $fullname,
string $email,
string $middlename = null,
string $firstnamephonetic = null,
string $lastnamephonetic = null,
string $alternatename = null,
string $imagealt = null
) {
$this->id = $id;
$this->pictureitemid = $pictureitemid;
$this->firstname = $firstname;
$this->lastname = $lastname;
$this->fullname = $fullname;
$this->email = $email;
$this->middlename = $middlename;
$this->firstnamephonetic = $firstnamephonetic;
$this->lastnamephonetic = $lastnamephonetic;
$this->alternatename = $alternatename;
$this->imagealt = $imagealt;
}
/**
* Return the id.
*
* @return int
*/
public function get_id() : int {
return $this->id;
}
/**
* Return the picture item id.
*
* @return int
*/
public function get_picture_item_id() : int {
return $this->pictureitemid;
}
/**
* Return the first name.
*
* @return string
*/
public function get_first_name() : string {
return $this->firstname;
}
/**
* Return the last name.
*
* @return string
*/
public function get_last_name() : string {
return $this->lastname;
}
/**
* Return the full name.
*
* @return string
*/
public function get_full_name() : string {
return $this->fullname;
}
/**
* Return the email.
*
* @return string
*/
public function get_email() : string {
return $this->email;
}
/**
* Return the middle name.
*
* @return string|null
*/
public function get_middle_name() : ?string {
return $this->middlename;
}
/**
* Return the first name phonetic.
*
* @return string|null
*/
public function get_first_name_phonetic() : ?string {
return $this->firstnamephonetic;
}
/**
* Return the last name phonetic.
*
* @return string|null
*/
public function get_last_name_phonetic() : ?string {
return $this->lastnamephonetic;
}
/**
* Return the alternate name.
*
* @return string|null
*/
public function get_alternate_name() : ?string {
return $this->alternatename;
}
/**
* Return the image alt.
*
* @return string|null
*/
public function get_image_alt() : ?string {
return $this->imagealt;
}
}
@@ -0,0 +1,259 @@
<?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/>.
/**
* Discussion class.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\entities;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\post as post_entity;
/**
* Discussion class.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class discussion {
/** @var int $id ID */
private $id;
/** @var int $courseid Course id */
private $courseid;
/** @var int $forumid Forum id */
private $forumid;
/** @var string $name Discussion name */
private $name;
/** @var int $firstpostid Id of the first post in the discussion */
private $firstpostid;
/** @var int $userid Id of the user that created the discussion */
private $userid;
/** @var int $groupid Group id if it's a group dicussion */
private $groupid;
/** @var bool $assessed Is the discussion assessed? */
private $assessed;
/** @var int $timemodified Timestamp for last modification to the discussion */
private $timemodified;
/** @var int $usermodified Id of user that last modified the discussion */
private $usermodified;
/** @var int $timestart Start time for the discussion */
private $timestart;
/** @var int $timeend End time for the discussion */
private $timeend;
/** @var bool $pinned Is the discussion pinned? */
private $pinned;
/**
* Constructor.
*
* @param int $id ID
* @param int $courseid Course id
* @param int $forumid Forum id
* @param string $name Discussion name
* @param int $firstpostid Id of the first post in the discussion
* @param int $userid Id of the user that created the discussion
* @param int $groupid Group id if it's a group dicussion
* @param bool $assessed Is the discussion assessed?
* @param int $timemodified Timestamp for last modification to the discussion
* @param int $usermodified Id of user that last modified the discussion
* @param int $timestart Start time for the discussion
* @param int $timeend End time for the discussion
* @param bool $pinned Is the discussion pinned?
*/
public function __construct(
int $id,
int $courseid,
int $forumid,
string $name,
int $firstpostid,
int $userid,
int $groupid,
bool $assessed,
int $timemodified,
int $usermodified,
int $timestart,
int $timeend,
bool $pinned
) {
$this->id = $id;
$this->courseid = $courseid;
$this->forumid = $forumid;
$this->name = $name;
$this->firstpostid = $firstpostid;
$this->userid = $userid;
$this->groupid = $groupid;
$this->assessed = $assessed;
$this->timemodified = $timemodified;
$this->usermodified = $usermodified;
$this->timestart = $timestart;
$this->timeend = $timeend;
$this->pinned = $pinned;
}
/**
* Get the discussion id.
*
* @return int
*/
public function get_id() : int {
return $this->id;
}
/**
* Get the course id.
*
* @return int
*/
public function get_course_id() : int {
return $this->courseid;
}
/**
* Get the forum id.
*
* @return int
*/
public function get_forum_id() : int {
return $this->forumid;
}
/**
* Get the name of the discussion.
*
* @return string
*/
public function get_name() : string {
return $this->name;
}
/**
* Get the id of the fist post in the discussion.
*
* @return int
*/
public function get_first_post_id() : int {
return $this->firstpostid;
}
/**
* Get the id of the user that created the discussion.
*
* @return int
*/
public function get_user_id() : int {
return $this->userid;
}
/**
* Get the id of the group that this discussion belongs to.
*
* @return int
*/
public function get_group_id() : int {
return $this->groupid;
}
/**
* Check if this discussion is assessed.
*
* @return bool
*/
public function is_assessed() : bool {
return $this->assessed;
}
/**
* Get the timestamp for when this discussion was last modified.
*
* @return int
*/
public function get_time_modified() : int {
return $this->timemodified;
}
/**
* Get the id of the user that last modified this discussion.
*
* @return int
*/
public function get_user_modified() : int {
return $this->usermodified;
}
/**
* Get the start time of this discussion. Returns zero if the discussion
* has no designated start time.
*
* @return int
*/
public function get_time_start() : int {
return $this->timestart;
}
/**
* Get the end time of this discussion. Returns zero if the discussion
* has no designated end time.
*
* @return int
*/
public function get_time_end() : int {
return $this->timeend;
}
/**
* Check if this discussion is pinned.
*
* @return bool
*/
public function is_pinned() : bool {
return $this->pinned;
}
/**
* Check if the given post is the first post in this discussion.
*
* @param post_entity $post The post to check
* @return bool
*/
public function is_first_post(post_entity $post) : bool {
return $this->get_first_post_id() === $post->get_id();
}
/**
* Check if the discussion has started yet.
*
* @return bool
*/
public function has_started() : bool {
$startime = $this->get_time_start();
return empty($startime) || $startime < time();
}
/**
* Check if the discussion belongs to a group.
*
* @return bool
*/
public function has_group() : bool {
return $this->get_group_id() > 0;
}
}
@@ -0,0 +1,104 @@
<?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/>.
/**
* Discussion summary class.
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\entities;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\post as post_entity;
use mod_forum\local\entities\author as author_entity;
/**
* Discussion summary class.
*
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class discussion_summary {
/** @var discussion_entity $discussion The discussion being summarised */
private $discussion;
/** @var author_entity $firstpostauthor Author of the first post in the discussion */
private $firstpostauthor;
/** @var post_entity $firstpost First post in the discussion */
private $firstpost;
/** @var author_entity $latestpostauthor Author of the last post in the discussion */
private $latestpostauthor;
/**
* Constructor.
*
* @param discussion_entity $discussion The discussion being summarised
* @param post_entity $firstpost First post in the discussion
* @param author_entity $firstpostauthor Author of the first post in the discussion
* @param author_entity $latestpostauthor Author of the last post in the discussion
*/
public function __construct(
discussion_entity $discussion,
post_entity $firstpost,
author_entity $firstpostauthor,
author_entity $latestpostauthor
) {
$this->discussion = $discussion;
$this->firstpostauthor = $firstpostauthor;
$this->firstpost = $firstpost;
$this->latestpostauthor = $latestpostauthor;
}
/**
* Get the discussion entity.
*
* @return discussion_entity
*/
public function get_discussion() : discussion_entity {
return $this->discussion;
}
/**
* Get the author entity for the first post.
*
* @return author_entity
*/
public function get_first_post_author() : author_entity {
return $this->firstpostauthor;
}
/**
* Get the author entity for the last post.
*
* @return author_entity
*/
public function get_latest_post_author() : author_entity {
return $this->latestpostauthor;
}
/**
* Get the post entity for the first post.
*
* @return post_entity
*/
public function get_first_post() : post_entity {
return $this->firstpost;
}
}
+512
View File
@@ -0,0 +1,512 @@
<?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 class.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\entities;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/rating/lib.php');
use mod_forum\local\entities\discussion as discussion_entity;
use context;
use stdClass;
/**
* Forum class.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class forum {
/** @var context $context The forum module context */
private $context;
/** @var stdClass $coursemodule The forum course module record */
private $coursemodule;
/** @var stdClass $course The forum course record */
private $course;
/** @var int $effectivegroupmode The effective group mode */
private $effectivegroupmode;
/** @var int $id ID */
private $id;
/** @var int $courseid Id of the course this forum is in */
private $courseid;
/** @var string $type The forum type, e.g. single, qanda, etc */
private $type;
/** @var string $name Name of the forum */
private $name;
/** @var string $intro Intro text */
private $intro;
/** @var int $introformat Format of the intro text */
private $introformat;
/** @var int $assessed The forum rating aggregate */
private $assessed;
/** @var int $assesstimestart Timestamp to begin assessment */
private $assesstimestart;
/** @var int $assesstimefinish Timestamp to end assessment */
private $assesstimefinish;
/** @var int $scale The rating scale */
private $scale;
/** @var int $maxbytes Maximum attachment size */
private $maxbytes;
/** @var int $maxattachments Maximum number of attachments */
private $maxattachments;
/** @var bool $forcesubscription Does the forum force users to subscribe? */
private $forcesubscription;
/** @var int $trackingtype Tracking type */
private $trackingtype;
/** @var int $rsstype RSS type */
private $rsstype;
/** @var int $rssarticles RSS articles */
private $rssarticles;
/** @var int $timemodified Timestamp when the forum was last modified */
private $timemodified;
/** @var int $warnafter Warn after */
private $warnafter;
/** @var int $blockafter Block after */
private $blockafter;
/** @var int $blockperiod Block period */
private $blockperiod;
/** @var int $completiondiscussions Completion discussions */
private $completiondiscussions;
/** @var int $completionreplies Completion replies */
private $completionreplies;
/** @var int $completionposts Completion posts */
private $completionposts;
/** @var bool $displaywordcounts Should display word counts in posts */
private $displaywordcounts;
/** @var bool $lockdiscussionafter Timestamp after which discussions should be locked */
private $lockdiscussionafter;
/**
* Constructor
*
* @param context $context The forum module context
* @param stdClass $coursemodule The forum course module record
* @param stdClass $course The forum course record
* @param int $effectivegroupmode The effective group mode
* @param int $id ID
* @param int $courseid Id of the course this forum is in
* @param string $type The forum type, e.g. single, qanda, etc
* @param string $name Name of the forum
* @param string $intro Intro text
* @param int $introformat Format of the intro text
* @param int $assessed The forum rating aggregate
* @param int $assesstimestart Timestamp to begin assessment
* @param int $assesstimefinish Timestamp to end assessment
* @param int $scale The rating scale
* @param int $maxbytes Maximum attachment size
* @param int $maxattachments Maximum number of attachments
* @param bool $forcesubscribe Does the forum force users to subscribe?
* @param int $trackingtype Tracking type
* @param int $rsstype RSS type
* @param int $rssarticles RSS articles
* @param int $timemodified Timestamp when the forum was last modified
* @param int $warnafter Warn after
* @param int $blockafter Block after
* @param int $blockperiod Block period
* @param int $completiondiscussions Completion discussions
* @param int $completionreplies Completion replies
* @param int $completionposts Completion posts
* @param bool $displaywordcount Should display word counts in posts
* @param int $lockdiscussionafter Timestamp after which discussions should be locked
*/
public function __construct(
context $context,
stdClass $coursemodule,
stdClass $course,
int $effectivegroupmode,
int $id,
int $courseid,
string $type,
string $name,
string $intro,
int $introformat,
int $assessed,
int $assesstimestart,
int $assesstimefinish,
int $scale,
int $maxbytes,
int $maxattachments,
bool $forcesubscribe,
int $trackingtype,
int $rsstype,
int $rssarticles,
int $timemodified,
int $warnafter,
int $blockafter,
int $blockperiod,
int $completiondiscussions,
int $completionreplies,
int $completionposts,
bool $displaywordcount,
int $lockdiscussionafter
) {
$this->context = $context;
$this->coursemodule = $coursemodule;
$this->course = $course;
$this->effectivegroupmode = $effectivegroupmode;
$this->id = $id;
$this->courseid = $courseid;
$this->type = $type;
$this->name = $name;
$this->intro = $intro;
$this->introformat = $introformat;
$this->assessed = $assessed;
$this->assesstimestart = $assesstimestart;
$this->assesstimefinish = $assesstimefinish;
$this->scale = $scale;
$this->maxbytes = $maxbytes;
$this->maxattachments = $maxattachments;
$this->forcesubscribe = $forcesubscribe;
$this->trackingtype = $trackingtype;
$this->rsstype = $rsstype;
$this->rssarticles = $rssarticles;
$this->timemodified = $timemodified;
$this->warnafter = $warnafter;
$this->blockafter = $blockafter;
$this->blockperiod = $blockperiod;
$this->completiondiscussions = $completiondiscussions;
$this->completionreplies = $completionreplies;
$this->completionposts = $completionposts;
$this->displaywordcount = $displaywordcount;
$this->lockdiscussionafter = $lockdiscussionafter;
}
/**
* Get the forum module context.
*
* @return context
*/
public function get_context() : context {
return $this->context;
}
/**
* Get the forum course module record
*
* @return stdClass
*/
public function get_course_module_record() : stdClass {
return $this->coursemodule;
}
/**
* Get the effective group mode.
*
* @return int
*/
public function get_effective_group_mode() : int {
return $this->effectivegroupmode;
}
/**
* Check if the forum is set to group mode.
*
* @return bool
*/
public function is_in_group_mode() : bool {
return $this->get_effective_group_mode() !== NOGROUPS;
}
/**
* Get the course record.
*
* @return stdClass
*/
public function get_course_record() : stdClass {
return $this->course;
}
/**
* Get the forum id.
*
* @return int
*/
public function get_id() : int {
return $this->id;
}
/**
* Get the id of the course that the forum belongs to.
*
* @return int
*/
public function get_course_id() : int {
return $this->courseid;
}
/**
* Get the forum type.
*
* @return string
*/
public function get_type() : string {
return $this->type;
}
/**
* Get the forum name.
*
* @return string
*/
public function get_name() : string {
return $this->name;
}
/**
* Get the forum intro text.
*
* @return string
*/
public function get_intro() : string {
return $this->intro;
}
/**
* Get the forum intro text format.
*
* @return int
*/
public function get_intro_format() : int {
return $this->introformat;
}
/**
* Get the rating aggregate.
*
* @return int
*/
public function get_rating_aggregate() : int {
return $this->assessed;
}
/**
* Does the forum have a rating aggregate?
*
* @return bool
*/
public function has_rating_aggregate() : bool {
return $this->get_rating_aggregate() != RATING_AGGREGATE_NONE;
}
/**
* Get the timestamp for when the assessment period begins.
*
* @return int
*/
public function get_assess_time_start() : int {
return $this->assesstimestart;
}
/**
* Get the timestamp for when the assessment period ends.
*
* @return int
*/
public function get_assess_time_finish() : int {
return $this->assesstimefinish;
}
/**
* Get the rating scale.
*
* @return int
*/
public function get_scale() : int {
return $this->scale;
}
/**
* Get the maximum bytes.
*
* @return int
*/
public function get_max_bytes() : int {
return $this->maxbytes;
}
/**
* Get the maximum number of attachments.
*
* @return int
*/
public function get_max_attachments() : int {
return $this->maxattachments;
}
/**
* Does the forum force subscription?
*
* @return bool
*/
public function is_subscription_forced() : bool {
return $this->forcesubscribe;
}
/**
* Get the tracking type.
*
* @return int
*/
public function get_tracking_type() : int {
return $this->trackingtype;
}
/**
* Get the RSS type.
*
* @return int
*/
public function get_rss_type() : int {
return $this->rsstype;
}
/**
* Get the RSS articles.
*
* @return int
*/
public function get_rss_articles() : int {
return $this->rssarticles;
}
/**
* Get the timestamp for when the forum was last modified.
*
* @return int
*/
public function get_time_modified() : int {
return $this->timemodified;
}
/**
* Get warn after.
*
* @return int
*/
public function get_warn_after() : int {
return $this->warnafter;
}
/**
* Get block after.
*
* @return int
*/
public function get_block_after() : int {
return $this->blockafter;
}
/**
* Get the block period.
*
* @return int
*/
public function get_block_period() : int {
return $this->blockperiod;
}
/**
* Does the forum have blocking enabled?
*
* @return bool
*/
public function has_blocking_enabled() : bool {
return !empty($this->get_block_after()) && !empty($this->get_block_period());
}
/**
* Get the completion discussions.
*
* @return int
*/
public function get_completion_discussions() : int {
return $this->completiondiscussions;
}
/**
* Get the completion replies.
*
* @return int
*/
public function get_completion_replies() : int {
return $this->completionreplies;
}
/**
* Get the completion posts.
*
* @return int
*/
public function get_completion_posts() : int {
return $this->completionposts;
}
/**
* Should the word counts be shown in the posts?
*
* @return bool
*/
public function should_display_word_count() : bool {
return $this->displaywordcount;
}
/**
* Get the timestamp after which the discussion should be locked.
*
* @return int
*/
public function get_lock_discussions_after() : int {
return $this->lockdiscussionafter;
}
/**
* Does the forum have a discussion locking timestamp?
*
* @return bool
*/
public function has_lock_discussions_after() : bool {
return !empty($this->get_lock_discussions_after());
}
/**
* Is the discussion locked?
*
* @param discussion_entity $discussion The discussion to check
* @return bool
*/
public function is_discussion_locked(discussion_entity $discussion) : bool {
if (!$this->has_lock_discussions_after()) {
return false;
}
if ($this->get_type() === 'single') {
// It does not make sense to lock a single discussion forum.
return false;
}
return (($discussion->get_time_modified() + $this->get_lock_discussions_after()) < time());
}
}
+284
View File
@@ -0,0 +1,284 @@
<?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 class.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\entities;
defined('MOODLE_INTERNAL') || die();
use stdClass;
/**
* Post class.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class post {
/** @var int $id ID */
private $id;
/** @var int $discussionid The id of the discussion this post belongs to */
private $discussionid;
/** @var int $parentid The id of the post that this post is replying to. Zero if it isn't a reply. */
private $parentid;
/** @var int $authorid The id of user who authored the post */
private $authorid;
/** @var int $timecreated Timestamp for when the post was created */
private $timecreated;
/** @var int $timemodified Timestamp for when the post last modified */
private $timemodified;
/** @var bool $mailed If the post has been mailed */
private $mailed;
/** @var string $subject Post subject */
private $subject;
/** @var string $message Post message */
private $message;
/** @var int $messageformat Format of the post message */
private $messageformat;
/** @var bool $messagetrust Is this a trusted message, i.e. created by a trusted user. */
private $messagetrust;
/** @var bool $hasattachments Does the post have attachments */
private $hasattachments;
/** @var int $totalscore Total score */
private $totalscore;
/** @var bool $mailnow Should this post be mailed immediately */
private $mailnow;
/** @var bool $deleted Is the post deleted */
private $deleted;
/**
* Constructor.
*
* @param int $id ID
* @param int $discussionid The id of the discussion this post belongs to
* @param int $parentid The id of the post that this post is replying to. Zero if it isn't a reply.
* @param int $authorid The id of user who authored the post
* @param int $timecreated Timestamp for when the post was created
* @param int $timemodified Timestamp for when the post last modified
* @param bool $mailed If the post has been mailed
* @param string $subject Post subject
* @param string $message Post message
* @param int $messageformat Format of the post message
* @param bool $messagetrust Is this a trusted message, i.e. created by a trusted user.
* @param bool $hasattachments Does the post have attachments
* @param int $totalscore Total score
* @param bool $mailnow Should this post be mailed immediately
* @param bool $deleted Is the post deleted
*/
public function __construct(
int $id,
int $discussionid,
int $parentid,
int $authorid,
int $timecreated,
int $timemodified,
bool $mailed,
string $subject,
string $message,
int $messageformat,
bool $messagetrust,
bool $hasattachments,
int $totalscore,
bool $mailnow,
bool $deleted
) {
$this->id = $id;
$this->discussionid = $discussionid;
$this->parentid = $parentid;
$this->authorid = $authorid;
$this->timecreated = $timecreated;
$this->timemodified = $timemodified;
$this->mailed = $mailed;
$this->subject = $subject;
$this->message = $message;
$this->messageformat = $messageformat;
$this->messagetrust = $messagetrust;
$this->hasattachments = $hasattachments;
$this->totalscore = $totalscore;
$this->mailnow = $mailnow;
$this->deleted = $deleted;
}
/**
* Get the post id.
*
* @return int
*/
public function get_id() : int {
return $this->id;
}
/**
* Get the discussion id.
*
* @return int
*/
public function get_discussion_id() : int {
return $this->discussionid;
}
/**
* Get the id of the parent post. Returns zero if this post is not a reply.
*
* @return int
*/
public function get_parent_id() : int {
return $this->parentid;
}
/**
* Does this post have a parent? I.e. is it a reply?
*
* @return bool
*/
public function has_parent() : bool {
return $this->get_parent_id() > 0;
}
/**
* Get the id of the user that authored the post.
*
* @return int
*/
public function get_author_id() : int {
return $this->authorid;
}
/**
* Get the timestamp for when this post was created.
*
* @return int
*/
public function get_time_created() : int {
return $this->timecreated;
}
/**
* Get the timestamp for when this post was last modified.
*
* @return int
*/
public function get_time_modified() : int {
return $this->timemodified;
}
/**
* Has this post been mailed?
*
* @return bool
*/
public function has_been_mailed() : bool {
return $this->mailed;
}
/**
* Get the post subject.
*
* @return string
*/
public function get_subject() : string {
return $this->subject;
}
/**
* Get the post message.
*
* @return string
*/
public function get_message() : string {
return $this->message;
}
/**
* Get the post message format.
*
* @return int
*/
public function get_message_format() : int {
return $this->messageformat;
}
/**
* Is this a trusted message? I.e. was it authored by a trusted user?
*
* @return bool
*/
public function is_message_trusted() : bool {
return $this->messagetrust;
}
/**
* Does this post have attachments?
*
* @return bool
*/
public function has_attachments() : bool {
return $this->hasattachments;
}
/**
* Get the total score.
*
* @return int
*/
public function get_total_score() : int {
return $this->totalscore;
}
/**
* Should this post be mailed now?
*
* @return bool
*/
public function should_mail_now() : bool {
return $this->mailnow;
}
/**
* Is this post deleted?
*
* @return bool
*/
public function is_deleted() : bool {
return $this->deleted;
}
/**
* Get the post's age in seconds.
*
* @return int
*/
public function get_age() : int {
return time() - $this->get_time_created();
}
/**
* Check if the given user authored this post.
*
* @param stdClass $user The user to check.
* @return bool
*/
public function is_owned_by_user(stdClass $user) : bool {
return $this->get_author_id() == $user->id;
}
}
@@ -0,0 +1,86 @@
<?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 read receipt collection class.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\entities;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\post as post_entity;
use stdClass;
/**
* Post read receipt collection class.
*
* Contains the list of read receipts for posts.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class post_read_receipt_collection {
/** @var stdClass[] $receiptsbypostid Receipt records indexed by post id */
private $receiptsbypostid = [];
/**
* Constructor.
*
* @param array $records The list of post read receipt records.
*/
public function __construct(array $records) {
foreach ($records as $record) {
$postid = $record->postid;
if (isset($this->receiptsbypostid[$postid])) {
$this->receiptsbypostid[$postid][] = $record;
} else {
$this->receiptsbypostid[$postid] = [$record];
}
}
}
/**
* Check whether a user has read a post.
*
* @param stdClass $user The user to check
* @param post_entity $post The post to check
* @return bool
*/
public function has_user_read_post(stdClass $user, post_entity $post) : bool {
global $CFG;
$isoldpost = ($post->get_time_modified() < (time() - ($CFG->forum_oldpostdays * 24 * 3600)));
if ($isoldpost) {
return true;
}
$receipts = isset($this->receiptsbypostid[$post->get_id()]) ? $this->receiptsbypostid[$post->get_id()] : [];
foreach ($receipts as $receipt) {
if ($receipt->userid == $user->id) {
return true;
}
}
return false;
}
}
+152
View File
@@ -0,0 +1,152 @@
<?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/>.
/**
* Class to sort items.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\entities;
defined('MOODLE_INTERNAL') || die();
/**
* Class to sort lists of items.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sorter {
/** @var callable $getid Function used to get the id from an item */
private $getid;
/** @var callable $getparentid Function used to get the parent id from an item */
private $getparentid;
/**
* Constructor.
*
* Allows the calling code to provide 2 functions to get the id and parent id from
* the list of items it is intended to process.
*
* This allows this class to be composed in numerous different ways to support various
* types of items while keeping the underlying sorting algorithm consistent.
*
* @param callable $getid Function used to get the id from an item
* @param callable $getparentid Function used to get the parent id from an item
*/
public function __construct(callable $getid, callable $getparentid) {
$this->getid = $getid;
$this->getparentid = $getparentid;
}
/**
* Sort a list of items into a parent/child data structure. The resulting data structure
* is a recursive array of arrays where the first element is the parent and the second is
* an array of it's children.
*
* For example
* If we have an array of items A, B, C, and D where D is a child of C, B and C are children
* of A.
*
* This function would sort them into the following:
* [
* [
* A,
* [
* [
* B,
* []
* ],
* [
* C,
* [
* [
* D,
* []
* ]
* ]
* ]
* ]
* ]
* ]
*
* @param array $items The list of items to sort.
* @return array
*/
public function sort_into_children(array $items) : array {
$ids = array_reduce($items, function($carry, $item) {
$carry[($this->getid)($item)] = true;
return $carry;
}, []);
// Split out the items into "parents" and "replies" (children). These are unsorted
// at this point.
[$parents, $replies] = array_reduce($items, function($carry, $item) use ($ids) {
$parentid = ($this->getparentid)($item);
if (!empty($ids[$parentid])) {
// This is a child to another item in the list so add it to the children list.
$carry[1][] = $item;
} else {
// This isn't a child to anything in our list so it's a parent.
$carry[0][] = $item;
}
return $carry;
}, [[], []]);
if (empty($replies)) {
return array_map(function($parent) {
return [$parent, []];
}, $parents);
}
// Recurse to sort the replies into the correct nesting.
$sortedreplies = $this->sort_into_children($replies);
// Sort the parents and sorted replies into their matching pairs.
return array_map(function($parent) use ($sortedreplies) {
$parentid = ($this->getid)($parent);
return [
$parent,
array_values(array_filter($sortedreplies, function($replydata) use ($parentid) {
return ($this->getparentid)($replydata[0]) == $parentid;
}))
];
}, $parents);
}
/**
* Take the data structure returned from "sort_into_children" and flatten it back
* into an array. It does a depth first flatten which maintains the reply ordering.
*
* @param array $items Items in the data structure returned by "sort_into_children"
* @return array A flat array.
*/
public function flatten_children(array $items) : array {
$result = [];
foreach ($items as [$item, $children]) {
$result[] = $item;
$result = array_merge($result, $this->flatten_children($children));
}
return $result;
}
}
@@ -0,0 +1,174 @@
<?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/>.
/**
* Author exporter.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @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\author as author_entity;
use core\external\exporter;
use renderer_base;
require_once($CFG->dirroot . '/mod/forum/lib.php');
/**
* Author exporter.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class author extends exporter {
/** @var author_entity $author Author entity */
private $author;
/** @var array $authorgroups List of groups that the author belongs to */
private $authorgroups;
/** @var bool $canview Should the author be anonymised? */
private $canview;
/**
* Constructor.
*
* @param author_entity $author The author entity to export
* @param stdClass[] $authorgroups The list of groups that the author belongs to
* @param bool $canview Can the requesting user view this author or should it be anonymised?
* @param array $related The related data for the export.
*/
public function __construct(author_entity $author, array $authorgroups = [], bool $canview = true, array $related = []) {
$this->author = $author;
$this->authorgroups = $authorgroups;
$this->canview = $canview;
return parent::__construct([], $related);
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'id' => [
'type' => PARAM_INT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'fullname' => [
'type' => PARAM_TEXT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'groups' => [
'multiple' => true,
'optional' => true,
'type' => [
'id' => ['type' => PARAM_INT],
'urls' => [
'type' => [
'image' => [
'type' => PARAM_URL,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
]
]
]
]
],
'urls' => [
'type' => [
'profile' => [
'type' => PARAM_URL,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'profileimage' => [
'type' => PARAM_URL,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
]
]
];
}
/**
* 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) {
$author = $this->author;
$urlfactory = $this->related['urlfactory'];
if ($this->canview) {
$groups = array_map(function($group) {
$imageurl = get_group_picture_url($group, $group->courseid);
return [
'id' => $group->id,
'urls' => [
'image' => $imageurl ? $imageurl->out(false) : null
]
];
}, $this->authorgroups);
return [
'id' => $author->get_id(),
'fullname' => $author->get_full_name(),
'groups' => $groups,
'urls' => [
'profile' => ($urlfactory->get_author_profile_url($author))->out(false),
'profileimage' => ($urlfactory->get_author_profile_image_url($author))->out(false)
]
];
} else {
// The author should be anonymised.
return [
'id' => null,
'fullname' => get_string('forumauthorhidden', 'mod_forum'),
'groups' => [],
'urls' => [
'profile' => null,
'profileimage' => null
]
];
}
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'urlfactory' => 'mod_forum\local\factories\url',
'context' => 'context'
];
}
}
@@ -0,0 +1,229 @@
<?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/>.
/**
* Discussion exporter class.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @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\discussion as discussion_entity;
use mod_forum\local\exporters\post as post_exporter;
use mod_forum\local\factories\exporter as exporter_factory;
use core\external\exporter;
use renderer_base;
/**
* Discussion exporter class.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class discussion extends exporter {
/** @var discussion_entity $discussion Discussion to export */
private $discussion;
/**
* Constructor.
*
* @param discussion_entity $discussion Discussion to export
* @param array $related The related export data
*/
public function __construct(discussion_entity $discussion, array $related = []) {
$this->discussion = $discussion;
return parent::__construct([], $related);
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'id' => ['type' => PARAM_INT],
'forumid' => ['type' => PARAM_INT],
'pinned' => ['type' => PARAM_BOOL],
'name' => ['type' => PARAM_TEXT],
'group' => [
'optional' => true,
'type' => [
'name' => ['type' => PARAM_TEXT],
'urls' => [
'type' => [
'picture' => [
'optional' => true,
'type' => PARAM_URL,
],
'userlist' => [
'optional' => true,
'type' => PARAM_URL,
],
],
],
],
],
'times' => [
'type' => [
'modified' => ['type' => PARAM_INT],
'start' => ['type' => PARAM_INT],
'end' => ['type' => PARAM_INT],
],
],
'userstate' => [
'type' => [
'subscribed' => ['type' => PARAM_BOOL],
],
],
'capabilities' => [
'type' => [
'subscribe' => ['type' => PARAM_BOOL],
'move' => ['type' => PARAM_BOOL],
'pin' => ['type' => PARAM_BOOL],
'post' => ['type' => PARAM_BOOL]
]
],
'urls' => [
'type' => [
'view' => ['type' => PARAM_URL],
'viewlatest' => [
'optional' => true,
'type' => PARAM_URL
],
'viewfirstunread' => [
'optional' => true,
'type' => PARAM_URL,
],
'markasread' => ['type' => PARAM_URL],
],
]
];
}
/**
* 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) {
$capabilitymanager = $this->related['capabilitymanager'];
$urlfactory = $this->related['urlfactory'];
$forum = $this->related['forum'];
$forumrecord = $this->get_forum_record();
$user = $this->related['user'];
$discussion = $this->discussion;
$groupdata = null;
if ($discussion->has_group() && $group = $this->related['groupsbyid'][$discussion->get_group_id()]) {
$groupdata = [
'name' => $group->name,
'urls' => [],
];
$canviewparticipants = $capabilitymanager->can_view_participants($user, $discussion);
if (!$group->hidepicture) {
$url = get_group_picture_url($group, $forum->get_course_id());
if (!empty($url)) {
$groupdata['urls']['picture'] = $url;
}
}
if ($canviewparticipants) {
$groupdata['urls']['userlist'] = (new \moodle_url('/user/index.php', [
'id' => $forum->get_course_id(),
'group' => $group->id,
]));
}
}
$viewfirstunreadurl = $urlfactory->get_discussion_view_first_unread_post_url_from_discussion($discussion);
$data = [
'id' => $discussion->get_id(),
'forumid' => $forum->get_id(),
'pinned' => $discussion->is_pinned(),
'name' => format_string($discussion->get_name(), true, [
'context' => $this->related['context']
]),
'times' => [
'modified' => $discussion->get_time_modified(),
'start' => $discussion->get_time_start(),
'end' => $discussion->get_time_end(),
],
'userstate' => [
'subscribed' => \mod_forum\subscriptions::is_subscribed($user->id, $forumrecord, $discussion->get_id()),
],
'capabilities' => [
'subscribe' => $capabilitymanager->can_subscribe_to_discussion($user, $discussion),
'move' => $capabilitymanager->can_move_discussion($user, $discussion),
'pin' => $capabilitymanager->can_pin_discussion($user, $discussion),
'post' => $capabilitymanager->can_post_in_discussion($user, $discussion)
],
'urls' => [
'view' => $urlfactory->get_discussion_view_url_from_discussion($discussion)->out(false),
'viewfirstunread' => $viewfirstunreadurl->out(false),
'markasread' => $urlfactory->get_mark_discussion_as_read_url_from_discussion($forum, $discussion)->out(false),
]
];
if (!empty($this->related['latestpostid'])) {
$data['urls']['viewlatest'] = $urlfactory->get_discussion_view_latest_post_url_from_discussion(
$discussion,
$this->related['latestpostid']
)->out(false);
}
if ($groupdata) {
$data['group'] = $groupdata;
}
return $data;
}
/**
* Get the legacy forum record from the forum entity.
*
* @return stdClass
*/
private function get_forum_record() {
$forumdbdatamapper = $this->related['legacydatamapperfactory']->get_forum_data_mapper();
return $forumdbdatamapper->to_legacy_object($this->related['forum']);
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'legacydatamapperfactory' => 'mod_forum\local\factories\legacy_data_mapper',
'context' => 'context',
'forum' => 'mod_forum\local\entities\forum',
'capabilitymanager' => 'mod_forum\local\managers\capability',
'urlfactory' => 'mod_forum\local\factories\url',
'user' => 'stdClass',
'groupsbyid' => 'stdClass[]',
'latestpostid' => 'int?'
];
}
}
@@ -0,0 +1,157 @@
<?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/>.
/**
* Discussion summaries exporter.
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <[email protected]>
* @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\discussion as discussion_entity;
use mod_forum\local\exporters\post as post_exporter;
use core\external\exporter;
use renderer_base;
/**
* Discussion summaries exporter.
*
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class discussion_summaries extends exporter {
/** @var discussion_summary_entity[] The list of discussion summaries to export */
private $discussions;
/** @var stdClass[] The group information for each author */
private $groupsbyid;
/** @var stdClass[] The group information for each author */
private $groupsbyauthorid;
/** @var int[] Discussion reply counts indexed by dicussion id */
private $discussionreplycount;
/** @var int[] Discussion unread counts indexed by dicussion id */
private $discussionunreadcount;
/** @var array The latest post in each discussion */
private $latestpostids;
/**
* Constructor.
*
* @param discussion_summary_entity[] $discussion The list of discussion summaries to export
* @param stdClass[] $groupsbyid The group information for each author
* @param stdClass[] $groupsbyauthorid The group information for each author
* @param int[] $discussionreplycount Discussion reply counts indexed by dicussion id
* @param int[] $discussionunreadcount Discussion unread counts indexed by dicussion id
* @param int[] $latestpostids List of latest post ids indexed by discussion id
* @param array $related The related
*/
public function __construct(
array $discussions,
array $groupsbyid,
array $groupsbyauthorid,
array $discussionreplycount,
array $discussionunreadcount,
array $latestpostids,
array $related = []
) {
$this->discussions = $discussions;
$this->groupsbyid = $groupsbyid;
$this->groupsbyauthorid = $groupsbyauthorid;
$this->discussionreplycount = $discussionreplycount;
$this->discussionunreadcount = $discussionunreadcount;
$this->latestpostids = $latestpostids;
return parent::__construct([], $related);
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'summaries' => [
'type' => discussion_summary::read_properties_definition(),
'multiple' => true
],
'state' => [
'type' => [
'hasdiscussions' => ['type' => PARAM_BOOL],
],
],
];
}
/**
* 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) {
$exporteddiscussions = [];
$related = $this->related;
foreach ($this->discussions as $discussion) {
$discussionid = $discussion->get_discussion()->get_id();
$replycount = isset($this->discussionreplycount[$discussionid]) ? $this->discussionreplycount[$discussionid] : 0;
$unreadcount = isset($this->discussionunreadcount[$discussionid]) ? $this->discussionunreadcount[$discussionid] : 0;
$latestpostid = isset($this->latestpostids[$discussionid]) ? $this->latestpostids[$discussionid] : 0;
$exporter = new discussion_summary(
$discussion,
$this->groupsbyid,
$this->groupsbyauthorid,
$replycount,
$unreadcount,
$latestpostid,
$related
);
$exporteddiscussions[] = $exporter->export($output);
}
return [
'summaries' => $exporteddiscussions,
'state' => [
'hasdiscussions' => !empty($exporteddiscussions),
],
];
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'legacydatamapperfactory' => 'mod_forum\local\factories\legacy_data_mapper',
'context' => 'context',
'forum' => 'mod_forum\local\entities\forum',
'capabilitymanager' => 'mod_forum\local\managers\capability',
'urlfactory' => 'mod_forum\local\factories\url',
'user' => 'stdClass',
];
}
}
@@ -0,0 +1,180 @@
<?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/>.
/**
* Discussion summary exporter class.
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <[email protected]>
* @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\discussion_summary as discussion_summary_entity;
use core\external\exporter;
use renderer_base;
/**
* Discussion summary exporter class.
*
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class discussion_summary extends exporter {
/** @var discussion_summary_entity The discussion summary information */
private $summary;
/** @var stdClass[] The group information for each author */
private $groupsbyid;
/** @var stdClass[] The group information for each author */
private $groupsbyauthorid;
/** @var int The number of replies to the discussion */
private $replycount;
/** @var int number of unread posts if the user is tracking these */
private $unreadcount;
/** @var int The latest post id in the discussion */
private $latestpostid;
/**
* Constructor.
*
* @param discussion_summary_entity $summary The discussion summary information
* @param stdClass[] $groupsbyid The group information for each author
* @param stdClass[] $groupsbyauthorid The group information for each author
* @param int $replycount The number of replies to the discussion
* @param int $unreadcount number of unread posts if the user is tracking these
* @param int $latestpostid The latest post id in the discussion
* @param array $related The related objects
*/
public function __construct(
discussion_summary_entity $summary,
array $groupsbyid,
array $groupsbyauthorid,
int $replycount,
int $unreadcount,
int $latestpostid,
array $related = []
) {
$this->summary = $summary;
$this->groupsbyid = $groupsbyid;
$this->groupsbyauthorid = $groupsbyauthorid;
$this->replycount = $replycount;
$this->unreadcount = $unreadcount;
$this->latestpostid = $latestpostid;
return parent::__construct([], $related);
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'id' => ['type' => PARAM_INT],
'discussion' => [
'type' => discussion::read_properties_definition(),
],
'replies' => ['type' => PARAM_INT],
'unread' => ['type' => PARAM_INT],
'firstpostauthor' => [
'type' => author::read_properties_definition(),
],
'latestpostauthor' => [
'type' => author::read_properties_definition(),
],
'latestpostid' => ['type' => PARAM_INT],
];
}
/**
* 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) {
$capabilitymanager = $this->related['capabilitymanager'];
$forum = $this->related['forum'];
$user = $this->related['user'];
$discussion = $this->summary->get_discussion();
$related = (array) (object) $this->related;
$related['latestpostid'] = $this->latestpostid;
$related['groupsbyid'] = $this->groupsbyid;
$discussionexporter = new discussion($discussion, $related);
$related = [
'urlfactory' => $this->related['urlfactory'],
'context' => $this->related['forum']->get_context(),
];
$firstpostauthor = new author(
$this->summary->get_first_post_author(),
$this->groupsbyauthorid[$this->summary->get_first_post_author()->get_id()],
$capabilitymanager->can_view_post(
$user,
$discussion,
$this->summary->get_first_post()
),
$related
);
$latestpostauthor = new author(
$this->summary->get_latest_post_author(),
[],
$capabilitymanager->can_view_post(
$user,
$discussion,
$this->summary->get_first_post()
),
$related
);
return [
'id' => $discussion->get_id(),
'discussion' => $discussionexporter->export($output),
'replies' => $this->replycount,
'unread' => $this->unreadcount,
'firstpostauthor' => $firstpostauthor->export($output),
'latestpostauthor' => $latestpostauthor->export($output),
'latestpostid' => $this->latestpostid,
];
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'legacydatamapperfactory' => 'mod_forum\local\factories\legacy_data_mapper',
'context' => 'context',
'forum' => 'mod_forum\local\entities\forum',
'capabilitymanager' => 'mod_forum\local\managers\capability',
'urlfactory' => 'mod_forum\local\factories\url',
'user' => 'stdClass',
];
}
}
+146
View File
@@ -0,0 +1,146 @@
<?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 Exporter.
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <[email protected]>
* @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\forum as forum_entity;
use mod_forum\local\exporters\post as post_exporter;
use core\external\exporter;
use renderer_base;
use stdClass;
/**
* Forum class.
*
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class forum extends exporter {
/** @var forum_entity The entity relating to the forum being displayed */
private $forum;
/**
* Constructor for the forum exporter.
*
* @param forum_entity $forum The forum being displayed
* @param array $related The related objects
*/
public function __construct(forum_entity $forum, $related = []) {
$this->forum = $forum;
return parent::__construct([], $related);
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'id' => ['type' => PARAM_INT],
'state' => [
'type' => [
'groupmode' => ['type' => PARAM_INT],
],
],
'userstate' => [
'type' => [
'tracked' => ['type' => PARAM_INT],
],
],
'capabilities' => [
'type' => [
'viewdiscussions' => ['type' => PARAM_BOOL],
'create' => ['type' => PARAM_BOOL],
'subscribe' => ['type' => PARAM_BOOL],
]
],
'urls' => [
'type' => [
'create' => ['type' => PARAM_URL],
'markasread' => ['type' => PARAM_URL],
],
],
];
}
/**
* 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) {
$capabilitymanager = $this->related['capabilitymanager'];
$urlfactory = $this->related['urlfactory'];
$user = $this->related['user'];
$currentgroup = $this->related['currentgroup'];
return [
'id' => $this->forum->get_id(),
'state' => [
'groupmode' => $this->forum->get_effective_group_mode(),
],
'userstate' => [
'tracked' => forum_tp_is_tracked($this->get_forum_record(), $this->related['user']),
],
'capabilities' => [
'viewdiscussions' => $capabilitymanager->can_view_discussions($user),
'create' => $capabilitymanager->can_create_discussions($user, $currentgroup),
'subscribe' => $capabilitymanager->can_subscribe_to_forum($user),
],
'urls' => [
'create' => $urlfactory->get_discussion_create_url($this->forum)->out(false),
'markasread' => $urlfactory->get_mark_all_discussions_as_read_url($this->forum)->out(false),
],
];
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'legacydatamapperfactory' => 'mod_forum\local\factories\legacy_data_mapper',
'capabilitymanager' => 'mod_forum\local\managers\capability',
'urlfactory' => 'mod_forum\local\factories\url',
'user' => 'stdClass',
'currentgroup' => 'int?',
];
}
/**
* Get the legacy forum record for this forum.
*
* @return stdClass
*/
private function get_forum_record() : stdClass {
$forumdbdatamapper = $this->related['legacydatamapperfactory']->get_forum_data_mapper();
return $forumdbdatamapper->to_legacy_object($this->forum);
}
}
+557
View File
@@ -0,0 +1,557 @@
<?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 <[email protected]>
* @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 <[email protected]>
* @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,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
]
]
];
$attachmentdefinition['html'] = [
'type' => [
'plagiarism' => [
'type' => PARAM_RAW,
'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],
'haswordcount' => ['type' => PARAM_BOOL],
'wordcount' => [
'type' => PARAM_INT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'capabilities' => [
'type' => [
'view' => ['type' => PARAM_BOOL],
'edit' => ['type' => PARAM_BOOL],
'delete' => ['type' => PARAM_BOOL],
'split' => ['type' => PARAM_BOOL],
'reply' => ['type' => PARAM_BOOL],
'export' => ['type' => PARAM_BOOL],
'controlreadstatus' => ['type' => PARAM_BOOL]
]
],
'urls' => [
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED,
'type' => [
'view' => [
'type' => PARAM_URL,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'viewisolated' => [
'type' => PARAM_URL,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'viewparent' => [
'type' => PARAM_URL,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'edit' => [
'type' => PARAM_URL,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'delete' => [
'type' => PARAM_URL,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'split' => [
'type' => PARAM_URL,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'reply' => [
'type' => PARAM_URL,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'export' => [
'type' => PARAM_URL,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'markasread' => [
'type' => PARAM_URL,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'markasunread' => [
'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],
'tagid' => ['type' => PARAM_INT],
'isstandard' => ['type' => PARAM_BOOL],
'displayname' => ['type' => PARAM_TEXT],
'flag' => ['type' => PARAM_BOOL],
'urls' => [
'type' => [
'view' => ['type' => PARAM_URL]
]
]
]
],
'html' => [
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED,
'type' => [
'rating' => [
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED,
'type' => PARAM_RAW
],
'taglist' => [
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED,
'type' => PARAM_RAW
],
'authorsubheading' => [
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED,
'type' => PARAM_RAW
],
]
]
];
}
/**
* 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) {
global $CFG;
$post = $this->post;
$authorgroups = $this->related['authorgroups'];
$forum = $this->related['forum'];
$discussion = $this->related['discussion'];
$author = $this->related['author'];
$user = $this->related['user'];
$context = $this->related['context'];
$readreceiptcollection = $this->related['readreceiptcollection'];
$rating = $this->related['rating'];
$tags = $this->related['tags'];
$attachments = $this->related['attachments'];
$includehtml = $this->related['includehtml'];
$forumrecord = $this->get_forum_record();
$discussionrecord = $this->get_discussion_record();
$postrecord = $this->get_post_record();
$isdeleted = $post->is_deleted();
$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);
$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;
$authorexporter = new author_exporter($author, $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,
'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
],
'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,
],
'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 [
'legacydatamapperfactory' => 'mod_forum\local\factories\legacy_data_mapper',
'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',
'user' => 'stdClass',
'context' => 'context',
'authorgroups' => 'stdClass[]',
'attachments' => '\stored_file[]?',
'tags' => '\core_tag_tag[]?',
'rating' => 'rating?',
'includehtml' => 'bool'
];
}
/**
* Get the message text from a post.
*
* @param post_entity $post The post
* @return string
*/
private function get_message(post_entity $post) : string {
$context = $this->related['context'];
$message = file_rewrite_pluginfile_urls(
$post->get_message(),
'pluginfile.php',
$context->id,
'mod_forum',
'post',
$post->get_id()
);
if (!empty($CFG->enableplagiarism)) {
require_once($CFG->libdir . '/plagiarismlib.php');
$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()
]);
}
$message = format_text(
$message,
$post->get_message_format(),
(object) [
'para' => false,
'trusted' => $post->is_message_trusted(),
'context' => $context
]
);
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
) {
$contextid = $attachment->get_contextid();
$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
*/
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]);
}
/**
* Get the legacy forum record.
*
* @return stdClass
*/
private function get_forum_record() : stdClass {
$forumdbdatamapper = $this->related['legacydatamapperfactory']->get_forum_data_mapper();
return $forumdbdatamapper->to_legacy_object($this->related['forum']);
}
/**
* Get the legacy discussion record.
*
* @return stdClass
*/
private function get_discussion_record() : stdClass {
$discussiondbdatamapper = $this->related['legacydatamapperfactory']->get_discussion_data_mapper();
return $discussiondbdatamapper->to_legacy_object($this->related['discussion']);
}
/**
* Get the legacy post record.
*
* @return stdClass
*/
private function get_post_record() : stdClass {
$postdbdatamapper = $this->related['legacydatamapperfactory']->get_post_data_mapper();
return $postdbdatamapper->to_legacy_object($this->post);
}
}
+163
View File
@@ -0,0 +1,163 @@
<?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/>.
/**
* Posts exporter class.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @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\exporters\post as post_exporter;
use core\external\exporter;
use renderer_base;
require_once($CFG->dirroot . '/mod/forum/lib.php');
/**
* Posts exporter class.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class posts extends exporter {
/** @var post_entity[] $posts List of posts to export */
private $posts;
/** @var author_entity[] $authorsbyid List of authors for the posts indexed by author id */
private $authorsbyid;
/** @var array $attachmentsbypostid List of attachments indexed by post id */
private $attachmentsbypostid;
/** @var array $groupsbyauthorid List of author's groups indexed by author id */
private $groupsbyauthorid;
/** @var array $tagsbypostid List of tags indexed by post id */
private $tagsbypostid;
/** @var array $ratingbypostid List of ratings indexed by post id */
private $ratingbypostid;
/**
* Constructor.
*
* @param post_entity[] $posts List of posts to export
* @param author_entity[] $authorsbyid List of authors for the posts indexed by author id
* @param array $attachmentsbypostid List of attachments indexed by post id
* @param array $groupsbyauthorid List of author's groups indexed by author id
* @param array $tagsbypostid List of tags indexed by post id
* @param array $ratingbypostid List of ratings indexed by post id
* @param array $related The related objects for exporting
*/
public function __construct(
array $posts,
array $authorsbyid = [],
array $attachmentsbypostid = [],
array $groupsbyauthorid = [],
array $tagsbypostid = [],
array $ratingbypostid = [],
array $related = []
) {
$this->posts = $posts;
$this->authorsbyid = $authorsbyid;
$this->attachmentsbypostid = $attachmentsbypostid;
$this->groupsbyauthorid = $groupsbyauthorid;
$this->tagsbypostid = $tagsbypostid;
$this->ratingbypostid = $ratingbypostid;
return parent::__construct([], $related);
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'posts' => [
'type' => post_exporter::read_properties_definition(),
'multiple' => true
]
];
}
/**
* 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) {
$related = $this->related;
$authorsbyid = $this->authorsbyid;
$attachmentsbypostid = $this->attachmentsbypostid;
$groupsbyauthorid = $this->groupsbyauthorid;
$tagsbypostid = $this->tagsbypostid;
$ratingbypostid = $this->ratingbypostid;
$exportedposts = array_map(
function($post) use (
$related,
$authorsbyid,
$attachmentsbypostid,
$groupsbyauthorid,
$tagsbypostid,
$ratingbypostid,
$output
) {
$authorid = $post->get_author_id();
$postid = $post->get_id();
$author = isset($authorsbyid[$authorid]) ? $authorsbyid[$authorid] : [];
$attachments = isset($attachmentsbypostid[$postid]) ? $attachmentsbypostid[$postid] : [];
$authorgroups = isset($groupsbyauthorid[$authorid]) ? $groupsbyauthorid[$authorid] : [];
$tags = isset($tagsbypostid[$postid]) ? $tagsbypostid[$postid] : [];
$rating = isset($ratingbypostid[$postid]) ? $ratingbypostid[$postid] : null;
$exporter = new post_exporter($post, array_merge($related, [
'author' => $author,
'attachments' => $attachments,
'authorgroups' => $authorgroups,
'tags' => $tags,
'rating' => $rating
]));
return $exporter->export($output);
},
$this->posts
);
return [
'posts' => $exportedposts
];
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'legacydatamapperfactory' => 'mod_forum\local\factories\legacy_data_mapper',
'capabilitymanager' => 'mod_forum\local\managers\capability',
'urlfactory' => 'mod_forum\local\factories\url',
'forum' => 'mod_forum\local\entities\forum',
'discussion' => 'mod_forum\local\entities\discussion',
'readreceiptcollection' => 'mod_forum\local\entities\post_read_receipt_collection?',
'user' => 'stdClass',
'context' => 'context',
'includehtml' => 'bool'
];
}
}
@@ -0,0 +1,95 @@
<?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/>.
/**
* Builder factory.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\factories;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\builders\exported_posts as exported_posts_builder;
use mod_forum\local\factories\vault as vault_factory;
use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
use mod_forum\local\factories\exporter as exporter_factory;
use mod_forum\local\factories\manager as manager_factory;
use renderer_base;
/**
* Builder factory to construct any builders for forum.
*
* See:
* https://designpatternsphp.readthedocs.io/en/latest/Creational/SimpleFactory/README.html
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class builder {
/** @var legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory */
private $legacydatamapperfactory;
/** @var exporter_factory $exporterfactory Exporter factory */
private $exporterfactory;
/** @var vault_factory $vaultfactory Vault factory */
private $vaultfactory;
/** @var manager_factory $managerfactory Manager factory */
private $managerfactory;
/** @var renderer_base $rendererbase Renderer base */
private $rendererbase;
/**
* Constructor.
*
* @param legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory
* @param exporter_factory $exporterfactory Exporter factory
* @param vault_factory $vaultfactory Vault factory
* @param manager_factory $managerfactory Manager factory
* @param renderer_base $rendererbase Renderer base
*/
public function __construct(
legacy_data_mapper_factory $legacydatamapperfactory,
exporter_factory $exporterfactory,
vault_factory $vaultfactory,
manager_factory $managerfactory,
renderer_base $rendererbase
) {
$this->legacydatamapperfactory = $legacydatamapperfactory;
$this->exporterfactory = $exporterfactory;
$this->vaultfactory = $vaultfactory;
$this->managerfactory = $managerfactory;
$this->rendererbase = $rendererbase;
}
/**
* Get an instance of the exported posts builder.
*
* @return exported_posts_builder
*/
public function get_exported_posts_builder() : exported_posts_builder {
return new exported_posts_builder(
$this->rendererbase,
$this->legacydatamapperfactory,
$this->exporterfactory,
$this->vaultfactory,
$this->managerfactory->get_rating_manager()
);
}
}
@@ -0,0 +1,249 @@
<?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/>.
/**
* Entity factory.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\factories;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\author as author_entity;
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\discussion_summary as discussion_summary_entity;
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\entities\post as post_entity;
use mod_forum\local\entities\post_read_receipt_collection as post_read_receipt_collection_entity;
use mod_forum\local\entities\sorter as sorter_entity;
use stdClass;
use context;
use cm_info;
use user_picture;
use moodle_url;
/**
* Entity factory to create the forum entities.
*
* See:
* https://designpatternsphp.readthedocs.io/en/latest/Creational/SimpleFactory/README.html
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class entity {
/**
* Create a forum entity from a stdClass (legacy forum object).
*
* @param stdClass $record The forum record
* @param context $context The forum module context
* @param stdClass $coursemodule Course module record for the forum
* @param stdClass $course Course the forum belongs to
* @return forum_entity
*/
public function get_forum_from_stdclass(
stdClass $record,
context $context,
stdClass $coursemodule,
stdClass $course
) : forum_entity {
// Note: cm_info::create loads a cm_info in the context of the current user which
// creates hidden dependency on the logged in user (very bad) however it's the best
// option to load some data we need which doesn't require the logged in user.
// Only use properties which do not require the logged in user.
$cm = \cm_info::create($coursemodule);
return new forum_entity(
$context,
$coursemodule,
$course,
// This property is a general module property that isn't affected by the logged in user.
$cm->effectivegroupmode,
$record->id,
$record->course,
$record->type,
$record->name,
$record->intro,
$record->introformat,
$record->assessed,
$record->assesstimestart,
$record->assesstimefinish,
$record->scale,
$record->maxbytes,
$record->maxattachments,
$record->forcesubscribe,
$record->trackingtype,
$record->rsstype,
$record->rssarticles,
$record->timemodified,
$record->warnafter,
$record->blockafter,
$record->blockperiod,
$record->completiondiscussions,
$record->completionreplies,
$record->completionposts,
$record->displaywordcount,
$record->lockdiscussionafter
);
}
/**
* Create a discussion entity from an stdClass (legacy dicussion object).
*
* @param stdClass $record Discussion record
* @return discussion_entity
*/
public function get_discussion_from_stdclass(stdClass $record) : discussion_entity {
return new discussion_entity(
$record->id,
$record->course,
$record->forum,
$record->name,
$record->firstpost,
$record->userid,
$record->groupid,
$record->assessed,
$record->timemodified,
$record->usermodified,
$record->timestart,
$record->timeend,
$record->pinned
);
}
/**
* Create a post entity from an stdClass (legacy post object).
*
* @param stdClass $record The post record
* @return post_entity
*/
public function get_post_from_stdclass(stdClass $record) : post_entity {
return new post_entity(
$record->id,
$record->discussion,
$record->parent,
$record->userid,
$record->created,
$record->modified,
$record->mailed,
$record->subject,
$record->message,
$record->messageformat,
$record->messagetrust,
$record->attachment,
$record->totalscore,
$record->mailnow,
$record->deleted
);
}
/**
* Create an author entity from a user record.
*
* @param stdClass $record The user record
* @return author_entity
*/
public function get_author_from_stdclass(stdClass $record) : author_entity {
return new author_entity(
$record->id,
$record->picture,
$record->firstname,
$record->lastname,
fullname($record),
$record->email,
$record->middlename,
$record->firstnamephonetic,
$record->lastnamephonetic,
$record->alternatename,
$record->imagealt
);
}
/**
* Create a discussion summary enttiy from stdClasses.
*
* @param stdClass $discussion The discussion record
* @param stdClass $firstpost A post record for the first post in the discussion
* @param stdClass $firstpostauthor A user record for the author of the first post
* @param stdClass $latestpostauthor A user record for the author of the latest post in the discussion
* @return discussion_summary_entity
*/
public function get_discussion_summary_from_stdclass(
stdClass $discussion,
stdClass $firstpost,
stdClass $firstpostauthor,
stdClass $latestpostauthor
) : discussion_summary_entity {
$firstpostauthorentity = $this->get_author_from_stdclass($firstpostauthor);
return new discussion_summary_entity(
$this->get_discussion_from_stdclass($discussion),
$this->get_post_from_stdclass($firstpost, $firstpostauthorentity),
$firstpostauthorentity,
$this->get_author_from_stdclass($latestpostauthor)
);
}
/**
* Create a post read receipt collection entity from a list of read receipt records.
*
* @param array $records A list of read receipt records.
* @return post_read_receipt_collection_entity
*/
public function get_post_read_receipt_collection_from_stdclasses(array $records) : post_read_receipt_collection_entity {
return new post_read_receipt_collection_entity($records);
}
/**
* Create a sorter entity to sort post entities.
*
* @return sorter_entity
*/
public function get_posts_sorter() : sorter_entity {
return new sorter_entity(
// Get id function for a post_entity.
function(post_entity $post) {
return $post->get_id();
},
// Get parent id function for a post_entity.
function(post_entity $post) {
return $post->get_parent_id();
}
);
}
/**
* Create a sorter entity to sort exported posts.
*
* @return sorter_entity
*/
public function get_exported_posts_sorter() : sorter_entity {
return new sorter_entity(
// Get id function for an exported post.
function(stdClass $post) {
return $post->id;
},
// Get parent id function for an exported post.
function(stdClass $post) {
return $post->parentid;
}
);
}
}
@@ -0,0 +1,248 @@
<?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 Exporter factory.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\factories;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\entities\post as post_entity;
use mod_forum\local\entities\post_read_receipt_collection as post_read_receipt_collection_entity;
use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
use mod_forum\local\factories\manager as manager_factory;
use mod_forum\local\factories\url as url_factory;
use mod_forum\local\exporters\forum as forum_exporter;
use mod_forum\local\exporters\discussion as discussion_exporter;
use mod_forum\local\exporters\discussion_summaries as discussion_summaries_exporter;
use mod_forum\local\exporters\post as post_exporter;
use mod_forum\local\exporters\posts as posts_exporter;
use context;
use rating;
use stdClass;
/**
* The exporter factory class used to fetch an instance of the different exporter types.
*
* See:
* https://designpatternsphp.readthedocs.io/en/latest/Creational/SimpleFactory/README.html
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class exporter {
/** @var legacy_data_mapper_factory The factory to fetch a legacy data mapper */
private $legacydatamapperfactory;
/** @var manager_factory The factory to fetch a new manager */
private $managerfactory;
/** @var url_factory The factory to create urls */
private $urlfactory;
/**
* Constructor for the exporter factory.
*
* @param legacy_data_mapper_factory $legacydatamapperfactory The factory to fetch a legacy data mapper instance
* @param manager_factory $managerfactory The factory fo fetch a manager instance
* @param url_factory $urlfactory The factory to create urls
*/
public function __construct(
legacy_data_mapper_factory $legacydatamapperfactory,
manager_factory $managerfactory,
url_factory $urlfactory
) {
$this->legacydatamapperfactory = $legacydatamapperfactory;
$this->managerfactory = $managerfactory;
$this->urlfactory = $urlfactory;
}
/**
* Construct a new forum exporter for the specified user and forum.
*
* @param stdClass $user The user viewing the forum
* @param forum_entity $forum The forum being viewed
* @param int $currentgroup The group currently being viewed
* @return forum_exporter
*/
public function get_forum_exporter(
stdClass $user,
forum_entity $forum,
?int $currentgroup
) : forum_exporter {
return new forum_exporter($forum, [
'legacydatamapperfactory' => $this->legacydatamapperfactory,
'capabilitymanager' => $this->managerfactory->get_capability_manager($forum),
'urlfactory' => $this->urlfactory,
'user' => $user,
'currentgroup' => $currentgroup,
]);
}
/**
* Fetch the structure of the forum exporter.
*
* @return array
*/
public static function get_forum_export_structure() : array {
return forum_exporter::read_properties_definition();
}
/**
* Construct a new discussion exporter for the specified user and forum discussion.
*
* @param stdClass $user The user viewing the forum
* @param forum_entity $forum The forum being viewed
* @param discussion_entity $discussion The discussion being viewed
* @param stdClass[] $groupsbyid The list of groups in the forum
* @return discussion_exporter
*/
public function get_discussion_exporter(
stdClass $user,
forum_entity $forum,
discussion_entity $discussion,
array $groupsbyid = []
) : discussion_exporter {
return new discussion_exporter($discussion, [
'context' => $forum->get_context(),
'forum' => $forum,
'capabilitymanager' => $this->managerfactory->get_capability_manager($forum),
'urlfactory' => $this->urlfactory,
'user' => $user,
'legacydatamapperfactory' => $this->legacydatamapperfactory,
'latestpostid' => null,
'groupsbyid' => $groupsbyid
]);
}
/**
* Fetch the structure of the discussion exporter.
*
* @return array
*/
public static function get_discussion_export_structure() {
return discussion_exporter::read_properties_definition();
}
/**
* Construct a new discussion summaries exporter for the specified user and set of discussions.
*
* @param stdClass $user The user viewing the forum
* @param forum_entity $forum The forum being viewed
* @param discussion_entity[] $discussions The set of discussions to be shown
* @param stdClass[] $groupsbyauthorid The set of groups in an associative array for each author
* @param stdClass[] $groupsbyid The set of groups in the forum in an associative array for each group
* @param int[] $discussionreplycount The number of replies for each discussion
* @param int[] $discussionunreadcount The number of unread posts for each discussion
* @param int[] $latestpostids The latest post id for each discussion
* @return discussion_summaries_exporter
*/
public function get_discussion_summaries_exporter(
stdClass $user,
forum_entity $forum,
array $discussions,
array $groupsbyid = [],
array $groupsbyauthorid = [],
array $discussionreplycount = [],
array $discussionunreadcount = [],
array $latestpostid = []
) : discussion_summaries_exporter {
return new discussion_summaries_exporter(
$discussions,
$groupsbyid,
$groupsbyauthorid,
$discussionreplycount,
$discussionunreadcount,
$latestpostid,
[
'legacydatamapperfactory' => $this->legacydatamapperfactory,
'context' => $forum->get_context(),
'forum' => $forum,
'capabilitymanager' => $this->managerfactory->get_capability_manager($forum),
'urlfactory' => $this->urlfactory,
'user' => $user,
]
);
}
/**
* Fetch the structure of the discussion summaries exporter.
*
* @return array
*/
public static function get_discussion_summaries_export_structure() {
return discussion_summaries_exporter::read_properties_definition();
}
/**
* Construct a new post exporter for the specified user and set of post.
*
* @param stdClass $user The user viewing the forum
* @param forum_entity $forum The forum being viewed
* @param discussion_entity $discussion The discussion that the post is in
* @param post_entity[] $posts The set of posts to be exported
* @param author_entity[] $authorsbyid List of authors indexed by author id
* @param array $attachmentsbypostid List of attachments for each post indexed by post id
* @param array $groupsbyauthorid List of groups for the post authors indexed by author id
* @param post_read_receipt_collection_entity|null $readreceiptcollection Details of read receipts for each post
* @param array $tagsbypostid List of tags for each post indexed by post id
* @param rating[] $ratingbypostid List of ratings for each post indexed by post id
* @param bool $includehtml Include some pre-constructed HTML in the export
* @return post_exporter
*/
public function get_posts_exporter(
stdClass $user,
forum_entity $forum,
discussion_entity $discussion,
array $posts,
array $authorsbyid = [],
array $attachmentsbypostid = [],
array $groupsbyauthorid = [],
post_read_receipt_collection_entity $readreceiptcollection = null,
array $tagsbypostid = [],
array $ratingbypostid = [],
bool $includehtml = false
) : posts_exporter {
return new posts_exporter($posts, $authorsbyid, $attachmentsbypostid, $groupsbyauthorid, $tagsbypostid, $ratingbypostid, [
'legacydatamapperfactory' => $this->legacydatamapperfactory,
'capabilitymanager' => $this->managerfactory->get_capability_manager($forum),
'urlfactory' => $this->urlfactory,
'forum' => $forum,
'discussion' => $discussion,
'user' => $user,
'context' => $forum->get_context(),
'readreceiptcollection' => $readreceiptcollection,
'includehtml' => $includehtml
]);
}
/**
* Fetch the structure of the posts exporter.
*
* @return array
*/
public static function get_posts_export_structure() {
return posts_exporter::read_properties_definition();
}
}
@@ -0,0 +1,79 @@
<?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/>.
/**
* Legacy data mapper factory.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\factories;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\data_mappers\legacy\author as author_data_mapper;
use mod_forum\local\data_mappers\legacy\discussion as discussion_data_mapper;
use mod_forum\local\data_mappers\legacy\forum as forum_data_mapper;
use mod_forum\local\data_mappers\legacy\post as post_data_mapper;
/**
* Legacy data mapper factory.
*
* See:
* https://designpatternsphp.readthedocs.io/en/latest/Creational/SimpleFactory/README.html
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class legacy_data_mapper {
/**
* Create a legacy forum data mapper.
*
* @return forum_data_mapper
*/
public function get_forum_data_mapper() : forum_data_mapper {
return new forum_data_mapper();
}
/**
* Create a legacy discussion data mapper.
*
* @return discussion_data_mapper
*/
public function get_discussion_data_mapper() : discussion_data_mapper {
return new discussion_data_mapper();
}
/**
* Create a legacy post data mapper.
*
* @return post_data_mapper
*/
public function get_post_data_mapper() : post_data_mapper {
return new post_data_mapper();
}
/**
* Create a legacy author data mapper.
*
* @return author_data_mapper
*/
public function get_author_data_mapper() : author_data_mapper {
return new author_data_mapper();
}
}
@@ -0,0 +1,81 @@
<?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/>.
/**
* Managers factory.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\factories;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/rating/lib.php');
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\managers\capability as capability_manager;
use rating_manager;
/**
* Managers factory.
*
* See:
* https://designpatternsphp.readthedocs.io/en/latest/Creational/SimpleFactory/README.html
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class manager {
/** @var legacy_data_mapper $legacydatamapperfactory Legacy data mapper factory */
private $legacydatamapperfactory;
/**
* Constructor.
*
* @param legacy_data_mapper $legacydatamapperfactory Legacy data mapper factory
*/
public function __construct(legacy_data_mapper $legacydatamapperfactory) {
$this->legacydatamapperfactory = $legacydatamapperfactory;
}
/**
* Create a capability manager for the given forum.
*
* @param forum_entity $forum The forum to manage capabilities for
* @return capability_manager
*/
public function get_capability_manager(forum_entity $forum) {
return new capability_manager(
$forum,
$this->legacydatamapperfactory->get_forum_data_mapper(),
$this->legacydatamapperfactory->get_discussion_data_mapper(),
$this->legacydatamapperfactory->get_post_data_mapper()
);
}
/**
* Create a rating manager.
*
* @return rating_manager
*/
public function get_rating_manager() : rating_manager {
return new rating_manager();
}
}
@@ -0,0 +1,384 @@
<?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/>.
/**
* Renderer factory.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\factories;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\factories\vault as vault_factory;
use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
use mod_forum\local\factories\entity as entity_factory;
use mod_forum\local\factories\exporter as exporter_factory;
use mod_forum\local\factories\manager as manager_factory;
use mod_forum\local\factories\builder as builder_factory;
use mod_forum\local\factories\url as url_factory;
use mod_forum\local\renderers\discussion as discussion_renderer;
use mod_forum\local\renderers\discussion_list as discussion_list_renderer;
use mod_forum\local\renderers\posts as posts_renderer;
use moodle_page;
use moodle_url;
/**
* Renderer factory.
*
* See:
* https://designpatternsphp.readthedocs.io/en/latest/Creational/SimpleFactory/README.html
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer {
/** @var legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory */
private $legacydatamapperfactory;
/** @var exporter_factory $exporterfactory Exporter factory */
private $exporterfactory;
/** @var vault_factory $vaultfactory Vault factory */
private $vaultfactory;
/** @var manager_factory $managerfactory Manager factory */
private $managerfactory;
/** @var entity_factory $entityfactory Entity factory */
private $entityfactory;
/** @var builder_factory $builderfactory Builder factory */
private $builderfactory;
/** @var url_factory $urlfactory URL factory */
private $urlfactory;
/** @var renderer_base $rendererbase Renderer base */
private $rendererbase;
/** @var moodle_page $page Moodle page */
private $page;
/**
* Constructor.
*
* @param legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory
* @param exporter_factory $exporterfactory Exporter factory
* @param vault_factory $vaultfactory Vault factory
* @param manager_factory $managerfactory Manager factory
* @param entity_factory $entityfactory Entity factory
* @param builder_factory $builderfactory Builder factory
* @param url_factory $urlfactory URL factory
* @param moodle_page $page Moodle page
*/
public function __construct(
legacy_data_mapper_factory $legacydatamapperfactory,
exporter_factory $exporterfactory,
vault_factory $vaultfactory,
manager_factory $managerfactory,
entity_factory $entityfactory,
builder_factory $builderfactory,
url_factory $urlfactory,
moodle_page $page
) {
$this->legacydatamapperfactory = $legacydatamapperfactory;
$this->exporterfactory = $exporterfactory;
$this->vaultfactory = $vaultfactory;
$this->managerfactory = $managerfactory;
$this->entityfactory = $entityfactory;
$this->builderfactory = $builderfactory;
$this->urlfactory = $urlfactory;
$this->page = $page;
$this->rendererbase = $page->get_renderer('mod_forum');
}
/**
* Create a discussion renderer for the given forum and discussion.
*
* @param forum_entity $forum Forum the discussion belongs to
* @param discussion_entity $discussion Discussion to render
* @param int $displaymode How should the posts be formatted?
* @return discussion_renderer
*/
public function get_discussion_renderer(
forum_entity $forum,
discussion_entity $discussion,
int $displaymode
) : discussion_renderer {
$capabilitymanager = $this->managerfactory->get_capability_manager($forum);
$ratingmanager = $this->managerfactory->get_rating_manager();
$rendererbase = $this->rendererbase;
$baseurl = $this->urlfactory->get_discussion_view_url_from_discussion($discussion);
$notifications = [];
switch ($forum->get_type()) {
case 'single':
$baseurl = new moodle_url("/mod/forum/view.php", ['f' => $forum->get_id()]);
break;
case 'qanda':
if ($capabilitymanager->must_post_before_viewing_discussion($user, $forum, $discussion)) {
$notifications[] = $rendererbase->notification(get_string('qandanotify', 'forum'));
}
break;
}
return new discussion_renderer(
$discussion,
$forum,
$displaymode,
$rendererbase,
$this->get_single_discussion_posts_renderer($displaymode, false),
$this->page,
$this->legacydatamapperfactory,
$this->exporterfactory,
$this->vaultfactory,
$capabilitymanager,
$ratingmanager,
$this->entityfactory->get_exported_posts_sorter(),
$baseurl,
$notifications
);
}
/**
* Create a posts renderer to render posts without defined parent/reply relationships.
*
* @return posts_renderer
*/
public function get_posts_renderer() : posts_renderer {
return new posts_renderer(
$this->rendererbase,
$this->builderfactory->get_exported_posts_builder(),
'mod_forum/forum_discussion_posts'
);
}
/**
* Create a posts renderer to render a list of posts in a single discussion.
*
* @param int|null $displaymode How should the posts be formatted?
* @param bool $readonly Should the posts include the actions to reply, delete, etc?
* @return posts_renderer
*/
public function get_single_discussion_posts_renderer(int $displaymode = null, bool $readonly = false) : posts_renderer {
$exportedpostssorter = $this->entityfactory->get_exported_posts_sorter();
switch ($displaymode) {
case FORUM_MODE_THREADED:
$template = 'mod_forum/forum_discussion_threaded_posts';
break;
case FORUM_MODE_NESTED:
$template = 'mod_forum/forum_discussion_nested_posts';
break;
default;
$template = 'mod_forum/forum_discussion_posts';
break;
}
return new posts_renderer(
$this->rendererbase,
$this->builderfactory->get_exported_posts_builder(),
$template,
// Post process the exported posts for our template. This function will add the "replies"
// and "hasreplies" properties to the exported posts. It will also sort them into the
// reply tree structure if the display mode requires it.
function($exportedposts, $forums) use ($displaymode, $readonly, $exportedpostssorter) {
$forum = array_shift($forums);
$seenfirstunread = false;
$exportedposts = array_map(
function($exportedpost) use ($forum, $readonly, $seenfirstunread) {
if ($forum->get_type() == 'single' && !$exportedpost->hasparent) {
// Remove the author from any posts that don't have a parent.
unset($exportedpost->author);
}
$exportedpost->readonly = $readonly;
$exportedpost->hasreplies = false;
$exportedpost->replies = [];
$exportedpost->isfirstunread = false;
if (!$seenfirstunread && $exportedpost->unread) {
$exportedpost->isfirstunread = true;
$seenfirstunread = true;
}
return $exportedpost;
},
$exportedposts
);
if ($displaymode === FORUM_MODE_NESTED || $displaymode === FORUM_MODE_THREADED) {
$sortedposts = $exportedpostssorter->sort_into_children($exportedposts);
$sortintoreplies = function($nestedposts) use (&$sortintoreplies) {
return array_map(function($postdata) use (&$sortintoreplies) {
[$post, $replies] = $postdata;
$post->replies = $sortintoreplies($replies);
$post->hasreplies = !empty($post->replies);
return $post;
}, $nestedposts);
};
// Set the "replies" property on the exported posts.
$exportedposts = $sortintoreplies($sortedposts);
} else if ($displaymode === FORUM_MODE_FLATNEWEST || $displaymode === FORUM_MODE_FLATOLDEST) {
$exportedfirstpost = array_shift($exportedposts);
$exportedfirstpost->replies = $exportedposts;
$exportedfirstpost->hasreplies = true;
$exportedposts = [$exportedfirstpost];
}
return $exportedposts;
}
);
}
/**
* Create a posts renderer to render posts in the forum search results.
*
* @param string[] $searchterms The search terms to be highlighted in the posts
* @return posts_renderer
*/
public function get_posts_search_results_renderer(array $searchterms) : posts_renderer {
$urlfactory = $this->urlfactory;
return new posts_renderer(
$this->rendererbase,
$this->builderfactory->get_exported_posts_builder(),
'mod_forum/forum_posts_with_context_links',
// Post process the exported posts to add the highlighting of the search terms to the post
// and also the additional context links in the subject.
function($exportedposts, $forumsbyid, $discussionsbyid) use ($searchterms, $urlfactory) {
$highlightwords = implode(' ', $searchterms);
return array_map(
function($exportedpost) use (
$forumsbyid,
$discussionsbyid,
$searchterms,
$highlightwords,
$urlfactory
) {
$discussion = $discussionsbyid[$exportedpost->discussionid];
$forum = $forumsbyid[$discussion->get_forum_id()];
$viewdiscussionurl = $urlfactory->get_discussion_view_url_from_discussion($discussion);
$exportedpost->urls['viewforum'] = $urlfactory->get_forum_view_url_from_forum($forum)->out(false);
$exportedpost->urls['viewdiscussion'] = $viewdiscussionurl->out(false);
$exportedpost->subject = highlight($highlightwords, $exportedpost->subject);
$exportedpost->forumname = format_string($forum->get_name(), true);
$exportedpost->discussionname = highlight($highlightwords, format_string($discussion->get_name(), true));
$exportedpost->showdiscussionname = $forum->get_type() != 'single';
// Identify search terms only found in HTML markup, and add a warning about them to
// the start of the message text. This logic was copied exactly as is from the previous
// implementation.
$missingterms = '';
$exportedpost->message = highlight(
$highlightwords,
$exportedpost->message,
0,
'<fgw9sdpq4>',
'</fgw9sdpq4>'
);
foreach ($searchterms as $searchterm) {
if (
preg_match("/$searchterm/i", $exportedpost->message) &&
!preg_match('/<fgw9sdpq4>' . $searchterm . '<\/fgw9sdpq4>/i', $exportedpost->message)
) {
$missingterms .= " $searchterm";
}
}
$exportedpost->message = str_replace('<fgw9sdpq4>', '<span class="highlight">', $exportedpost->message);
$exportedpost->message = str_replace('</fgw9sdpq4>', '</span>', $exportedpost->message);
if ($missingterms) {
$strmissingsearchterms = get_string('missingsearchterms', 'forum');
$exportedpost->message = '<p class="highlight2">' . $strmissingsearchterms . ' '
. $missingterms . '</p>' . $exportedpost->message;
}
return $exportedpost;
},
$exportedposts
);
}
);
}
/**
* Create a posts renderer to render posts in mod/forum/user.php.
*
* @param bool $addlinkstocontext Should links to the course, forum, and discussion be included?
* @return posts_renderer
*/
public function get_user_forum_posts_report_renderer(bool $addlinkstocontext) : posts_renderer {
$urlfactory = $this->urlfactory;
return new posts_renderer(
$this->rendererbase,
$this->builderfactory->get_exported_posts_builder(),
'mod_forum/forum_posts_with_context_links',
function($exportedposts, $forumsbyid, $discussionsbyid) use ($urlfactory, $addlinkstocontext) {
return array_map(function($exportedpost) use ($forumsbyid, $discussionsbyid, $addlinkstocontext, $urlfactory) {
$discussion = $discussionsbyid[$exportedpost->discussionid];
$forum = $forumsbyid[$discussion->get_forum_id()];
$courserecord = $forum->get_course_record();
if ($addlinkstocontext) {
$viewdiscussionurl = $urlfactory->get_discussion_view_url_from_discussion($discussion);
$exportedpost->urls['viewforum'] = $urlfactory->get_forum_view_url_from_forum($forum)->out(false);
$exportedpost->urls['viewdiscussion'] = $viewdiscussionurl->out(false);
$exportedpost->urls['viewcourse'] = $urlfactory->get_course_url_from_forum($forum)->out(false);
}
$exportedpost->forumname = format_string($forum->get_name(), true);
$exportedpost->discussionname = format_string($discussion->get_name(), true);
$exportedpost->coursename = format_string($courserecord->shortname, true);
$exportedpost->showdiscussionname = $forum->get_type() != 'single';
return $exportedpost;
}, $exportedposts);
}
);
}
/**
* Create a discussion list renderer.
*
* @param forum_entity $forum The forum that the discussions belong to
* @return discussion_list_renderer
*/
public function get_discussion_list_renderer(
forum_entity $forum
) : discussion_list_renderer {
$capabilitymanager = $this->managerfactory->get_capability_manager($forum);
$rendererbase = $this->rendererbase;
$notifications = [];
return new discussion_list_renderer(
$forum,
$rendererbase,
$this->legacydatamapperfactory,
$this->exporterfactory,
$this->vaultfactory,
$capabilitymanager,
$this->urlfactory,
$notifications
);
}
}
+446
View File
@@ -0,0 +1,446 @@
<?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/>.
/**
* A URL factory for the forum.
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\factories;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\author as author_entity;
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\post as post_entity;
use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
use moodle_url;
use stored_file;
use user_picture;
require_once($CFG->dirroot . '/mod/forum/lib.php');
/**
* A URL factory for the forum.
*
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class url {
/** @var legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory */
private $legacydatamapperfactory;
/**
* Constructor.
*
* @param legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory
*/
public function __construct(legacy_data_mapper_factory $legacydatamapperfactory) {
$this->legacydatamapperfactory = $legacydatamapperfactory;
}
/**
* Get the course url from the given course id.
*
* @param int $courseid The course id
* @return moodle_url
*/
public function get_course_url_from_courseid(int $courseid) : moodle_url {
return new moodle_url('/course/view.php', [
'id' => $courseid,
]);
}
/**
* Get the course url from the given forum entity.
*
* @param forum_entity $forum The forum entity
* @return moodle_url
*/
public function get_course_url_from_forum(forum_entity $forum) : moodle_url {
return $this->get_course_url_from_courseid($forum->get_course_id());
}
/**
* Get the create discussion url for the given forum.
*
* @param forum_entity $forum The forum entity
* @return moodle_url
*/
public function get_discussion_create_url(forum_entity $forum) : moodle_url {
return new moodle_url('/mod/forum/post.php', [
'forum' => $forum->get_id(),
]);
}
/**
* Get the view forum url for the given forum and optionally a page number.
*
* @param forum_entity $forum The forum entity
* @param int|null $pageno The page number
* @return moodle_url
*/
public function get_forum_view_url_from_forum(forum_entity $forum, ?int $pageno = null) : moodle_url {
return $this->get_forum_view_url_from_course_module_id($forum->get_course_module_record()->id, $pageno);
}
/**
* Get the view forum url for the given course module id and optionally a page number.
*
* @param int $coursemoduleid The course module id
* @param int|null $pageno The page number
* @return moodle_url
*/
public function get_forum_view_url_from_course_module_id(int $coursemoduleid, ?int $pageno = null) : moodle_url {
$url = new moodle_url('/mod/forum/discussions.php', [
'id' => $coursemoduleid,
]);
if (null !== $pageno) {
$url->param('page', $pageno);
}
return $url;
}
/**
* Get the view discussion url from the given discussion id.
*
* @param int $discussionid The discussion id
* @return moodle_url
*/
public function get_discussion_view_url_from_discussion_id(int $discussionid) : moodle_url {
return new moodle_url('/mod/forum/discuss.php', [
'd' => $discussionid
]);
}
/**
* Get the view discussion url from the given discussion.
*
* @param discussion_entity $discussion The discussion
* @return moodle_url
*/
public function get_discussion_view_url_from_discussion(discussion_entity $discussion) : moodle_url {
return $this->get_discussion_view_url_from_discussion_id($discussion->get_id());
}
/**
* Get the url to view the first unread post in a discussion.
*
* @param discussion_entity $discussion The discussion
* @return moodle_url
*/
public function get_discussion_view_first_unread_post_url_from_discussion(discussion_entity $discussion) {
$viewurl = $this->get_discussion_view_url_from_discussion_id($discussion->get_id());
$viewurl->set_anchor('unread');
return $viewurl;
}
/**
* Get the url to view the latest post in a discussion.
*
* @param discussion_entity $discussion The discussion
* @param int|null $latestpost The id of the latest post
* @return moodle_url
*/
public function get_discussion_view_latest_post_url_from_discussion(discussion_entity $discussion, ?int $latestpost) {
$viewurl = $this->get_discussion_view_url_from_discussion_id($discussion->get_id());
if (null === $latestpost) {
return $viewurl;
} else {
return new moodle_url($viewurl, ['parent' => $latestpost]);
}
}
/**
* Get the url to view a discussion from a post.
*
* @param post_entity $post The post
* @return moodle_url
*/
public function get_discussion_view_url_from_post(post_entity $post) : moodle_url {
return $this->get_discussion_view_url_from_discussion_id($post->get_discussion_id());
}
/**
* Get the url to view a discussion from a discussion id and post id.
*
* @param int $discussionid The discussion id
* @param int $postid The post id
* @return moodle_url
*/
public function get_view_post_url_from_post_id(int $discussionid, int $postid) : moodle_url {
$url = $this->get_discussion_view_url_from_discussion_id($discussionid);
$url->set_anchor('p' . $postid);
return $url;
}
/**
* Get the url to view a post in the context of the rest of the discussion.
*
* @param post_entity $post The post
* @return moodle_url
*/
public function get_view_post_url_from_post(post_entity $post) : moodle_url {
return $this->get_view_post_url_from_post_id($post->get_discussion_id(), $post->get_id());
}
/**
* Get the url to view a post and it's replies in isolation without the rest of the
* discussion.
*
* @param int $discussionid The discussion id
* @param int $postid The post id
* @return moodle_url
*/
public function get_view_isolated_post_url_from_post_id(int $discussionid, int $postid) : moodle_url {
$url = $this->get_discussion_view_url_from_discussion_id($discussionid);
$url->params(['parent' => $postid]);
return $url;
}
/**
* Get the url to view a post and it's replies in isolation without the rest of the
* discussion.
*
* @param post_entity $post The post
* @return moodle_url
*/
public function get_view_isolated_post_url_from_post(post_entity $post) : moodle_url {
return $this->get_view_isolated_post_url_from_post_id($post->get_discussion_id(), $post->get_id());
}
/**
* Get the url to edit a post.
*
* @param forum_entity $forum The forum the post belongs to
* @param post_entity $post The post
* @return moodle_url
*/
public function get_edit_post_url_from_post(forum_entity $forum, post_entity $post) : moodle_url {
if ($forum->get_type() == 'single') {
return new moodle_url('/course/modedit.php', [
'update' => $forum->get_course_module_record()->id,
'sesskey' => sesskey(),
'return' => 1
]);
} else {
return new moodle_url('/mod/forum/post.php', [
'edit' => $post->get_id()
]);
}
}
/**
* Get the url to split a discussion at a post.
*
* @param post_entity $post The post
* @return moodle_url
*/
public function get_split_discussion_at_post_url_from_post(post_entity $post) : moodle_url {
return new moodle_url('/mod/forum/post.php', [
'prune' => $post->get_id()
]);
}
/**
* Get the url to delete a post.
*
* @param post_entity $post The post
* @return moodle_url
*/
public function get_delete_post_url_from_post(post_entity $post) : moodle_url {
return new moodle_url('/mod/forum/post.php', [
'delete' => $post->get_id()
]);
}
/**
* Get the url to reply to a post.
*
* @param post_entity $post The post
* @return moodle_url
*/
public function get_reply_to_post_url_from_post(post_entity $post) : moodle_url {
return new moodle_url('/mod/forum/post.php#mformforum', [
'reply' => $post->get_id()
]);
}
/**
* Get the url to export (see portfolios) a post.
*
* @param post_entity $post The post
* @return moodle_url
*/
public function get_export_post_url_from_post(post_entity $post) : ?moodle_url {
global $CFG;
require_once($CFG->libdir . '/portfoliolib.php');
$button = new \portfolio_add_button();
$button->set_callback_options('forum_portfolio_caller', ['postid' => $post->get_id()], 'mod_forum');
if ($post->has_attachments()) {
$button->set_formats(PORTFOLIO_FORMAT_RICHHTML);
} else {
$button->set_formats(PORTFOLIO_FORMAT_PLAINHTML);
}
$url = $button->to_html(PORTFOLIO_ADD_MOODLE_URL);
return $url ?: null;
}
/**
* Get the url to mark a post as read.
*
* @param post_entity $post The post
* @param int $displaymode The display mode to show the forum in after marking as read
* @return moodle_url
*/
public function get_mark_post_as_read_url_from_post(post_entity $post, int $displaymode = FORUM_MODE_THREADED) : moodle_url {
$params = [
'd' => $post->get_discussion_id(),
'postid' => $post->get_id(),
'mark' => 'read'
];
$url = new moodle_url('/mod/forum/discuss.php', $params);
if ($displaymode == FORUM_MODE_THREADED) {
$url->param('parent', $post->get_parent_id());
} else {
$url->set_anchor('p' . $post->get_id());
}
return $url;
}
/**
* Get the url to mark a post as unread.
*
* @param post_entity $post The post
* @param int $displaymode The display mode to show the forum in after marking as unread
* @return moodle_url
*/
public function get_mark_post_as_unread_url_from_post(post_entity $post, int $displaymode = FORUM_MODE_THREADED) : moodle_url {
$params = [
'd' => $post->get_discussion_id(),
'postid' => $post->get_id(),
'mark' => 'unread'
];
$url = new moodle_url('/mod/forum/discuss.php', $params);
if ($displaymode == FORUM_MODE_THREADED) {
$url->param('parent', $post->get_parent_id());
} else {
$url->set_anchor('p' . $post->get_id());
}
return $url;
}
/**
* Get the url to export attachments for a post.
*
* @param post_entity $post The post
* @param stored_file $attachment
* @return moodle_url|null
*/
public function get_export_attachment_url_from_post_and_attachment(post_entity $post, stored_file $attachment) : ?moodle_url {
global $CFG;
require_once($CFG->libdir . '/portfoliolib.php');
$button = new \portfolio_add_button();
$button->set_callback_options(
'forum_portfolio_caller',
['postid' => $post->get_id(), 'attachment' => $attachment->get_id()],
'mod_forum'
);
$button->set_format_by_file($attachment);
$url = $button->to_html(PORTFOLIO_ADD_MOODLE_URL);
return $url ?: null;
}
/**
* Get the url to view an author's profile.
*
* @param author_entity $author The author
* @return moodle_url
*/
public function get_author_profile_url(author_entity $author) : moodle_url {
return new moodle_url('/user/view.php', [
'id' => $author->get_id()
]);
}
/**
* Get the url to view the author's profile image.
*
* @param author_entity $author The author
* @return moodle_url
*/
public function get_author_profile_image_url(author_entity $author) : moodle_url {
global $PAGE;
$datamapper = $this->legacydatamapperfactory->get_author_data_mapper();
$record = $datamapper->to_legacy_object($author);
$userpicture = new user_picture($record);
$userpicture->size = 2;
return $userpicture->get_url($PAGE);
}
/**
* Get the url to mark a discussion as read.
*
* @param forum_entity $forum The forum that the discussion belongs to
* @param discussion_entity $discussion The discussion
* @return moodle_url
*/
public function get_mark_discussion_as_read_url_from_discussion(
forum_entity $forum,
discussion_entity $discussion
) : moodle_url {
return new moodle_url('/mod/forum/markposts.php', [
'f' => $discussion->get_forum_id(),
'd' => $discussion->get_id(),
'mark' => 'read',
'sesskey' => sesskey(),
'return' => $this->get_forum_view_url_from_forum($forum)->out(),
]);
}
/**
* Get the url to mark all discussions as read.
*
* @param forum_entity $forum The forum that the discussions belong to
* @return moodle_url
*/
public function get_mark_all_discussions_as_read_url(forum_entity $forum) : moodle_url {
return new moodle_url('/mod/forum/markposts.php', [
'f' => $forum->get_id(),
'mark' => 'read',
'sesskey' => sesskey(),
'return' => $this->get_forum_view_url_from_forum($forum)->out(),
]);
}
}
+152
View File
@@ -0,0 +1,152 @@
<?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/>.
/**
* Vault factory.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\factories;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\factories\entity as entity_factory;
use mod_forum\local\vaults\author as author_vault;
use mod_forum\local\vaults\discussion as discussion_vault;
use mod_forum\local\vaults\discussion_list as discussion_list_vault;
use mod_forum\local\vaults\forum as forum_vault;
use mod_forum\local\vaults\post as post_vault;
use mod_forum\local\vaults\post_attachment as post_attachment_vault;
use mod_forum\local\vaults\post_read_receipt_collection as post_read_receipt_collection_vault;
use file_storage;
use moodle_database;
/**
* Vault factory.
*
* See:
* https://designpatternsphp.readthedocs.io/en/latest/Creational/SimpleFactory/README.html
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class vault {
/** @var entity_factory $entityfactory Entity factory */
private $entityfactory;
/** @var moodle_database $db A moodle database */
private $db;
/** @var file_storage $filestorage A file storage instance */
private $filestorage;
/**
* Constructor.
*
* @param moodle_database $db A moodle database
* @param entity_factory $entityfactory Entity factory
* @param file_storage $filestorage A file storage instance
*/
public function __construct(moodle_database $db, entity_factory $entityfactory, file_storage $filestorage) {
$this->db = $db;
$this->entityfactory = $entityfactory;
$this->filestorage = $filestorage;
}
/**
* Create a forum vault.
*
* @return forum_vault
*/
public function get_forum_vault() : forum_vault {
return new forum_vault(
$this->db,
$this->entityfactory
);
}
/**
* Create a discussion vault.
*
* @return discussion_vault
*/
public function get_discussion_vault() : discussion_vault {
return new discussion_vault(
$this->db,
$this->entityfactory
);
}
/**
* Create a discussion list vault.
*
* @return discussion_list_vault
*/
public function get_discussions_in_forum_vault() : discussion_list_vault {
return new discussion_list_vault(
$this->db,
$this->entityfactory
);
}
/**
* Create a post vault.
*
* @return post_vault
*/
public function get_post_vault() : post_vault {
return new post_vault(
$this->db,
$this->entityfactory
);
}
/**
* Create an author vault.
*
* @return author_vault
*/
public function get_author_vault() : author_vault {
return new author_vault(
$this->db,
$this->entityfactory
);
}
/**
* Create a post read receipt collection vault.
*
* @return post_read_receipt_collection_vault
*/
public function get_post_read_receipt_collection_vault() : post_read_receipt_collection_vault {
return new post_read_receipt_collection_vault(
$this->db,
$this->entityfactory
);
}
/**
* Create a post attachment vault.
*
* @return post_attachment_vault
*/
public function get_post_attachment_vault() : post_attachment_vault {
return new post_attachment_vault(
$this->filestorage
);
}
}
@@ -0,0 +1,492 @@
<?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/>.
/**
* Capability manager for the forum.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\managers;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\data_mappers\legacy\forum as legacy_forum_data_mapper;
use mod_forum\local\data_mappers\legacy\discussion as legacy_discussion_data_mapper;
use mod_forum\local\data_mappers\legacy\post as legacy_post_data_mapper;
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\entities\post as post_entity;
use mod_forum\subscriptions;
use context;
use context_system;
use stdClass;
require_once($CFG->dirroot . '/mod/forum/lib.php');
/**
* Capability manager for the forum.
*
* Defines all the business rules for what a user can and can't do in the forum.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class capability {
/** @var legacy_forum_data_mapper $forumdatamapper Legacy forum data mapper */
private $forumdatamapper;
/** @var legacy_discussion_data_mapper $discussiondatamapper Legacy discussion data mapper */
private $discussiondatamapper;
/** @var legacy_post_data_mapper $postdatamapper Legacy post data mapper */
private $postdatamapper;
/** @var forum_entity $forum Forum entity */
private $forum;
/** @var stdClass $forumrecord Legacy forum record */
private $forumrecord;
/** @var context $context Module context for the forum */
private $context;
/**
* Constructor.
*
* @param forum_entity $forum The forum entity to manage capabilities for.
* @param legacy_forum_data_mapper $forumdatamapper Legacy forum data mapper
* @param legacy_discussion_data_mapper $discussiondatamapper Legacy discussion data mapper
* @param legacy_post_data_mapper $postdatamapper Legacy post data mapper
*/
public function __construct(
forum_entity $forum,
legacy_forum_data_mapper $forumdatamapper,
legacy_discussion_data_mapper $discussiondatamapper,
legacy_post_data_mapper $postdatamapper
) {
$this->forumdatamapper = $forumdatamapper;
$this->discussiondatamapper = $discussiondatamapper;
$this->postdatamapper = $postdatamapper;
$this->forum = $forum;
$this->forumrecord = $forumdatamapper->to_legacy_object($forum);
$this->context = $forum->get_context();
}
/**
* Can the user subscribe to this forum?
*
* @param stdClass $user The user to check
* @return bool
*/
public function can_subscribe_to_forum(stdClass $user) : bool {
return !is_guest($this->get_context(), $user) &&
subscriptions::is_subscribable($this->get_forum_record());
}
/**
* Can the user create discussions in this forum?
*
* @param stdClass $user The user to check
* @param int|null $groupid The current activity group id
* @return bool
*/
public function can_create_discussions(stdClass $user, int $groupid = null) : bool {
if (isguestuser($user) or !isloggedin()) {
return false;
}
switch ($this->forum->get_type()) {
case 'news':
$capability = 'mod/forum:addnews';
break;
case 'qanda':
$capability = 'mod/forum:addquestion';
break;
default:
$capability = 'mod/forum:startdiscussion';
}
if (!has_capability($capability, $this->forum->get_context(), $user)) {
return false;
}
if ($this->forum->is_in_group_mode()) {
return $groupid ? $this->can_access_group($user, $groupid) : $this->can_access_all_groups($user);
} else {
return true;
}
}
/**
* Can the user access all groups?
*
* @param stdClass $user The user to check
* @return bool
*/
public function can_access_all_groups(stdClass $user) {
return has_capability('moodle/site:accessallgroups', $this->get_context(), $user);
}
/**
* Can the user access the given group?
*
* @param stdClass $user The user to check
* @param int $groupid The id of the group that the forum is set to
* @return bool
*/
public function can_access_group(stdClass $user, int $groupid) {
if ($this->can_access_all_groups($user)) {
// This user has access to all groups.
return true;
}
// This is a group discussion for a forum in separate groups mode.
// Check if the user is a member.
// This is the most expensive check.
return groups_is_member($groupid, $user->id);
}
/**
* Can the user view discussions in this forum?
*
* @param stdClass $user The user to check
* @return bool
*/
public function can_view_discussions(stdClass $user) : bool {
return has_capability('mod/forum:viewdiscussion', $this->get_context(), $user);
}
/**
* Can the user move discussions in this forum?
*
* @param stdClass $user The user to check
* @return bool
*/
public function can_move_discussions(stdClass $user) : bool {
$forum = $this->get_forum();
return $forum->get_type() !== 'single' && has_capability('mod/forum:movediscussions', $this->get_context(), $user);
}
/**
* Can the user pin discussions in this forum?
*
* @param stdClass $user The user to check
* @return bool
*/
public function can_pin_discussions(stdClass $user) : bool {
return has_capability('mod/forum:pindiscussions', $this->get_context(), $user);
}
/**
* Can the user split discussions in this forum?
*
* @param stdClass $user The user to check
* @return bool
*/
public function can_split_discussions(stdClass $user) : bool {
$forum = $this->get_forum();
return $forum->get_type() !== 'single' && has_capability('mod/forum:splitdiscussions', $this->get_context(), $user);
}
/**
* Can the user export (see portfolios) discussions in this forum?
*
* @param stdClass $user The user to check
* @return bool
*/
public function can_export_discussions(stdClass $user) : bool {
global $CFG;
return $CFG->enableportfolios && has_capability('mod/forum:exportdiscussion', $this->get_context(), $user);
}
/**
* Can the user manually mark posts as read/unread in this forum?
*
* @param stdClass $user The user to check
* @return bool
*/
public function can_manually_control_post_read_status(stdClass $user) : bool {
global $CFG;
return $CFG->forum_usermarksread && isloggedin() && forum_tp_is_tracked($this->get_forum_record(), $user);
}
/**
* Is the user required to post in the discussion before they can view it?
*
* @param stdClass $user The user to check
* @param discussion_entity $discussion The discussion to check
* @return bool
*/
public function must_post_before_viewing_discussion(stdClass $user, discussion_entity $discussion) : bool {
$forum = $this->get_forum();
if ($forum->get_type() === 'qanda') {
// If it's a Q and A forum then the user must either have the capability to view without
// posting or the user must have posted before they can view the discussion.
return !has_capability('mod/forum:viewqandawithoutposting', $this->get_context(), $user) &&
!forum_user_has_posted($forum->get_id(), $discussion->get_id(), $user->id);
} else {
// No other forum types require posting before viewing.
return false;
}
}
/**
* Can the user subscribe to the give discussion?
*
* @param stdClass $user The user to check
* @param discussion_entity $discussion The discussion to check
* @return bool
*/
public function can_subscribe_to_discussion(stdClass $user, discussion_entity $discussion) : bool {
return $this->can_subscribe_to_forum($user);
}
/**
* Can the user move the discussion in this forum?
*
* @param stdClass $user The user to check
* @param discussion_entity $discussion The discussion to check
* @return bool
*/
public function can_move_discussion(stdClass $user, discussion_entity $discussion) : bool {
return $this->can_move_discussions($user);
}
/**
* Is the user pin the discussion?
*
* @param stdClass $user The user to check
* @param discussion_entity $discussion The discussion to check
* @return bool
*/
public function can_pin_discussion(stdClass $user, discussion_entity $discussion) : bool {
return $this->can_pin_discussions($user);
}
/**
* Can the user post in this discussion?
*
* @param stdClass $user The user to check
* @param discussion_entity $discussion The discussion to check
* @return bool
*/
public function can_post_in_discussion(stdClass $user, discussion_entity $discussion) : bool {
$forum = $this->get_forum();
$forumrecord = $this->get_forum_record();
$discussionrecord = $this->get_discussion_record($discussion);
$context = $this->get_context();
$coursemodule = $forum->get_course_module_record();
$course = $forum->get_course_record();
return forum_user_can_post($forumrecord, $discussionrecord, $user, $coursemodule, $course, $context);
}
/**
* Can the user view the post in this discussion?
*
* @param stdClass $user The user to check
* @param discussion_entity $discussion The discussion to check
* @param post_entity $post The post the user wants to view
* @return bool
*/
public function can_view_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
$forum = $this->get_forum();
$forumrecord = $this->get_forum_record();
$discussionrecord = $this->get_discussion_record($discussion);
$postrecord = $this->get_post_record($post);
$coursemodule = $forum->get_course_module_record();
return forum_user_can_see_post($forumrecord, $discussionrecord, $postrecord, $user, $coursemodule, false);
}
/**
* Can the user edit the post in this discussion?
*
* @param stdClass $user The user to check
* @param discussion_entity $discussion The discussion to check
* @param post_entity $post The post the user wants to edit
* @return bool
*/
public function can_edit_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
global $CFG;
$context = $this->get_context();
$ownpost = $post->is_owned_by_user($user);
$ineditingtime = $post->get_age() < $CFG->maxeditingtime;
switch ($this->forum->get_type()) {
case 'news':
// Allow editing of news posts once the discussion has started.
$ineditingtime = !$post->has_parent() && $discussion->has_started();
break;
case 'single':
return $discussion->is_first_post($post) && has_capability('moodle/course:manageactivities', $context, $user);
}
return ($ownpost && $ineditingtime) || has_capability('mod/forum:editanypost', $context, $user);
}
/**
* Can the user delete the post in this discussion?
*
* @param stdClass $user The user to check
* @param discussion_entity $discussion The discussion to check
* @param post_entity $post The post the user wants to delete
* @return bool
*/
public function can_delete_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
global $CFG;
$forum = $this->get_forum();
if ($forum->get_type() == 'single' && $discussion->is_first_post($post)) {
// Do not allow deleting of first post in single simple type.
return false;
} else {
$context = $this->get_context();
$ownpost = $post->is_owned_by_user($user);
$ineditingtime = $post->get_age() < $CFG->maxeditingtime;
return ($ownpost && $ineditingtime && has_capability('mod/forum:deleteownpost', $context, $user)) ||
has_capability('mod/forum:deleteanypost', $context, $user);
}
}
/**
* Can the user split the post in this discussion?
*
* @param stdClass $user The user to check
* @param discussion_entity $discussion The discussion to check
* @param post_entity $post The post the user wants to split
* @return bool
*/
public function can_split_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
return $this->can_split_discussions($user) && $post->has_parent();
}
/**
* Can the user reply to the post in this discussion?
*
* @param stdClass $user The user to check
* @param discussion_entity $discussion The discussion to check
* @param post_entity $post The post the user wants to reply to
* @return bool
*/
public function can_reply_to_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
return $this->can_post_in_discussion($user, $discussion);
}
/**
* Can the user export (see portfolios) the post in this discussion?
*
* @param stdClass $user The user to check
* @param post_entity $post The post the user wants to export
* @return bool
*/
public function can_export_post(stdClass $user, post_entity $post) : bool {
global $CFG;
$context = $this->get_context();
return $CFG->enableportfolios && (has_capability('mod/forum:exportpost', $context, $user) ||
($post->is_owned_by_user($user) && has_capability('mod/forum:exportownpost', $context, $user)));
}
/**
* Get the forum entity for this capability manager.
*
* @return forum_entity
*/
protected function get_forum() : forum_entity {
return $this->forum;
}
/**
* Get the legacy forum record for this forum.
*
* @return stdClass
*/
protected function get_forum_record() : stdClass {
return $this->forumrecord;
}
/**
* Get the context for this capability manager.
*
* @return context
*/
protected function get_context() : context {
return $this->context;
}
/**
* Get the legacy discussion record for the given discussion entity.
*
* @param discussion_entity $discussion The discussion to convert
* @return stdClass
*/
protected function get_discussion_record(discussion_entity $discussion) : stdClass {
return $this->discussiondatamapper->to_legacy_object($discussion);
}
/**
* Get the legacy post record for the given post entity.
*
* @param post_entity $post The post to convert
* @return stdClass
*/
protected function get_post_record(post_entity $post) : stdClass {
return $this->postdatamapper->to_legacy_object($post);
}
/**
* Can the user view the participants of this discussion?
*
* @param stdClass $user The user to check
* @param discussion_entity $discussion The discussion to check
* @return bool
*/
public function can_view_participants(stdClass $user, discussion_entity $discussion) : bool {
return course_can_view_participants($this->get_context()) &&
!$this->must_post_before_viewing_discussion($user, $discussion);
}
/**
* Can the user view hidden posts in this forum?
*
* @param stdClass $user The user to check
* @return bool
*/
public function can_view_hidden_posts(stdClass $user) : bool {
return has_capability('mod/forum:viewhiddentimedposts', $this->get_context(), $user);
}
/**
* Can the user manage this forum?
*
* @param stdClass $user The user to check
* @return bool
*/
public function can_manage_forum(stdClass $user) {
return has_capability('moodle/course:manageactivities', $this->get_context(), $user);
}
/**
* Can the user manage tags on the site?
*
* @param stdClass $user The user to check
* @return bool
*/
public function can_manage_tags(stdClass $user) : bool {
return has_capability('moodle/tag:manage', context_system::instance(), $user);
}
}
@@ -0,0 +1,413 @@
<?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/>.
/**
* Discussion renderer.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\renderers;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\entities\post as post_entity;
use mod_forum\local\entities\sorter as sorter_entity;
use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
use mod_forum\local\factories\exporter as exporter_factory;
use mod_forum\local\factories\vault as vault_factory;
use mod_forum\local\managers\capability as capability_manager;
use mod_forum\local\renderers\posts as posts_renderer;
use core\output\notification;
use context;
use context_module;
use html_writer;
use moodle_exception;
use moodle_page;
use moodle_url;
use rating_manager;
use renderer_base;
use single_button;
use single_select;
use stdClass;
use url_select;
require_once($CFG->dirroot . '/mod/forum/lib.php');
/**
* Discussion renderer class.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class discussion {
/** @var discussion_entity $discussion The discussion to render */
private $discussion;
/** @var stdClass $discussionrecord Legacy discussion record */
private $discussionrecord;
/** @var forum_entity $forum The forum that the discussion belongs to */
private $forum;
/** @var stdClass $forumrecord Legacy forum record */
private $forumrecord;
/** @var int $displaymode The display mode to render the discussion in */
private $displaymode;
/** @var renderer_base $renderer Renderer base */
private $renderer;
/** @var posts_renderer $postsrenderer A posts renderer */
private $postsrenderer;
/** @var moodle_page $page The page this discussion is being rendered for */
private $page;
/** @var legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory */
private $legacydatamapperfactory;
/** @var exporter_factory $exporterfactory Exporter factory */
private $exporterfactory;
/** @var vault_factory $vaultfactory Vault factory */
private $vaultfactory;
/** @var capability_manager $capabilitymanager Capability manager */
private $capabilitymanager;
/** @var rating_manager $ratingmanager Rating manager */
private $ratingmanager;
/** @var moodle_url $baseurl The base URL for the discussion */
private $baseurl;
/** @var array $notifications List of HTML notifications to display */
private $notifications;
/** @var sorter_entity $exportedpostsorter Sorter for the exported posts */
private $exportedpostsorter;
/**
* Constructor.
*
* @param discussion_entity $discussion The discussion to render
* @param forum_entity $forum The forum that the discussion belongs to
* @param int $displaymode The display mode to render the discussion in
* @param renderer_base $renderer Renderer base
* @param posts_renderer $postsrenderer A posts renderer
* @param moodle_page $page The page this discussion is being rendered for
* @param legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory
* @param exporter_factory $exporterfactory Exporter factory
* @param vault_factory $vaultfactory Vault factory
* @param capability_manager $capabilitymanager Capability manager
* @param rating_manager $ratingmanager Rating manager
* @param sorter_entity $exportedpostsorter Sorter for the exported posts
* @param moodle_url $baseurl The base URL for the discussion
* @param array $notifications List of HTML notifications to display
*/
public function __construct(
discussion_entity $discussion,
forum_entity $forum,
int $displaymode,
renderer_base $renderer,
posts_renderer $postsrenderer,
moodle_page $page,
legacy_data_mapper_factory $legacydatamapperfactory,
exporter_factory $exporterfactory,
vault_factory $vaultfactory,
capability_manager $capabilitymanager,
rating_manager $ratingmanager,
sorter_entity $exportedpostsorter,
moodle_url $baseurl,
array $notifications = []
) {
$this->discussion = $discussion;
$this->forum = $forum;
$this->displaymode = $displaymode;
$this->renderer = $renderer;
$this->postsrenderer = $postsrenderer;
$this->page = $page;
$this->baseurl = $baseurl;
$this->legacydatamapperfactory = $legacydatamapperfactory;
$this->exporterfactory = $exporterfactory;
$this->vaultfactory = $vaultfactory;
$this->capabilitymanager = $capabilitymanager;
$this->ratingmanager = $ratingmanager;
$this->notifications = $notifications;
$this->exportedpostsorter = $exportedpostsorter;
$forumdatamapper = $this->legacydatamapperfactory->get_forum_data_mapper();
$this->forumrecord = $forumdatamapper->to_legacy_object($forum);
$discussiondatamapper = $this->legacydatamapperfactory->get_discussion_data_mapper();
$this->discussionrecord = $discussiondatamapper->to_legacy_object($discussion);
}
/**
* Render the discussion for the given user in the specified display mode.
*
* @param stdClass $user The user viewing the discussion
* @param post_entity $firstpost The first post in the discussion
* @param array $replies List of replies to the first post
* @return string HTML for the discussion
*/
public function render(
stdClass $user,
post_entity $firstpost,
array $replies
) : string {
global $CFG;
$displaymode = $this->displaymode;
$capabilitymanager = $this->capabilitymanager;
$forum = $this->forum;
// Make sure we can render.
if (!$capabilitymanager->can_view_discussions($user)) {
throw new moodle_exception('noviewdiscussionspermission', 'mod_forum');
}
$posts = array_merge([$firstpost], array_values($replies));
$exporteddiscussion = $this->get_exported_discussion($user);
$exporteddiscussion = array_merge($exporteddiscussion, [
'notifications' => $this->get_notifications(),
'html' => [
'posts' => $this->postsrenderer->render($user, [$this->forum], [$this->discussion], $posts),
'modeselectorform' => $this->get_display_mode_selector_html($displaymode),
'subscribe' => null,
'movediscussion' => null,
'pindiscussion' => null,
'neighbourlinks' => $this->get_neighbour_links_html(),
'exportdiscussion' => !empty($CFG->enableportfolios) ? $this->get_export_discussion_html() : null
]
]);
$capabilities = (array) $exporteddiscussion['capabilities'];
if ($capabilities['subscribe']) {
$exporteddiscussion['html']['subscribe'] = $this->get_subscription_button_html();
}
if ($capabilities['move']) {
$exporteddiscussion['html']['movediscussion'] = $this->get_move_discussion_html();
}
if ($capabilities['pin']) {
$exporteddiscussion['html']['pindiscussion'] = $this->get_pin_discussion_html();
}
return $this->renderer->render_from_template('mod_forum/forum_discussion', $exporteddiscussion);
}
/**
* Get the groups details for all groups available to the forum.
*
* @return stdClass[]
*/
private function get_groups_available_in_forum() : array {
$course = $this->forum->get_course_record();
$coursemodule = $this->forum->get_course_module_record();
return groups_get_all_groups($course->id, 0, $coursemodule->groupingid);
}
/**
* Get the exported discussion.
*
* @param stdClass $user The user viewing the discussion
* @return array
*/
private function get_exported_discussion(stdClass $user) : array {
$discussionexporter = $this->exporterfactory->get_discussion_exporter(
$user,
$this->forum,
$this->discussion,
$this->get_groups_available_in_forum()
);
return (array) $discussionexporter->export($this->renderer);
}
/**
* Get the HTML for the display mode selector.
*
* @param int $displaymode The current display mode
* @return string
*/
private function get_display_mode_selector_html(int $displaymode) : string {
$baseurl = $this->baseurl;
$select = new single_select(
$baseurl,
'mode',
forum_get_layout_modes(),
$displaymode,
null,
'mode'
);
$select->set_label(get_string('displaymode', 'forum'), ['class' => 'accesshide']);
return $this->renderer->render($select);
}
/**
* Get the HTML to render the subscription button.
*
* @return string
*/
private function get_subscription_button_html() : string {
global $PAGE;
$forumrecord = $this->forumrecord;
$discussion = $this->discussion;
$html = html_writer::div(
forum_get_discussion_subscription_icon($forumrecord, $discussion->get_id(), null, true),
'discussionsubscription'
);
$html .= forum_get_discussion_subscription_icon_preloaders();
// Add the subscription toggle JS.
$PAGE->requires->yui_module('moodle-mod_forum-subscriptiontoggle', 'Y.M.mod_forum.subscriptiontoggle.init');
return $html;
}
/**
* Get the HTML to render the move discussion selector and button.
*
* @return string
*/
private function get_move_discussion_html() : string {
global $DB;
$forum = $this->forum;
$discussion = $this->discussion;
$courseid = $forum->get_course_id();
// Popup menu to move discussions to other forums. The discussion in a
// single discussion forum can't be moved.
$modinfo = get_fast_modinfo($courseid);
if (isset($modinfo->instances['forum'])) {
$forummenu = [];
// Check forum types and eliminate simple discussions.
$forumcheck = $DB->get_records('forum', ['course' => $courseid], '', 'id, type');
foreach ($modinfo->instances['forum'] as $forumcm) {
if (!$forumcm->uservisible || !has_capability('mod/forum:startdiscussion',
context_module::instance($forumcm->id))) {
continue;
}
$section = $forumcm->sectionnum;
$sectionname = get_section_name($courseid, $section);
if (empty($forummenu[$section])) {
$forummenu[$section] = [$sectionname => []];
}
$forumidcompare = $forumcm->instance != $forum->get_id();
$forumtypecheck = $forumcheck[$forumcm->instance]->type !== 'single';
if ($forumidcompare and $forumtypecheck) {
$url = "/mod/forum/discuss.php?d={$discussion->get_id()}&move=$forumcm->instance&sesskey=".sesskey();
$forummenu[$section][$sectionname][$url] = format_string($forumcm->name);
}
}
if (!empty($forummenu)) {
$html = '<div class="movediscussionoption">';
$select = new url_select($forummenu, '',
['/mod/forum/discuss.php?d=' . $discussion->get_id() => get_string("movethisdiscussionto", "forum")],
'forummenu', get_string('move'));
$html .= $this->renderer->render($select);
$html .= "</div>";
return $html;
}
}
return null;
}
/**
* Get the HTML to render the pin discussion button.
*
* @return string
*/
private function get_pin_discussion_html() : string {
$discussion = $this->discussion;
if ($discussion->is_pinned()) {
$pinlink = FORUM_DISCUSSION_UNPINNED;
$pintext = get_string('discussionunpin', 'forum');
} else {
$pinlink = FORUM_DISCUSSION_PINNED;
$pintext = get_string('discussionpin', 'forum');
}
$button = new single_button(
new moodle_url('discuss.php', ['pin' => $pinlink, 'd' => $discussion->get_id()]),
$pintext,
'post'
);
return $this->renderer->render($button);
}
/**
* Get the HTML to render the export discussion button.
*
* @return string|null
*/
private function get_export_discussion_html() : ?string {
global $CFG;
require_once($CFG->libdir . '/portfoliolib.php');
$discussion = $this->discussion;
$button = new \portfolio_add_button();
$button->set_callback_options('forum_portfolio_caller', ['discussionid' => $discussion->get_id()], 'mod_forum');
$button = $button->to_html(PORTFOLIO_ADD_FULL_FORM, get_string('exportdiscussion', 'mod_forum'));
return $button ?: null;
}
/**
* Get a list of notification HTML to render in the page.
*
* @return string[]
*/
private function get_notifications() : array {
$notifications = $this->notifications;
$discussion = $this->discussion;
$forum = $this->forum;
$renderer = $this->renderer;
if ($forum->is_discussion_locked($discussion)) {
$notifications[] = (new notification(
get_string('discussionlocked', 'forum'),
notification::NOTIFY_INFO
))
->set_extra_classes(['discussionlocked'])
->set_show_closebutton();
}
if ($forum->has_blocking_enabled()) {
$notifications[] = (new notification(
get_string('thisforumisthrottled', 'forum', [
'blockafter' => $forum->get_block_after(),
'blockperiod' => get_string('secondstotime' . $forum->get_block_period())
])
))->set_show_closebutton();
}
return array_map(function($notification) {
return $notification->export_for_template($this->renderer);
}, $notifications);
}
/**
* Get HTML to display the neighbour links.
*
* @return string
*/
private function get_neighbour_links_html() : string {
$forum = $this->forum;
$coursemodule = $forum->get_course_module_record();
$neighbours = forum_get_discussion_neighbours($coursemodule, $this->discussionrecord, $this->forumrecord);
return $this->renderer->neighbouring_discussion_navigation($neighbours['prev'], $neighbours['next']);
}
}
@@ -0,0 +1,436 @@
<?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/>.
/**
* Discussion list renderer.
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\renderers;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
use mod_forum\local\factories\exporter as exporter_factory;
use mod_forum\local\factories\vault as vault_factory;
use mod_forum\local\factories\url as url_factory;
use mod_forum\local\managers\capability as capability_manager;
use mod_forum\local\vaults\discussion_list as discussion_list_vault;
use renderer_base;
use stdClass;
use core\output\notification;
require_once($CFG->dirroot . '/mod/forum/lib.php');
/**
* The discussion list renderer.
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class discussion_list {
/** @var forum_entity The forum being rendered */
private $forum;
/** @var stdClass The DB record for the forum being rendered */
private $forumrecord;
/** @var renderer_base The renderer used to render the view */
private $renderer;
/** @var legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory */
private $legacydatamapperfactory;
/** @var exporter_factory $exporterfactory Exporter factory */
private $exporterfactory;
/** @var vault_factory $vaultfactory Vault factory */
private $vaultfactory;
/** @var capability_manager $capabilitymanager Capability manager */
private $capabilitymanager;
/** @var url_factory $urlfactory URL factory */
private $urlfactory;
/** @var array $notifications List of notification HTML */
private $notifications;
/**
* Constructor for a new discussion list renderer.
*
* @param forum_entity $forum The forum entity to be rendered
* @param renderer_base $renderer The renderer used to render the view
* @param legacy_data_mapper_factory $legacydatamapperfactory The factory used to fetch a legacy record
* @param exporter_factory $exporterfactory The factory used to fetch exporter instances
* @param vault_factory $vaultfactory The factory used to fetch the vault instances
* @param capability_manager $capabilitymanager The managed used to check capabilities on the forum
* @param url_factory $urlfactory The factory used to create URLs in the forum
* @param notification[] $notifications A list of any notifications to be displayed within the page
*/
public function __construct(
forum_entity $forum,
renderer_base $renderer,
legacy_data_mapper_factory $legacydatamapperfactory,
exporter_factory $exporterfactory,
vault_factory $vaultfactory,
capability_manager $capabilitymanager,
url_factory $urlfactory,
array $notifications = []
) {
$this->forum = $forum;
$this->renderer = $renderer;
$this->legacydatamapperfactory = $legacydatamapperfactory;
$this->exporterfactory = $exporterfactory;
$this->vaultfactory = $vaultfactory;
$this->capabilitymanager = $capabilitymanager;
$this->urlfactory = $urlfactory;
$this->notifications = $notifications;
$forumdatamapper = $this->legacydatamapperfactory->get_forum_data_mapper();
$this->forumrecord = $forumdatamapper->to_legacy_object($forum);
}
/**
* Render for the specified user.
*
* @param stdClass $user The user to render for
* @param cm_info $cm The course module info for this discussion list
* @param int $groupid The group to render
* @param int $sortorder The sort order to use when selecting the discussions in the list
* @param int $pageno The zero-indexed page number to use
* @param int $pagesize The number of discussions to show on the page
* @return string The rendered content for display
*/
public function render(stdClass $user, \cm_info $cm, ?int $groupid, ?int $sortorder, ?int $pageno, ?int $pagesize) : string {
$capabilitymanager = $this->capabilitymanager;
$forum = $this->forum;
$pagesize = $this->get_page_size($pagesize);
$pageno = $this->get_page_number($pageno);
$groupids = $this->get_groups_from_groupid($user, $groupid);
$forumexporter = $this->exporterfactory->get_forum_exporter(
$user,
$this->forum,
$groupid
);
$forumview = array_merge(
[
'notifications' => $this->get_notifications($user, $groupid),
'forum' => (array) $forumexporter->export($this->renderer),
'groupchangemenu' => groups_print_activity_menu(
$cm,
$this->urlfactory->get_forum_view_url_from_forum($forum),
true
),
],
(array) $this->get_exported_discussions($user, $groupids, $sortorder, $pageno, $pagesize)
);
return $this->renderer->render_from_template($this->get_template(), $forumview);
}
/**
* Get the list of groups to show based on the current user and requested groupid.
*
* @param stdClass $user The user viewing
* @param int $groupid The groupid requested
* @return array The list of groups to show
*/
private function get_groups_from_groupid(stdClass $user, ?int $groupid) : ?array {
$forum = $this->forum;
$effectivegroupmode = $forum->get_effective_group_mode();
if (empty($effectivegroupmode)) {
// This forum is not in a group mode. Show all posts always.
return null;
}
if (null == $groupid) {
// No group was specified.
$showallgroups = (VISIBLEGROUPS == $effectivegroupmode);
$showallgroups = $showallgroups || $this->capabilitymanager->can_access_all_groups($user);
if ($showallgroups) {
// Return null to show all groups.
return null;
} else {
// No group was specified. Only show the users current groups.
return array_keys(
groups_get_all_groups(
$forum->get_course_id(),
$user->id,
$forum->get_course_module_record()->groupingid
)
);
}
} else {
// A group was specified. Just show that group.
return [$groupid];
}
}
/**
* Fetch the data used to display the discussions on the current page.
*
* @param stdClass $user The user to render for
* @param int[]|null $groupids The group ids for this list of discussions
* @param int|null $sortorder The sort order to use when selecting the discussions in the list
* @param int|null $pageno The zero-indexed page number to use
* @param int|null $pagesize The number of discussions to show on the page
* @return stdClass The data to use for display
*/
private function get_exported_discussions(stdClass $user, ?array $groupids, ?int $sortorder, ?int $pageno, ?int $pagesize) {
$forum = $this->forum;
$discussionvault = $this->vaultfactory->get_discussions_in_forum_vault();
if (null === $groupids) {
$discussions = $discussionvault->get_from_forum_id(
$forum->get_id(),
$this->capabilitymanager->can_view_hidden_posts($user),
$user->id,
$sortorder,
$this->get_page_size($pagesize),
$this->get_page_number($pageno));
} else {
$discussions = $discussionvault->get_from_forum_id_and_group_id(
$forum->get_id(),
$groupids,
$this->capabilitymanager->can_view_hidden_posts($user),
$user->id,
$sortorder,
$this->get_page_size($pagesize),
$this->get_page_number($pageno));
}
$discussionids = array_keys($discussions);
$discussioncount = count($discussionids);
if ($discussioncount >= $pagesize) {
if (null === $groupids) {
$discussioncount = $discussionvault->get_total_discussion_count_from_forum_id(
$forum->get_id(),
$this->capabilitymanager->can_view_hidden_posts($user),
$user->id);
} else {
$discussioncount = $discussionvault->get_total_discussion_count_from_forum_id_and_group_id(
$forum->get_id(),
$groupids,
$this->capabilitymanager->can_view_hidden_posts($user),
$user->id);
}
}
$pagedcontent = new \core\external\paged_content_exporter(
$pagesize,
$pageno,
$discussioncount,
function ($pageno, $pagelimit) : \moodle_url {
return $this->urlfactory->get_forum_view_url_from_forum($this->forum, $pageno);
}
);
$postvault = $this->vaultfactory->get_post_vault();
$posts = $postvault->get_from_discussion_ids($discussionids);
$groupsbyid = $this->get_groups_available_in_forum();
$groupsbyauthorid = $this->get_author_groups_from_posts($posts);
$replycounts = $postvault->get_reply_count_for_discussion_ids($discussionids);
$latestposts = $postvault->get_latest_post_id_for_discussion_ids($discussionids);
$unreadcounts = [];
if (forum_tp_can_track_forums($this->forumrecord)) {
$unreadcounts = $postvault->get_unread_count_for_discussion_ids($user, $discussionids);
}
$summaryexporter = $this->exporterfactory->get_discussion_summaries_exporter(
$user,
$forum,
$discussions,
$groupsbyid,
$groupsbyauthorid,
$replycounts,
$unreadcounts,
$latestposts
);
return $summaryexporter->export($this->renderer);
}
/**
* Fetch the page size to use when displaying the page.
*
* @param int $pagesize The number of discussions to show on the page
* @return int The normalised page size
*/
private function get_page_size(?int $pagesize) : int {
if (null === $pagesize || $pagesize <= 0) {
$pagesize = discussion_list_vault::PAGESIZE_DEFAULT;
}
return $pagesize;
}
/**
* Fetch the current page number (zero-indexed).
*
* @param int $pageno The zero-indexed page number to use
* @return int The normalised page number
*/
private function get_page_number(?int $pageno) : int {
if (null === $pageno || $pageno < 0) {
$pageno = 0;
}
return $pageno;
}
/**
* Fetch the name of the template to use for the current forum and view modes.
*
* @return string
*/
private function get_template() : string {
switch ($this->forum->get_type()) {
case 'news':
return 'mod_forum/news_discussion_list';
break;
case 'blog':
return 'mod_forum/blog_discussion_list';
break;
case 'qanda':
return 'mod_forum/qanda_discussion_list';
break;
default:
return 'mod_forum/discussion_list';
}
}
/**
* Get the groups details for all groups available to the forum.
*
* @return stdClass[]
*/
private function get_groups_available_in_forum() : array {
$course = $this->forum->get_course_record();
$coursemodule = $this->forum->get_course_module_record();
return groups_get_all_groups($course->id, 0, $coursemodule->groupingid);
}
/**
* Get the author's groups for a list of posts.
*
* @param post_entity[] $posts The list of posts
* @return array Author groups indexed by author id
*/
private function get_author_groups_from_posts(array $posts) : array {
$course = $this->forum->get_course_record();
$coursemodule = $this->forum->get_course_module_record();
$authorids = array_reduce($posts, function($carry, $post) {
$carry[$post->get_author_id()] = true;
return $carry;
}, []);
$authorgroups = groups_get_all_groups(
$course->id,
array_keys($authorids),
$coursemodule->groupingid,
'g.*, gm.id, gm.groupid, gm.userid'
);
$authorgroups = array_reduce($authorgroups, function($carry, $group) {
// Clean up data returned from groups_get_all_groups.
$userid = $group->userid;
$groupid = $group->groupid;
unset($group->userid);
unset($group->groupid);
$group->id = $groupid;
if (!isset($carry[$userid])) {
$carry[$userid] = [$group];
} else {
$carry[$userid][] = $group;
}
return $carry;
}, []);
foreach (array_diff(array_keys($authorids), array_keys($authorgroups)) as $authorid) {
$authorgroups[$authorid] = [];
}
return $authorgroups;
}
/**
* Get the list of notification for display.
*
* @param stdClass $user The viewing user
* @param int|null $groupid The forum's group id
* @return array
*/
private function get_notifications(stdClass $user, ?int $groupid) : array {
$notifications = $this->notifications;
$forum = $this->forum;
$renderer = $this->renderer;
$capabilitymanager = $this->capabilitymanager;
if ($forum->has_blocking_enabled()) {
$notifications[] = (new notification(
get_string('thisforumisthrottled', 'forum', [
'blockafter' => $forum->get_block_after(),
'blockperiod' => get_string('secondstotime' . $forum->get_block_period())
])
))->set_show_closebutton();
}
if ($forum->is_in_group_mode()) {
if (
($groupid === null && !$capabilitymanager->can_access_all_groups($user)) ||
($groupid !== null && !$capabilitymanager->can_access_group($user, $groupid))
) {
// Cannot post to the current group.
$notifications[] = (new notification(
get_string('cannotadddiscussion', 'mod_forum'),
\core\output\notification::NOTIFY_WARNING
))->set_show_closebutton();
}
}
if ('qanda' === $forum->get_type() && !$capabilitymanager->can_manage_forum($user)) {
$notifications[] = (new notification(
get_string('qandanotify', 'forum'),
notification::NOTIFY_INFO
))->set_show_closebutton();
}
if ('eachuser' === $forum->get_type()) {
$notifications[] = (new notification(
get_string('allowsdiscussions', 'forum'),
notification::NOTIFY_INFO)
)->set_show_closebutton();
}
return array_map(function($notification) {
return $notification->export_for_template($this->renderer);
}, $notifications);
}
}
+111
View File
@@ -0,0 +1,111 @@
<?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/>.
/**
* Posts renderer.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\renderers;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\builders\exported_posts as exported_posts_builder;
use renderer_base;
use stdClass;
/**
* Posts renderer class.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class posts {
/** @var renderer_base $renderer Renderer base */
private $renderer;
/** @var exported_posts_builder $exportedpostsbuilder Builder for building exported posts */
private $exportedpostsbuilder;
/** @var string $template The template to render */
private $template;
/** @var callable $postprocessfortemplate Function to process exported posts before template rendering */
private $postprocessfortemplate;
/**
* Constructor.
*
* @param renderer_base $renderer Renderer base
* @param exported_posts_builder $exportedpostsbuilder Builder for building exported posts
* @param string $template The template to render
* @param callable $postprocessfortemplate Function to process exported posts before template rendering
*/
public function __construct(
renderer_base $renderer,
exported_posts_builder $exportedpostsbuilder,
string $template,
callable $postprocessfortemplate = null
) {
$this->renderer = $renderer;
$this->exportedpostsbuilder = $exportedpostsbuilder;
$this->template = $template;
$this->postprocessfortemplate = $postprocessfortemplate;
}
/**
* Render the given posts for the forums and discussions.
*
* @param stdClass $user The user viewing the posts
* @param forum_entity[] $forums A list of all forums for these posts
* @param discussion_entity[] $discussions A list of all discussions for these posts
* @param post_entity[] $posts The posts to render
* @return string
*/
public function render(
stdClass $user,
array $forums,
array $discussions,
array $posts
) : string {
// Format the forums and discussion to make them more easily accessed later.
$forums = array_reduce($forums, function($carry, $forum) {
$carry[$forum->get_id()] = $forum;
return $carry;
}, []);
$discussions = array_reduce($discussions, function($carry, $discussion) {
$carry[$discussion->get_id()] = $discussion;
return $carry;
}, []);
$exportedposts = $this->exportedpostsbuilder->build(
$user,
$forums,
$discussions,
$posts
);
if ($this->postprocessfortemplate !== null) {
// We've got some post processing to do!
$exportedposts = ($this->postprocessfortemplate)($exportedposts, $forums, $discussions, $user);
}
return $this->renderer->render_from_template(
$this->template,
['posts' => array_values($exportedposts)]
);
}
}
+101
View File
@@ -0,0 +1,101 @@
<?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/>.
/**
* Author vault class.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\vaults;
defined('MOODLE_INTERNAL') || die();
/**
* Author vault class.
*
* This should be the only place that accessed the database.
*
* This uses the repository pattern. See:
* https://designpatternsphp.readthedocs.io/en/latest/More/Repository/README.html
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class author extends db_table_vault {
/** The table for this vault */
private const TABLE = 'user';
/**
* Get the table alias.
*
* @return string
*/
protected function get_table_alias() : string {
return 'a';
}
/**
* Build the SQL to be used in get_records_sql.
*
* @param string|null $wheresql Where conditions for the SQL
* @param string|null $sortsql Order by conditions for the SQL
* @return string
*/
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null) : string {
$selectsql = 'SELECT * FROM {' . self::TABLE . '} ' . $this->get_table_alias();
$selectsql .= $wheresql ? ' WHERE ' . $wheresql : '';
$selectsql .= $sortsql ? ' ORDER BY ' . $sortsql : '';
return $selectsql;
}
/**
* Convert the DB records into author entities.
*
* @param array $results The DB records
* @return author_entity[]
*/
protected function from_db_records(array $results) {
$entityfactory = $this->get_entity_factory();
return array_map(function(array $result) use ($entityfactory) {
[
'record' => $record,
] = $result;
return $entityfactory->get_author_from_stdclass($record);
}, $results);
}
/**
* Get the authors for the given posts.
*
* Returns a distinct list of authors indexed by author id.
*
* @param post_entity[] $posts The list of posts
* @return author_entity[]
*/
public function get_authors_for_posts(array $posts) : array {
$authorids = array_reduce($posts, function($carry, $post) {
$carry[$post->get_author_id()] = true;
return $carry;
}, []);
$authorids = array_keys($authorids);
return $this->get_from_ids($authorids);
}
}
@@ -0,0 +1,173 @@
<?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/>.
/**
* Abstract class for loading records from the DB.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\vaults;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\factories\entity as entity_factory;
use moodle_database;
/**
* Abstract class for loading records from the DB.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class db_table_vault {
/** @var moodle_database $db A moodle database */
private $db;
/** @var entity_factory $entityfactory Entity factory */
private $entityfactory;
/**
* Constructor.
*
* @param moodle_database $db A moodle database
* @param entity_factory $entityfactory Entity factory
*/
public function __construct(
moodle_database $db,
entity_factory $entityfactory
) {
$this->db = $db;
$this->entityfactory = $entityfactory;
}
/**
* Get the table alias.
*
* @return string
*/
abstract protected function get_table_alias() : string;
/**
* Build the SQL to be used in get_records_sql.
*
* @param string|null $wheresql Where conditions for the SQL
* @param string|null $sortsql Order by conditions for the SQL
* @return string
*/
abstract protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null) : string;
/**
* Convert the DB records into entities. The list of records will have been
* passed through any preprocessors that may be defined before being given to
* this function.
*
* Each result will from the list will be an associative array where the key
* is set to the identifier given to the preprocessor and the result is the value
* generated by the preprocessor.
*
* All results will have a 'record' key who's value is the original DB record.
*
* @param array $results The DB records
*/
abstract protected function from_db_records(array $results);
/**
* Get the list of preprocessors to run on the DB record results. The preprocessors
* should be defined using an associative array. The key used to identify the
* preprocessor in this list will be used to identify the value of that preprocessor
* in the list of results when passed to the from_db_records function.
*
* @return array
*/
protected function get_preprocessors() : array {
return [];
}
/**
* Get the moodle database.
*
* @return moodle_database
*/
protected function get_db() : moodle_database {
return $this->db;
}
/**
* Get the entity factory.
*
* @return entity_factory
*/
protected function get_entity_factory() : entity_factory {
return $this->entityfactory;
}
/**
* Execute the defined preprocessors on the DB record results and then convert
* them into entities.
*
* @param stdClass[] $records List of DB results
* @return array
*/
protected function transform_db_records_to_entities(array $records) {
$preprocessors = $this->get_preprocessors();
$result = array_map(function($record) {
return ['record' => $record];
}, $records);
$result = array_reduce(array_keys($preprocessors), function($carry, $preprocessor) use ($records, $preprocessors) {
$step = $preprocessors[$preprocessor];
$dependencies = $step->execute($records);
foreach ($dependencies as $index => $dependency) {
// Add the new dependency to the list.
$carry[$index] = array_merge($carry[$index], [$preprocessor => $dependency]);
}
return $carry;
}, $result);
return $this->from_db_records($result);
}
/**
* Get the entity for the given id.
*
* @param int $id Identifier for the entity
* @return object|null
*/
public function get_from_id(int $id) {
$records = $this->get_from_ids([$id]);
return count($records) ? array_shift($records) : null;
}
/**
* Get the list of entities for the given ids.
*
* @param int[] $ids Identifiers
* @return array
*/
public function get_from_ids(array $ids) {
$alias = $this->get_table_alias();
list($insql, $params) = $this->get_db()->get_in_or_equal($ids);
$wheresql = $alias . '.id ' . $insql;
$sql = $this->generate_get_records_sql($wheresql);
$records = $this->get_db()->get_records_sql($sql, $params);
return $this->transform_db_records_to_entities($records);
}
}
@@ -0,0 +1,102 @@
<?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/>.
/**
* Discussion vault class.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\vaults;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\entities\discussion as discussion_entity;
/**
* Discussion vault class.
*
* This should be the only place that accessed the database.
*
* This uses the repository pattern. See:
* https://designpatternsphp.readthedocs.io/en/latest/More/Repository/README.html
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class discussion extends db_table_vault {
/** The table for this vault */
private const TABLE = 'forum_discussions';
/**
* Get the table alias.
*
* @return string
*/
protected function get_table_alias() : string {
return 'd';
}
/**
* Build the SQL to be used in get_records_sql.
*
* @param string|null $wheresql Where conditions for the SQL
* @param string|null $sortsql Order by conditions for the SQL
* @return string
*/
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null) : string {
$selectsql = 'SELECT * FROM {' . self::TABLE . '} ' . $this->get_table_alias();
$selectsql .= $wheresql ? ' WHERE ' . $wheresql : '';
$selectsql .= $sortsql ? ' ORDER BY ' . $sortsql : '';
return $selectsql;
}
/**
* Convert the DB records into discussion entities.
*
* @param array $results The DB records
* @return discussion_entity[]
*/
protected function from_db_records(array $results) {
$entityfactory = $this->get_entity_factory();
return array_map(function(array $result) use ($entityfactory) {
[
'record' => $record,
] = $result;
return $entityfactory->get_discussion_from_stdclass($record);
}, $results);
}
/**
* Get the first discussion in the specified forum.
*
* @param forum_entity $forum
* @return discussion_entity|null
*/
public function get_first_discussion_in_forum(forum_entity $forum) : ?discussion_entity {
$records = $this->get_db()->get_records(self::TABLE, [
'forum' => $forum->get_id(),
], 'timemodified ASC', '*', 0, 1);
$records = $this->transform_db_records_to_entities($records);
return count($records) ? array_shift($records) : null;
}
}
@@ -0,0 +1,385 @@
<?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/>.
/**
* Vault class for a discussion list.
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\vaults;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\vaults\preprocessors\extract_record as extract_record_preprocessor;
use mod_forum\local\vaults\preprocessors\extract_user as extract_user_preprocessor;
use mod_forum\local\renderers\discussion_list as discussion_list_renderer;
use stdClass;
/**
* Discussion list vault.
*
* This should be the only place that accessed the database.
*
* This uses the repository pattern. See:
* https://designpatternsphp.readthedocs.io/en/latest/More/Repository/README.html
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class discussion_list extends db_table_vault {
/** The table for this vault */
private const TABLE = 'forum_discussions';
/** Alias for first author id */
private const FIRST_AUTHOR_ID_ALIAS = 'userpictureid';
/** Alias for author fields */
private const FIRST_AUTHOR_ALIAS = 'userrecord';
/** Alias for last author id */
private const LATEST_AUTHOR_ID_ALIAS = 'userpictureid';
/** Alias for last author fields */
private const LATEST_AUTHOR_ALIAS = 'userrecord';
/** Default limit */
public const PAGESIZE_DEFAULT = 100;
/** Sort by newest first */
public const SORTORDER_NEWEST_FIRST = 1;
/** Sort by oldest first */
public const SORTORDER_OLDEST_FIRST = 2;
/**
* Get the table alias.
*
* @return string
*/
protected function get_table_alias() : string {
return 'd';
}
/**
* Build the SQL to be used in get_records_sql.
*
* @param string|null $wheresql Where conditions for the SQL
* @param string|null $sortsql Order by conditions for the SQL
* @return string
*/
protected function generate_get_records_sql(string $wheresql = null, ?string $sortsql = null) : string {
$alias = $this->get_table_alias();
$db = $this->get_db();
// Fetch:
// - Discussion
// - First post
// - Author
// - Most recent editor.
$tablefields = $db->get_preload_columns(self::TABLE, $alias);
$postfields = $db->get_preload_columns('forum_posts', 'p_');
$firstauthorfields = \user_picture::fields('fa', null, self::FIRST_AUTHOR_ID_ALIAS, self::FIRST_AUTHOR_ALIAS);
$latestuserfields = \user_picture::fields('la', null, self::LATEST_AUTHOR_ID_ALIAS, self::LATEST_AUTHOR_ALIAS);
$fields = implode(', ', [
$db->get_preload_columns_sql($tablefields, $alias),
$db->get_preload_columns_sql($postfields, 'fp'),
$firstauthorfields,
$latestuserfields,
]);
$tables = '{' . self::TABLE . '} ' . $alias;
$tables .= ' JOIN {user} fa ON fa.id = ' . $alias . '.userid';
$tables .= ' JOIN {user} la ON la.id = ' . $alias . '.usermodified';
$tables .= ' JOIN {forum_posts} fp ON fp.id = ' . $alias . '.firstpost';
$selectsql = 'SELECT ' . $fields . ' FROM ' . $tables;
$selectsql .= $wheresql ? ' WHERE ' . $wheresql : '';
$selectsql .= $sortsql ? ' ORDER BY ' . $sortsql : '';
return $selectsql;
}
/**
* Build the SQL to be used in count_records_sql.
*
* @param string|null $wheresql Where conditions for the SQL
* @return string
*/
protected function generate_count_records_sql(string $wheresql = null) : string {
$alias = $this->get_table_alias();
$db = $this->get_db();
$selectsql = "SELECT COUNT(1) FROM {" . self::TABLE . "} {$alias}";
$selectsql .= $wheresql ? ' WHERE ' . $wheresql : '';
return $selectsql;
}
/**
* Get a list of preprocessors to execute on the DB results before being converted
* into entities.
*
* @return array
*/
protected function get_preprocessors() : array {
return array_merge(
parent::get_preprocessors(),
[
'discussion' => new extract_record_preprocessor($this->get_db(), self::TABLE, $this->get_table_alias()),
'firstpost' => new extract_record_preprocessor($this->get_db(), 'forum_posts', 'p_'),
'firstpostauthor' => new extract_user_preprocessor(self::FIRST_AUTHOR_ID_ALIAS, self::FIRST_AUTHOR_ALIAS),
'latestpostauthor' => new extract_user_preprocessor(self::LATEST_AUTHOR_ID_ALIAS, self::LATEST_AUTHOR_ALIAS),
]
);
}
/**
* Convert the DB records into discussion list entities.
*
* @param array $results The DB records
* @return discussion_list[]
*/
protected function from_db_records(array $results) {
$entityfactory = $this->get_entity_factory();
return array_map(function(array $result) use ($entityfactory) {
[
'discussion' => $discussion,
'firstpost' => $firstpost,
'firstpostauthor' => $firstpostauthor,
'latestpostauthor' => $latestpostauthor,
] = $result;
return $entityfactory->get_discussion_summary_from_stdclass(
$discussion,
$firstpost,
$firstpostauthor,
$latestpostauthor
);
}, $results);
}
/**
* Get the sort order SQL for a sort method.
*
* @param int|null $sortmethod
*/
public function get_sort_order(?int $sortmethod) : string {
global $CFG;
$alias = $this->get_table_alias();
// TODO consider user favourites...
$keyfield = "{$alias}.timemodified";
$direction = "DESC";
if ($sortmethod == self::SORTORDER_OLDEST_FIRST) {
$direction = "ASC";
}
if (!empty($CFG->forum_enabletimedposts)) {
$keyfield = "CASE WHEN {$keyfield} < {$alias}.timestart THEN {$alias}.timestart ELSE {$keyfield} END";
}
return "{$alias}.pinned DESC, {$keyfield} {$direction}";
}
/**
* Fetch any required SQL to respect timed posts.
*
* @param bool $includehiddendiscussions Whether to include hidden discussions or not
* @param int $includepostsforuser Which user to include posts for, if any
* @return array The SQL and parameters to include
*/
protected function get_hidden_post_sql(bool $includehiddendiscussions, ?int $includepostsforuser) {
$wheresql = '';
$params = [];
if (!$includehiddendiscussions) {
$now = time();
$wheresql = " AND ((d.timestart <= :timestart AND (d.timeend = 0 OR d.timeend > :timeend))";
$params['timestart'] = $now;
$params['timeend'] = $now;
if (null !== $includepostsforuser) {
$wheresql .= " OR d.userid = :byuser";
$params['byuser'] = $includepostsforuser;
}
$wheresql .= ")";
}
return [
'wheresql' => $wheresql,
'params' => $params,
];
}
/**
* Get each discussion, first post, first and last post author for the given forum, considering timed posts, and
* pagination.
*
* @param int $forumid The forum to fetch the discussion set for
* @param bool $includehiddendiscussions Whether to include hidden discussions or not
* @param int|null $includepostsforuser Which user to include posts for, if any
* @param int $sortorder The sort order to use
* @param int $limit The number of discussions to fetch
* @param int $offset The record offset
* @return array The set of data fetched
*/
public function get_from_forum_id(
int $forumid,
bool $includehiddendiscussions,
?int $includepostsforuser,
?int $sortorder,
int $limit,
int $offset
) {
$alias = $this->get_table_alias();
$wheresql = "{$alias}.forum = :forumid";
[
'wheresql' => $hiddensql,
'params' => $hiddenparams
] = $this->get_hidden_post_sql($includehiddendiscussions, $includepostsforuser);
$wheresql .= $hiddensql;
$params = array_merge($hiddenparams, [
'forumid' => $forumid,
]);
$sql = $this->generate_get_records_sql($wheresql, $this->get_sort_order($sortorder));
$records = $this->get_db()->get_records_sql($sql, $params, $offset, $limit);
return $this->transform_db_records_to_entities($records);
}
/**
* Get each discussion, first post, first and last post author for the given forum, and the set of groups to display
* considering timed posts, and pagination.
*
* @param int $forumid The forum to fetch the discussion set for
* @param int[] $groupids The list of real groups to filter on
* @param bool $includehiddendiscussions Whether to include hidden discussions or not
* @param int|null $includepostsforuser Which user to include posts for, if any
* @param int $sortorder The sort order to use
* @param int $limit The number of discussions to fetch
* @param int $offset The record offset
* @return array The set of data fetched
*/
public function get_from_forum_id_and_group_id(
int $forumid,
array $groupids,
bool $includehiddendiscussions,
?int $includepostsforuser,
?int $sortorder,
int $limit,
int $offset
) {
$alias = $this->get_table_alias();
$wheresql = "{$alias}.forum = :forumid AND ";
$groupparams = [];
if (empty($groupids)) {
$wheresql .= "{$alias}.groupid = :allgroupsid";
} else {
list($insql, $groupparams) = $this->get_db()->get_in_or_equal($groupids, SQL_PARAMS_NAMED, 'gid');
$wheresql .= "({$alias}.groupid = :allgroupsid OR {$alias}.groupid {$insql})";
}
[
'wheresql' => $hiddensql,
'params' => $hiddenparams
] = $this->get_hidden_post_sql($includehiddendiscussions, $includepostsforuser);
$wheresql .= $hiddensql;
$params = array_merge($hiddenparams, $groupparams, [
'forumid' => $forumid,
'allgroupsid' => -1,
]);
$sql = $this->generate_get_records_sql($wheresql, $this->get_sort_order($sortorder));
$records = $this->get_db()->get_records_sql($sql, $params, $offset, $limit);
return $this->transform_db_records_to_entities($records);
}
/**
* Count the number of discussions in the forum.
*
* @param int $forumid Id of the forum to count discussions in
* @param bool $includehiddendiscussions Include hidden dicussions in the count?
* @param int|null $includepostsforuser Include discussions created by this user in the count
* (only works if not including hidden discussions).
* @return int
*/
public function get_total_discussion_count_from_forum_id(
int $forumid,
bool $includehiddendiscussions,
?int $includepostsforuser
) {
$alias = $this->get_table_alias();
$wheresql = "{$alias}.forum = :forumid";
[
'wheresql' => $hiddensql,
'params' => $hiddenparams
] = $this->get_hidden_post_sql($includehiddendiscussions, $includepostsforuser);
$wheresql .= $hiddensql;
$params = array_merge($hiddenparams, [
'forumid' => $forumid,
]);
return $this->get_db()->count_records_sql($this->generate_count_records_sql($wheresql), $params);
}
/**
* Count the number of discussions in all groups and the list of groups provided.
*
* @param int $forumid Id of the forum to count discussions in
* @param int[] $groupids List of group ids to include in the count (discussions in all groups will always be counted)
* @param bool $includehiddendiscussions Include hidden dicussions in the count?
* @param int|null $includepostsforuser Include discussions created by this user in the count
* (only works if not including hidden discussions).
* @return int
*/
public function get_total_discussion_count_from_forum_id_and_group_id(
int $forumid,
array $groupids,
bool $includehiddendiscussions,
?int $includepostsforuser
) {
$alias = $this->get_table_alias();
$wheresql = "{$alias}.forum = :forumid AND ";
$groupparams = [];
if (empty($groupids)) {
$wheresql .= "{$alias}.groupid = :allgroupsid";
} else {
list($insql, $groupparams) = $this->get_db()->get_in_or_equal($groupids, SQL_PARAMS_NAMED, 'gid');
$wheresql .= "({$alias}.groupid = :allgroupsid OR {$alias}.groupid {$insql})";
}
[
'wheresql' => $hiddensql,
'params' => $hiddenparams
] = $this->get_hidden_post_sql($includehiddendiscussions, $includepostsforuser);
$wheresql .= $hiddensql;
$params = array_merge($hiddenparams, $groupparams, [
'forumid' => $forumid,
'allgroupsid' => -1,
]);
return $this->get_db()->count_records_sql($this->generate_count_records_sql($wheresql), $params);
}
}
+156
View File
@@ -0,0 +1,156 @@
<?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 vault class.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\vaults;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\vaults\preprocessors\extract_context as extract_context_preprocessor;
use mod_forum\local\vaults\preprocessors\extract_record as extract_record_preprocessor;
use context_helper;
/**
* Forum vault class.
*
* This should be the only place that accessed the database.
*
* This uses the repository pattern. See:
* https://designpatternsphp.readthedocs.io/en/latest/More/Repository/README.html
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class forum extends db_table_vault {
/** The table for this vault */
private const TABLE = 'forum';
/**
* Get the table alias.
*
* @return string
*/
protected function get_table_alias() : string {
return 'f';
}
/**
* Build the SQL to be used in get_records_sql.
*
* @param string|null $wheresql Where conditions for the SQL
* @param string|null $sortsql Order by conditions for the SQL
* @return string
*/
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null) : string {
$db = $this->get_db();
$alias = $this->get_table_alias();
$tablefields = $db->get_preload_columns(self::TABLE, $alias);
$coursemodulefields = $db->get_preload_columns('course_modules', 'cm_');
$coursefields = $db->get_preload_columns('course', 'c_');
$fields = implode(', ', [
$db->get_preload_columns_sql($tablefields, $alias),
context_helper::get_preload_record_columns_sql('ctx'),
$db->get_preload_columns_sql($coursemodulefields, 'cm'),
$db->get_preload_columns_sql($coursefields, 'c'),
]);
$tables = '{' . self::TABLE . '} ' . $alias;
$tables .= " JOIN {modules} m ON m.name = 'forum'";
$tables .= " JOIN {course_modules} cm ON cm.module = m.id AND cm.instance = {$alias}.id";
$tables .= ' JOIN {context} ctx ON ctx.contextlevel = ' . CONTEXT_MODULE . ' AND ctx.instanceid = cm.id';
$tables .= " JOIN {course} c ON c.id = {$alias}.course";
$selectsql = 'SELECT ' . $fields . ' FROM ' . $tables;
$selectsql .= $wheresql ? ' WHERE ' . $wheresql : '';
$selectsql .= $sortsql ? ' ORDER BY ' . $sortsql : '';
return $selectsql;
}
/**
* Get a list of preprocessors to execute on the DB results before being converted
* into entities.
*
* @return array
*/
protected function get_preprocessors() : array {
return array_merge(
parent::get_preprocessors(),
[
'forum' => new extract_record_preprocessor($this->get_db(), self::TABLE, $this->get_table_alias()),
'course_module' => new extract_record_preprocessor($this->get_db(), 'course_modules', 'cm_'),
'course' => new extract_record_preprocessor($this->get_db(), 'course', 'c_'),
'context' => new extract_context_preprocessor(),
]
);
}
/**
* Convert the DB records into forum entities.
*
* @param array $results The DB records
* @return forum_entity[]
*/
protected function from_db_records(array $results) : array {
$entityfactory = $this->get_entity_factory();
return array_map(function(array $result) use ($entityfactory) {
[
'forum' => $forumrecord,
'course_module' => $coursemodule,
'course' => $course,
'context' => $context,
] = $result;
return $entityfactory->get_forum_from_stdclass($forumrecord, $context, $coursemodule, $course);
}, $results);
}
/**
* Get the forum for the given course module id.
*
* @param int $id The course module id
* @return forum_entity|null
*/
public function get_from_course_module_id(int $id) : ?forum_entity {
$records = $this->get_from_course_module_ids([$id]);
return count($records) ? array_shift($records) : null;
}
/**
* Get the forums for the given course module ids
*
* @param int[] $ids The course module ids
* @return forum_entity[]
*/
public function get_from_course_module_ids(array $ids) : array {
$alias = $this->get_table_alias();
list($insql, $params) = $this->get_db()->get_in_or_equal($ids);
$wheresql = 'cm.id ' . $insql;
$sql = $this->generate_get_records_sql($wheresql);
$records = $this->get_db()->get_records_sql($sql, $params);
return $this->transform_db_records_to_entities($records);
}
}
+271
View File
@@ -0,0 +1,271 @@
<?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 vault class.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\vaults;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\post as post_entity;
use mod_forum\local\factories\entity as entity_factory;
use stdClass;
/**
* Post vault class.
*
* This should be the only place that accessed the database.
*
* This uses the repository pattern. See:
* https://designpatternsphp.readthedocs.io/en/latest/More/Repository/README.html
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class post extends db_table_vault {
/** The table for this vault */
private const TABLE = 'forum_posts';
/** Alias for user id */
private const USER_ID_ALIAS = 'userpictureid';
/** Alias for user fields */
private const USER_ALIAS = 'userrecord';
/**
* Get the table alias.
*
* @return string
*/
protected function get_table_alias() : string {
return 'p';
}
/**
* Build the SQL to be used in get_records_sql.
*
* @param string|null $wheresql Where conditions for the SQL
* @param string|null $sortsql Order by conditions for the SQL
* @return string
*/
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null) : string {
$table = self::TABLE;
$alias = $this->get_table_alias();
$fields = $alias . '.*';
$tables = "{{$table}} {$alias}";
$selectsql = "SELECT {$fields} FROM {$tables}";
$selectsql .= $wheresql ? ' WHERE ' . $wheresql : '';
$selectsql .= $sortsql ? ' ORDER BY ' . $sortsql : '';
return $selectsql;
}
/**
* Convert the DB records into post entities.
*
* @param array $results The DB records
* @return post_entity[]
*/
protected function from_db_records(array $results) {
$entityfactory = $this->get_entity_factory();
return array_map(function(array $result) use ($entityfactory) {
['record' => $record] = $result;
return $entityfactory->get_post_from_stdclass($record);
}, $results);
}
/**
* Get the post ids for the given discussion.
*
* @param int $discussionid The discussion to load posts for
* @param string $orderby Order the results
* @return post_entity[]
*/
public function get_from_discussion_id(int $discussionid, string $orderby = 'created ASC') : array {
$alias = $this->get_table_alias();
$wheresql = $alias . '.discussion = ?';
$orderbysql = $alias . '.' . $orderby;
$sql = $this->generate_get_records_sql($wheresql, $orderbysql);
$records = $this->get_db()->get_records_sql($sql, [$discussionid]);
return $this->transform_db_records_to_entities($records);
}
/**
* Get the list of posts for the given discussions.
*
* @param int[] $discussionids The list of discussion ids to load posts for
* @return post_entity[]
*/
public function get_from_discussion_ids(array $discussionids) : array {
if (empty($discussionids)) {
return [];
}
$alias = $this->get_table_alias();
list($insql, $params) = $this->get_db()->get_in_or_equal($discussionids);
$wheresql = "{$alias}.discussion {$insql}";
$sql = $this->generate_get_records_sql($wheresql, '');
$records = $this->get_db()->get_records_sql($sql, $params);
return $this->transform_db_records_to_entities($records);
}
/**
* Load a list of replies to the given post. This will load all descendants of the post.
* That is, all direct replies and replies to those replies etc.
*
* The return value will be a flat array of posts in the requested order.
*
* @param post_entity $post The post to load replies for
* @param string $orderby How to order the replies
* @return post_entity[]
*/
public function get_replies_to_post(post_entity $post, string $orderby = 'created ASC') : array {
$alias = $this->get_table_alias();
$params = [$post->get_discussion_id(), $post->get_time_created()];
// Unfortunately the best we can do to filter down the query is ignore all posts
// that were created before the given post (since they can't be replies).
$wheresql = "{$alias}.discussion = ? and {$alias}.created > ?";
$orderbysql = $alias . '.' . $orderby;
$sql = $this->generate_get_records_sql($wheresql, $orderbysql);
$records = $this->get_db()->get_records_sql($sql, $params);
$posts = $this->transform_db_records_to_entities($records);
$sorter = $this->get_entity_factory()->get_posts_sorter();
// We need to sort all of the values into the replies tree in order to capture
// the full list of descendants.
$sortedposts = $sorter->sort_into_children($posts);
$replies = [];
// From the sorted list we can grab the first elements and check if they are replies
// to the post we care about. If so we keep them.
foreach ($sortedposts as $candidate) {
[$candidatepost, $candidatereplies] = $candidate;
if ($candidatepost->has_parent() && $candidatepost->get_parent_id() == $post->get_id()) {
$replies[] = $candidate;
}
}
if (empty($replies)) {
return $replies;
}
$getreplypostids = function($candidates) use (&$getreplypostids) {
$ids = [];
foreach ($candidates as $candidate) {
[$reply, $replies] = $candidate;
$ids = array_merge($ids, [$reply->get_id()], $getreplypostids($replies));
}
return $ids;
};
// Recursively build a list of the ids of all posts in the full reply tree.
$replypostids = $getreplypostids($replies);
// Now go back and filter the original result set down to just the posts that
// we've flagged as in the reply tree. We need to filter the original set of values
// so that we can maintain the requested sort order.
return array_values(array_filter($posts, function($post) use ($replypostids) {
return in_array($post->get_id(), $replypostids);
}));
}
/**
* Get a mapping of replies to the specified discussions.
*
* @param int[] $discussionids The list of discussions to fetch counts for
* @return int[] The number of replies for each discussion returned in an associative array
*/
public function get_reply_count_for_discussion_ids(array $discussionids) : array {
if (empty($discussionids)) {
return [];
}
list($insql, $params) = $this->get_db()->get_in_or_equal($discussionids);
$sql = "SELECT discussion, COUNT(1) FROM {" . self::TABLE . "} p " .
"WHERE p.discussion {$insql} AND p.parent > 0 GROUP BY discussion";
return $this->get_db()->get_records_sql_menu($sql, $params);
}
/**
* Get a mapping of unread post counts for the specified discussions.
*
* @param stdClass $user The user to fetch counts for
* @param int[] $discussionids The list of discussions to fetch counts for
* @return int[] The count of unread posts for each discussion returned in an associative array
*/
public function get_unread_count_for_discussion_ids(stdClass $user, array $discussionids) : array {
global $CFG;
if (empty($discussionids)) {
return [];
}
$alias = $this->get_table_alias();
list($insql, $params) = $this->get_db()->get_in_or_equal($discussionids, SQL_PARAMS_NAMED);
$sql = "SELECT p.discussion, COUNT(p.id) FROM {" . self::TABLE . "} p
LEFT JOIN {forum_read} r ON r.postid = p.id AND r.userid = :userid
WHERE p.discussion {$insql} AND p.modified > :cutofftime AND r.id IS NULL
GROUP BY p.discussion";
$params['userid'] = $user->id;
$params['cutofftime'] = floor((new \DateTime())
->sub(new \DateInterval("P{$CFG->forum_oldpostdays}D"))
->format('U') / 60) * 60;
return $this->get_db()->get_records_sql_menu($sql, $params);
}
/**
* Get a mapping of the most recent post in each discussion based on post creation time.
*
* @param int[] $discussionids The list of discussions to fetch counts for
* @return int[] The post id of the most recent post for each discussions returned in an associative array
*/
public function get_latest_post_id_for_discussion_ids(array $discussionids) : array {
global $CFG;
if (empty($discussionids)) {
return [];
}
$alias = $this->get_table_alias();
list($insql, $params) = $this->get_db()->get_in_or_equal($discussionids, SQL_PARAMS_NAMED);
$sql = "
SELECT p.discussion, MAX(p.id)
FROM {" . self::TABLE . "} p
JOIN (
SELECT mp.discussion, MAX(mp.created) AS created
FROM {" . self::TABLE . "} mp
WHERE mp.discussion {$insql}
GROUP BY mp.discussion
) lp ON lp.discussion = p.discussion AND lp.created = p.created
GROUP BY p.discussion";
return $this->get_db()->get_records_sql_menu($sql, $params);
}
}
@@ -0,0 +1,98 @@
<?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 attachment vault class.
*
* @package mod_forum
* @copyright 2018 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\vaults;
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\post as post_entity;
use context;
use file_storage;
/**
* Post attachment vault class.
*
* This should be the only place that accessed the database.
*
* This uses the repository pattern. See:
* https://designpatternsphp.readthedocs.io/en/latest/More/Repository/README.html
*
* @copyright 2018 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class post_attachment {
/** The component for attachments */
private const COMPONENT = 'mod_forum';
/** File area for attachments */
private const FILE_AREA = 'attachment';
/** Sort the attachments by filename */
private const SORT = 'filename';
/** Don't include directories */
private const INCLUDE_DIRECTORIES = false;
/** @var file_storage $filestorage File storage */
private $filestorage;
/**
* Construct.
*
* @param file_storage $filestorage File storage
*/
public function __construct(file_storage $filestorage) {
$this->filestorage = $filestorage;
}
/**
* Get the attachments for the given posts. The results are indexed by
* post id.
*
* @param context $context The (forum) context that the posts are in
* @param post_entity[] $posts The list of posts to load attachments for
* @return array Post attachments indexed by post id
*/
public function get_attachments_for_posts(context $context, array $posts) {
$itemids = array_map(function($post) {
return $post->get_id();
}, $posts);
$files = $this->filestorage->get_area_files(
$context->id,
self::COMPONENT,
self::FILE_AREA,
$itemids,
self::SORT,
self::INCLUDE_DIRECTORIES
);
$filesbyid = array_reduce($posts, function($carry, $post) {
$carry[$post->get_id()] = [];
return $carry;
}, []);
return array_reduce($files, function($carry, $file) {
$itemid = $file->get_itemid();
$carry[$itemid] = array_merge($carry[$itemid], [$file]);
return $carry;
}, $filesbyid);
}
}
@@ -0,0 +1,120 @@
<?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 read receipt collection class.
*
* @package mod_forum
* @copyright 2018 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\vaults;
defined('MOODLE_INTERNAL') || die();
use stdClass;
/**
* Post read receipt collection class.
*
* This should be the only place that accessed the database.
*
* This uses the repository pattern. See:
* https://designpatternsphp.readthedocs.io/en/latest/More/Repository/README.html
*
* @copyright 2018 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class post_read_receipt_collection extends db_table_vault {
/** The table for this vault */
private const TABLE = 'forum_read';
/**
* Get the table alias.
*
* @return string
*/
protected function get_table_alias() : string {
return 'fr';
}
/**
* Build the SQL to be used in get_records_sql.
*
* @param string|null $wheresql Where conditions for the SQL
* @param string|null $sortsql Order by conditions for the SQL
* @return string
*/
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null) : string {
$selectsql = 'SELECT * FROM {' . self::TABLE . '} ' . $this->get_table_alias();
$selectsql .= $wheresql ? ' WHERE ' . $wheresql : '';
$selectsql .= $sortsql ? ' ORDER BY ' . $sortsql : '';
return $selectsql;
}
/**
* Convert the DB records into post_read_receipt_collection entities.
*
* @param array $results The DB records
* @return post_read_receipt_collection
*/
protected function from_db_records(array $results) {
$entityfactory = $this->get_entity_factory();
$records = array_map(function($result) {
return $result['record'];
}, $results);
return $entityfactory->get_post_read_receipt_collection_from_stdclasses($records);
}
/**
* Load the post_read_receipt_collection for the given user and set
* of posts.
*
* @param int $userid Id of the user to load receipts for
* @param int[] $postids List of post ids to load receipts for
* @return post_read_receipt_collection
*/
public function get_from_user_id_and_post_ids(int $userid, array $postids) {
$alias = $this->get_table_alias();
[$postidinsql, $params] = $this->get_db()->get_in_or_equal($postids);
$params[] = $userid;
$wheresql = "{$alias}.postid {$postidinsql}";
$wheresql .= " AND {$alias}.userid = ?";
$sql = $this->generate_get_records_sql($wheresql);
$records = $this->get_db()->get_records_sql($sql, $params);
return $this->transform_db_records_to_entities($records);
}
/**
* Load the post_read_receipt_collection for the given user and set
* of posts.
*
* @param stdClass $user The user to load receipts for
* @param post_entity[] $posts List of posts to load receipts for
* @return post_read_receipt_collection
*/
public function get_from_user_and_posts(stdClass $user, array $posts) {
$postids = array_map(function($post) {
return $post->get_id();
}, $posts);
return $this->get_from_user_id_and_post_ids($user->id, $postids);
}
}
@@ -0,0 +1,53 @@
<?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/>.
/**
* Extract context vault preprocessor.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\vaults\preprocessors;
defined('MOODLE_INTERNAL') || die();
use context;
use context_helper;
/**
* Extract context vault preprocessor.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class extract_context {
/**
* Extract the contexts from a list of records.
*
* @param stdClass[] $records The list of records which have context properties
* @return context[] List of contexts matching the records.
*/
public function execute(array $records) : array {
return array_map(function($record) {
$contextid = $record->ctxid;
context_helper::preload_from_record($record);
$context = context::instance_by_id($contextid);
return $context;
}, $records);
}
}
@@ -0,0 +1,76 @@
<?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/>.
/**
* Extract record vault preprocessor.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\vaults\preprocessors;
defined('MOODLE_INTERNAL') || die();
use moodle_database;
/**
* Extract record vault preprocessor.
*
* Extract record vault preprocessor. Typically used to separate out records
* when two tables have been joined together in a query.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class extract_record {
/** @var moodle_database $db A moodle database */
private $db;
/** @var string $table The table name where the records were loaded from */
private $table;
/** @var string $alias The table alias used as the record property prefix */
private $alias;
/**
* Constructor.
*
* @param moodle_database $db A moodle database
* @param string $table The table name where the records were loaded from
* @param string $alias The table alias used as the record property prefix
*/
public function __construct(moodle_database $db, string $table, string $alias) {
$this->db = $db;
$this->table = $table;
$this->alias = $alias;
}
/**
* Extract a record embedded in the properties of another record out into a
* separate record.
*
* @param stdClass[] $records The list of records to process
* @return stdClass[] The extracted records
*/
public function execute(array $records) : array {
$db = $this->db;
$fields = $this->db->get_preload_columns($this->table, $this->alias);
return array_map(function($record) use ($db, $fields) {
return $db->extract_fields_from_object($fields, $record);
}, $records);
}
}
@@ -0,0 +1,71 @@
<?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/>.
/**
* Extract user vault preprocessor.
*
* @package mod_forum
* @copyright 2018 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\local\vaults\preprocessors;
defined('MOODLE_INTERNAL') || die();
use user_picture;
/**
* Extract user vault preprocessor.
*
* Used to separate out the user record
* from a list of DB records that have been joined on the user table.
*
* @copyright 2018 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class extract_user {
/** @var string $idalias The alias for the id property of the user */
private $idalias;
/** @var string $alias The prefix used for each of the user properties */
private $alias;
/**
* Constructor.
*
* @param string $idalias The alias for the id property of the user
* @param string $alias The prefix used for each of the user properties
*/
public function __construct(string $idalias, string $alias) {
$this->idalias = $idalias;
$this->alias = $alias;
}
/**
* Extract the user records from a list of DB records.
*
* @param stdClass[] $records The DB records
* @return stdClass[] The list of extracted users
*/
public function execute(array $records) : array {
$idalias = $this->idalias;
$alias = $this->alias;
return array_map(function($record) use ($idalias, $alias) {
return user_picture::unalias($record, null, $idalias, $alias);
}, $records);
}
}
+13 -15
View File
@@ -3111,7 +3111,7 @@ function forum_print_discussion_header(&$post, $forum, $group = -1, $datestring
echo $post->unread;
echo '</a>';
echo '<a title="'.$strmarkalldread.'" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
$forum->id.'&amp;d='.$post->discussion.'&amp;mark=read&amp;returnpage=view.php&amp;sesskey=' . sesskey() . '">' .
$forum->id.'&amp;d='.$post->discussion.'&amp;mark=read&amp;return=/mod/forum/view.php&amp;sesskey=' . sesskey() . '">' .
$OUTPUT->pix_icon('t/markasread', $strmarkalldread) . '</a>';
echo '</span>';
} else {
@@ -4819,7 +4819,7 @@ function forum_print_latest_discussions($course, $forum, $maxdiscussions = -1, $
if ($forumtracked) {
echo '<a title="'.get_string('markallread', 'forum').
'" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
$forum->id.'&amp;mark=read&amp;returnpage=view.php&amp;sesskey=' . sesskey() . '">'.
$forum->id.'&amp;mark=read&amp;return=/mod/forum/view.php&amp;sesskey=' . sesskey() . '">'.
$OUTPUT->pix_icon('t/markasread', get_string('markallread', 'forum')) . '</a>';
}
echo '</th>';
@@ -7471,20 +7471,18 @@ function mod_forum_inplace_editable($itemtype, $itemid, $newvalue) {
* @return bool
*/
function forum_discussion_is_locked($forum, $discussion) {
if (empty($forum->lockdiscussionafter)) {
return false;
}
$entityfactory = \mod_forum\local\container::get_entity_factory();
$coursemoduleinfo = get_fast_modinfo($forum->course);
$cminfo = $coursemoduleinfo->instances['forum'][$forum->id];
$forumentity = $entityfactory->get_forum_from_stdclass(
$forum,
context_module::instance($cminfo->id),
$cminfo->get_course_module_record(),
$cminfo->get_course()
);
$discussionentity = $entityfactory->get_discussion_from_stdclass($discussion);
if ($forum->type === 'single') {
// It does not make sense to lock a single discussion forum.
return false;
}
if (($discussion->timemodified + $forum->lockdiscussionafter) < time()) {
return true;
}
return false;
return $forumentity->is_discussion_locked($discussionentity);
}
/**
+6 -14
View File
@@ -29,14 +29,14 @@ require_once("lib.php");
$f = required_param('f',PARAM_INT); // The forum to mark
$mark = required_param('mark',PARAM_ALPHA); // Read or unread?
$d = optional_param('d',0,PARAM_INT); // Discussion to mark.
$returnpage = optional_param('returnpage', 'index.php', PARAM_FILE); // Page to return to.
$return = optional_param('return', null, PARAM_LOCALURL); // Page to return to.
$url = new moodle_url('/mod/forum/markposts.php', array('f'=>$f, 'mark'=>$mark));
if ($d !== 0) {
$url->param('d', $d);
}
if ($returnpage !== 'index.php') {
$url->param('returnpage', $returnpage);
if (null !== $return) {
$url->param('return', $return);
}
$PAGE->set_url($url);
@@ -57,10 +57,10 @@ $user = $USER;
require_login($course, false, $cm);
require_sesskey();
if ($returnpage == 'index.php') {
$returnto = new moodle_url("/mod/forum/$returnpage", array('id' => $course->id));
if (null === $return) {
$returnto = new moodle_url("/mod/forum/index.php", ['id' => $course->id]);
} else {
$returnto = new moodle_url("/mod/forum/$returnpage", array('f' => $forum->id));
$returnto = new moodle_url($return);
}
if (isguestuser()) { // Guests can't change forum
@@ -93,14 +93,6 @@ if ($mark == 'read') {
}
forum_tp_mark_forum_read($user, $forum->id, $currentgroup);
}
/// FUTURE - Add ability to mark them as unread.
// } else { // subscribe
// if (forum_tp_start_tracking($forum->id, $user->id)) {
// redirect($returnto, get_string("nowtracking", "forum", $info), 1);
// } else {
// print_error("Could not start tracking that forum", get_local_referer());
// }
}
redirect($returnto);
@@ -0,0 +1,37 @@
{{!
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/>.
}}
{{!
@template mod_forum/qanda_discussion_list
Template which defines a forum post for sending in a single-post HTML email.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
{{< mod_forum/discussion_list }}
{{$discussion_create_text}}
{{#str}}addanewtopic, forum{{/str}}
{{/discussion_create_text}}
{{/ mod_forum/discussion_list }}
@@ -0,0 +1,205 @@
{{!
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/>.
}}
{{!
@template mod_forum/discussion_list
Template to display the list of forum discussions.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* TODO
Example context (json):
{
}
}}
<span id="{{uniqid}}">
{{{groupchangemenu}}}
{{#notifications}}
{{> core/notification}}
{{/notifications}}
{{#forum.capabilities.create}}
<div class="p-t-1 p-b-1">
<a href="{{forum.urls.create}}" class="btn btn-primary">
{{$discussion_create_text}}
{{#str}}addanewdiscussion, forum{{/str}}
{{/discussion_create_text}}
</a>
</div>
{{/forum.capabilities.create}}
{{#state.hasdiscussions}}
<table class="table table-hover table-striped">
{{$discussion_list_header}}
<thead>
<tr>
<th scope="col">&nbsp;</th>
<th scope="col" class="p-l-0">{{#str}}discussion, mod_forum{{/str}}</th>
<th scope="col" class="author">{{#str}}startedby, mod_forum{{/str}}</th>
{{#forum.state.groupmode}}
<th scope="col" class="group">{{#str}}group{{/str}}</th>
{{/forum.state.groupmode}}
{{#forum.capabilities.viewdiscussions}}
<th scope="col" class="text-center">{{#str}}replies, mod_forum{{/str}}</th>
{{#forum.userstate.tracked}}
<th scope="col" class="text-center">
{{#str}}unread, mod_forum{{/str}}
<a href="{{{forum.urls.markasread}}}">{{#pix}}t/markasread, core, {{#str}}markallread, mod_forum{{/str}}{{/pix}}</a>
</th>
{{/forum.userstate.tracked}}
{{/forum.capabilities.viewdiscussions}}
<th scope="col" class="lastpost">{{#str}}lastpost, mod_forum{{/str}}</th>
</tr>
</thead>
{{/discussion_list_header}}
{{$discussion_list_body}}
<tbody>
{{#summaries}}
<tr>
<td scope="col" class="pinned p-0 text-center align-middle">
{{#discussion.pinned}}
{{#pix}}i/pinned, mod_forum, {{#str}}discussionpinned, mod_forum{{/str}}{{/pix}}
{{/discussion.pinned}}
</td>
<td scope="col" class="topic p-0">
<a class="p-3 p-l-0 w-100 h-100 d-block" href="{{discussion.urls.view}}">{{discussion.name}}</a>
</td>
<td scope="col" class="author">
{{#firstpostauthor}}
<a href="{{urls.profile}}">
<div class="d-flex flex-row">
<div class="align-middle p-0">
<a href="{{urls.profile}}">
<img {{!
}} class="h-auto rounded-circle userpicture" {{!
}} src="{{urls.profileimage}}" {{!
}} alt="{{#str}}pictureof, moodle, {{fullname}}{{/str}}" {{!
}}>
</a>
</div>
<div class="align-middle p-2">
<a href="{{urls.profile}}">{{fullname}}</a>
</div>
</div>
</a>
{{/firstpostauthor}}
</td>
{{#forum.state.groupmode}}
<td scope="col" class="group">
{{#discussion.group}}
{{#urls.picture}}
{{#urls.userlist}}
<a href="{{{urls.userlist}}}">
<img class="border rounded h-auto rounded-circle" src="{{{urls.picture}}}">
</a>
{{/urls.userlist}}
{{^urls.userlist}}
<img class="border rounded h-auto rounded-circle" src="{{{urls.picture}}}">
{{/urls.userlist}}
{{/urls.picture}}
{{^urls.picture}}
{{#urls.userlist}}
<a href="{{{urls.userlist}}}">{{name}}</a>
{{/urls.userlist}}
{{^urls.userlist}}
{{name}}
{{/urls.userlist}}
{{/urls.picture}}
{{/discussion.group}}
</td>
{{/forum.state.groupmode}}
{{#forum.capabilities.viewdiscussions}}
<td scope="col" class="p-0 text-center">
<a href="{{discussion.urls.view}}" class="p-3 w-100 h-100 d-block">
{{replies}}
</a>
</td>
{{#forum.userstate.tracked}}
<td scope="col" class="p-0 text-center p-3 w-100 h-100 d-block">
{{#unread}}
{{! TODO Rewrite as AJAX}}
<a href="{{{discussion.urls.viewfirstunread}}}">{{unread}}</a>
<a href="{{{discussion.urls.markasread}}}">{{#pix}}t/markasread, core, {{#str}}markalldread, mod_forum{{/str}}{{/pix}}</a>
{{/unread}}
{{^unread}}
<span class="p-3 w-100 h-100 d-block">
0
</span>
{{/unread}}
</td>
{{/forum.userstate.tracked}}
{{/forum.capabilities.viewdiscussions}}
<td scope="col" class="text-left">
{{! TODO Check q&a, eachuser }}
{{#latestpostid}}
<div class="d-flex flex-row">
<div class="align-middle p-0">
<a href="{{latestpostauthor.urls.profile}}">
<img {{!
}} class="h-auto rounded-circle userpicture" {{!
}} src="{{latestpostauthor.urls.profileimage}}" {{!
}} alt="{{#str}}pictureof, moodle, {{latestpostauthor.fullname}}{{/str}}" {{!
}}>
</a>
</div>
<div class="p-2 p-t-0 p-b-0 d-inline-flex flex-column">
<div>
<a href="{{latestpostauthor.urls.profile}}">{{latestpostauthor.fullname}}</a>
</div>
<div>
<a href="{{{discussion.urls.viewlatest}}}">{{#userdate}}
{{discussion.times.modified}}, {{#str}}strftimerecentfull{{/str}}
{{/userdate}}</a>
</div>
</div>
</div>
{{/latestpostid}}
</td>
</tr>
{{/summaries}}
</tbody>
{{/discussion_list_body}}
</table>
{{#pageno}}
<a href="{{{prev_page_link}}}">Previous</a>
{{/pageno}}
{{#more_pages}}
<a href="{{{next_page_link}}}">Next</a>
{{/more_pages}}
{{#can_create_discussion}}
<div class="forumaddnew">
<a href="{{create_discussion_link}}" class="btn btn-primary">{{create_discussion_link_text}}</a>
</div>
{{/can_create_discussion}}
{{/state.hasdiscussions}}
{{^state.hasdiscussions}}
<div class="forumnodiscuss">
{{$no_discussions_text}}
({{#str}}nodiscussions, forum{{/str}})
{{/no_discussions_text}}
</div>
{{/state.hasdiscussions}}
</span>
@@ -0,0 +1,51 @@
{{!
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/>.
}}
{{!
@template mod_forum/forum_post_email_htmlemail
Template which defines a forum post for sending in a single-post HTML email.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
{{#html}}
{{{subscribe}}}
{{{neighbourlinks}}}
<div class="d-flex flex-wrap m-b-1">
<div>{{{exportdiscussion}}}</div>
<div class="{{#exportdiscussion}}ml-2{{/exportdiscussion}}">{{{modeselectorform}}}</div>
<div class="ml-2">{{{movediscussion}}}</div>
<div class="ml-2">{{{pindiscussion}}}</div>
</div>
{{/html}}
{{#notifications}}
{{> core/notification}}
{{/notifications}}
{{{html.posts}}}
{{#html.neighbourlinks}}{{{.}}}{{/html.neighbourlinks}}
@@ -0,0 +1,40 @@
{{!
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/>.
}}
{{!
@template mod_forum/forum_post_email_htmlemail
Template which defines a forum post for sending in a single-post HTML email.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
{{< mod_forum/forum_discussion_post }}
{{$replies}}
<div class="indent">
{{#replies}}
{{> mod_forum/forum_discussion_nested_post }}
{{/replies}}
</div>
{{/replies}}
{{/ mod_forum/forum_discussion_post }}
@@ -0,0 +1,34 @@
{{!
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/>.
}}
{{!
@template mod_forum/forum_post_email_htmlemail
Template which defines a forum post for sending in a single-post HTML email.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
{{#posts}}
{{> mod_forum/forum_discussion_nested_post }}
{{/posts}}
@@ -0,0 +1,175 @@
{{!
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/>.
}}
{{!
@template mod_forum/forum_post_email_htmlemail
Template which defines a forum post for sending in a single-post HTML email.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
<article id="p{{id}}" class="relativelink mb-2" tabindex="-1" data-post-id="{{id}}">
<div class="d-flex border p-2 mb-2 forumpost {{#unread}}unread{{/unread}}">
{{#isfirstunread}}<a id="unread"></a>{{/isfirstunread}}
<div style="width: 45px;">
{{^isdeleted}}
{{#author}}
{{#urls.profileimage}}
<img class="rounded-circle w-100" src="{{{.}}}">
{{/urls.profileimage}}
{{#groups}}
<img class="rounded-circle w-100" src="{{{urls.image}}}">
{{/groups}}
{{/author}}
{{/isdeleted}}
</div>
<div class="d-flex flex-column ml-2 w-100">
<header class="mb-2 header row">
<h3 class="h6 font-weight-bold mb-0">{{$subject}}{{{subject}}}{{/subject}}</h3>
{{^isdeleted}}
<address>
{{{html.authorsubheading}}}
</address>
{{/isdeleted}}
</header>
<div>
{{{message}}}
</div>
{{^isdeleted}}
{{#haswordcount}}
<p class="font-italic mb-0"><small>{{#str}} numwords, core, {{wordcount}} {{/str}}</small></p>
{{/haswordcount}}
{{#attachments}}
{{#isimage}}
<div>
<img src="{{{url}}}" alt="{{filename}}" style="max-width: 100%">
{{#urls.export}}
<a href="{{{.}}}" title="{{#str}} addtoportfolio, core_portfolio {{/str}}">
{{#pix}} t/portfolioadd, core {{/pix}}
</a>
{{/urls.export}}
{{#html.plagiarism}}
<div>{{{.}}}</div>
{{/html.plagiarism}}
</div>
{{/isimage}}
{{/attachments}}
{{{html.taglist}}}
{{#attachments}}
{{^isimage}}
<div>
<a href="{{{url}}}">{{#pix}} {{icon}}, core {{/pix}} {{filename}}</a>
{{#urls.export}}
<a href="{{{.}}}" title="{{#str}} addtoportfolio, core_portfolio {{/str}}">
{{#pix}} t/portfolioadd, core {{/pix}}
</a>
{{/urls.export}}
{{#html.plagiarism}}
<div>{{{.}}}</div>
{{/html.plagiarism}}
</div>
{{/isimage}}
{{/attachments}}
{{#html.rating}}
<div class="mt-2">{{{.}}}</div>
{{/html.rating}}
{{$actions}}
{{^readonly}}
<div class="d-flex justify-content-end">
{{#capabilities}}
{{#view}}
<a href="{{{urls.view}}}" class="btn btn-link">
{{#str}} permalink, mod_forum {{/str}}
</a>
{{/view}}
{{#controlreadstatus}}
{{#unread}}
<a href="{{{urls.markasread}}}" class="btn btn-link">
{{#str}} markread, mod_forum {{/str}}
</a>
{{/unread}}
{{^unread}}
<a href="{{{urls.markasunread}}}" class="btn btn-link">
{{#str}} markunread, mod_forum {{/str}}
</a>
{{/unread}}
{{/controlreadstatus}}
{{#urls.viewparent}}
<a href="{{{.}}}" class="btn btn-link">
{{#str}} parent, mod_forum {{/str}}
</a>
{{/urls.viewparent}}
{{#edit}}
<a href="{{{urls.edit}}}" class="btn btn-link">
{{#str}} edit, mod_forum {{/str}}
</a>
{{/edit}}
{{#split}}
<a href="{{{urls.split}}}" class="btn btn-link">
{{#str}} prune, mod_forum {{/str}}
</a>
{{/split}}
{{#delete}}
<a href="{{{urls.delete}}}" class="btn btn-link">
{{#str}} delete, mod_forum {{/str}}
</a>
{{/delete}}
{{#reply}}
<a href="{{{urls.reply}}}" class="btn btn-link">
{{#str}} reply, mod_forum {{/str}}
</a>
{{/reply}}
{{#export}}
<a href="{{{urls.export}}}" class="btn btn-link">
{{#str}} addtoportfolio, core_portfolio {{/str}}
</a>
{{/export}}
{{/capabilities}}
</div>
{{/readonly}}
{{/actions}}
{{$footer}}{{/footer}}
{{/isdeleted}}
</div>
</div>
{{$replies}}
{{#hasreplies}}
<div>
{{#replies}}
{{> mod_forum/forum_discussion_post }}
{{/replies}}
</div>
{{/hasreplies}}
{{/replies}}
</article>
@@ -0,0 +1,34 @@
{{!
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/>.
}}
{{!
@template mod_forum/forum_post_email_htmlemail
Template which defines a forum post for sending in a single-post HTML email.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
{{#posts}}
{{> mod_forum/forum_discussion_post }}
{{/posts}}
@@ -0,0 +1,45 @@
{{!
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/>.
}}
{{!
@template mod_forum/forum_post_email_htmlemail
Template which defines a forum post for sending in a single-post HTML email.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
<div class="mb-2" data-post-id="{{id}}">
<a href="{{{urls.viewisolated}}}">{{subject}}</a>
{{^isdeleted}}
<address class="d-inline-block mb-0">
{{{html.authorsubheading}}}
</address>
{{/isdeleted}}
<div class="indent">
{{#replies}}
{{> mod_forum/forum_discussion_threaded_post }}
{{/replies}}
</div>
</div>
@@ -0,0 +1,43 @@
{{!
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/>.
}}
{{!
@template mod_forum/forum_post_email_htmlemail
Template which defines a forum post for sending in a single-post HTML email.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
{{#posts}}
{{< mod_forum/forum_discussion_post }}
{{$replies}}
<div class="indent">
{{#replies}}
{{> mod_forum/forum_discussion_threaded_post }}
{{/replies}}
</div>
{{/replies}}
{{/ mod_forum/forum_discussion_post }}
{{/posts}}
@@ -0,0 +1,71 @@
{{!
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/>.
}}
{{!
@template mod_forum/forum_post_email_htmlemail
Template which defines a forum post for sending in a single-post HTML email.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
{{#posts}}
{{< mod_forum/forum_discussion_post }}
{{$subject}}
{{#coursename}}
{{#urls.viewcourse}}
<a href="{{{urls.viewcourse}}}">{{{coursename}}}</a> ->
{{/urls.viewcourse}}
{{^urls.viewcourse}}
{{coursename}} ->
{{/urls.viewcourse}}
{{/coursename}}
{{#urls.viewforum}}
<a href="{{{urls.viewforum}}}">{{{forumname}}}</a>
{{/urls.viewforum}}
{{^urls.viewforum}}
{{{forumname}}}
{{/urls.viewforum}}
{{#showdiscussionname}}
{{#urls.viewdiscussion}}
-> <a href="{{{urls.viewdiscussion}}}">{{{discussionname}}}</a>
{{/urls.viewdiscussion}}
{{^urls.viewdiscussion}}
{{{discussionname}}}
{{/urls.viewdiscussion}}
{{#hasparent}}
{{#urls.viewparent}}
-> <a href="{{{urls.viewparent}}}">{{{subject}}}</a>
{{/urls.viewparent}}
{{^urls.viewparent}}
{{{subject}}}
{{/urls.viewparent}}
{{/hasparent}}
{{/showdiscussionname}}
{{/subject}}
{{$replies}}{{/replies}}
{{$footer}}
<div><a href="{{{urls.view}}}">{{#str}} postincontext, mod_forum {{/str}}</a></div>
{{/footer}}
{{/ mod_forum/forum_discussion_post }}
{{/posts}}
@@ -0,0 +1,39 @@
{{!
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/>.
}}
{{!
@template mod_forum/qanda_discussion_list
Template which defines a forum post for sending in a single-post HTML email.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
{{< mod_forum/discussion_list }}
{{$discussion_create_text}}
{{#str}}addanewtopic, forum{{/str}}
{{/discussion_create_text}}
{{$no_discussions_text}}
({{#str}}nonews, forum{{/str}})
{{/no_discussions_text}}
{{/ mod_forum/discussion_list }}
@@ -0,0 +1,39 @@
{{!
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/>.
}}
{{!
@template mod_forum/qanda_discussion_list
Template which defines a forum post for sending in a single-post HTML email.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
{{< mod_forum/discussion_list }}
{{$discussion_create_text}}
{{#str}}addanewquestion, forum{{/str}}
{{/discussion_create_text}}
{{$no_discussions_text}}
({{#str}}noquestions, forum{{/str}})
{{/no_discussions_text}}
{{/ mod_forum/discussion_list }}
@@ -0,0 +1,524 @@
<?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/>.
/**
* The exported_posts builder tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/generator_trait.php');
/**
* The exported_posts builder tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_builders_exported_posts_testcase extends advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/** @var \mod_forum\local\builders\exported_posts */
private $builder;
/**
* Set up function for tests.
*/
public function setUp() {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
$builderfactory = \mod_forum\local\container::get_builder_factory();
$this->builder = $builderfactory->get_exported_posts_builder();
}
/**
* Tear down function for tests.
*/
public function tearDown() {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
}
/**
* Convert the stdClass values into their proper entity classes.
*
* @param stdClass[] $forums List of forums
* @param stdClass[] $discussions List of discussions
* @param stdClass[] $posts List of posts
* @return array
*/
private function convert_to_entities(array $forums, array $discussions, array $posts) {
global $DB;
$entityfactory = \mod_forum\local\container::get_entity_factory();
return [
// Forums.
array_map(function($forum) use ($entityfactory, $DB) {
$course = $DB->get_record('course', ['id' => $forum->course]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = context_module::instance($coursemodule->id);
return $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
}, $forums),
// Discussions.
array_map(function($discussion) use ($entityfactory) {
return $entityfactory->get_discussion_from_stdclass($discussion);
}, $discussions),
// Posts.
array_map(function($post) use ($entityfactory) {
return $entityfactory->get_post_from_stdclass($post);
}, $posts)
];
}
/**
* Test the build function throws exception if not given all of the forums for
* the list of posts.
*/
public function test_build_throws_exception_on_missing_forums() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user);
[$discussion2, $post2] = $this->helper_post_to_forum($forum2, $user);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2],
[$post1, $post2]
);
$this->expectException('moodle_exception');
$this->builder->build($user, [$forums[0]], $discussions, $posts);
}
/**
* Test the build function throws exception if not given all of the discussions for
* the list of posts.
*/
public function test_build_throws_exception_on_missing_discussions() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user);
[$discussion2, $post2] = $this->helper_post_to_forum($forum2, $user);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2],
[$post1, $post2]
);
$this->expectException('moodle_exception');
$this->builder->build($user, $forums, [$discussions[0]], $posts);
}
/**
* Test the build function returns the exported posts in the order that the posts are
* given.
*/
public function test_build_returns_posts_in_order() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user1 = $datagenerator->create_user();
$user2 = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user1);
[$discussion2, $post2] = $this->helper_post_to_forum($forum1, $user2);
$post3 = $this->helper_reply_to_post($post1, $user1);
$post4 = $this->helper_reply_to_post($post1, $user2);
[$discussion3, $post5] = $this->helper_post_to_forum($forum2, $user1);
[$discussion4, $post6] = $this->helper_post_to_forum($forum2, $user2);
$post7 = $this->helper_reply_to_post($post2, $user1);
$post8 = $this->helper_reply_to_post($post2, $user2);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2, $discussion3, $discussion4],
[$post1, $post2, $post3, $post4, $post5, $post6, $post7, $post8]
);
// Randomly order the posts.
shuffle($posts);
$exportedposts = $this->builder->build($user1, $forums, $discussions, $posts);
$expectedpostids = array_map(function($post) {
return $post->get_id();
}, $posts);
$actualpostids = array_map(function($exportedpost) {
return (int) $exportedpost->id;
}, $exportedposts);
$this->assertEquals($expectedpostids, $actualpostids);
}
/**
* Test the build function loads authors.
*/
public function test_build_loads_authors() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user1 = $datagenerator->create_user();
$user2 = $datagenerator->create_user();
$user3 = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user1);
[$discussion2, $post2] = $this->helper_post_to_forum($forum1, $user2);
$post3 = $this->helper_reply_to_post($post1, $user1);
$post4 = $this->helper_reply_to_post($post1, $user2);
[$discussion3, $post5] = $this->helper_post_to_forum($forum2, $user1);
[$discussion4, $post6] = $this->helper_post_to_forum($forum2, $user2);
// These 2 replies from user 3 won't be inlcuded in the export.
$post7 = $this->helper_reply_to_post($post2, $user3);
$post8 = $this->helper_reply_to_post($post2, $user3);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2, $discussion3, $discussion4],
[$post1, $post2, $post3, $post4, $post5, $post6]
);
$datagenerator->enrol_user($user1->id, $course->id);
$exportedposts = $this->builder->build($user1, $forums, $discussions, $posts);
// We didn't include any posts from user 3 so we shouldn't see the authors
// that match that user.
$expectedids = [$user1->id, $user2->id];
$actualids = array_unique(array_map(function($exportedpost) {
return (int) $exportedpost->author->id;
}, $exportedposts));
sort($expectedids);
sort($actualids);
$this->assertEquals($expectedids, $actualids);
}
/**
* Test the build function loads attachments.
*/
public function test_build_loads_attachments() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user1 = $datagenerator->create_user();
$user2 = $datagenerator->create_user();
$user3 = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user1);
[$discussion2, $post2] = $this->helper_post_to_forum($forum1, $user2);
$post3 = $this->helper_reply_to_post($post1, $user1);
$post4 = $this->helper_reply_to_post($post1, $user2);
[$discussion3, $post5] = $this->helper_post_to_forum($forum2, $user1);
[$discussion4, $post6] = $this->helper_post_to_forum($forum2, $user2);
$post7 = $this->helper_reply_to_post($post5, $user3);
$post8 = $this->helper_reply_to_post($post5, $user3);
$filestorage = get_file_storage();
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2, $discussion3, $discussion4],
[$post1, $post2, $post3, $post4, $post5, $post6, $post7, $post8]
);
// Add an attachment to a post in forum 1.
$attachment1 = $filestorage->create_file_from_string(
[
'contextid' => $forums[0]->get_context()->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post1->id,
'filepath' => '/',
'filename' => 'example1.jpg',
],
'image contents'
);
// Add an attachment to a post in forum 2.
$attachment2 = $filestorage->create_file_from_string(
[
'contextid' => $forums[1]->get_context()->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post7->id,
'filepath' => '/',
'filename' => 'example2.jpg',
],
'image contents'
);
// Enrol the user so that they can see the posts.
$datagenerator->enrol_user($user1->id, $course->id);
$exportedposts = $this->builder->build($user1, $forums, $discussions, $posts);
$expected = ['example1.jpg', 'example2.jpg'];
$actual = array_reduce($exportedposts, function($carry, $exportedpost) {
if (!empty($exportedpost->attachments)) {
foreach ($exportedpost->attachments as $attachment) {
$carry[] = $attachment->filename;
}
}
return $carry;
}, []);
sort($expected);
sort($actual);
$this->assertEquals($expected, $actual);
}
/**
* Test the build function loads author groups.
*/
public function test_build_loads_author_groups() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user1 = $datagenerator->create_user();
$user2 = $datagenerator->create_user();
$user3 = $datagenerator->create_user();
$course1 = $datagenerator->create_course();
$course2 = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course1->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course1->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user1);
[$discussion2, $post2] = $this->helper_post_to_forum($forum1, $user2);
$post3 = $this->helper_reply_to_post($post1, $user1);
$post4 = $this->helper_reply_to_post($post1, $user2);
[$discussion3, $post5] = $this->helper_post_to_forum($forum2, $user1);
[$discussion4, $post6] = $this->helper_post_to_forum($forum2, $user2);
$post7 = $this->helper_reply_to_post($post5, $user3);
$post8 = $this->helper_reply_to_post($post5, $user3);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2, $discussion3, $discussion4],
[$post1, $post2, $post3, $post4, $post5, $post6, $post7, $post8]
);
// Enrol the user so that they can see the posts.
$datagenerator->enrol_user($user1->id, $course1->id);
$datagenerator->enrol_user($user1->id, $course2->id);
$datagenerator->enrol_user($user2->id, $course1->id);
$datagenerator->enrol_user($user2->id, $course2->id);
$datagenerator->enrol_user($user3->id, $course1->id);
$datagenerator->enrol_user($user3->id, $course2->id);
$group1 = $datagenerator->create_group(['courseid' => $course1->id]);
$group2 = $datagenerator->create_group(['courseid' => $course1->id]);
// This group shouldn't be included in the results since it's in a different course.
$group3 = $datagenerator->create_group(['courseid' => $course2->id]);
$datagenerator->create_group_member(['userid' => $user1->id, 'groupid' => $group1->id]);
$datagenerator->create_group_member(['userid' => $user2->id, 'groupid' => $group1->id]);
$datagenerator->create_group_member(['userid' => $user1->id, 'groupid' => $group2->id]);
$datagenerator->create_group_member(['userid' => $user1->id, 'groupid' => $group3->id]);
$exportedposts = $this->builder->build($user1, $forums, $discussions, $posts);
$expected = [
$user1->id => [$group1->id, $group2->id],
$user2->id => [$group1->id],
$user3->id => []
];
$actual = array_reduce($exportedposts, function($carry, $exportedpost) {
$author = $exportedpost->author;
$authorid = $author->id;
if (!isset($carry[$authorid])) {
$carry[$authorid] = array_map(function($group) {
return $group['id'];
}, $author->groups);
}
return $carry;
}, []);
$this->assertEquals($expected, $actual);
}
/**
* Test the build function loads tags.
*/
public function test_build_loads_tags() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user1 = $datagenerator->create_user();
$user2 = $datagenerator->create_user();
$user3 = $datagenerator->create_user();
$course1 = $datagenerator->create_course();
$course2 = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course1->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course1->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user1);
[$discussion2, $post2] = $this->helper_post_to_forum($forum1, $user2);
$post3 = $this->helper_reply_to_post($post1, $user1);
$post4 = $this->helper_reply_to_post($post1, $user2);
[$discussion3, $post5] = $this->helper_post_to_forum($forum2, $user1);
[$discussion4, $post6] = $this->helper_post_to_forum($forum2, $user2);
$post7 = $this->helper_reply_to_post($post5, $user3);
$post8 = $this->helper_reply_to_post($post5, $user3);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2, $discussion3, $discussion4],
[$post1, $post2, $post3, $post4, $post5, $post6, $post7, $post8]
);
// Enrol the user so that they can see the posts.
$datagenerator->enrol_user($user1->id, $course1->id);
$datagenerator->enrol_user($user1->id, $course2->id);
$datagenerator->enrol_user($user2->id, $course1->id);
$datagenerator->enrol_user($user2->id, $course2->id);
$datagenerator->enrol_user($user3->id, $course1->id);
$datagenerator->enrol_user($user3->id, $course2->id);
\core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post1->id, $forums[0]->get_context(), ['foo', 'bar']);
\core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post4->id, $forums[0]->get_context(), ['foo', 'baz']);
\core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post7->id, $forums[1]->get_context(), ['bip']);
$exportedposts = $this->builder->build($user1, $forums, $discussions, $posts);
$expected = [
$post1->id => ['foo', 'bar'],
$post4->id => ['foo', 'baz'],
$post7->id => ['bip']
];
$actual = array_reduce($exportedposts, function($carry, $exportedpost) {
if (!empty($exportedpost->tags)) {
$carry[$exportedpost->id] = array_map(function($tag) {
return $tag['displayname'];
}, $exportedpost->tags);
}
return $carry;
}, []);
$this->assertEquals($expected, $actual);
}
/**
* Test the build function loads read_receipts.
*/
public function test_build_loads_read_receipts() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user1 = $datagenerator->create_user(['trackforums' => 1]);
$user2 = $datagenerator->create_user(['trackforums' => 0]);
$course1 = $datagenerator->create_course();
$course2 = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course1->id, 'trackingtype' => FORUM_TRACKING_OPTIONAL]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course1->id, 'trackingtype' => FORUM_TRACKING_OFF]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum1, $user1);
[$discussion2, $post2] = $this->helper_post_to_forum($forum1, $user2);
$post3 = $this->helper_reply_to_post($post1, $user1);
$post4 = $this->helper_reply_to_post($post1, $user2);
[$discussion3, $post5] = $this->helper_post_to_forum($forum2, $user1);
[$discussion4, $post6] = $this->helper_post_to_forum($forum2, $user2);
$post7 = $this->helper_reply_to_post($post5, $user1);
$post8 = $this->helper_reply_to_post($post5, $user1);
[$forums, $discussions, $posts] = $this->convert_to_entities(
[$forum1, $forum2],
[$discussion1, $discussion2, $discussion3, $discussion4],
[$post1, $post2, $post3, $post4, $post5, $post6, $post7, $post8]
);
// Enrol the user so that they can see the posts.
$datagenerator->enrol_user($user1->id, $course1->id);
$datagenerator->enrol_user($user1->id, $course2->id);
$datagenerator->enrol_user($user2->id, $course1->id);
$datagenerator->enrol_user($user2->id, $course2->id);
forum_tp_add_read_record($user1->id, $post1->id);
forum_tp_add_read_record($user1->id, $post4->id);
forum_tp_add_read_record($user1->id, $post7->id);
forum_tp_add_read_record($user2->id, $post1->id);
forum_tp_add_read_record($user2->id, $post4->id);
forum_tp_add_read_record($user2->id, $post7->id);
// User 1 has tracking enabled.
$exportedposts = $this->builder->build($user1, $forums, $discussions, $posts);
$expected = [
// Tracking set for forum 1 for user 1.
$post1->id => false,
$post2->id => true,
$post3->id => true,
$post4->id => false,
// Tracking is off for forum 2 so everything should be null.
$post5->id => null,
$post6->id => null,
$post7->id => null,
$post8->id => null
];
$actual = array_reduce($exportedposts, function($carry, $exportedpost) {
$carry[$exportedpost->id] = $exportedpost->unread;
return $carry;
}, []);
$this->assertEquals($expected, $actual);
// User 2 has tracking disabled.
$exportedposts = $this->builder->build($user2, $forums, $discussions, $posts);
// Tracking is off for user 2 so everything should be null.
$expected = [
$post1->id => null,
$post2->id => null,
$post3->id => null,
$post4->id => null,
$post5->id => null,
$post6->id => null,
$post7->id => null,
$post8->id => null
];
$actual = array_reduce($exportedposts, function($carry, $exportedpost) {
$carry[$exportedpost->id] = $exportedpost->unread;
return $carry;
}, []);
$this->assertEquals($expected, $actual);
}
}
+48
View File
@@ -0,0 +1,48 @@
<?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/>.
defined('MOODLE_INTERNAL') || die();
/**
* Coverage information for the mod_forum component.
*
* @package mod_forum
* @category phpunit
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Coverage information for the core subsystem.
*
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
return new class extends phpunit_coverage_info {
/** @var array The list of folders relative to the plugin root to whitelist in coverage generation. */
protected $whitelistfolders = [
'classes/local',
];
/** @var array The list of files relative to the plugin root to whitelist in coverage generation. */
protected $whitelistfiles = [];
/** @var array The list of folders relative to the plugin root to excludelist in coverage generation. */
protected $excludelistfolders = [];
/** @var array The list of files relative to the plugin root to excludelist in coverage generation. */
protected $excludelistfiles = [];
};
+69
View File
@@ -0,0 +1,69 @@
<?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/>.
/**
* The author entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\author as author_entity;
/**
* The author entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_entities_author_testcase extends advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity() {
$this->resetAfterTest();
$author = new author_entity(
1,
2,
'test',
'person',
'test person',
'[email protected]',
'middle',
'tteeeeest',
'ppppeeerssson',
'maverick',
'image alt'
);
$this->assertEquals(1, $author->get_id());
$this->assertEquals(2, $author->get_picture_item_id());
$this->assertEquals('test', $author->get_first_name());
$this->assertEquals('person', $author->get_last_name());
$this->assertEquals('test person', $author->get_full_name());
$this->assertEquals('[email protected]', $author->get_email());
$this->assertEquals('middle', $author->get_middle_name());
$this->assertEquals('tteeeeest', $author->get_first_name_phonetic());
$this->assertEquals('ppppeeerssson', $author->get_last_name_phonetic());
$this->assertEquals('maverick', $author->get_alternate_name());
$this->assertEquals('image alt', $author->get_image_alt());
}
}
@@ -0,0 +1,102 @@
<?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/>.
/**
* The discussion_summary entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\author as author_entity;
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\discussion_summary as discussion_summary_entity;
use mod_forum\local\entities\post as post_entity;
/**
* The discussion_summary entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_entities_discussion_summary_testcase extends advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity() {
$this->resetAfterTest();
$firstauthor = new author_entity(
1,
2,
'test',
'person',
'test person',
'[email protected]'
);
$lastauthor = new author_entity(
2,
3,
'test 2',
'person 2',
'test 2 person 2',
'[email protected]'
);
$discussion = new discussion_entity(
1,
1,
1,
'test discussion',
1,
1,
0,
false,
time(),
time(),
0,
0,
false
);
$firstpost = new post_entity(
1,
1,
0,
1,
time(),
time(),
true,
'post subject',
'post message',
1,
true,
false,
0,
false,
false
);
$discussionsummary = new discussion_summary_entity($discussion, $firstpost, $firstauthor, $lastauthor);
$this->assertEquals($discussion, $discussionsummary->get_discussion());
$this->assertEquals($firstauthor, $discussionsummary->get_first_post_author());
$this->assertEquals($lastauthor, $discussionsummary->get_latest_post_author());
$this->assertEquals($firstpost, $discussionsummary->get_first_post());
}
}
@@ -0,0 +1,114 @@
<?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/>.
/**
* The discussion entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\post as post_entity;
/**
* The discussion entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_entities_discussion_testcase extends advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity() {
$this->resetAfterTest();
// In the past to ensure the time started is true.
$time = time() + 10;
$discussion = new discussion_entity(
1,
2,
3,
'test discussion',
4,
5,
6,
false,
$time,
$time,
0,
0,
false
);
$firstpost = new post_entity(
4,
1,
0,
1,
time(),
time(),
true,
'post subject',
'post message',
1,
true,
false,
0,
false,
false
);
$notfirstpost = new post_entity(
1,
1,
0,
1,
time(),
time(),
true,
'post subject',
'post message',
1,
true,
false,
0,
false,
false
);
$this->assertEquals(1, $discussion->get_id());
$this->assertEquals(2, $discussion->get_course_id());
$this->assertEquals(3, $discussion->get_forum_id());
$this->assertEquals('test discussion', $discussion->get_name());
$this->assertEquals(4, $discussion->get_first_post_id());
$this->assertEquals(5, $discussion->get_user_id());
$this->assertEquals(6, $discussion->get_group_id());
$this->assertEquals(false, $discussion->is_assessed());
$this->assertEquals($time, $discussion->get_time_modified());
$this->assertEquals($time, $discussion->get_user_modified());
$this->assertEquals(0, $discussion->get_time_start());
$this->assertEquals(0, $discussion->get_time_end());
$this->assertEquals(false, $discussion->is_pinned());
$this->assertEquals(true, $discussion->is_first_post($firstpost));
$this->assertEquals(false, $discussion->is_first_post($notfirstpost));
$this->assertEquals(true, $discussion->has_started());
$this->assertEquals(true, $discussion->has_group());
}
}
+164
View File
@@ -0,0 +1,164 @@
<?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/>.
/**
* The forum entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/rating/lib.php');
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\entities\forum as forum_entity;
/**
* The forum entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_entities_forum_testcase extends advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity() {
$this->resetAfterTest();
$time = time() - 10;
$discussion = new discussion_entity(
1,
2,
3,
'test discussion',
4,
5,
6,
false,
$time,
$time,
0,
0,
false
);
$past = time() - 100;
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = context_module::instance($coursemodule->id);
$effectivegroupmode = NOGROUPS;
$id = 1;
$courseid = 2;
$type = 'standard';
$name = 'test forum';
$intro = 'this is the intro';
$introformat = FORMAT_MOODLE;
$assessed = RATING_AGGREGATE_NONE;
$assesstimestart = 0;
$assesstimefinish = 0;
$scale = 0;
$maxbytes = 200;
$maxattachments = 5;
$forcesubscribe = false;
$trackingtype = 1;
$rsstype = 0;
$rssarticles = 0;
$timemodified = $past;
$warnafter = 0;
$blockafter = 0;
$blockperiod = 0;
$completiondiscussions = 0;
$completionreplies = 0;
$completionposts = 0;
$displaywordcount = false;
$lockdiscussionafter = 0;
$forum = new forum_entity(
$context,
$coursemodule,
$course,
$effectivegroupmode,
$id,
$courseid,
$type,
$name,
$intro,
$introformat,
$assessed,
$assesstimestart,
$assesstimefinish,
$scale,
$maxbytes,
$maxattachments,
$forcesubscribe,
$trackingtype,
$rsstype,
$rssarticles,
$timemodified,
$warnafter,
$blockafter,
$blockperiod,
$completiondiscussions,
$completionreplies,
$completionposts,
$displaywordcount,
$lockdiscussionafter
);
$this->assertEquals($context, $forum->get_context());
$this->assertEquals($coursemodule, $forum->get_course_module_record());
$this->assertEquals($coursemodule, $forum->get_course_module_record());
$this->assertEquals($effectivegroupmode, $forum->get_effective_group_mode());
$this->assertEquals(false, $forum->is_in_group_mode());
$this->assertEquals($course, $forum->get_course_record());
$this->assertEquals($id, $forum->get_id());
$this->assertEquals($courseid, $forum->get_course_id());
$this->assertEquals($name, $forum->get_name());
$this->assertEquals($intro, $forum->get_intro());
$this->assertEquals($introformat, $forum->get_intro_format());
$this->assertEquals($assessed, $forum->get_rating_aggregate());
// Rating aggregate is set to none.
$this->assertEquals(false, $forum->has_rating_aggregate());
$this->assertEquals($assesstimestart, $forum->get_assess_time_start());
$this->assertEquals($assesstimefinish, $forum->get_assess_time_finish());
$this->assertEquals($scale, $forum->get_scale());
$this->assertEquals($maxbytes, $forum->get_max_bytes());
$this->assertEquals($maxattachments, $forum->get_max_attachments());
$this->assertEquals($forcesubscribe, $forum->is_subscription_forced());
$this->assertEquals($trackingtype, $forum->get_tracking_type());
$this->assertEquals($rsstype, $forum->get_rss_type());
$this->assertEquals($rssarticles, $forum->get_rss_articles());
$this->assertEquals($timemodified, $forum->get_time_modified());
$this->assertEquals($warnafter, $forum->get_warn_after());
$this->assertEquals($blockafter, $forum->get_block_after());
$this->assertEquals($blockperiod, $forum->get_block_period());
$this->assertEquals(false, $forum->has_blocking_enabled());
$this->assertEquals($completiondiscussions, $forum->get_completion_discussions());
$this->assertEquals($completionreplies, $forum->get_completion_replies());
$this->assertEquals($completionposts, $forum->get_completion_posts());
$this->assertEquals($displaywordcount, $forum->should_display_word_count());
$this->assertEquals($lockdiscussionafter, $forum->get_lock_discussions_after());
$this->assertEquals(false, $forum->has_lock_discussions_after());
$this->assertEquals(false, $forum->is_discussion_locked($discussion));
}
}
@@ -0,0 +1,97 @@
<?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/>.
/**
* The post_read_receipt_collection entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\post_read_receipt_collection as collection_entity;
use mod_forum\local\entities\post as post_entity;
/**
* The post_read_receipt_collection entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_entities_post_read_receipt_collection_testcase extends advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity() {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$missingpost = new post_entity(
4,
1,
0,
1,
time(),
time(),
true,
'post subject',
'post message',
1,
true,
false,
0,
false,
false
);
$post = new post_entity(
1,
1,
0,
1,
time(),
time(),
true,
'post subject',
'post message',
1,
true,
false,
0,
false,
false
);
$collection = new collection_entity([
(object) [
'postid' => 1,
'userid' => $user->id + 1
],
(object) [
'postid' => 1,
'userid' => $user->id
],
(object) [
'postid' => 4,
'userid' => $user->id + 1
]
]);
$this->assertEquals(true, $collection->has_user_read_post($user, $post));
$this->assertEquals(false, $collection->has_user_read_post($user, $missingpost));
}
}
+84
View File
@@ -0,0 +1,84 @@
<?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/>.
/**
* The post entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\post as post_entity;
/**
* The post entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_entities_post_testcase extends advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity() {
$this->resetAfterTest();
$owner = $this->getDataGenerator()->create_user();
$notowner = $this->getDataGenerator()->create_user();
$past = time() - 10;
$post = new post_entity(
4,
1,
0,
$owner->id,
$past,
$past,
true,
'post subject',
'post message',
FORMAT_MOODLE,
true,
false,
0,
false,
false
);
$this->assertEquals(4, $post->get_id());
$this->assertEquals(1, $post->get_discussion_id());
$this->assertEquals(0, $post->get_parent_id());
$this->assertEquals(false, $post->has_parent());
$this->assertEquals($owner->id, $post->get_author_id());
$this->assertEquals($past, $post->get_time_created());
$this->assertEquals($past, $post->get_time_modified());
$this->assertEquals(true, $post->has_been_mailed());
$this->assertEquals('post subject', $post->get_subject());
$this->assertEquals('post message', $post->get_message());
$this->assertEquals(FORMAT_MOODLE, $post->get_message_format());
$this->assertEquals(true, $post->is_message_trusted());
$this->assertEquals(false, $post->has_attachments());
$this->assertEquals(0, $post->get_total_score());
$this->assertEquals(false, $post->should_mail_now());
$this->assertEquals(false, $post->is_deleted());
$this->assertTrue($post->get_age() >= 10);
$this->assertEquals(true, $post->is_owned_by_user($owner));
$this->assertEquals(false, $post->is_owned_by_user($notowner));
}
}
+93
View File
@@ -0,0 +1,93 @@
<?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/>.
/**
* The discussion entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\sorter as sorter_entity;
/**
* The discussion entity tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_entities_sorter_testcase extends advanced_testcase {
/**
* Test the entity returns expected values.
*/
public function test_entity_sort_into_children() {
$this->resetAfterTest();
$sorter = new sorter_entity(
function($entity) {
return $entity['id'];
},
function($entity) {
return $entity['parent'];
}
);
$a = ['id' => 1, 'parent' => 0];
$b = ['id' => 2, 'parent' => 1];
$c = ['id' => 3, 'parent' => 1];
$d = ['id' => 4, 'parent' => 2];
$expected = [
[$a, [[$b, [[$d, []]]], [$c, []]]]
];
$actual = $sorter->sort_into_children([$d, $b, $a, $c]);
$this->assertEquals($expected, $actual);
}
/**
* Test the entity returns expected values.
*/
public function test_entity_flatten_children() {
$this->resetAfterTest();
$sorter = new sorter_entity(
function($entity) {
return $entity['id'];
},
function($entity) {
return $entity['parent'];
}
);
$a = ['id' => 1, 'parent' => 0];
$b = ['id' => 2, 'parent' => 1];
$c = ['id' => 3, 'parent' => 1];
$d = ['id' => 4, 'parent' => 3];
$sorted = [
[$a, [[$b, [[$d, []]]], [$c, []]]]
];
$expected = [$a, $b, $d, $c];
$actual = $sorter->flatten_children($sorted);
$this->assertEquals($expected, $actual);
}
}
+153
View File
@@ -0,0 +1,153 @@
<?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/>.
/**
* The author exporter tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\author as author_entity;
use mod_forum\local\exporters\author as author_exporter;
global $CFG;
/**
* The author exporter tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_exporters_author_testcase extends advanced_testcase {
/**
* Test the export function returns expected values.
*/
public function test_export_author() {
global $PAGE;
$this->resetAfterTest();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = context_module::instance($coursemodule->id);
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
$author = new author_entity(
1,
1,
'test',
'user',
'test user',
'[email protected]'
);
$exporter = new author_exporter($author, [], true, [
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'context' => $context
]);
$exportedauthor = $exporter->export($renderer);
$this->assertEquals(1, $exportedauthor->id);
$this->assertEquals('test user', $exportedauthor->fullname);
$this->assertEquals([], $exportedauthor->groups);
$this->assertNotEquals(null, $exportedauthor->urls['profile']);
$this->assertNotEquals(null, $exportedauthor->urls['profileimage']);
}
/**
* Test the export function with groups.
*/
public function test_export_author_with_groups() {
global $PAGE;
$this->resetAfterTest();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = context_module::instance($coursemodule->id);
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
$author = new author_entity(
1,
1,
'test',
'user',
'test user',
'[email protected]'
);
$group = $datagenerator->create_group(['courseid' => $course->id]);
$exporter = new author_exporter($author, [$group], true, [
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'context' => $context
]);
$exportedauthor = $exporter->export($renderer);
$this->assertCount(1, $exportedauthor->groups);
$this->assertEquals($group->id, $exportedauthor->groups[0]['id']);
}
/**
* Test the export function with no view capability.
*/
public function test_export_author_no_view_capability() {
global $PAGE;
$this->resetAfterTest();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = context_module::instance($coursemodule->id);
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
$author = new author_entity(
1,
1,
'test',
'user',
'test user',
'[email protected]'
);
$group = $datagenerator->create_group(['courseid' => $course->id]);
$exporter = new author_exporter($author, [$group], false, [
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'context' => $context
]);
$exportedauthor = $exporter->export($renderer);
$this->assertEquals(null, $exportedauthor->id);
$this->assertNotEquals('test user', $exportedauthor->fullname);
$this->assertEquals([], $exportedauthor->groups);
$this->assertEquals(null, $exportedauthor->urls['profile']);
$this->assertEquals(null, $exportedauthor->urls['profileimage']);
}
}
@@ -0,0 +1,115 @@
<?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/>.
/**
* The discussion exporter tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\discussion as discussion_entity;
use mod_forum\local\exporters\discussion as discussion_exporter;
/**
* The discussion exporter tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_exporters_discussion_testcase extends advanced_testcase {
/**
* Test set up function.
*/
public function setUp() {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
$builderfactory = \mod_forum\local\container::get_builder_factory();
$this->builder = $builderfactory->get_exported_posts_builder();
}
/**
* Test tear down function.
*/
public function tearDown() {
// We must clear the subscription caches. This has to be done both before each test, and after in case of other
// tests using these functions.
\mod_forum\subscriptions::reset_forum_cache();
}
/**
* Test the export function returns expected values.
*/
public function test_export() {
global $PAGE;
$this->resetAfterTest();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = context_module::instance($coursemodule->id);
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
$group = $datagenerator->create_group(['courseid' => $course->id]);
$now = time();
$discussion = new discussion_entity(
1,
$course->id,
$forum->get_id(),
'test discussion',
1,
$user->id,
$group->id,
false,
$now,
$now,
0,
0,
false
);
$exporter = new discussion_exporter($discussion, [
'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'capabilitymanager' => (\mod_forum\local\container::get_manager_factory())->get_capability_manager($forum),
'context' => $context,
'forum' => $forum,
'user' => $user,
'groupsbyid' => [$group->id => $group],
'latestpostid' => 7
]);
$exporteddiscussion = $exporter->export($renderer);
$this->assertEquals(1, $exporteddiscussion->id);
$this->assertEquals($forum->get_id(), $exporteddiscussion->forumid);
$this->assertEquals(false, $exporteddiscussion->pinned);
$this->assertEquals('test discussion', $exporteddiscussion->name);
$this->assertEquals($now, $exporteddiscussion->times['modified']);
$this->assertEquals(0, $exporteddiscussion->times['start']);
$this->assertEquals(0, $exporteddiscussion->times['end']);
$this->assertEquals($group->name, $exporteddiscussion->group['name']);
}
}
+77
View File
@@ -0,0 +1,77 @@
<?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/>.
/**
* The discussion forum tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
use mod_forum\local\entities\forum as forum_entity;
use mod_forum\local\exporters\forum as forum_exporter;
/**
* The discussion forum tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_exporters_forum_testcase extends advanced_testcase {
/**
* Test the export function returns expected values.
*/
public function test_export() {
global $PAGE;
$this->resetAfterTest();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', [
'course' => $course->id,
'groupmode' => VISIBLEGROUPS
]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = context_module::instance($coursemodule->id);
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
$exporter = new forum_exporter($forum, [
'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'capabilitymanager' => (\mod_forum\local\container::get_manager_factory())->get_capability_manager($forum),
'user' => $user,
'currentgroup' => null
]);
$exportedforum = $exporter->export($renderer);
$this->assertEquals($forum->get_id(), $exportedforum->id);
$this->assertEquals(VISIBLEGROUPS, $exportedforum->state['groupmode']);
$this->assertEquals(false, $exportedforum->userstate['tracked']);
$this->assertEquals(false, $exportedforum->capabilities['viewdiscussions']);
$this->assertEquals(false, $exportedforum->capabilities['create']);
$this->assertEquals(false, $exportedforum->capabilities['subscribe']);
$this->assertNotEquals(null, $exportedforum->urls['create']);
$this->assertNotEquals(null, $exportedforum->urls['markasread']);
}
}
+519
View File
@@ -0,0 +1,519 @@
<?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/>.
/**
* The post exporter tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
use \mod_forum\local\entities\discussion as discussion_entity;
use \mod_forum\local\entities\post as post_entity;
use \mod_forum\local\exporters\post as post_exporter;
use \mod_forum\local\managers\capability as capability_manager;
global $CFG;
require_once(__DIR__ . '/generator_trait.php');
require_once($CFG->dirroot . '/rating/lib.php');
/**
* The post exporter tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_exporters_post_testcase extends advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/**
* Test the export function returns expected values.
*/
public function test_export_post() {
global $CFG, $PAGE;
$this->resetAfterTest();
$CFG->enableportfolios = true;
$filestorage = get_file_storage();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = context_module::instance($coursemodule->id);
$discussion = $forumgenerator->create_discussion((object) [
'course' => $forum->course,
'userid' => $user->id,
'forum' => $forum->id
]);
$now = time();
$post = $forumgenerator->create_post((object) [
'discussion' => $discussion->id,
'parent' => 0,
'userid' => $user->id,
'created' => $now,
'modified' => $now,
'subject' => 'This is the subject',
'message' => 'This is the message',
'messagetrust' => 1,
'attachment' => 0,
'totalscore' => 0,
'mailnow' => 1,
'deleted' => 0
]);
\core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post->id, $context, ['foo', 'bar']);
$tags = \core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id);
$attachment = $filestorage->create_file_from_string(
[
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post->id,
'filepath' => '/',
'filename' => 'example1.jpg',
],
'image contents'
);
$canview = true;
$canedit = true;
$candelete = true;
$cansplit = true;
$canreply = true;
$canexport = true;
$cancontrolreadstatus = true;
$capabilitymanager = new test_capability_manager(
$canview,
$canedit,
$candelete,
$cansplit,
$canreply,
$canexport,
$cancontrolreadstatus
);
$managerfactory = \mod_forum\local\container::get_manager_factory();
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
$discussion = $entityfactory->get_discussion_from_stdclass($discussion);
$post = $entityfactory->get_post_from_stdclass($post);
$author = $entityfactory->get_author_from_stdclass($user);
$exporter = new post_exporter($post, [
'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
'capabilitymanager' => $capabilitymanager,
'readreceiptcollection' => null,
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'forum' => $forum,
'discussion' => $discussion,
'author' => $author,
'user' => $user,
'context' => $context,
'authorgroups' => [],
'attachments' => [$attachment],
'tags' => $tags,
'rating' => null,
'includehtml' => true
]);
$exportedpost = $exporter->export($renderer);
$this->assertEquals('This is the subject', $exportedpost->subject);
$this->assertEquals('This is the message', $exportedpost->message);
$this->assertEquals($user->id, $exportedpost->author->id);
$this->assertEquals($discussion->get_id(), $exportedpost->discussionid);
$this->assertEquals(false, $exportedpost->hasparent);
$this->assertEquals(null, $exportedpost->parentid);
$this->assertEquals($now, $exportedpost->timecreated);
$this->assertEquals(null, $exportedpost->unread);
$this->assertEquals(false, $exportedpost->isdeleted);
$this->assertEquals($canview, $exportedpost->capabilities['view']);
$this->assertEquals($canedit, $exportedpost->capabilities['edit']);
$this->assertEquals($candelete, $exportedpost->capabilities['delete']);
$this->assertEquals($cansplit, $exportedpost->capabilities['split']);
$this->assertEquals($canreply, $exportedpost->capabilities['reply']);
$this->assertEquals($canexport, $exportedpost->capabilities['export']);
$this->assertEquals($cancontrolreadstatus, $exportedpost->capabilities['controlreadstatus']);
$this->assertNotEmpty($exportedpost->urls['view']);
$this->assertNotEmpty($exportedpost->urls['viewisolated']);
$this->assertNotEmpty($exportedpost->urls['edit']);
$this->assertNotEmpty($exportedpost->urls['delete']);
$this->assertNotEmpty($exportedpost->urls['split']);
$this->assertNotEmpty($exportedpost->urls['reply']);
$this->assertNotEmpty($exportedpost->urls['markasread']);
$this->assertNotEmpty($exportedpost->urls['markasunread']);
$this->assertCount(1, $exportedpost->attachments);
$this->assertEquals('example1.jpg', $exportedpost->attachments[0]->filename);
$this->assertCount(2, $exportedpost->tags);
$this->assertEquals('foo', $exportedpost->tags[0]['displayname']);
$this->assertEquals('bar', $exportedpost->tags[1]['displayname']);
$this->assertEquals(null, $exportedpost->html['rating']);
$this->assertNotEquals(null, $exportedpost->html['taglist']);
$this->assertNotEmpty($exportedpost->html['authorsubheading']);
}
/**
* Test exporting of a deleted post.
*/
public function test_export_deleted_post() {
global $CFG, $PAGE;
$this->resetAfterTest();
$CFG->enableportfolios = true;
$filestorage = get_file_storage();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = context_module::instance($coursemodule->id);
$discussion = $forumgenerator->create_discussion((object) [
'course' => $forum->course,
'userid' => $user->id,
'forum' => $forum->id
]);
$now = time();
$post = $forumgenerator->create_post((object) [
'discussion' => $discussion->id,
'parent' => 0,
'userid' => $user->id,
'created' => $now,
'modified' => $now,
'subject' => 'This is the subject',
'message' => 'This is the message',
'messagetrust' => 1,
'attachment' => 0,
'totalscore' => 0,
'mailnow' => 1,
'deleted' => 1
]);
\core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post->id, $context, ['foo', 'bar']);
$tags = \core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id);
$attachment = $filestorage->create_file_from_string(
[
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post->id,
'filepath' => '/',
'filename' => 'example1.jpg',
],
'image contents'
);
$canview = true;
$canedit = true;
$candelete = true;
$cansplit = true;
$canreply = true;
$canexport = true;
$cancontrolreadstatus = true;
$capabilitymanager = new test_capability_manager(
$canview,
$canedit,
$candelete,
$cansplit,
$canreply,
$canexport,
$cancontrolreadstatus
);
$managerfactory = \mod_forum\local\container::get_manager_factory();
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
$discussion = $entityfactory->get_discussion_from_stdclass($discussion);
$post = $entityfactory->get_post_from_stdclass($post);
$author = $entityfactory->get_author_from_stdclass($user);
$exporter = new post_exporter($post, [
'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
'capabilitymanager' => $capabilitymanager,
'readreceiptcollection' => null,
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'forum' => $forum,
'discussion' => $discussion,
'author' => $author,
'user' => $user,
'context' => $context,
'authorgroups' => [],
'attachments' => [$attachment],
'tags' => $tags,
'rating' => null,
'includehtml' => true
]);
$exportedpost = $exporter->export($renderer);
$this->assertNotEquals('This is the subject', $exportedpost->subject);
$this->assertNotEquals('This is the message', $exportedpost->message);
$this->assertEquals(null, $exportedpost->timecreated);
$this->assertEquals(null, $exportedpost->unread);
$this->assertEquals(true, $exportedpost->isdeleted);
$this->assertEquals([], $exportedpost->attachments);
$this->assertEquals([], $exportedpost->tags);
$this->assertEquals(null, $exportedpost->html['rating']);
$this->assertEquals(null, $exportedpost->html['taglist']);
$this->assertEquals(null, $exportedpost->html['authorsubheading']);
}
/**
* Test exporting of a post the user can't view.
*/
public function test_export_post_no_view_capability() {
global $CFG, $PAGE;
$this->resetAfterTest();
$CFG->enableportfolios = true;
$filestorage = get_file_storage();
$renderer = $PAGE->get_renderer('core');
$datagenerator = $this->getDataGenerator();
$forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = context_module::instance($coursemodule->id);
$discussion = $forumgenerator->create_discussion((object) [
'course' => $forum->course,
'userid' => $user->id,
'forum' => $forum->id
]);
$now = time();
$post = $forumgenerator->create_post((object) [
'discussion' => $discussion->id,
'parent' => 0,
'userid' => $user->id,
'created' => $now,
'modified' => $now,
'subject' => 'This is the subject',
'message' => 'This is the message',
'messagetrust' => 1,
'attachment' => 0,
'totalscore' => 0,
'mailnow' => 1,
'deleted' => 0
]);
\core_tag_tag::set_item_tags('mod_forum', 'forum_posts', $post->id, $context, ['foo', 'bar']);
$tags = \core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id);
$attachment = $filestorage->create_file_from_string(
[
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post->id,
'filepath' => '/',
'filename' => 'example1.jpg',
],
'image contents'
);
$canview = false;
$canedit = true;
$candelete = true;
$cansplit = true;
$canreply = true;
$canexport = true;
$cancontrolreadstatus = true;
$capabilitymanager = new test_capability_manager(
$canview,
$canedit,
$candelete,
$cansplit,
$canreply,
$canexport,
$cancontrolreadstatus
);
$managerfactory = \mod_forum\local\container::get_manager_factory();
$entityfactory = \mod_forum\local\container::get_entity_factory();
$forum = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
$discussion = $entityfactory->get_discussion_from_stdclass($discussion);
$post = $entityfactory->get_post_from_stdclass($post);
$author = $entityfactory->get_author_from_stdclass($user);
$exporter = new post_exporter($post, [
'legacydatamapperfactory' => \mod_forum\local\container::get_legacy_data_mapper_factory(),
'capabilitymanager' => $capabilitymanager,
'readreceiptcollection' => null,
'urlfactory' => \mod_forum\local\container::get_url_factory(),
'forum' => $forum,
'discussion' => $discussion,
'author' => $author,
'user' => $user,
'context' => $context,
'authorgroups' => [],
'attachments' => [$attachment],
'tags' => $tags,
'rating' => null,
'includehtml' => true
]);
$exportedpost = $exporter->export($renderer);
$this->assertNotEquals('This is the subject', $exportedpost->subject);
$this->assertNotEquals('This is the message', $exportedpost->message);
$this->assertEquals(null, $exportedpost->timecreated);
$this->assertEquals(null, $exportedpost->unread);
$this->assertEquals(false, $exportedpost->isdeleted);
$this->assertEquals([], $exportedpost->attachments);
$this->assertEquals([], $exportedpost->tags);
$this->assertEquals(null, $exportedpost->html['rating']);
$this->assertEquals(null, $exportedpost->html['taglist']);
$this->assertEquals(null, $exportedpost->html['authorsubheading']);
}
}
/**
* Test implementation of the capability manager.
*
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class test_capability_manager extends capability_manager {
/** @var bool $view Value for can_view_post */
private $view;
/** @var bool $edit Value for can_edit_post */
private $edit;
/** @var bool $delete Value for can_delete_post */
private $delete;
/** @var bool $split Value for can_split_post */
private $split;
/** @var bool $reply Value for can_reply_to_post */
private $reply;
/** @var bool $export Value for can_export_post */
private $export;
/** @var bool $controlreadstatus Value for can_manually_control_post_read_status */
private $controlreadstatus;
/**
* Constructor.
*
* @param bool $view Value for can_view_post
* @param bool $edit Value for can_edit_post
* @param bool $delete Value for can_delete_post
* @param bool $split Value for can_split_post
* @param bool $reply Value for can_reply_to_post
* @param bool $export Value for can_export_post
* @param bool $controlreadstatus Value for can_manually_control_post_read_status
*/
public function __construct(
bool $view = true,
bool $edit = true,
bool $delete = true,
bool $split = true,
bool $reply = true,
bool $export = true,
bool $controlreadstatus = true
) {
$this->view = $view;
$this->edit = $edit;
$this->delete = $delete;
$this->split = $split;
$this->reply = $reply;
$this->export = $export;
$this->controlreadstatus = $controlreadstatus;
}
/**
* Override can_view_post
*
* @param stdClass $user The user
* @param discussion_entity $discussion The discussion
* @param post_entity $post The post
* @return bool
*/
public function can_view_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
return $this->view;
}
/**
* Override can_edit_post
*
* @param stdClass $user The user
* @param discussion_entity $discussion The discussion
* @param post_entity $post The post
* @return bool
*/
public function can_edit_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
return $this->edit;
}
/**
* Override can_delete_post
*
* @param stdClass $user The user
* @param discussion_entity $discussion The discussion
* @param post_entity $post The post
* @return bool
*/
public function can_delete_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
return $this->delete;
}
/**
* Override can_split_post
*
* @param stdClass $user The user
* @param discussion_entity $discussion The discussion
* @param post_entity $post The post
* @return bool
*/
public function can_split_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
return $this->split;
}
/**
* Override can_reply_to_post
*
* @param stdClass $user The user
* @param discussion_entity $discussion The discussion
* @param post_entity $post The post
* @return bool
*/
public function can_reply_to_post(stdClass $user, discussion_entity $discussion, post_entity $post) : bool {
return $this->reply;
}
/**
* Override can_export_post
*
* @param stdClass $user The user
* @param post_entity $post The post
* @return bool
*/
public function can_export_post(stdClass $user, post_entity $post) : bool {
return $this->export;
}
/**
* Override can_manually_control_post_read_status
*
* @param stdClass $user The user
* @return bool
*/
public function can_manually_control_post_read_status(stdClass $user) : bool {
return $this->controlreadstatus;
}
}
+4
View File
@@ -300,6 +300,10 @@ class mod_forum_generator extends testing_module_generator {
$record['mailnow'] = 0;
}
if (!isset($record['deleted'])) {
$record['deleted'] = 0;
}
$record = (object) $record;
// Add the post.
+26 -20
View File
@@ -3009,6 +3009,22 @@ class mod_forum_lib_testcase extends advanced_testcase {
* @param bool $expect
*/
public function test_forum_discussion_is_locked($forum, $discussion, $expect) {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$plugingenerator = $datagenerator->get_plugin_generator('mod_forum');
$course = $datagenerator->create_course();
$user = $datagenerator->create_user();
$forum = $datagenerator->create_module('forum', (object) array_merge([
'course' => $course->id
], $forum));
$discussion = $plugingenerator->create_discussion((object) array_merge([
'course' => $course->id,
'userid' => $user->id,
'forum' => $forum->id,
], $discussion));
$this->assertEquals($expect, forum_discussion_is_locked($forum, $discussion));
}
@@ -3019,39 +3035,29 @@ class mod_forum_lib_testcase extends advanced_testcase {
*/
public function forum_discussion_is_locked_provider() {
return [
'Unlocked: lockdiscussionafter is unset' => [
(object) [],
(object) [],
false
],
'Unlocked: lockdiscussionafter is false' => [
(object) ['lockdiscussionafter' => false],
(object) [],
false
],
'Unlocked: lockdiscussionafter is null' => [
(object) ['lockdiscussionafter' => null],
(object) [],
['lockdiscussionafter' => false],
[],
false
],
'Unlocked: lockdiscussionafter is set; forum is of type single; post is recent' => [
(object) ['lockdiscussionafter' => DAYSECS, 'type' => 'single'],
(object) ['timemodified' => time()],
['lockdiscussionafter' => DAYSECS, 'type' => 'single'],
['timemodified' => time()],
false
],
'Unlocked: lockdiscussionafter is set; forum is of type single; post is old' => [
(object) ['lockdiscussionafter' => MINSECS, 'type' => 'single'],
(object) ['timemodified' => time() - DAYSECS],
['lockdiscussionafter' => MINSECS, 'type' => 'single'],
['timemodified' => time() - DAYSECS],
false
],
'Unlocked: lockdiscussionafter is set; forum is of type eachuser; post is recent' => [
(object) ['lockdiscussionafter' => DAYSECS, 'type' => 'eachuser'],
(object) ['timemodified' => time()],
['lockdiscussionafter' => DAYSECS, 'type' => 'eachuser'],
['timemodified' => time()],
false
],
'Locked: lockdiscussionafter is set; forum is of type eachuser; post is old' => [
(object) ['lockdiscussionafter' => MINSECS, 'type' => 'eachuser'],
(object) ['timemodified' => time() - DAYSECS],
['lockdiscussionafter' => MINSECS, 'type' => 'eachuser'],
['timemodified' => time() - DAYSECS],
true
],
];
+106
View File
@@ -0,0 +1,106 @@
<?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/>.
/**
* Tests for the local\container class.
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Tests for the local\container class.
*
* @copyright 2019 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \mod_forum\local\container
*/
class mod_forum_local_container_testcase extends advanced_testcase {
/**
* Ensure that a renderer factory is returned.
*
* @covers ::get_renderer_factory
*/
public function test_get_renderer_factory() {
$this->assertInstanceOf(\mod_forum\local\factories\renderer::class, \mod_forum\local\container::get_renderer_factory());
}
/**
* Ensure that a legacy_data_mapper_factory factory is returned.
*
* @covers ::get_legacy_data_mapper_factory
*/
public function test_get_legacy_data_mapper_factory() {
$this->assertInstanceOf(\mod_forum\local\factories\legacy_data_mapper::class, \mod_forum\local\container::get_legacy_data_mapper_factory());
}
/**
* Ensure that a exporter factory is returned.
*
* @covers ::get_exporter_factory
*/
public function test_get_exporter_factory() {
$this->assertInstanceOf(\mod_forum\local\factories\exporter::class, \mod_forum\local\container::get_exporter_factory());
}
/**
* Ensure that a vault factory is returned.
*
* @covers ::get_vault_factory
*/
public function test_get_vault_factory() {
$this->assertInstanceOf(\mod_forum\local\factories\vault::class, \mod_forum\local\container::get_vault_factory());
}
/**
* Ensure that a manager factory is returned.
*
* @covers ::get_manager_factory
*/
public function test_get_manager_factory() {
$this->assertInstanceOf(\mod_forum\local\factories\manager::class, \mod_forum\local\container::get_manager_factory());
}
/**
* Ensure that a entity factory is returned.
*
* @covers ::get_entity_factory
*/
public function test_get_entity_factory() {
$this->assertInstanceOf(\mod_forum\local\factories\entity::class, \mod_forum\local\container::get_entity_factory());
}
/**
* Ensure that a builder factory is returned.
*
* @covers ::get_builder_factory
*/
public function test_get_builder_factory() {
$this->assertInstanceOf(\mod_forum\local\factories\builder::class, \mod_forum\local\container::get_builder_factory());
}
/**
* Ensure that a url factory is returned.
*
* @covers ::get_url_factory
*/
public function test_get_url_factory() {
$this->assertInstanceOf(\mod_forum\local\factories\url::class, \mod_forum\local\container::get_url_factory());
}
}
File diff suppressed because it is too large Load Diff
+49
View File
@@ -0,0 +1,49 @@
<?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/>.
/**
* The author vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* The author vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_vaults_author_testcase extends advanced_testcase {
/**
* Test get_from_id.
*/
public function test_get_from_id() {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$authorvault = $vaultfactory->get_author_vault();
$author = $authorvault->get_from_id($user->id);
$this->assertEquals($user->id, $author->get_id());
}
}
@@ -0,0 +1,269 @@
<?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/>.
/**
* The discussion_list vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/generator_trait.php');
/**
* The discussion_list vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_vaults_discussion_list_testcase extends advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/** @var \mod_forum\local\vaults\discussion_list */
private $vault;
/**
* Set up function for tests.
*/
public function setUp() {
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$this->vault = $vaultfactory->get_discussions_in_forum_vault();
}
/**
* Test get_from_id.
*/
public function test_get_from_id() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$vault = $this->vault;
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion, $post] = $this->helper_post_to_forum($forum, $user);
$discussionlist = $vault->get_from_id($discussion->id);
$this->assertEquals($discussion->id, $discussionlist->get_discussion()->get_id());
$this->assertEquals($post->id, $discussionlist->get_first_post()->get_id());
$this->assertEquals($user->id, $discussionlist->get_first_post_author()->get_id());
$this->assertEquals($user->id, $discussionlist->get_latest_post_author()->get_id());
}
/**
* Test get_from_forum_id.
*/
public function test_get_from_forum_id() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$vault = $this->vault;
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$this->assertEquals([], $vault->get_from_forum_id($forum->id, true, $user->id, null, 0, 0));
$now = time();
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 10, 'timemodified' => 1]);
[$discussion2, $post2] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 9, 'timemodified' => 2]);
[$hiddendiscussion, $post3] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now + 10, 'timemodified' => 3]);
$summaries = array_values($vault->get_from_forum_id($forum->id, false, null, null, 0, 0));
$this->assertCount(2, $summaries);
$this->assertEquals($discussion2->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[1]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null, 0, 0));
$this->assertCount(3, $summaries);
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[2]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, false, $user->id, null, 0, 0));
$this->assertCount(3, $summaries);
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[2]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null, 1, 0));
$this->assertCount(1, $summaries);
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null, 1, 1));
$this->assertCount(1, $summaries);
$this->assertEquals($discussion2->id, $summaries[0]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null, 1, 2));
$this->assertCount(1, $summaries);
$this->assertEquals($discussion1->id, $summaries[0]->get_discussion()->get_id());
}
/**
* Test get_from_forum_id_and_group_id.
*/
public function test_get_from_forum_id_and_group_id() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$vault = $this->vault;
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$this->assertEquals([], $vault->get_from_forum_id($forum->id, true, true, null, 0, 0));
$now = time();
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 10, 'timemodified' => 1]);
[$discussion2, $post2] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 9, 'timemodified' => 2]);
[$hiddendiscussion, $post3] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now + 10, 'timemodified' => 3]);
[$groupdiscussion1, $post4] = $this->helper_post_to_forum(
$forum,
$user,
['timestart' => $now - 8, 'timemodified' => 4, 'groupid' => 1]
);
[$groupdiscussion2, $post5] = $this->helper_post_to_forum(
$forum,
$user,
['timestart' => $now - 7, 'timemodified' => 5, 'groupid' => 2]
);
[$hiddengroupdiscussion, $post6] = $this->helper_post_to_forum(
$forum,
$user,
['timestart' => $now + 11, 'timemodified' => 6, 'groupid' => 3]
);
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null, null, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($hiddengroupdiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[5]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], false, $user->id, null, 0, 0));
$this->assertCount(6, $summaries);
$this->assertEquals($hiddengroupdiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($hiddendiscussion->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[3]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[4]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[5]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null, null, 1, 0));
$this->assertCount(1, $summaries);
$this->assertEquals($hiddengroupdiscussion->id, $summaries[0]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null, null, 1, 1));
$this->assertCount(1, $summaries);
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null, null, 1, 2));
$this->assertCount(1, $summaries);
$this->assertEquals($groupdiscussion2->id, $summaries[0]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], false, null, null, 0, 0));
$this->assertCount(4, $summaries);
$this->assertEquals($groupdiscussion2->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($groupdiscussion1->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[2]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[3]->get_discussion()->get_id());
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [], true, null, null, 0, 0));
$this->assertCount(3, $summaries);
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
$this->assertEquals($discussion1->id, $summaries[2]->get_discussion()->get_id());
}
/**
* Test get_total_discussion_count_from_forum_id.
*/
public function test_get_total_discussion_count_from_forum_id() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$vault = $this->vault;
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$this->assertEquals(0, $vault->get_total_discussion_count_from_forum_id($forum->id, true, null));
$now = time();
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 10, 'timemodified' => 1]);
[$discussion2, $post2] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 9, 'timemodified' => 2]);
[$hiddendiscussion, $post3] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now + 10, 'timemodified' => 3]);
$this->assertEquals(2, $vault->get_total_discussion_count_from_forum_id($forum->id, false, null));
$this->assertEquals(3, $vault->get_total_discussion_count_from_forum_id($forum->id, true, null));
$this->assertEquals(3, $vault->get_total_discussion_count_from_forum_id($forum->id, false, $user->id));
}
/**
* Test get_total_discussion_count_from_forum_id_and_group_id.
*/
public function test_get_total_discussion_count_from_forum_id_and_group_id() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$vault = $this->vault;
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$this->assertEquals([], $vault->get_from_forum_id($forum->id, true, true, null, 0, 0));
$now = time();
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 10, 'timemodified' => 1]);
[$discussion2, $post2] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 9, 'timemodified' => 2]);
[$hiddendiscussion, $post3] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now + 10, 'timemodified' => 3]);
[$groupdiscussion1, $post4] = $this->helper_post_to_forum(
$forum,
$user,
['timestart' => $now - 8, 'timemodified' => 4, 'groupid' => 1]
);
[$groupdiscussion2, $post5] = $this->helper_post_to_forum(
$forum,
$user,
['timestart' => $now - 7, 'timemodified' => 5, 'groupid' => 2]
);
[$hiddengroupdiscussion, $post6] = $this->helper_post_to_forum(
$forum,
$user,
['timestart' => $now + 11, 'timemodified' => 6, 'groupid' => 3]
);
$this->assertEquals(6, $vault->get_total_discussion_count_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null));
$this->assertEquals(6, $vault->get_total_discussion_count_from_forum_id_and_group_id(
$forum->id,
[1, 2, 3],
false,
$user->id
));
$this->assertEquals(4, $vault->get_total_discussion_count_from_forum_id_and_group_id($forum->id, [1, 2, 3], false, null));
$this->assertEquals(3, $vault->get_total_discussion_count_from_forum_id_and_group_id($forum->id, [], true, null));
}
}
@@ -0,0 +1,94 @@
<?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/>.
/**
* The discussion vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/generator_trait.php');
/**
* The discussion vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_vaults_discussion_testcase extends advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/** @var \mod_forum\local\vaults\discussion */
private $vault;
/**
* Set up function for tests.
*/
public function setUp() {
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$this->vault = $vaultfactory->get_discussion_vault();
}
/**
* Test get_from_id.
*/
public function test_get_from_id() {
$this->resetAfterTest();
$vault = $this->vault;
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussionrecord, $post] = $this->helper_post_to_forum($forum, $user);
$discussion = $vault->get_from_id($discussionrecord->id);
$this->assertEquals($discussionrecord->id, $discussion->get_id());
}
/**
* Test get_first_discussion_in_forum.
*/
public function test_get_first_discussion_in_forum() {
$this->resetAfterTest();
$vault = $this->vault;
$entityfactory = \mod_forum\local\container::get_entity_factory();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = context_module::instance($coursemodule->id);
$forumentity = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $course);
$this->assertEquals(null, $vault->get_first_discussion_in_forum($forumentity));
[$discussion1, $post] = $this->helper_post_to_forum($forum, $user, ['timemodified' => 2]);
[$discussion2, $post] = $this->helper_post_to_forum($forum, $user, ['timemodified' => 1]);
[$discussion3, $post] = $this->helper_post_to_forum($forum, $user, ['timemodified' => 3]);
$discussionentity = $vault->get_first_discussion_in_forum($forumentity);
$this->assertEquals($discussion2->id, $discussionentity->get_id());
}
}
+114
View File
@@ -0,0 +1,114 @@
<?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/>.
/**
* The forum vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* The forum vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_vaults_forum_testcase extends advanced_testcase {
/** @var \mod_forum\local\vaults\discussion */
private $vault;
/**
* Set up function for tests.
*/
public function setUp() {
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$this->vault = $vaultfactory->get_forum_vault();
}
/**
* Test get_from_id.
*/
public function test_get_from_id() {
$this->resetAfterTest();
$vault = $this->vault;
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$entity = $vault->get_from_id($forum->id);
$this->assertEquals($forum->id, $entity->get_id());
}
/**
* Test get_from_course_module_id.
*/
public function test_get_from_course_module_id() {
$this->resetAfterTest();
$vault = $this->vault;
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule1 = get_coursemodule_from_instance('forum', $forum1->id);
$coursemodule2 = get_coursemodule_from_instance('forum', $forum2->id);
// Don't exist.
$entity = $vault->get_from_course_module_id($coursemodule1->id + 100);
$this->assertEquals(null, $entity);
$entity = $vault->get_from_course_module_id($coursemodule1->id);
$this->assertEquals($forum1->id, $entity->get_id());
}
/**
* Test get_from_course_module_ids.
*/
public function test_get_from_course_module_ids() {
$this->resetAfterTest();
$vault = $this->vault;
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum1 = $datagenerator->create_module('forum', ['course' => $course->id]);
$forum2 = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule1 = get_coursemodule_from_instance('forum', $forum1->id);
$coursemodule2 = get_coursemodule_from_instance('forum', $forum2->id);
// Don't exist.
$entities = array_values($vault->get_from_course_module_ids([$coursemodule1->id + 100, $coursemodule1->id + 200]));
$this->assertEquals([], $entities);
$entities = array_values($vault->get_from_course_module_ids([$coursemodule1->id, $coursemodule2->id]));
$this->assertCount(2, $entities);
$this->assertEquals($forum2->id, $entities[0]->get_id());
$this->assertEquals($forum1->id, $entities[1]->get_id());
$entities = array_values($vault->get_from_course_module_ids([$coursemodule1->id]));
$this->assertCount(1, $entities);
$this->assertEquals($forum1->id, $entities[0]->get_id());
}
}
@@ -0,0 +1,107 @@
<?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/>.
/**
* The post_attachment vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/generator_trait.php');
/**
* The post_attachment vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_vaults_post_attachment_testcase extends advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/**
* Test get_attachments_for_posts.
*/
public function test_get_attachments_for_posts() {
$this->resetAfterTest();
$filestorage = get_file_storage();
$entityfactory = \mod_forum\local\container::get_entity_factory();
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$vault = $vaultfactory->get_post_attachment_vault();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$coursemodule = get_coursemodule_from_instance('forum', $forum->id);
$context = context_module::instance($coursemodule->id);
[$discussion, $post1] = $this->helper_post_to_forum($forum, $user);
$post2 = $this->helper_reply_to_post($post1, $user);
$post3 = $this->helper_reply_to_post($post1, $user);
$attachment1 = $filestorage->create_file_from_string(
[
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post1->id,
'filepath' => '/',
'filename' => 'example1.jpg',
],
'image contents'
);
$attachment2 = $filestorage->create_file_from_string(
[
'contextid' => $context->id,
'component' => 'mod_forum',
'filearea' => 'attachment',
'itemid' => $post2->id,
'filepath' => '/',
'filename' => 'example2.jpg',
],
'image contents'
);
$post1 = $entityfactory->get_post_from_stdclass($post1);
$post2 = $entityfactory->get_post_from_stdclass($post2);
$post3 = $entityfactory->get_post_from_stdclass($post3);
$results = $vault->get_attachments_for_posts(context_system::instance(), [$post1, $post2, $post3]);
$this->assertCount(3, $results);
$this->assertEquals([], $results[$post1->get_id()]);
$this->assertEquals([], $results[$post2->get_id()]);
$this->assertEquals([], $results[$post3->get_id()]);
$results = $vault->get_attachments_for_posts($context, [$post1]);
$this->assertCount(1, $results);
$this->assertEquals($attachment1->get_filename(), $results[$post1->get_id()][0]->get_filename());
$results = $vault->get_attachments_for_posts($context, [$post1, $post2]);
$this->assertCount(2, $results);
$this->assertEquals($attachment1->get_filename(), $results[$post1->get_id()][0]->get_filename());
$this->assertEquals($attachment2->get_filename(), $results[$post2->get_id()][0]->get_filename());
$results = $vault->get_attachments_for_posts($context, [$post1, $post2, $post3]);
$this->assertCount(3, $results);
$this->assertEquals($attachment1->get_filename(), $results[$post1->get_id()][0]->get_filename());
$this->assertEquals($attachment2->get_filename(), $results[$post2->get_id()][0]->get_filename());
$this->assertEquals([], $results[$post3->get_id()]);
}
}
@@ -0,0 +1,99 @@
<?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/>.
/**
* The post read receipt collection vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/generator_trait.php');
/**
* The post read receipt collection vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_vaults_post_read_receipt_collection_testcase extends advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/**
* Test get_from_user_id_and_post_ids.
*/
public function test_get_from_user_id_and_post_ids() {
global $DB;
$this->resetAfterTest();
$entityfactory = \mod_forum\local\container::get_entity_factory();
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$vault = $vaultfactory->get_post_read_receipt_collection_vault();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$otheruser = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user);
[$discussion2, $post2] = $this->helper_post_to_forum($forum, $user);
$post3 = $this->helper_reply_to_post($post2, $user);
$DB->insert_record('forum_read', [
'userid' => $user->id,
'forumid' => $forum->id,
'discussionid' => $discussion1->id,
'postid' => $post1->id,
'firstread' => time(),
'lastread' => time()
]);
// Receipt for other user.
$DB->insert_record('forum_read', [
'userid' => $otheruser->id,
'forumid' => $forum->id,
'discussionid' => $discussion1->id,
'postid' => $post1->id,
'firstread' => time(),
'lastread' => time()
]);
$DB->insert_record('forum_read', [
'userid' => $user->id,
'forumid' => $forum->id,
'discussionid' => $discussion2->id,
'postid' => $post3->id,
'firstread' => time(),
'lastread' => time()
]);
$post1 = $entityfactory->get_post_from_stdclass($post1);
$post2 = $entityfactory->get_post_from_stdclass($post2);
$post3 = $entityfactory->get_post_from_stdclass($post3);
$collection = $vault->get_from_user_id_and_post_ids($user->id, [$post1->get_id(), $post2->get_id()]);
// True because there is a read receipt for this user.
$this->assertTrue($collection->has_user_read_post($user, $post1));
// False because other user wasn't included in the fetch.
$this->assertFalse($collection->has_user_read_post($otheruser, $post1));
// No read receipt.
$this->assertFalse($collection->has_user_read_post($user, $post2));
// Wasn't included in fetch.
$this->assertFalse($collection->has_user_read_post($user, $post3));
}
}
+402
View File
@@ -0,0 +1,402 @@
<?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/>.
/**
* The post vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/generator_trait.php');
/**
* The post vault tests.
*
* @package mod_forum
* @copyright 2019 Ryan Wyllie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \mod_forum\local\vaults\post
*/
class mod_forum_vaults_post_testcase extends advanced_testcase {
// Make use of the test generator trait.
use mod_forum_tests_generator_trait;
/** @var \mod_forum\local\vaults\post */
private $vault;
/**
* Set up function for tests.
*/
public function setUp() {
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$this->vault = $vaultfactory->get_post_vault();
}
/**
* Teardown for all tests.
*/
public function tearDown() {
unset($this->vault);
}
/**
* Test get_from_id.
*/
public function test_get_from_id() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion, $post] = $this->helper_post_to_forum($forum, $user);
$postentity = $this->vault->get_from_id($post->id);
$this->assertEquals($post->id, $postentity->get_id());
}
/**
* Test get_from_discussion_id.
*
* @covers ::get_from_discussion_id
* @covers ::<!public>
*/
public function test_get_from_discussion_id() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user);
$post2 = $this->helper_reply_to_post($post1, $user);
$post3 = $this->helper_reply_to_post($post1, $user);
[$discussion2, $post4] = $this->helper_post_to_forum($forum, $user);
$entities = array_values($this->vault->get_from_discussion_id($discussion1->id));
$this->assertCount(3, $entities);
$this->assertEquals($post1->id, $entities[0]->get_id());
$this->assertEquals($post2->id, $entities[1]->get_id());
$this->assertEquals($post3->id, $entities[2]->get_id());
$entities = array_values($this->vault->get_from_discussion_id($discussion1->id + 1000));
$this->assertCount(0, $entities);
}
/**
* Test get_from_discussion_ids when no discussion ids were provided.
*
* @covers ::get_from_discussion_ids
*/
public function test_get_from_discussion_ids_empty() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$this->assertEquals([], $this->vault->get_from_discussion_ids([]));
}
/**
* Test get_from_discussion_ids.
*
* @covers ::get_from_discussion_ids
* @covers ::<!public>
*/
public function test_get_from_discussion_ids() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user);
$post2 = $this->helper_reply_to_post($post1, $user);
$post3 = $this->helper_reply_to_post($post1, $user);
[$discussion2, $post4] = $this->helper_post_to_forum($forum, $user);
$entities = array_values($this->vault->get_from_discussion_ids([$discussion1->id]));
usort($entities, function($a, $b) {
return $a <=> $b;
});
$this->assertCount(3, $entities);
$this->assertEquals($post1->id, $entities[0]->get_id());
$this->assertEquals($post2->id, $entities[1]->get_id());
$this->assertEquals($post3->id, $entities[2]->get_id());
$entities = array_values($this->vault->get_from_discussion_ids([$discussion1->id, $discussion2->id]));
usort($entities, function($a, $b) {
return $a <=> $b;
});
$this->assertCount(4, $entities);
$this->assertEquals($post1->id, $entities[0]->get_id());
$this->assertEquals($post2->id, $entities[1]->get_id());
$this->assertEquals($post3->id, $entities[2]->get_id());
$this->assertEquals($post4->id, $entities[3]->get_id());
}
/**
* Test get_replies_to_post.
*
* @covers ::get_replies_to_post
* @covers ::<!public>
*/
public function test_get_replies_to_post() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user);
$post2 = $this->helper_reply_to_post($post1, $user);
$post3 = $this->helper_reply_to_post($post1, $user);
$post4 = $this->helper_reply_to_post($post2, $user);
[$discussion2, $post5] = $this->helper_post_to_forum($forum, $user);
$entityfactory = \mod_forum\local\container::get_entity_factory();
$post1 = $entityfactory->get_post_from_stdclass($post1);
$post2 = $entityfactory->get_post_from_stdclass($post2);
$post3 = $entityfactory->get_post_from_stdclass($post3);
$post4 = $entityfactory->get_post_from_stdclass($post4);
$entities = $this->vault->get_replies_to_post($post1);
$this->assertCount(3, $entities);
$this->assertEquals($post2->get_id(), $entities[0]->get_id());
$this->assertEquals($post3->get_id(), $entities[1]->get_id());
$this->assertEquals($post4->get_id(), $entities[2]->get_id());
$entities = $this->vault->get_replies_to_post($post2);
$this->assertCount(1, $entities);
$this->assertEquals($post4->get_id(), $entities[0]->get_id());
$entities = $this->vault->get_replies_to_post($post3);
$this->assertCount(0, $entities);
}
/**
* Test get_reply_count_for_discussion_ids when no discussion ids were provided.
*
* @covers ::get_reply_count_for_discussion_ids
*/
public function test_get_reply_count_for_discussion_ids_empty() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$counts = $this->vault->get_reply_count_for_discussion_ids([]);
$this->assertCount(0, $counts);
}
/**
* Test get_reply_count_for_discussion_ids.
*
* @covers ::get_reply_count_for_discussion_ids
* @covers ::<!public>
*/
public function test_get_reply_count_for_discussion_ids() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user);
$post2 = $this->helper_reply_to_post($post1, $user);
$post3 = $this->helper_reply_to_post($post1, $user);
$post4 = $this->helper_reply_to_post($post2, $user);
[$discussion2, $post5] = $this->helper_post_to_forum($forum, $user);
$post6 = $this->helper_reply_to_post($post5, $user);
[$discussion3, $post7] = $this->helper_post_to_forum($forum, $user);
$counts = $this->vault->get_reply_count_for_discussion_ids([$discussion1->id]);
$this->assertCount(1, $counts);
$this->assertEquals(3, $counts[$discussion1->id]);
$counts = $this->vault->get_reply_count_for_discussion_ids([$discussion1->id, $discussion2->id]);
$this->assertCount(2, $counts);
$this->assertEquals(3, $counts[$discussion1->id]);
$this->assertEquals(1, $counts[$discussion2->id]);
$counts = $this->vault->get_reply_count_for_discussion_ids([$discussion1->id, $discussion2->id, $discussion3->id]);
$this->assertCount(2, $counts);
$this->assertEquals(3, $counts[$discussion1->id]);
$this->assertEquals(1, $counts[$discussion2->id]);
$counts = $this->vault->get_reply_count_for_discussion_ids([
$discussion1->id,
$discussion2->id,
$discussion3->id,
$discussion3->id + 1000
]);
$this->assertCount(2, $counts);
$this->assertEquals(3, $counts[$discussion1->id]);
$this->assertEquals(1, $counts[$discussion2->id]);
}
/**
* Test get_unread_count_for_discussion_ids.
*
* @covers ::get_unread_count_for_discussion_ids
* @covers ::<!public>
*/
public function test_get_unread_count_for_discussion_ids() {
global $CFG;
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$otheruser = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user);
$post2 = $this->helper_reply_to_post($post1, $user);
$post3 = $this->helper_reply_to_post($post1, $user);
$post4 = $this->helper_reply_to_post($post2, $user);
[$discussion2, $post5] = $this->helper_post_to_forum($forum, $user);
$post6 = $this->helper_reply_to_post($post5, $user);
$modgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$post7 = $modgenerator->create_post((object) [
'discussion' => $post5->discussion,
'parent' => $post5->id,
'userid' => $user->id,
'mailnow' => 1,
'subject' => 'old post',
// Two days ago which makes it an "old post".
'modified' => time() - 172800
]);
forum_tp_add_read_record($user->id, $post1->id);
forum_tp_add_read_record($user->id, $post4->id);
$CFG->forum_oldpostdays = 1;
$counts = $this->vault->get_unread_count_for_discussion_ids($user, [$discussion1->id]);
$this->assertCount(1, $counts);
$this->assertEquals(2, $counts[$discussion1->id]);
$counts = $this->vault->get_unread_count_for_discussion_ids($user, [$discussion1->id, $discussion2->id]);
$this->assertCount(2, $counts);
$this->assertEquals(2, $counts[$discussion1->id]);
$this->assertEquals(2, $counts[$discussion2->id]);
$counts = $this->vault->get_unread_count_for_discussion_ids($user, [
$discussion1->id,
$discussion2->id,
$discussion2->id + 1000
]);
$this->assertCount(2, $counts);
$this->assertEquals(2, $counts[$discussion1->id]);
$this->assertEquals(2, $counts[$discussion2->id]);
$counts = $this->vault->get_unread_count_for_discussion_ids($otheruser, [$discussion1->id, $discussion2->id]);
$this->assertCount(2, $counts);
$this->assertEquals(4, $counts[$discussion1->id]);
$this->assertEquals(2, $counts[$discussion2->id]);
}
/**
* Test get_unread_count_for_discussion_ids when no discussion ids were provided.
*
* @covers ::get_unread_count_for_discussion_ids
*/
public function test_get_unread_count_for_discussion_ids_empty() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$this->assertEquals([], $this->vault->get_unread_count_for_discussion_ids($user, [], false));
}
/**
* Test get_latest_post_id_for_discussion_ids.
*
* @covers ::get_latest_post_id_for_discussion_ids
* @covers ::<!public>
*/
public function test_get_latest_post_id_for_discussion_ids() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user);
$post2 = $this->helper_reply_to_post($post1, $user);
$post3 = $this->helper_reply_to_post($post1, $user);
$post4 = $this->helper_reply_to_post($post2, $user);
[$discussion2, $post5] = $this->helper_post_to_forum($forum, $user);
$post6 = $this->helper_reply_to_post($post5, $user);
[$discussion3, $post7] = $this->helper_post_to_forum($forum, $user);
$ids = $this->vault->get_latest_post_id_for_discussion_ids([$discussion1->id]);
$this->assertCount(1, $ids);
$this->assertEquals($post4->id, $ids[$discussion1->id]);
$ids = $this->vault->get_latest_post_id_for_discussion_ids([$discussion1->id, $discussion2->id]);
$this->assertCount(2, $ids);
$this->assertEquals($post4->id, $ids[$discussion1->id]);
$this->assertEquals($post6->id, $ids[$discussion2->id]);
$ids = $this->vault->get_latest_post_id_for_discussion_ids([$discussion1->id, $discussion2->id, $discussion3->id]);
$this->assertCount(3, $ids);
$this->assertEquals($post4->id, $ids[$discussion1->id]);
$this->assertEquals($post6->id, $ids[$discussion2->id]);
$this->assertEquals($post7->id, $ids[$discussion3->id]);
$ids = $this->vault->get_latest_post_id_for_discussion_ids([
$discussion1->id,
$discussion2->id,
$discussion3->id,
$discussion3->id + 1000
]);
$this->assertCount(3, $ids);
$this->assertEquals($post4->id, $ids[$discussion1->id]);
$this->assertEquals($post6->id, $ids[$discussion2->id]);
$this->assertEquals($post7->id, $ids[$discussion3->id]);
}
/**
* Test get_latest_post_id_for_discussion_ids when no discussion ids were provided.
*
* @covers ::get_latest_post_id_for_discussion_ids
* @covers ::<!public>
*/
public function test_get_latest_post_id_for_discussion_ids_empty() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$user = $datagenerator->create_user();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
$this->assertEquals([], $this->vault->get_latest_post_id_for_discussion_ids([]));
}
}