%PDF- %PDF-
Direktori : /home2/vacivi36/ava/mod/forum/classes/local/factories/ |
Current File : //home2/vacivi36/ava/mod/forum/classes/local/factories/renderer.php |
<?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Renderer factory. * * @package mod_forum * @copyright 2019 Ryan Wyllie <ryan@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace mod_forum\local\factories; defined('MOODLE_INTERNAL') || die(); use mod_forum\grades\forum_gradeitem; 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 core\output\notification; /** * Renderer factory. * * See: * https://designpatternsphp.readthedocs.io/en/latest/Creational/SimpleFactory/README.html * * @copyright 2019 Ryan Wyllie <ryan@moodle.com> * @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 = []; return new discussion_renderer( $forum, $discussion, $displaymode, $rendererbase, $this->get_single_discussion_posts_renderer($displaymode, false), $this->page, $this->legacydatamapperfactory, $this->exporterfactory, $this->vaultfactory, $this->urlfactory, $this->entityfactory, $capabilitymanager, $ratingmanager, $this->entityfactory->get_exported_posts_sorter(), $baseurl, $notifications, function($discussion, $user, $forum) { $exportbuilder = $this->builderfactory->get_exported_discussion_builder(); return $exportbuilder->build( $user, $forum, $discussion ); } ); } /** * 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; case FORUM_MODE_NESTED_V2: $template = 'mod_forum/forum_discussion_nested_v2_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, $discussions) use ($displaymode, $readonly, $exportedpostssorter) { $forum = array_shift($forums); $seenfirstunread = false; $postcount = count($exportedposts); $discussionsbyid = array_reduce($discussions, function($carry, $discussion) { $carry[$discussion->get_id()] = $discussion; return $carry; }, []); $exportedposts = array_map( function($exportedpost) use ($forum, $discussionsbyid, $readonly, $seenfirstunread, $displaymode) { $discussion = $discussionsbyid[$exportedpost->discussionid] ?? null; if ($forum->get_type() == 'single' && !$exportedpost->hasparent) { // Remove the author from any posts that don't have a parent. unset($exportedpost->author); unset($exportedpost->html['authorsubheading']); } $exportedpost->firstpost = false; $exportedpost->readonly = $readonly; $exportedpost->hasreplycount = false; $exportedpost->hasreplies = false; $exportedpost->replies = []; $exportedpost->discussionlocked = $discussion ? $discussion->is_locked() : null; $exportedpost->isfirstunread = false; if (!$seenfirstunread && $exportedpost->unread) { $exportedpost->isfirstunread = true; $seenfirstunread = true; } if ($displaymode === FORUM_MODE_NESTED_V2) { $exportedpost->showactionmenu = $exportedpost->capabilities['view'] || $exportedpost->capabilities['controlreadstatus'] || $exportedpost->capabilities['edit'] || $exportedpost->capabilities['split'] || $exportedpost->capabilities['delete'] || $exportedpost->capabilities['export'] || !empty($exportedpost->urls['viewparent']); } return $exportedpost; }, $exportedposts ); if ( $displaymode === FORUM_MODE_NESTED || $displaymode === FORUM_MODE_THREADED || $displaymode === FORUM_MODE_NESTED_V2 ) { $sortedposts = $exportedpostssorter->sort_into_children($exportedposts); $sortintoreplies = function($nestedposts) use (&$sortintoreplies) { return array_map(function($postdata) use (&$sortintoreplies) { [$post, $replies] = $postdata; $totalreplycount = 0; if (empty($replies)) { $post->replies = []; $post->hasreplies = false; } else { $sortedreplies = $sortintoreplies($replies); // Set the parent author name on the replies. This is used for screen // readers to help them identify the structure of the discussion. $sortedreplies = array_map(function($reply) use ($post) { if (isset($post->author)) { $reply->parentauthorname = $post->author->fullname; } else { // The only time the author won't be set is for a single discussion // forum. See above for where it gets unset. $reply->parentauthorname = get_string('firstpost', 'mod_forum'); } return $reply; }, $sortedreplies); $totalreplycount = array_reduce($sortedreplies, function($carry, $reply) { return $carry + 1 + $reply->totalreplycount; }, $totalreplycount); $post->replies = $sortedreplies; $post->hasreplies = true; } $post->totalreplycount = $totalreplycount; 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]; } if (!empty($exportedposts)) { // Need to identify the first post so that we can use it in behat tests. $exportedposts[0]->firstpost = true; $exportedposts[0]->hasreplycount = true; $exportedposts[0]->replycount = $postcount - 1; } 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_search_results', // 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 standard type 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 = []; switch ($forum->get_type()) { case 'news': if (SITEID == $forum->get_course_id()) { $template = 'mod_forum/frontpage_news_discussion_list'; } else { $template = 'mod_forum/news_discussion_list'; } break; case 'qanda': $template = 'mod_forum/qanda_discussion_list'; break; default: $template = 'mod_forum/discussion_list'; } return new discussion_list_renderer( $forum, $rendererbase, $this->legacydatamapperfactory, $this->exporterfactory, $this->vaultfactory, $this->builderfactory, $capabilitymanager, $this->urlfactory, forum_gradeitem::load_from_forum_entity($forum), $template, $notifications, function($discussions, $user, $forum) { $exporteddiscussionsummarybuilder = $this->builderfactory->get_exported_discussion_summaries_builder(); return $exporteddiscussionsummarybuilder->build( $user, $forum, $discussions ); } ); } /** * Create a discussion list renderer which shows more information about the first post. * * @param forum_entity $forum The forum that the discussions belong to * @param string $template The template to use * @return discussion_list_renderer */ private function get_detailed_discussion_list_renderer( forum_entity $forum, string $template ) : 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, $this->builderfactory, $capabilitymanager, $this->urlfactory, forum_gradeitem::load_from_forum_entity($forum), $template, $notifications, function($discussions, $user, $forum) use ($capabilitymanager) { $exportedpostsbuilder = $this->builderfactory->get_exported_posts_builder(); $discussionentries = []; $postentries = []; foreach ($discussions as $discussion) { $discussionentries[] = $discussion->get_discussion(); $discussionentriesids[] = $discussion->get_discussion()->get_id(); $postentries[] = $discussion->get_first_post(); } $exportedposts['posts'] = $exportedpostsbuilder->build( $user, [$forum], $discussionentries, $postentries ); $postvault = $this->vaultfactory->get_post_vault(); $canseeanyprivatereply = $capabilitymanager->can_view_any_private_reply($user); $discussionrepliescount = $postvault->get_reply_count_for_discussion_ids( $user, $discussionentriesids, $canseeanyprivatereply ); $forumdatamapper = $this->legacydatamapperfactory->get_forum_data_mapper(); $forumrecord = $forumdatamapper->to_legacy_object($forum); if (forum_tp_is_tracked($forumrecord, $user)) { $discussionunreadscount = $postvault->get_unread_count_for_discussion_ids( $user, $discussionentriesids, $canseeanyprivatereply ); } else { $discussionunreadscount = []; } array_walk($exportedposts['posts'], function($post) use ($discussionrepliescount, $discussionunreadscount) { $post->discussionrepliescount = $discussionrepliescount[$post->discussionid] ?? 0; $post->discussionunreadscount = $discussionunreadscount[$post->discussionid] ?? 0; // TODO: Find a better solution due to language differences when defining the singular and plural form. $post->isreplyplural = $post->discussionrepliescount != 1 ? true : false; $post->isunreadplural = $post->discussionunreadscount != 1 ? true : false; }); $exportedposts['state']['hasdiscussions'] = $exportedposts['posts'] ? true : false; return $exportedposts; } ); } /** * Create a blog type discussion list renderer. * * @param forum_entity $forum The forum that the discussions belong to * @return discussion_list_renderer */ public function get_blog_discussion_list_renderer( forum_entity $forum ) : discussion_list_renderer { return $this->get_detailed_discussion_list_renderer($forum, 'mod_forum/blog_discussion_list'); } /** * Create a discussion list renderer for the social course format. * * @param forum_entity $forum The forum that the discussions belong to * @return discussion_list_renderer */ public function get_social_discussion_list_renderer( forum_entity $forum ) : discussion_list_renderer { return $this->get_detailed_discussion_list_renderer($forum, 'mod_forum/social_discussion_list'); } /** * Create a discussion list renderer for the social course format. * * @param forum_entity $forum The forum that the discussions belong to * @return discussion_list_renderer */ public function get_frontpage_news_discussion_list_renderer( forum_entity $forum ) : discussion_list_renderer { return $this->get_detailed_discussion_list_renderer($forum, 'mod_forum/frontpage_social_discussion_list'); } /** * Create a single type discussion list renderer. * * @param forum_entity $forum Forum the discussion belongs to * @param discussion_entity $discussion The discussion entity * @param bool $hasmultiplediscussions Whether the forum has multiple discussions (more than one) * @param int $displaymode How should the posts be formatted? * @return discussion_renderer */ public function get_single_discussion_list_renderer( forum_entity $forum, discussion_entity $discussion, bool $hasmultiplediscussions, int $displaymode ) : discussion_renderer { $capabilitymanager = $this->managerfactory->get_capability_manager($forum); $ratingmanager = $this->managerfactory->get_rating_manager(); $rendererbase = $this->rendererbase; $cmid = $forum->get_course_module_record()->id; $baseurl = $this->urlfactory->get_forum_view_url_from_course_module_id($cmid); $notifications = array(); if ($hasmultiplediscussions) { $notifications[] = (new notification(get_string('warnformorepost', 'forum'))) ->set_show_closebutton(true); } return new discussion_renderer( $forum, $discussion, $displaymode, $rendererbase, $this->get_single_discussion_posts_renderer($displaymode, false), $this->page, $this->legacydatamapperfactory, $this->exporterfactory, $this->vaultfactory, $this->urlfactory, $this->entityfactory, $capabilitymanager, $ratingmanager, $this->entityfactory->get_exported_posts_sorter(), $baseurl, $notifications ); } }