diff --git a/backup/moodle1/lib.php b/backup/moodle1/lib.php index 69f3f8c7..337ab44c 100644 --- a/backup/moodle1/lib.php +++ b/backup/moodle1/lib.php @@ -1,5 +1,4 @@ cm->instance; if (!$hsuforum = $DB->get_record('hsuforum', ['id' => $hsuforumid])) { - throw new \moodle_exception('Unable to find hsuforum with id ' . $hsuforumid); + throw new \core\exception\moodle_exception('Unable to find hsuforum with id ' . $hsuforumid); } $postcountparams = ['userid' => $userid, 'hsuforumid' => $hsuforumid]; diff --git a/classes/controller/controller_abstract.php b/classes/controller/controller_abstract.php index 2b81c22b..723f0bb2 100644 --- a/classes/controller/controller_abstract.php +++ b/classes/controller/controller_abstract.php @@ -53,7 +53,7 @@ public function set_renderer(\mod_hsuforum_renderer $renderer) { * Generate a new URL to this page * * @param array $params - * @return \moodle_url + * @return \core\url */ public function new_url($params = array()) { global $PAGE; @@ -68,7 +68,7 @@ public function new_url($params = array()) { * action is invoked. * * @param string $action This is the action that is about to be invoked - * @throws \moodle_exception + * @throws \core\exception\moodle_exception */ public function init($action) { $this->require_capability($action); diff --git a/classes/controller/edit_controller.php b/classes/controller/edit_controller.php index 153b8e22..9efa0dbf 100644 --- a/classes/controller/edit_controller.php +++ b/classes/controller/edit_controller.php @@ -26,7 +26,7 @@ namespace mod_hsuforum\controller; -use coding_exception; +use \core\exception\coding_exception; use mod_hsuforum\response\json_response; use mod_hsuforum\service\discussion_service; use mod_hsuforum\service\form_service; @@ -119,7 +119,7 @@ public function reply_action() { 'message' => $message, 'messageformat' => $messageformat, 'reveal' => $reveal, - 'mailnow' => $mailnow + 'mailnow' => $mailnow, ); if (!empty($subject)) { $data['subject'] = $subject; @@ -175,7 +175,7 @@ public function add_discussion_action() { 'reveal' => $reveal, 'timestart' => $timestart, 'timeend' => $timeend, - 'mailnow' => $mailnow + 'mailnow' => $mailnow, ); return $this->discussionservice->handle_add_discussion($course, $cm, $forum, $context, $options, $posttomygroups); } catch (\Exception $e) { @@ -238,7 +238,7 @@ public function update_post_action() { 'reveal' => $reveal, 'privatereply' => $privatereply, 'timestart' => $timestart, - 'timeend' => $timeend + 'timeend' => $timeend, )); } catch (\Exception $e) { return new json_response($e); @@ -256,7 +256,7 @@ public function edit_post_form_action() { $postid = required_param('postid', PARAM_INT); if (!$post = hsuforum_get_post_full($postid)) { - throw new \moodle_exception('invalidpostid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidpostid', 'hsuforum'); } $discussion = $DB->get_record('hsuforum_discussions', array('id' => $post->discussion), '*', MUST_EXIST); @@ -281,20 +281,20 @@ public function edit_post_form_action() { 'isdiscussion' => true, 'timestart' => $discussion->timestart, 'timeend' => $discussion->timeend, - 'offset' => $offset + 'offset' => $offset, ]); } } /** * @return json_response - * @throws \coding_exception + * @throws \core\exception\coding_exception */ public function delete_post_action() { global $USER, $DB, $PAGE; if (!AJAX_SCRIPT) { - throw new coding_exception('This is an AJAX action and you cannot access it directly'); + throw new \core\exception\coding_exception('This is an AJAX action and you cannot access it directly'); } require_sesskey(); @@ -306,7 +306,7 @@ public function delete_post_action() { $candeleteown = ($post->userid == $USER->id && has_capability('mod/hsuforum:deleteownpost', $PAGE->context)); if (!($candeleteown || has_capability('mod/hsuforum:deleteanypost', $PAGE->context))) { - throw new \moodle_exception('cannotdeletepost', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotdeletepost', 'hsuforum'); } $redirect = hsuforum_verify_and_delete_post($PAGE->course, $PAGE->cm, @@ -319,7 +319,7 @@ public function delete_post_action() { } else { $message = get_string('deleteddiscussion', 'hsuforum'); } - /** @var \core_renderer $renderer */ + /** @var \core\output\core_renderer $renderer */ $renderer = $PAGE->get_renderer('core', null, RENDERER_TARGET_GENERAL); return new json_response(array( diff --git a/classes/controller/export_controller.php b/classes/controller/export_controller.php index 017a82b5..3055ebc7 100644 --- a/classes/controller/export_controller.php +++ b/classes/controller/export_controller.php @@ -55,7 +55,7 @@ public function require_capability($action) { require_capability('mod/hsuforum:viewdiscussion', $PAGE->context); if (is_guest($PAGE->context)) { - throw new \moodle_exception('noguest'); + throw new \core\exception\moodle_exception('noguest'); } } @@ -73,7 +73,7 @@ public function export_action() { ), 'post', '', array('onreset' => '')); if ($mform->is_cancelled()) { - redirect(new \moodle_url('/mod/hsuforum/view.php', array('id' => $cm->id))); + redirect(new \core\url('/mod/hsuforum/view.php', array('id' => $cm->id))); } else if ($data = $mform->get_data()) { if ($data->format == 'print') { $adapter = new print_adapter($cm); @@ -83,7 +83,7 @@ public function export_action() { } else if ($data->format == 'text') { $format = new text_format(); } else { - throw new \coding_exception('Unrecognized export format: '.$data->format); + throw new \core\exception\coding_exception('Unrecognized export format: '.$data->format); } $adapter = new file_adapter($cm, $format, (boolean) $data->attachments); } diff --git a/classes/controller/flag_controller.php b/classes/controller/flag_controller.php index e907cf07..dda4a4f9 100644 --- a/classes/controller/flag_controller.php +++ b/classes/controller/flag_controller.php @@ -66,7 +66,7 @@ public function flag_action() { } } if (!AJAX_SCRIPT) { - redirect(new \moodle_url($returnurl)); + redirect(new \core\url($returnurl)); } } } diff --git a/classes/controller/kernel.php b/classes/controller/kernel.php index ec75c66c..856464c4 100644 --- a/classes/controller/kernel.php +++ b/classes/controller/kernel.php @@ -117,7 +117,7 @@ public function resolve_controller_callback($action) { * * @param callable $callback * @return string - * @throws \coding_exception + * @throws \core\exception\coding_exception */ public function generate_response($callback) { ob_start(); @@ -125,8 +125,8 @@ public function generate_response($callback) { $buffer = trim(ob_get_contents()); ob_end_clean(); - if (!empty($response) and !empty($buffer)) { - throw new \coding_exception('Mixed return output and buffer output', "Buffer: $buffer"); + if (!empty($response) && !empty($buffer)) { + throw new \core\exception\coding_exception('Mixed return output and buffer output', "Buffer: $buffer"); } else if (!empty($buffer)) { $response = $buffer; } diff --git a/classes/controller/posts_controller.php b/classes/controller/posts_controller.php index 98823407..82b4982a 100644 --- a/classes/controller/posts_controller.php +++ b/classes/controller/posts_controller.php @@ -26,10 +26,10 @@ namespace mod_hsuforum\controller; -use coding_exception; +use \core\exception\coding_exception; use mod_hsuforum\response\json_response; use mod_hsuforum\service\discussion_service; -use moodle_url; +use \core\url; defined('MOODLE_INTERNAL') || die(); @@ -62,7 +62,7 @@ public function require_capability($action) { switch ($action) { case 'discsubscribers': if (!has_capability('mod/hsuforum:viewsubscribers', $PAGE->context)) { - throw new \moodle_exception('nopermissiontosubscribe', 'hsuforum'); + throw new \core\exception\moodle_exception('nopermissiontosubscribe', 'hsuforum'); } break; default: @@ -73,13 +73,13 @@ public function require_capability($action) { /** * Marks a post as read * - * @throws coding_exception + * @throws \core\exception\coding_exception */ public function markread_action() { global $PAGE, $DB, $CFG, $USER; if (!AJAX_SCRIPT) { - throw new coding_exception('This is an AJAX action and you cannot access it directly'); + throw new \core\exception\coding_exception('This is an AJAX action and you cannot access it directly'); } require_once($CFG->dirroot.'/rating/lib.php'); @@ -88,7 +88,7 @@ public function markread_action() { $cm = $PAGE->cm; if (!$post = hsuforum_get_post_full($postid)) { - throw new \moodle_exception("notexists", 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); + throw new \core\exception\moodle_exception("notexists", 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); } $discussion = $DB->get_record('hsuforum_discussions', array('id' => $post->discussion), '*', MUST_EXIST); @@ -96,11 +96,11 @@ public function markread_action() { if (!($USER->id == $discussion->userid || (($discussion->timestart == 0 || $discussion->timestart <= time()) && ($discussion->timeend == 0 || $discussion->timeend > time())))) { - throw new \moodle_exception('invaliddiscussionid', 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); + throw new \core\exception\moodle_exception('invaliddiscussionid', 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); } } if (!hsuforum_user_can_see_post($forum, $discussion, $post, null, $cm)) { - throw new \moodle_exception('nopermissiontoview', 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); + throw new \core\exception\moodle_exception('nopermissiontoview', 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); } hsuforum_tp_add_read_record($USER->id, $post->id); return new json_response(array('postid' => $postid, 'discussionid' => $discussion->id)); @@ -127,7 +127,7 @@ public function subscribedisc_action() { $subscribe->subscribe($discussionid); } if (!AJAX_SCRIPT) { - redirect(new moodle_url($returnurl)); + redirect(new url($returnurl)); } } @@ -156,7 +156,7 @@ public function discsubscribers_action() { $repo = new \hsuforum_repository_discussion(); if (hsuforum_is_forcesubscribed($forum)) { - throw new coding_exception('Cannot manage discussion subscriptions when subscription is forced'); + throw new \core\exception\coding_exception('Cannot manage discussion subscriptions when subscription is forced'); } $currentgroup = groups_get_activity_group($cm); @@ -170,7 +170,7 @@ public function discsubscribers_action() { $unsubscribe = (bool)optional_param('unsubscribe', false, PARAM_RAW); /** It has to be one or the other, not both or neither */ if (!($subscribe xor $unsubscribe)) { - throw new \moodle_exception('invalidaction'); + throw new \core\exception\moodle_exception('invalidaction'); } if ($subscribe) { $users = $subscriberselector->get_selected_users(); @@ -194,7 +194,7 @@ public function discsubscribers_action() { // This works but it doesn't make a good navbar, would have to change the settings menu. // $PAGE->settingsnav->find('discsubscribers', navigation_node::TYPE_SETTING)->make_active(); - $PAGE->navbar->add(shorten_text(format_string($discussion->name)), new moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id))); + $PAGE->navbar->add(shorten_text(format_string($discussion->name)), new url('/mod/hsuforum/discuss.php', array('d' => $discussion->id))); $PAGE->navbar->add($strsubscribers); $PAGE->set_title($strsubscribers); $PAGE->set_heading($COURSE->fullname); diff --git a/classes/controller/router.php b/classes/controller/router.php index 8b6d933f..c843e0cd 100644 --- a/classes/controller/router.php +++ b/classes/controller/router.php @@ -24,7 +24,7 @@ namespace mod_hsuforum\controller; -use coding_exception; +use \core\exception\coding_exception; use SplObjectStorage; defined('MOODLE_INTERNAL') || die(); @@ -79,7 +79,7 @@ public function get_controllers() { * * @param $action * @return array The controller and method to execute - * @throws coding_exception + * @throws \core\exception\coding_exception */ public function route_action($action) { $method = "{$action}_action"; @@ -88,10 +88,10 @@ public function route_action($action) { if (!$reflection->hasMethod($method)) { continue; } else if ($reflection->getMethod($method)->isPublic() !== true) { - throw new coding_exception("The controller callback is not public: $method"); + throw new \core\exception\coding_exception("The controller callback is not public: $method"); } return array($controller, $method); } - throw new coding_exception("Unable to handle request for $method"); + throw new \core\exception\coding_exception("Unable to handle request for $method"); } } diff --git a/classes/event/assessable_uploaded.php b/classes/event/assessable_uploaded.php index f791bc7e..45f3c721 100644 --- a/classes/event/assessable_uploaded.php +++ b/classes/event/assessable_uploaded.php @@ -66,10 +66,10 @@ public static function get_name() { /** * Get URL related to the action. * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $this->other['discussionid'], 'parent' => $this->objectid)); + return new \core\url('/mod/hsuforum/discuss.php', array('d' => $this->other['discussionid'], 'parent' => $this->objectid)); } /** @@ -85,16 +85,16 @@ protected function init() { /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->other['discussionid'])) { - throw new \coding_exception('The \'discussionid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'discussionid\' value must be set in other.'); } else if (!isset($this->other['triggeredfrom'])) { - throw new \coding_exception('The \'triggeredfrom\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'triggeredfrom\' value must be set in other.'); } } diff --git a/classes/event/course_module_viewed.php b/classes/event/course_module_viewed.php index 4cf30824..5af23a31 100644 --- a/classes/event/course_module_viewed.php +++ b/classes/event/course_module_viewed.php @@ -50,10 +50,10 @@ protected function init() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/view.php', array('f' => $this->objectid)); + return new \core\url('/mod/hsuforum/view.php', array('f' => $this->objectid)); } public static function get_objectid_mapping() { diff --git a/classes/event/course_searched.php b/classes/event/course_searched.php index ab92f422..70f1a1c8 100644 --- a/classes/event/course_searched.php +++ b/classes/event/course_searched.php @@ -75,27 +75,27 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/search.php', + return new \core\url('/mod/hsuforum/search.php', array('id' => $this->courseid, 'search' => $this->other['searchterm'])); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->other['searchterm'])) { - throw new \coding_exception('The \'searchterm\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'searchterm\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_COURSE) { - throw new \coding_exception('Context level must be CONTEXT_COURSE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_COURSE.'); } } diff --git a/classes/event/discussion_created.php b/classes/event/discussion_created.php index 16a9bf67..4b01eac4 100644 --- a/classes/event/discussion_created.php +++ b/classes/event/discussion_created.php @@ -74,26 +74,26 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $this->objectid)); + return new \core\url('/mod/hsuforum/discuss.php', array('d' => $this->objectid)); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/discussion_deleted.php b/classes/event/discussion_deleted.php index c29b3c76..f758991c 100644 --- a/classes/event/discussion_deleted.php +++ b/classes/event/discussion_deleted.php @@ -75,26 +75,26 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/view.php', array('id' => $this->contextinstanceid)); + return new \core\url('/mod/hsuforum/view.php', array('id' => $this->contextinstanceid)); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/discussion_moved.php b/classes/event/discussion_moved.php index 7a81a5eb..4faf3fdd 100644 --- a/classes/event/discussion_moved.php +++ b/classes/event/discussion_moved.php @@ -75,30 +75,30 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $this->objectid)); + return new \core\url('/mod/hsuforum/discuss.php', array('d' => $this->objectid)); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->other['fromforumid'])) { - throw new \coding_exception('The \'fromforumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'fromforumid\' value must be set in other.'); } if (!isset($this->other['toforumid'])) { - throw new \coding_exception('The \'toforumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'toforumid\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/discussion_pinned.php b/classes/event/discussion_pinned.php index d859608e..22013f7d 100644 --- a/classes/event/discussion_pinned.php +++ b/classes/event/discussion_pinned.php @@ -64,28 +64,28 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $this->objectid)); + return new \core\url('/mod/hsuforum/discuss.php', array('d' => $this->objectid)); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->other['forumid'])) { - throw new \coding_exception('forumid must be set in $other.'); + throw new \core\exception\coding_exception('forumid must be set in $other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context passed must be module context.'); + throw new \core\exception\coding_exception('Context passed must be module context.'); } if (!isset($this->objectid)) { - throw new \coding_exception('objectid must be set to the discussionid.'); + throw new \core\exception\coding_exception('objectid must be set to the discussionid.'); } } diff --git a/classes/event/discussion_subscription_created.php b/classes/event/discussion_subscription_created.php index d382b82f..30472c18 100644 --- a/classes/event/discussion_subscription_created.php +++ b/classes/event/discussion_subscription_created.php @@ -75,10 +75,10 @@ public static function get_name() { /** * Get URL related to the action. * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/subscribe.php', array( + return new \core\url('/mod/hsuforum/subscribe.php', array( 'id' => $this->other['forumid'], 'd' => $this->other['discussion'], )); @@ -87,26 +87,26 @@ public function get_url() { /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->relateduserid)) { - throw new \coding_exception('The \'relateduserid\' must be set.'); + throw new \core\exception\coding_exception('The \'relateduserid\' must be set.'); } if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if (!isset($this->other['discussion'])) { - throw new \coding_exception('The \'discussion\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'discussion\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/discussion_subscription_deleted.php b/classes/event/discussion_subscription_deleted.php index 4bee2f2f..4e2803fb 100644 --- a/classes/event/discussion_subscription_deleted.php +++ b/classes/event/discussion_subscription_deleted.php @@ -75,10 +75,10 @@ public static function get_name() { /** * Get URL related to the action. * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/subscribe.php', array( + return new \core\url('/mod/hsuforum/subscribe.php', array( 'id' => $this->other['forumid'], 'd' => $this->other['discussion'], )); @@ -87,26 +87,26 @@ public function get_url() { /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->relateduserid)) { - throw new \coding_exception('The \'relateduserid\' must be set.'); + throw new \core\exception\coding_exception('The \'relateduserid\' must be set.'); } if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if (!isset($this->other['discussion'])) { - throw new \coding_exception('The \'discussion\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'discussion\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/discussion_unpinned.php b/classes/event/discussion_unpinned.php index b046ba6a..6680b902 100644 --- a/classes/event/discussion_unpinned.php +++ b/classes/event/discussion_unpinned.php @@ -64,28 +64,28 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $this->objectid)); + return new \core\url('/mod/hsuforum/discuss.php', array('d' => $this->objectid)); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->other['forumid'])) { - throw new \coding_exception('forumid must be set in $other.'); + throw new \core\exception\coding_exception('forumid must be set in $other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context passed must be module context.'); + throw new \core\exception\coding_exception('Context passed must be module context.'); } if (!isset($this->objectid)) { - throw new \coding_exception('objectid must be set to the discussionid.'); + throw new \core\exception\coding_exception('objectid must be set to the discussionid.'); } } diff --git a/classes/event/discussion_updated.php b/classes/event/discussion_updated.php index de3852e0..7d6c0371 100644 --- a/classes/event/discussion_updated.php +++ b/classes/event/discussion_updated.php @@ -74,27 +74,27 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $this->objectid)); + return new \core\url('/mod/hsuforum/discuss.php', array('d' => $this->objectid)); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/discussion_viewed.php b/classes/event/discussion_viewed.php index 98cb5b21..c93017ab 100644 --- a/classes/event/discussion_viewed.php +++ b/classes/event/discussion_viewed.php @@ -69,23 +69,23 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $this->objectid)); + return new \core\url('/mod/hsuforum/discuss.php', array('d' => $this->objectid)); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/post_created.php b/classes/event/post_created.php index 78749171..81eb274c 100644 --- a/classes/event/post_created.php +++ b/classes/event/post_created.php @@ -76,16 +76,16 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { if ($this->other['forumtype'] == 'single') { // Single discussion forums are an exception. We show // the forum itself since it only has one discussion // thread. - $url = new \moodle_url('/mod/hsuforum/view.php', array('f' => $this->other['forumid'])); + $url = new \core\url('/mod/hsuforum/view.php', array('f' => $this->other['forumid'])); } else { - $url = new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $this->other['discussionid'])); + $url = new \core\url('/mod/hsuforum/discuss.php', array('d' => $this->other['discussionid'])); } $url->set_anchor('p'.$this->objectid); return $url; @@ -94,26 +94,26 @@ public function get_url() { /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->other['discussionid'])) { - throw new \coding_exception('The \'discussionid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'discussionid\' value must be set in other.'); } if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if (!isset($this->other['forumtype'])) { - throw new \coding_exception('The \'forumtype\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumtype\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/post_deleted.php b/classes/event/post_deleted.php index 8a8e1505..204f88d4 100644 --- a/classes/event/post_deleted.php +++ b/classes/event/post_deleted.php @@ -76,16 +76,16 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { if ($this->other['forumtype'] == 'single') { // Single discussion forums are an exception. We show // the forum itself since it only has one discussion // thread. - $url = new \moodle_url('/mod/hsuforum/view.php', array('f' => $this->other['forumid'])); + $url = new \core\url('/mod/hsuforum/view.php', array('f' => $this->other['forumid'])); } else { - $url = new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $this->other['discussionid'])); + $url = new \core\url('/mod/hsuforum/discuss.php', array('d' => $this->other['discussionid'])); } return $url; } @@ -93,26 +93,26 @@ public function get_url() { /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->other['discussionid'])) { - throw new \coding_exception('The \'discussionid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'discussionid\' value must be set in other.'); } if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if (!isset($this->other['forumtype'])) { - throw new \coding_exception('The \'forumtype\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumtype\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/post_updated.php b/classes/event/post_updated.php index 9bfebf7b..37e4e8f1 100644 --- a/classes/event/post_updated.php +++ b/classes/event/post_updated.php @@ -76,16 +76,16 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { if ($this->other['forumtype'] == 'single') { // Single discussion forums are an exception. We show // the forum itself since it only has one discussion // thread. - $url = new \moodle_url('/mod/hsuforum/view.php', array('f' => $this->other['forumid'])); + $url = new \core\url('/mod/hsuforum/view.php', array('f' => $this->other['forumid'])); } else { - $url = new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $this->other['discussionid'])); + $url = new \core\url('/mod/hsuforum/discuss.php', array('d' => $this->other['discussionid'])); } $url->set_anchor('p'.$this->objectid); return $url; @@ -94,26 +94,26 @@ public function get_url() { /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->other['discussionid'])) { - throw new \coding_exception('The \'discussionid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'discussionid\' value must be set in other.'); } if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if (!isset($this->other['forumtype'])) { - throw new \coding_exception('The \'forumtype\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumtype\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/readtracking_disabled.php b/classes/event/readtracking_disabled.php index 5423ae4a..db4cb325 100644 --- a/classes/event/readtracking_disabled.php +++ b/classes/event/readtracking_disabled.php @@ -73,31 +73,31 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/view.php', array('f' => $this->other['forumid'])); + return new \core\url('/mod/hsuforum/view.php', array('f' => $this->other['forumid'])); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->relateduserid)) { - throw new \coding_exception('The \'relateduserid\' must be set.'); + throw new \core\exception\coding_exception('The \'relateduserid\' must be set.'); } if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/readtracking_enabled.php b/classes/event/readtracking_enabled.php index 08e24197..6f2ade20 100644 --- a/classes/event/readtracking_enabled.php +++ b/classes/event/readtracking_enabled.php @@ -73,31 +73,31 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/view.php', array('f' => $this->other['forumid'])); + return new \core\url('/mod/hsuforum/view.php', array('f' => $this->other['forumid'])); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->relateduserid)) { - throw new \coding_exception('The \'relateduserid\' must be set.'); + throw new \core\exception\coding_exception('The \'relateduserid\' must be set.'); } if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/subscribers_viewed.php b/classes/event/subscribers_viewed.php index f3e44228..c2df897d 100644 --- a/classes/event/subscribers_viewed.php +++ b/classes/event/subscribers_viewed.php @@ -74,27 +74,27 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/subscribers.php', array('id' => $this->other['forumid'])); + return new \core\url('/mod/hsuforum/subscribers.php', array('id' => $this->other['forumid'])); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/subscription_created.php b/classes/event/subscription_created.php index f824c931..95586737 100644 --- a/classes/event/subscription_created.php +++ b/classes/event/subscription_created.php @@ -74,31 +74,31 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/subscribers.php', array('id' => $this->other['forumid'])); + return new \core\url('/mod/hsuforum/subscribers.php', array('id' => $this->other['forumid'])); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->relateduserid)) { - throw new \coding_exception('The \'relateduserid\' must be set.'); + throw new \core\exception\coding_exception('The \'relateduserid\' must be set.'); } if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/subscription_deleted.php b/classes/event/subscription_deleted.php index 866845d9..4dae053b 100644 --- a/classes/event/subscription_deleted.php +++ b/classes/event/subscription_deleted.php @@ -74,31 +74,31 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - return new \moodle_url('/mod/hsuforum/subscribers.php', array('id' => $this->other['forumid'])); + return new \core\url('/mod/hsuforum/subscribers.php', array('id' => $this->other['forumid'])); } /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->relateduserid)) { - throw new \coding_exception('The \'relateduserid\' must be set.'); + throw new \core\exception\coding_exception('The \'relateduserid\' must be set.'); } if (!isset($this->other['forumid'])) { - throw new \coding_exception('The \'forumid\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'forumid\' value must be set in other.'); } if ($this->contextlevel != CONTEXT_MODULE) { - throw new \coding_exception('Context level must be CONTEXT_MODULE.'); + throw new \core\exception\coding_exception('Context level must be CONTEXT_MODULE.'); } } diff --git a/classes/event/user_report_viewed.php b/classes/event/user_report_viewed.php index 10c66321..c4079fe6 100644 --- a/classes/event/user_report_viewed.php +++ b/classes/event/user_report_viewed.php @@ -74,11 +74,11 @@ public static function get_name() { /** * Get URL related to the action * - * @return \moodle_url + * @return \core\url */ public function get_url() { - $url = new \moodle_url('/mod/hsuforum/user.php', array('id' => $this->relateduserid, + $url = new \core\url('/mod/hsuforum/user.php', array('id' => $this->relateduserid, 'mode' => $this->other['reportmode'])); if ($this->courseid != SITEID) { @@ -91,16 +91,16 @@ public function get_url() { /** * Custom validation. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ protected function validate_data() { parent::validate_data(); if (!isset($this->relateduserid)) { - throw new \coding_exception('The \'relateduserid\' must be set.'); + throw new \core\exception\coding_exception('The \'relateduserid\' must be set.'); } if (!isset($this->other['reportmode'])) { - throw new \coding_exception('The \'reportmode\' value must be set in other.'); + throw new \core\exception\coding_exception('The \'reportmode\' value must be set in other.'); } switch ($this->contextlevel) @@ -112,7 +112,7 @@ protected function validate_data() { break; default: // Unexpected contextlevel. - throw new \coding_exception('Context level must be either CONTEXT_SYSTEM, CONTEXT_COURSE or CONTEXT_USER.'); + throw new \core\exception\coding_exception('Context level must be either CONTEXT_SYSTEM, CONTEXT_COURSE or CONTEXT_USER.'); } } diff --git a/classes/export/file_adapter.php b/classes/export/file_adapter.php index 3e5a0fe9..3bd9a565 100644 --- a/classes/export/file_adapter.php +++ b/classes/export/file_adapter.php @@ -142,7 +142,7 @@ public function send_discussion($discussion, $posts) { /** * Exporting is done, wrap things up. * - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ public function finish() { @@ -158,7 +158,7 @@ public function finish() { $zip = new \zip_packer(); if (!$zip->archive_to_pathname($this->archivefiles, $zippath)) { - throw new \coding_exception('Failed to create zip archive'); + throw new \core\exception\coding_exception('Failed to create zip archive'); } send_file($zippath, $zipname, 0, 0, false, true, '', true); } else { diff --git a/classes/export/format_abstract.php b/classes/export/format_abstract.php index aa8854ca..9bc69304 100644 --- a/classes/export/format_abstract.php +++ b/classes/export/format_abstract.php @@ -43,14 +43,14 @@ abstract class format_abstract { * Init routine * * @param string $file Absolute path to the file to export to - * @throws \coding_exception + * @throws \core\exception\coding_exception * @return void */ public function init($file) { $this->fp = fopen($file, 'w'); if ($this->fp === false) { - throw new \coding_exception('Failed to open file for writing'); + throw new \core\exception\coding_exception('Failed to open file for writing'); } } diff --git a/classes/grades/forum_gradeitem.php b/classes/grades/forum_gradeitem.php new file mode 100644 index 00000000..0b1bfcee --- /dev/null +++ b/classes/grades/forum_gradeitem.php @@ -0,0 +1,292 @@ +. + +/** + * Grade item storage for mod_hsuforum. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +declare(strict_types = 1); + +namespace mod_hsuforum\grades; + +use \core\exception\coding_exception; +use context; +use core_grades\component_gradeitem; +use core_grades\local\gradeitem as gradeitem; +use mod_hsuforum\local\container as forum_container; +use mod_hsuforum\local\entities\forum as forum_entity; +use \core\exception\required_capability_exception; +use stdClass; + +/** + * Grade item storage for mod_hsuforum. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class forum_gradeitem extends component_gradeitem { + /** @var forum_entity The forum entity being graded */ + protected $forum; + + /** + * Return an instance based on the context in which it is used. + * + * @param context $context + */ + public static function load_from_context(context $context): parent { + // Get all the factories that are required. + $vaultfactory = forum_container::get_vault_factory(); + $forumvault = $vaultfactory->get_forum_vault(); + + $forum = $forumvault->get_from_course_module_id((int) $context->instanceid); + + return static::load_from_forum_entity($forum); + } + + /** + * Return an instance using the forum_entity instance. + * + * @param forum_entity $forum + * + * @return forum_gradeitem + */ + public static function load_from_forum_entity(forum_entity $forum): self { + $instance = new static('mod_hsuforum', $forum->get_context(), 'forum'); + $instance->forum = $forum; + + return $instance; + } + + /** + * The table name used for grading. + * + * @return string + */ + protected function get_table_name(): string { + return 'hsuforum_grades'; + } + + /** + * Whether grading is enabled for this item. + * + * @return bool + */ + public function is_grading_enabled(): bool { + return $this->forum->is_grading_enabled(); + } + + /** + * Whether the grader can grade the gradee. + * + * @param stdClass $gradeduser The user being graded + * @param stdClass $grader The user who is grading + * @return bool + */ + public function user_can_grade(stdClass $gradeduser, stdClass $grader): bool { + // Validate the required capabilities. + $managerfactory = forum_container::get_manager_factory(); + $capabilitymanager = $managerfactory->get_capability_manager($this->forum); + + return $capabilitymanager->can_grade($grader, $gradeduser); + } + + /** + * Require that the user can grade, throwing an exception if not. + * + * @param stdClass $gradeduser The user being graded + * @param stdClass $grader The user who is grading + * @throws required_capability_exception + */ + public function require_user_can_grade(stdClass $gradeduser, stdClass $grader): void { + if (!$this->user_can_grade($gradeduser, $grader)) { + throw new required_capability_exception($this->forum->get_context(), 'mod/hsuforum:grade', 'nopermissions', ''); + } + } + + /** + * Get the grade value for this instance. + * The itemname is translated to the relevant grade field on the forum entity. + * + * @return int + */ + protected function get_gradeitem_value(): int { + $getter = "get_grade_for_{$this->itemname}"; + + return $this->forum->{$getter}(); + } + + /** + * Create an empty forum_grade for the specified user and grader. + * + * @param stdClass $gradeduser The user being graded + * @param stdClass $grader The user who is grading + * @return stdClass The newly created grade record + * @throws \dml_exception + */ + public function create_empty_grade(stdClass $gradeduser, stdClass $grader): stdClass { + global $DB; + + $grade = (object) [ + 'forum' => $this->forum->get_id(), + 'itemnumber' => $this->itemnumber, + 'userid' => $gradeduser->id, + 'timemodified' => time(), + ]; + $grade->timecreated = $grade->timemodified; + + $gradeid = $DB->insert_record($this->get_table_name(), $grade); + + return $DB->get_record($this->get_table_name(), ['id' => $gradeid]); + } + + /** + * Get the grade for the specified user. + * + * @param stdClass $gradeduser The user being graded + * @param stdClass $grader The user who is grading + * @return stdClass The grade value + * @throws \dml_exception + */ + public function get_grade_for_user(stdClass $gradeduser, stdClass $grader = null): ?stdClass { + global $DB; + + $params = [ + 'forum' => $this->forum->get_id(), + 'itemnumber' => $this->itemnumber, + 'userid' => $gradeduser->id, + ]; + + $grade = $DB->get_record($this->get_table_name(), $params); + + if (empty($grade)) { + $grade = $this->create_empty_grade($gradeduser, $grader); + } + + return $grade ?: null; + } + + /** + * Get the grade status for the specified user. + * Check if a grade obj exists & $grade->grade !== null. + * If the user has a grade return true. + * + * @param stdClass $gradeduser The user being graded + * @return bool The grade exists + * @throws \dml_exception + */ + public function user_has_grade(stdClass $gradeduser): bool { + global $DB; + + $params = [ + 'forum' => $this->forum->get_id(), + 'itemnumber' => $this->itemnumber, + 'userid' => $gradeduser->id, + ]; + + $grade = $DB->get_record($this->get_table_name(), $params); + + if (empty($grade) || $grade->grade === null) { + return false; + } + return true; + } + + /** + * Get grades for all users for the specified gradeitem. + * + * @return stdClass[] The grades + * @throws \dml_exception + */ + public function get_all_grades(): array { + global $DB; + + return $DB->get_records($this->get_table_name(), [ + 'forum' => $this->forum->get_id(), + 'itemnumber' => $this->itemnumber, + ]); + } + + /** + * Get the grade item instance id. + * + * This is typically the cmid in the case of an activity, and relates to the iteminstance field in the grade_items + * table. + * + * @return int + */ + public function get_grade_instance_id(): int { + return (int) $this->forum->get_id(); + } + + /** + * Defines whether only active users in the course should be gradeable. + * + * @return bool Whether only active users in the course should be gradeable. + */ + public function should_grade_only_active_users(): bool { + global $CFG; + + $showonlyactiveenrolconfig = !empty($CFG->grade_report_showonlyactiveenrol); + // Grade only active users enrolled in the course either when the 'grade_report_showonlyactiveenrol' user + // preference is set to true or the current user does not have the capability to view suspended users in the + // course. In cases where the 'grade_report_showonlyactiveenrol' user preference is not set we are falling back + // to the set value for the 'grade_report_showonlyactiveenrol' config. + return get_user_preferences('grade_report_showonlyactiveenrol', $showonlyactiveenrolconfig) || + !has_capability('moodle/course:viewsuspendedusers', \context_course::instance($this->forum->get_course_id())); + } + + /** + * Create or update the grade. + * + * @param stdClass $grade + * @return bool Success + * @throws \dml_exception + * @throws \core\exception\moodle_exception + * @throws \core\exception\coding_exception + */ + protected function store_grade(stdClass $grade): bool { + global $CFG, $DB; + require_once("{$CFG->dirroot}/mod/hsuforum/lib.php"); + + if ($grade->forum != $this->forum->get_id()) { + throw new coding_exception('Incorrect forum provided for this grade'); + } + + if ($grade->itemnumber != $this->itemnumber) { + throw new coding_exception('Incorrect itemnumber provided for this grade'); + } + + // Ensure that the grade is valid. + $this->check_grade_validity($grade->grade); + + $grade->forum = $this->forum->get_id(); + $grade->timemodified = time(); + + $DB->update_record($this->get_table_name(), $grade); + + // Update in the gradebook (note that 'cmidnumber' is required in order to update grades). + $mapper = forum_container::get_legacy_data_mapper_factory()->get_forum_data_mapper(); + $forumrecord = $mapper->to_legacy_object($this->forum); + $forumrecord->cmidnumber = $this->forum->get_course_module_record()->idnumber; + + hsuforum_update_grades($forumrecord, $grade->userid); + + return true; + } +} diff --git a/classes/local.php b/classes/local.php index ed53d3ab..424f33e6 100644 --- a/classes/local.php +++ b/classes/local.php @@ -70,7 +70,7 @@ public static function shorten_post_name($name) { * Get discussion times from params. * * @return array - * @throws coding_exception + * @throws \core\exception\coding_exception */ public static function get_form_discussion_times() { global $USER, $CFG; diff --git a/classes/local/builders/exported_discussion.php b/classes/local/builders/exported_discussion.php new file mode 100644 index 00000000..77504e89 --- /dev/null +++ b/classes/local/builders/exported_discussion.php @@ -0,0 +1,153 @@ +. + +/** + * Exported discussion builder class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\builders; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\factories\legacy_data_mapper as legacy_data_mapper_factory; +use mod_hsuforum\local\factories\exporter as exporter_factory; +use mod_hsuforum\local\factories\vault as vault_factory; +use rating_manager; +use \core\output\renderer_base; +use stdClass; + +/** + * Exported discussion 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 discussion. + * + * It encapsulates the complexity involved with exporting discussions. 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 + * + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class exported_discussion { + /** @var \core\output\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 \core\output\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( + \core\output\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 any additional variables for the exported discussion for a given set of discussions. + * + * This will typically be used for a list of discussions in the same forum. + * + * @param stdClass $user The user to export the posts for. + * @param forum_entity $forum The forum that each of the $discussions belong to + * @param discussion_entity $discussion A list of all discussions that each of the $posts belong to + * @return stdClass[] List of exported posts in the same order as the $posts array. + */ + public function build( + stdClass $user, + forum_entity $forum, + discussion_entity $discussion + ): array { + + $favouriteids = []; + if ($this->is_favourited($discussion, $forum->get_context(), $user)) { + $favouriteids[] = $discussion->get_id(); + } + + $groupsbyid = $this->get_groups_available_in_forum($forum); + $discussionexporter = $this->exporterfactory->get_discussion_exporter( + $user, $forum, $discussion, $groupsbyid, $favouriteids + ); + + return (array) $discussionexporter->export($this->renderer); + } + + /** + * Get the groups details for all groups available to the forum. + * @param forum_entity $forum The forum entity + * @return stdClass[] + */ + private function get_groups_available_in_forum($forum): array { + $course = $forum->get_course_record(); + $coursemodule = $forum->get_course_module_record(); + + return groups_get_all_groups($course->id, 0, $coursemodule->groupingid); + } + + /** + * Check whether the provided discussion has been favourited by the user. + * + * @param discussion_entity $discussion The discussion record + * @param \context_module $forumcontext Forum context + * @param \stdClass $user The user to check the favourite against + * + * @return bool Whether or not the user has favourited the discussion + */ + public function is_favourited(discussion_entity $discussion, \context_module $forumcontext, \stdClass $user) { + if (!isloggedin()) { + return false; + } + + $usercontext = \context_user::instance($user->id); + $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext); + return $ufservice->favourite_exists('mod_hsuforum', 'discussions', $discussion->get_id(), $forumcontext); + } + + +} diff --git a/classes/local/builders/exported_discussion_summaries.php b/classes/local/builders/exported_discussion_summaries.php new file mode 100644 index 00000000..5fb17a97 --- /dev/null +++ b/classes/local/builders/exported_discussion_summaries.php @@ -0,0 +1,298 @@ +. + +/** + * Exported discussion summaries builder class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\builders; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\builders\discussion_summary_entity; +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\entities\post as post_entity; +use mod_hsuforum\local\factories\legacy_data_mapper as legacy_data_mapper_factory; +use mod_hsuforum\local\factories\exporter as exporter_factory; +use mod_hsuforum\local\factories\vault as vault_factory; +use mod_hsuforum\local\factories\manager as manager_factory; +use rating_manager; +use \core\output\renderer_base; +use stdClass; + +/** + * Exported discussion summaries 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 discussion summaries. + * + * It encapsulates the complexity involved with exporting discussions summaries. 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 + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class exported_discussion_summaries { + /** @var \core\output\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 manager_factory $managerfactory Manager factory */ + private $managerfactory; + + /** @var rating_manager $ratingmanager Rating manager */ + private $ratingmanager; + + /** + * Constructor. + * + * @param \core\output\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 manager_factory $managerfactory Manager factory + */ + public function __construct( + \core\output\renderer_base $renderer, + legacy_data_mapper_factory $legacydatamapperfactory, + exporter_factory $exporterfactory, + vault_factory $vaultfactory, + manager_factory $managerfactory + ) { + $this->renderer = $renderer; + $this->legacydatamapperfactory = $legacydatamapperfactory; + $this->exporterfactory = $exporterfactory; + $this->vaultfactory = $vaultfactory; + $this->managerfactory = $managerfactory; + $this->ratingmanager = $managerfactory->get_rating_manager(); + } + + /** + * Build the exported discussion summaries for a given set of discussions. + * + * This will typically be used for a list of discussions in the same forum. + * + * @param stdClass $user The user to export the posts for. + * @param forum_entity $forum The forum that each of the $discussions belong to + * @param discussion_summary_entity[] $discussions A list of all discussion summaries to export + * @return stdClass[] List of exported posts in the same order as the $posts array. + */ + public function build( + stdClass $user, + forum_entity $forum, + array $discussions + ): array { + $capabilitymanager = $this->managerfactory->get_capability_manager($forum); + $canseeanyprivatereply = $capabilitymanager->can_view_any_private_reply($user); + + $discussionids = array_keys($discussions); + + $postvault = $this->vaultfactory->get_post_vault(); + $posts = $postvault->get_from_discussion_ids($user, $discussionids, $canseeanyprivatereply); + $groupsbyid = $this->get_groups_available_in_forum($forum); + $groupsbyauthorid = $this->get_author_groups_from_posts($posts, $forum); + + $replycounts = $postvault->get_reply_count_for_discussion_ids($user, $discussionids, $canseeanyprivatereply); + $latestposts = $postvault->get_latest_posts_for_discussion_ids($user, $discussionids, $canseeanyprivatereply); + $latestauthors = $this->get_latest_posts_authors($latestposts); + $latestpostsids = array_map(function($post) { + return $post->get_id(); + }, $latestposts); + + $postauthorids = array_unique(array_reduce($discussions, function($carry, $summary) use ($latestposts){ + $firstpostauthorid = $summary->get_first_post_author()->get_id(); + $discussion = $summary->get_discussion(); + $lastpostauthorid = $latestposts[$discussion->get_id()]->get_author_id(); + return array_merge($carry, [$firstpostauthorid, $lastpostauthorid]); + }, [])); + $postauthorcontextids = $this->get_author_context_ids($postauthorids); + + $unreadcounts = []; + $favourites = $this->get_favourites($user); + $forumdatamapper = $this->legacydatamapperfactory->get_forum_data_mapper(); + $forumrecord = $forumdatamapper->to_legacy_object($forum); + + if (forum_tp_can_track_forums($forumrecord)) { + $unreadcounts = $postvault->get_unread_count_for_discussion_ids($user, $discussionids, $canseeanyprivatereply); + } + + $summaryexporter = $this->exporterfactory->get_discussion_summaries_exporter( + $user, + $forum, + $discussions, + $groupsbyid, + $groupsbyauthorid, + $replycounts, + $unreadcounts, + $latestpostsids, + $postauthorcontextids, + $favourites, + $latestauthors + ); + + $exportedposts = (array) $summaryexporter->export($this->renderer); + $firstposts = $postvault->get_first_post_for_discussion_ids($discussionids); + + array_walk($exportedposts['summaries'], function($summary) use ($firstposts, $latestposts) { + $summary->discussion->times['created'] = (int) $firstposts[$summary->discussion->firstpostid]->get_time_created(); + $summary->discussion->times['modified'] = (int) $latestposts[$summary->discussion->id]->get_time_created(); + }); + + // Pass the current, preferred sort order for the discussions list. + $discussionlistvault = $this->vaultfactory->get_discussions_in_forum_vault(); + $sortorder = get_user_preferences('forum_discussionlistsortorder', + $discussionlistvault::SORTORDER_LASTPOST_DESC); + + $sortoptions = array( + 'islastpostdesc' => $sortorder == $discussionlistvault::SORTORDER_LASTPOST_DESC, + 'islastpostasc' => $sortorder == $discussionlistvault::SORTORDER_LASTPOST_ASC, + 'isrepliesdesc' => $sortorder == $discussionlistvault::SORTORDER_REPLIES_DESC, + 'isrepliesasc' => $sortorder == $discussionlistvault::SORTORDER_REPLIES_ASC, + 'iscreateddesc' => $sortorder == $discussionlistvault::SORTORDER_CREATED_DESC, + 'iscreatedasc' => $sortorder == $discussionlistvault::SORTORDER_CREATED_ASC, + 'isdiscussiondesc' => $sortorder == $discussionlistvault::SORTORDER_DISCUSSION_DESC, + 'isdiscussionasc' => $sortorder == $discussionlistvault::SORTORDER_DISCUSSION_ASC, + 'isstarterdesc' => $sortorder == $discussionlistvault::SORTORDER_STARTER_DESC, + 'isstarterasc' => $sortorder == $discussionlistvault::SORTORDER_STARTER_ASC, + 'isgroupdesc' => $sortorder == $discussionlistvault::SORTORDER_GROUP_DESC, + 'isgroupasc' => $sortorder == $discussionlistvault::SORTORDER_GROUP_ASC, + ); + + $exportedposts['state']['sortorder'] = $sortoptions; + + return $exportedposts; + } + + /** + * Get a list of all favourited discussions. + * + * @param stdClass $user The user we are getting favourites for + * @return int[] A list of favourited itemids + */ + private function get_favourites(stdClass $user): array { + $ids = []; + + if (isloggedin()) { + $usercontext = \context_user::instance($user->id); + $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext); + $favourites = $ufservice->find_favourites_by_type('mod_hsuforum', 'discussions'); + foreach ($favourites as $favourite) { + $ids[] = $favourite->itemid; + } + } + + return $ids; + } + + /** + * Returns a mapped array of discussionid to the authors of the latest post + * + * @param array $latestposts Mapped array of discussion to latest posts. + * @return array Array of authors mapped to the discussion + */ + private function get_latest_posts_authors($latestposts) { + $authors = $this->vaultfactory->get_author_vault()->get_authors_for_posts($latestposts); + + $mappedauthors = array_reduce($latestposts, function($carry, $item) use ($authors) { + $carry[$item->get_discussion_id()] = $authors[$item->get_author_id()]; + + return $carry; + }, []); + return $mappedauthors; + } + + /** + * Get the groups details for all groups available to the forum. + * @param forum_entity $forum The forum entity + * @return stdClass[] + */ + private function get_groups_available_in_forum($forum): array { + $course = $forum->get_course_record(); + $coursemodule = $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 + * @param forum_entity $forum The forum entity + * @return array Author groups indexed by author id + */ + private function get_author_groups_from_posts(array $posts, $forum): array { + $course = $forum->get_course_record(); + $coursemodule = $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 user context ids for each of the authors. + * + * @param int[] $authorids The list of author ids to fetch context ids for. + * @return int[] Context ids indexed by author id + */ + private function get_author_context_ids(array $authorids): array { + $authorvault = $this->vaultfactory->get_author_vault(); + return $authorvault->get_context_ids_for_author_ids($authorids); + } +} diff --git a/classes/local/builders/exported_posts.php b/classes/local/builders/exported_posts.php new file mode 100644 index 00000000..54b56281 --- /dev/null +++ b/classes/local/builders/exported_posts.php @@ -0,0 +1,571 @@ +. + +/** + * Exported post builder class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\builders; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\entities\post as post_entity; +use mod_hsuforum\local\factories\legacy_data_mapper as legacy_data_mapper_factory; +use mod_hsuforum\local\factories\exporter as exporter_factory; +use mod_hsuforum\local\factories\vault as vault_factory; +use mod_hsuforum\local\factories\manager as manager_factory; +use core_tag_tag; +use \core\exception\moodle_exception; +use \core\output\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 hsuforums, 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 + * + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class exported_posts { + /** @var \core\output\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; + + /** @var manager_factory $managerfactory Manager factory */ + private $managerfactory; + + /** + * Constructor. + * + * @param \core\output\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 manager_factory $managerfactory Manager factory + */ + public function __construct( + \core\output\renderer_base $renderer, + legacy_data_mapper_factory $legacydatamapperfactory, + exporter_factory $exporterfactory, + vault_factory $vaultfactory, + manager_factory $managerfactory + ) { + $this->renderer = $renderer; + $this->legacydatamapperfactory = $legacydatamapperfactory; + $this->exporterfactory = $exporterfactory; + $this->vaultfactory = $vaultfactory; + $this->managerfactory = $managerfactory; + $this->ratingmanager = $managerfactory->get_rating_manager(); + } + + /** + * 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. + * + * Note: Some posts will be removed as part of the build process according to capabilities. + * A one-to-one mapping should not be expected. + * + * @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. + * @param bool $includeinlineattachments Whether inline attachments should be included or not. + * @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, + bool $includeinlineattachments = false + ): 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); + $authorcontextids = $this->get_author_context_ids(array_keys($authorsbyid)); + $attachmentsbypostid = $this->get_attachments_for_posts($groupedposts); + $inlineattachments = []; + if ($includeinlineattachments) { + $inlineattachments = $this->get_inline_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; + + // Exclude posts the user cannot see, such as certain posts in Q and A forums. + $capabilitymanager = $this->managerfactory->get_capability_manager($forum); + $groupedposts = array_filter($groupedposts, function($post) use ($capabilitymanager, $user, $discussion) { + return $capabilitymanager->can_view_post($user, $discussion, $post); + }); + + $forumid = $forum->get_id(); + $courseid = $forum->get_course_record()->id; + $postsexporter = $this->exporterfactory->get_posts_exporter( + $user, + $forum, + $discussion, + $groupedposts, + $authorsbyid, + $authorcontextids, + $attachmentsbypostid, + $groupsbycourseandauthorid[$courseid], + $readreceiptcollectionbyforumid[$forumid] ?? null, + $tagsbypostid, + $ratingbypostid, + true, + $inlineattachments + ); + ['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' => , + * 'discussion' => , + * 'posts' => [ + * , + * , + * + * ] + * ] + * ] + * + * @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); + } + + /** + * Get the user context ids for each of the authors. + * + * @param int[] $authorids The list of author ids to fetch context ids for. + * @return int[] Context ids indexed by author id + */ + private function get_author_context_ids(array $authorids): array { + $authorvault = $this->vaultfactory->get_author_vault(); + return $authorvault->get_context_ids_for_author_ids($authorids); + } + + /** + * Load the list of all inline 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_inline_attachments_for_posts(array $groupedposts): array { + $inlineattachmentsbypostid = []; + $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; + $inlineattachments = $postattachmentvault->get_inline_attachments_for_posts($forum->get_context(), $posts); + + // Have to loop in order to maintain the correct indexes since they are numeric. + foreach ($inlineattachments as $postid => $attachment) { + $inlineattachmentsbypostid[$postid] = $attachment; + } + } + + return $inlineattachmentsbypostid; + } + + /** + * 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_hsuforum', 'hsuforum_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_hsuforum', + '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; + } +} diff --git a/classes/local/container.php b/classes/local/container.php new file mode 100644 index 00000000..4fb21000 --- /dev/null +++ b/classes/local/container.php @@ -0,0 +1,153 @@ +. + +/** + * Container class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\factories\renderer as renderer_factory; +use mod_hsuforum\local\factories\legacy_data_mapper as legacy_data_mapper_factory; +use mod_hsuforum\local\factories\entity as entity_factory; +use mod_hsuforum\local\factories\exporter as exporter_factory; +use mod_hsuforum\local\factories\manager as manager_factory; +use mod_hsuforum\local\factories\vault as vault_factory; +use mod_hsuforum\local\factories\builder as builder_factory; +use mod_hsuforum\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 + * @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(), + self::get_vault_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(), + self::get_legacy_data_mapper_factory() + ); + } + + /** + * 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_hsuforum') + ); + } + + /** + * 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() + ); + } +} diff --git a/classes/local/data_mappers/legacy/author.php b/classes/local/data_mappers/legacy/author.php new file mode 100644 index 00000000..a97c8c1a --- /dev/null +++ b/classes/local/data_mappers/legacy/author.php @@ -0,0 +1,71 @@ +. + +/** + * Author data mapper. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\data_mappers\legacy; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\author as author_entity; +use stdClass; + +/** + * Convert an author entity into an stdClass. + * + * @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(), + 'deleted' => $author->is_deleted(), + '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]; + } +} diff --git a/classes/local/data_mappers/legacy/discussion.php b/classes/local/data_mappers/legacy/discussion.php new file mode 100644 index 00000000..34d33d57 --- /dev/null +++ b/classes/local/data_mappers/legacy/discussion.php @@ -0,0 +1,73 @@ +. + +/** + * Discussion data mapper. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\data_mappers\legacy; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\discussion as discussion_entity; +use stdClass; + +/** + * Convert a discussion entity into an stdClass. + * + * @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(), + 'timelocked' => $discussion->get_locked() + ]; + }, $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]; + } +} diff --git a/classes/local/data_mappers/legacy/forum.php b/classes/local/data_mappers/legacy/forum.php new file mode 100644 index 00000000..980e67c1 --- /dev/null +++ b/classes/local/data_mappers/legacy/forum.php @@ -0,0 +1,88 @@ +. + +/** + * Forum data mapper. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\data_mappers\legacy; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\forum as forum_entity; +use stdClass; + +/** + * Convert a forum entity into an stdClass. + * + * @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(), + 'grade_forum' => $forum->get_grade_for_forum(), + 'grade_forum_notify' => $forum->should_notify_students_default_when_grade_for_forum(), + 'maxbytes' => $forum->get_max_bytes(), + 'maxattachments' => $forum->get_max_attachments(), + 'forcesubscribe' => $forum->get_subscription_mode(), + '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(), + 'duedate' => $forum->get_due_date(), + 'cutoffdate' => $forum->get_cutoff_date() + ]; + }, $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]; + } +} diff --git a/classes/local/data_mappers/legacy/post.php b/classes/local/data_mappers/legacy/post.php new file mode 100644 index 00000000..a0720789 --- /dev/null +++ b/classes/local/data_mappers/legacy/post.php @@ -0,0 +1,77 @@ +. + +/** + * Post data mapper. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\data_mappers\legacy; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\post as post_entity; +use stdClass; + +/** + * Convert a post entity into an stdClass. + * + * @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(), + 'privatereplyto' => $post->get_private_reply_recipient_id(), + 'wordcount' => $post->get_wordcount(), + 'charcount' => $post->get_charcount(), + ]; + }, $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]; + } +} diff --git a/classes/local/entities/author.php b/classes/local/entities/author.php new file mode 100644 index 00000000..02c5e5f3 --- /dev/null +++ b/classes/local/entities/author.php @@ -0,0 +1,209 @@ +. + +/** + * Author class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\entities; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Author class. + * + * @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 bool $deleted Deleted */ + private $deleted; + /** @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, + bool $deleted, + 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->deleted = $deleted; + $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; + } + + /** + * Is the author deleted? + * + * @return bool + */ + public function is_deleted(): bool { + return !empty($this->deleted); + } + + /** + * 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; + } +} diff --git a/classes/local/entities/discussion.php b/classes/local/entities/discussion.php new file mode 100644 index 00000000..e73a16cc --- /dev/null +++ b/classes/local/entities/discussion.php @@ -0,0 +1,333 @@ +. + +/** + * Discussion class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\entities; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\post as post_entity; + +/** + * Discussion class. + * + * @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; + /** @var int $locked The timestamp of when the discussion was locked */ + private $timelocked; + + /** + * 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? + * @param int $locked Time this discussion was locked + */ + 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, + int $locked + ) { + $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; + $this->timelocked = $locked; + } + + /** + * 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; + } + + /** + * Get the locked time of this discussion. + * + * @return bool + */ + public function get_locked(): int { + return $this->timelocked; + } + + /** + * Is this discussion locked based on it's locked attribute + * + * @return bool + */ + public function is_locked(): bool { + return ($this->timelocked ? true : false); + } + + /** + * Set the locked timestamp + * + * @param int $timestamp The value we want to store into 'locked' + */ + public function toggle_locked_state(int $timestamp) { + // Check the current value against what we want the value to be i.e. '$timestamp'. + $this->timelocked = ($this->timelocked && $timestamp ? $this->timelocked : $timestamp); + } + + /** + * 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. DEFAULTS: true if not set + * + * @return bool + */ + public function has_started(): bool { + $startime = $this->get_time_start(); + return empty($startime) || $startime < time(); + } + + /** + * Check if the discussion has ended. DEFAULTS: false if not set + * + * @return bool + */ + public function has_ended(): bool { + $endtime = $this->get_time_end(); + return !empty($endtime) && $endtime < time(); + } + + /** + * Check if the discussion belongs to a group. + * + * @return bool + */ + public function has_group(): bool { + return $this->get_group_id() > 0; + } + + /** + * Set the pinned value for this entity + * + * @param int $targetstate The state to change the pin to + * @return bool + */ + public function set_pinned(int $targetstate): void { + if ($targetstate != $this->pinned) { + $this->pinned = $targetstate; + } + } + + /** + * Check if the discussion is timed. + * + * @return bool + */ + public function is_timed_discussion(): bool { + global $CFG; + + return !empty($CFG->forum_enabletimedposts) && + ($this->get_time_start() || $this->get_time_end()); + } + + /** + * Check if the timed discussion is visible. + * + * @return bool + */ + public function is_timed_discussion_visible(): bool { + return !$this->is_timed_discussion() || ($this->has_started() && !$this->has_ended()); + } +} diff --git a/classes/local/entities/discussion_summary.php b/classes/local/entities/discussion_summary.php new file mode 100644 index 00000000..bd80a19c --- /dev/null +++ b/classes/local/entities/discussion_summary.php @@ -0,0 +1,104 @@ +. + +/** + * Discussion summary class. + * + * @package mod_hsuforum + * @copyright 2019 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\entities; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\entities\post as post_entity; +use mod_hsuforum\local\entities\author as author_entity; + +/** + * Discussion summary class. + * + * @copyright 2019 Andrew Nicols + * @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; + } +} diff --git a/classes/local/entities/forum.php b/classes/local/entities/forum.php new file mode 100644 index 00000000..ea66b1ea --- /dev/null +++ b/classes/local/entities/forum.php @@ -0,0 +1,670 @@ +. + +/** + * Forum class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\entities; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/mod/hsuforum/lib.php'); +require_once($CFG->dirroot . '/rating/lib.php'); + +use mod_hsuforum\local\entities\discussion as discussion_entity; +use context; +use stdClass; + +/** + * Hsuforum class. + * + * @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 $gradeforum The grade for the forum when grading holistically */ + private $gradeforum; + /** @var bool $gradeforumnotify Whether to notify students when the forum is graded holistically */ + private $gradeforumnotify; + /** @var int $maxbytes Maximum attachment size */ + private $maxbytes; + /** @var int $maxattachments Maximum number of attachments */ + private $maxattachments; + /** @var int $forcesubscribe Does the forum force users to subscribe? */ + private $forcesubscribe; + /** @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 $displaywordcount Should display word counts in posts */ + private $displaywordcount; + /** @var bool $lockdiscussionafter Timestamp after which discussions should be locked */ + private $lockdiscussionafter; + /** @var int $duedate Timestamp that represents the due date for forum posts */ + private $duedate; + /** @var int $cutoffdate Timestamp after which forum posts will no longer be accepted */ + private $cutoffdate; + + /** + * 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 $gradeforum The holistic grade + * @param bool $gradeforumnotify Default for whether to notify students when grade holistically + * @param int $maxbytes Maximum attachment size + * @param int $maxattachments Maximum number of attachments + * @param int $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 + * @param int $duedate Timestamp that represents the due date for forum posts + * @param int $cutoffdate Timestamp after which forum posts will no longer be accepted + */ + 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 $gradeforum, + bool $gradeforumnotify, + int $maxbytes, + int $maxattachments, + int $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, + int $duedate, + int $cutoffdate + ) { + $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->gradeforum = $gradeforum; + $this->gradeforumnotify = $gradeforumnotify; + $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; + $this->duedate = $duedate; + $this->cutoffdate = $cutoffdate; + } + + /** + * 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 hsuforum 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 grade for the forum when grading holistically. + * + * @return int + */ + public function get_grade_for_forum(): int { + return $this->gradeforum; + } + + /** + * Whether grading is enabled for this item. + * + * @return bool + */ + public function is_grading_enabled(): bool { + return $this->get_grade_for_forum() !== 0; + } + + /** + * Get the default for whether the students should be notified when grading holistically. + * + * @return bool + */ + public function should_notify_students_default_when_grade_for_forum(): bool { + return $this->gradeforumnotify; + } + + /** + * 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; + } + + /** + * Get the subscription mode. + * + * @return int + */ + public function get_subscription_mode(): int { + return $this->forcesubscribe; + } + + /** + * Is the subscription mode set to optional. + * + * @return bool + */ + public function is_subscription_optional(): bool { + return $this->get_subscription_mode() === HSUFORUM_CHOOSESUBSCRIBE; + } + + /** + * Is the subscription mode set to forced. + * + * @return bool + */ + public function is_subscription_forced(): bool { + return $this->get_subscription_mode() === HSUFORUM_FORCESUBSCRIBE; + } + + /** + * Is the subscription mode set to automatic. + * + * @return bool + */ + public function is_subscription_automatic(): bool { + return $this->get_subscription_mode() === HSUFORUM_INITIALSUBSCRIBE; + } + + /** + * Is the subscription mode set to disabled. + * + * @return bool + */ + public function is_subscription_disabled(): bool { + return $this->get_subscription_mode() === HSUFORUM_DISALLOWSUBSCRIBE; + } + + /** + * 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 hsuforum 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()); + } + + /** + * Check whether the discussion is locked based on forum's time based locking criteria + * + * @param discussion_entity $discussion + * @return bool + */ + public function is_discussion_time_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()); + } + + /** + * Get the cutoff date. + * + * @return int + */ + public function get_cutoff_date(): int { + return $this->cutoffdate; + } + + /** + * Does the forum have a cutoff date? + * + * @return bool + */ + public function has_cutoff_date(): bool { + return !empty($this->get_cutoff_date()); + } + + /** + * Is the cutoff date for the hsuforum reached? + * + * @return bool + */ + public function is_cutoff_date_reached(): bool { + if ($this->has_cutoff_date() && ($this->get_cutoff_date() < time())) { + return true; + } + + return false; + } + + /** + * Get the due date. + * + * @return int + */ + public function get_due_date(): int { + return $this->duedate; + } + + /** + * Does the forum have a due date? + * + * @return bool + */ + public function has_due_date(): bool { + return !empty($this->get_due_date()); + } + + /** + * Is the due date for the forum reached? + * + * @return bool + */ + public function is_due_date_reached(): bool { + if ($this->has_due_date() && ($this->get_due_date() < time())) { + return true; + } + + return false; + } + + /** + * Is the discussion locked? - Takes into account both discussion settings AND forum's criteria + * + * @param discussion_entity $discussion The discussion to check + * @return bool + */ + public function is_discussion_locked(discussion_entity $discussion): bool { + if ($discussion->is_locked()) { + return true; + } + + return $this->is_discussion_time_locked($discussion); + } +} diff --git a/classes/local/entities/post.php b/classes/local/entities/post.php new file mode 100644 index 00000000..8c456500 --- /dev/null +++ b/classes/local/entities/post.php @@ -0,0 +1,355 @@ +. + +/** + * Post class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\entities; + +defined('MOODLE_INTERNAL') || die(); + +use stdClass; + +/** + * Post class. + * + * @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; + /** @var int $privatereplyto The user being privately replied to */ + private $privatereplyto; + /** @var int $wordcount Number of words in the message */ + private $wordcount; + /** @var int $charcount Number of chars in the message */ + private $charcount; + + /** + * 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 + * @param int $privatereplyto Which user this reply is intended for in a private reply situation + */ + 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, + int $privatereplyto, + ?int $wordcount, + ?int $charcount + ) { + $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; + $this->privatereplyto = $privatereplyto; + $this->wordcount = $wordcount; + $this->charcount = $charcount; + } + + /** + * 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; + } + + /** + * Is this post private? + * + * @return bool + */ + public function is_private_reply(): bool { + return !empty($this->privatereplyto); + } + + /** + * Get the id of the user that this post was intended for. + * + * @return int + */ + public function get_private_reply_recipient_id(): int { + return $this->privatereplyto; + } + + + /** + * 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; + } + + /** + * Check if the given post is a private reply intended for the given user. + * + * @param stdClass $user The user to check. + * @return bool + */ + public function is_private_reply_intended_for_user(stdClass $user): bool { + return $this->get_private_reply_recipient_id() == $user->id; + } + + /** + * Returns the word count. + * + * @return int|null + */ + public function get_wordcount(): ?int { + return $this->wordcount; + } + + /** + * Returns the char count. + * + * @return int|null + */ + public function get_charcount(): ?int { + return $this->charcount; + } + + /** + * This methods adds/updates hsuforum posts' word count and char count attributes based on $data->message. + * + * @param \stdClass $record A record ready to be inserted / updated in DB. + * @return void. + */ + public static function add_message_counts(\stdClass $record): void { + if (!empty($record->message)) { + $record->wordcount = count_words($record->message, $record->messageformat); + $record->charcount = count_letters($record->message, $record->messageformat); + } + } +} diff --git a/classes/local/entities/post_read_receipt_collection.php b/classes/local/entities/post_read_receipt_collection.php new file mode 100644 index 00000000..775cbcba --- /dev/null +++ b/classes/local/entities/post_read_receipt_collection.php @@ -0,0 +1,84 @@ +. + +/** + * Post read receipt collection class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\entities; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\post as post_entity; +use stdClass; + +/** + * Post read receipt collection class. + * + * Contains the list of read receipts for posts. + * + * @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; + } +} diff --git a/classes/local/entities/sorter.php b/classes/local/entities/sorter.php new file mode 100644 index 00000000..6abddb64 --- /dev/null +++ b/classes/local/entities/sorter.php @@ -0,0 +1,150 @@ +. + +/** + * Class to sort items. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\entities; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Class to sort lists of items. + * + * @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; + } +} diff --git a/classes/local/exporters/author.php b/classes/local/exporters/author.php new file mode 100644 index 00000000..f8205c70 --- /dev/null +++ b/classes/local/exporters/author.php @@ -0,0 +1,221 @@ +. + +/** + * Author exporter. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\exporters; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\author as author_entity; +use mod_hsuforum\local\exporters\group as group_exporter; +use core\external\exporter; +use mod_hsuforum\local\exporters\stdClass; +use \core\output\renderer_base; + +require_once($CFG->dirroot . '/mod/hsuforum/lib.php'); + +/** + * Author exporter. + * + * @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 int|null $authorcontextid The context id for the author entity */ + private $authorcontextid; + /** @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 int|null $authorcontextid The context id for the author entity to export (null if the user doesn't have one) + * @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, + ?int $authorcontextid, + array $authorgroups = [], + bool $canview = true, + array $related = [] + ) { + $this->author = $author; + $this->authorcontextid = $authorcontextid; + $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 + ], + 'isdeleted' => [ + 'type' => PARAM_BOOL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'groups' => [ + 'multiple' => true, + 'optional' => true, + 'type' => [ + 'id' => ['type' => PARAM_INT], + 'name' => ['type' => PARAM_TEXT], + 'urls' => [ + 'type' => [ + 'image' => [ + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ] + ] + ] + ] + ], + 'urls' => [ + 'type' => [ + 'profile' => [ + 'description' => 'The URL for the use profile page', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'profileimage' => [ + 'description' => 'The URL for the use profile image', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + ] + ] + ]; + } + + /** + * Get the additional values to inject while exporting. + * + * @param \core\output\renderer_base $output The renderer. + * @return array Keys are the property names, values are their values. + */ + protected function get_other_values(\core\output\renderer_base $output) { + $author = $this->author; + $authorcontextid = $this->authorcontextid; + $urlfactory = $this->related['urlfactory']; + $context = $this->related['context']; + $forum = $this->related['forum']; + + if ($this->canview) { + if ($author->is_deleted()) { + return [ + 'id' => $author->get_id(), + 'fullname' => get_string('deleteduser', 'mod_hsuforum'), + 'isdeleted' => true, + 'groups' => [], + 'urls' => [ + 'profile' => ($urlfactory->get_author_profile_url($author, $forum->get_course_id()))->out(false), + 'profileimage' => ($urlfactory->get_author_profile_image_url($author, $authorcontextid))->out(false) + ] + ]; + } else { + $groups = array_map(function($group) use ($urlfactory, $context, $output) { + $groupurl = null; + $imageurl = get_group_picture_url($group, $group->courseid, true); + + if (course_can_view_participants($context)) { + $groupurl = $urlfactory->get_author_group_url($group); + } + + return [ + 'id' => $group->id, + 'name' => format_string($group->name, true, ['context' => $context]), + 'urls' => [ + 'image' => $imageurl ? $imageurl->out(false) : null, + 'group' => $groupurl ? $groupurl->out(false) : null + + ] + ]; + }, $this->authorgroups); + + return [ + 'id' => $author->get_id(), + 'fullname' => $author->get_full_name(), + 'isdeleted' => false, + 'groups' => $groups, + 'urls' => [ + 'profile' => ($urlfactory->get_author_profile_url($author, $forum->get_course_id()))->out(false), + 'profileimage' => ($urlfactory->get_author_profile_image_url($author, $authorcontextid))->out(false) + ] + ]; + } + } else { + // The author should be anonymised. + return [ + 'id' => null, + 'fullname' => get_string('forumauthorhidden', 'mod_hsuforum'), + 'isdeleted' => null, + 'groups' => [], + 'urls' => [ + 'profile' => null, + 'profileimage' => null + ] + ]; + } + } + + /** + * Returns a list of objects that are related. + * + * @return array + */ + protected static function define_related() { + return [ + 'urlfactory' => 'mod_hsuforum\local\factories\url', + 'context' => 'context', + 'forum' => 'mod_hsuforum\local\entities\forum', + ]; + } +} diff --git a/classes/local/exporters/discussion.php b/classes/local/exporters/discussion.php new file mode 100644 index 00000000..ead22e07 --- /dev/null +++ b/classes/local/exporters/discussion.php @@ -0,0 +1,285 @@ +. + +/** + * Discussion exporter class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\exporters; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\exporters\post as post_exporter; +use mod_hsuforum\local\exporters\stdClass; +use mod_hsuforum\local\factories\exporter as exporter_factory; +use core\external\exporter; +use \core\output\renderer_base; + +/** + * Discussion exporter class. + * + * @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], + 'locked' => ['type' => PARAM_BOOL], + 'istimelocked' => ['type' => PARAM_BOOL], + 'name' => ['type' => PARAM_TEXT], + 'firstpostid' => ['type' => PARAM_INT], + '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], + 'locked' => ['type' => PARAM_INT], + ], + ], + 'userstate' => [ + 'type' => [ + 'subscribed' => ['type' => PARAM_BOOL], + 'favourited' => ['type' => PARAM_BOOL], + ], + ], + 'capabilities' => [ + 'type' => [ + 'subscribe' => ['type' => PARAM_BOOL], + 'move' => ['type' => PARAM_BOOL], + 'pin' => ['type' => PARAM_BOOL], + 'post' => ['type' => PARAM_BOOL], + 'manage' => ['type' => PARAM_BOOL], + 'favourite' => ['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], + 'subscribe' => ['type' => PARAM_URL], + 'pin' => [ + 'optional' => true, + 'type' => PARAM_URL, + ], + ], + ], + 'timed' => [ + 'type' => [ + 'istimed' => [ + 'type' => PARAM_BOOL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'visible' => [ + 'type' => PARAM_BOOL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ] + ] + ] + ]; + } + + /** + * Get the additional values to inject while exporting. + * + * @param \core\output\renderer_base $output The renderer. + * @return array Keys are the property names, values are their values. + */ + protected function get_other_values(\core\output\renderer_base $output) { + + $capabilitymanager = $this->related['capabilitymanager']; + $urlfactory = $this->related['urlfactory']; + $favouriteids = isset($this->related['favouriteids']) ? $this->related['favouriteids'] : []; + + $forum = $this->related['forum']; + $forumrecord = $this->get_forum_record(); + $user = $this->related['user']; + $discussion = $this->discussion; + + $groupdata = null; + if ($discussion->has_group()) { + $groupsbyid = $this->related['groupsbyid']; + $group = $groupsbyid[$discussion->get_group_id()] ?? null; + + // We may not have received the group if the caller doesn't want to include it in the export + // or if it's been deleted and the discussion record hasn't been updated. + if ($group) { + $groupdata = [ + 'name' => format_string($group->name, true, ['context' => $this->related['context']]), + 'urls' => [], + ]; + + // If not hiding the group picture, and the group has a picture then use it. Fallback to generic group image. + if ($url = get_group_picture_url($group, $forum->get_course_id(), true)) { + + $groupdata['urls']['picture'] = $url; + } else { + $groupdata['urls']['picture'] = $output->image_url('g/g1')->out(false); + } + + if ($capabilitymanager->can_view_participants($user, $discussion)) { + $groupdata['urls']['userlist'] = (new \core\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(), + 'locked' => $forum->is_discussion_locked($discussion), + 'istimelocked' => $forum->is_discussion_time_locked($discussion), + 'name' => format_string($discussion->get_name(), true, [ + 'context' => $this->related['context'] + ]), + 'firstpostid' => $discussion->get_first_post_id(), + 'times' => [ + 'modified' => $discussion->get_time_modified(), + 'start' => $discussion->get_time_start(), + 'end' => $discussion->get_time_end(), + 'locked' => $discussion->get_locked() + ], + 'userstate' => [ + 'subscribed' => \mod_hsuforum\subscriptions::is_subscribed($user->id, $forumrecord, $discussion->get_id()), + 'favourited' => in_array($discussion->get_id(), $favouriteids) ? true : false, + ], + '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), + 'manage' => $capabilitymanager->can_manage_forum($user), + 'favourite' => $capabilitymanager->can_favourite_discussion($user) // Defaulting to true until we get capabilities sorted + ], + '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), + 'subscribe' => $urlfactory->get_discussion_subscribe_url($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 ($capabilitymanager->can_pin_discussions($user)) { + $data['urls']['pin'] = $urlfactory->get_pin_discussion_url_from_discussion($discussion)->out(false); + } + + if ($groupdata) { + $data['group'] = $groupdata; + } + + $canviewhiddentimedposts = $capabilitymanager->can_view_hidden_posts($user); + $canalwaysseetimedpost = $user->id == $discussion->get_user_id() || $canviewhiddentimedposts; + $data['timed']['istimed'] = $canalwaysseetimedpost ? $discussion->is_timed_discussion() : null; + $data['timed']['visible'] = $canalwaysseetimedpost ? $discussion->is_timed_discussion_visible() : null; + + return $data; + } + + /** + * Get the legacy hsuforum 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_hsuforum\local\factories\legacy_data_mapper', + 'context' => 'context', + 'forum' => 'mod_hsuforum\local\entities\forum', + 'capabilitymanager' => 'mod_hsuforum\local\managers\capability', + 'urlfactory' => 'mod_hsuforum\local\factories\url', + 'user' => 'stdClass', + 'groupsbyid' => 'stdClass[]', + 'latestpostid' => 'int?', + 'favouriteids' => 'int[]?' + ]; + } +} diff --git a/classes/local/exporters/discussion_summaries.php b/classes/local/exporters/discussion_summaries.php new file mode 100644 index 00000000..7cb497e8 --- /dev/null +++ b/classes/local/exporters/discussion_summaries.php @@ -0,0 +1,171 @@ +. + +/** + * Discussion summaries exporter. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\exporters; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\exporters\discussion_summary; +use mod_hsuforum\local\exporters\discussion_summary_entity; +use mod_hsuforum\local\exporters\post as post_exporter; +use core\external\exporter; +use mod_hsuforum\local\exporters\stdClass; +use \core\output\renderer_base; + +/** + * Discussion summaries exporter. + * + * @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; + + /** @var int[] The context ids for the first and latest post authors (indexed by author id) */ + private $postauthorcontextids; + + /** + * 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 int[] $postauthorcontextids The context ids for the first and latest post authors (indexed by author id) + * @param array $related The related + */ + public function __construct( + array $discussions, + array $groupsbyid, + array $groupsbyauthorid, + array $discussionreplycount, + array $discussionunreadcount, + array $latestpostids, + array $postauthorcontextids, + array $related = [] + ) { + $this->discussions = $discussions; + $this->groupsbyid = $groupsbyid; + $this->groupsbyauthorid = $groupsbyauthorid; + $this->discussionreplycount = $discussionreplycount; + $this->discussionunreadcount = $discussionunreadcount; + $this->latestpostids = $latestpostids; + $this->postauthorcontextids = $postauthorcontextids; + 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 \core\output\renderer_base $output The renderer. + * @return array Keys are the property names, values are their values. + */ + protected function get_other_values(\core\output\renderer_base $output) { + $exporteddiscussions = []; + $related = $this->related; + $latestauthors = $this->related['latestauthors']; + + 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; + $latestauthor = $latestauthors[$discussionid] ?? null; + $related['latestauthor'] = $latestauthor; + $exporter = new discussion_summary( + $discussion, + $this->groupsbyid, + $this->groupsbyauthorid, + $replycount, + $unreadcount, + $latestpostid, + $this->postauthorcontextids[$discussion->get_first_post_author()->get_id()], + $this->postauthorcontextids[$latestauthor->get_id()], + $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_hsuforum\local\factories\legacy_data_mapper', + 'context' => 'context', + 'forum' => 'mod_hsuforum\local\entities\forum', + 'capabilitymanager' => 'mod_hsuforum\local\managers\capability', + 'urlfactory' => 'mod_hsuforum\local\factories\url', + 'user' => 'stdClass', + 'favouriteids' => 'int[]?', + 'latestauthors' => 'mod_hsuforum\local\entities\author[]?' + ]; + } +} diff --git a/classes/local/exporters/forum.php b/classes/local/exporters/forum.php new file mode 100644 index 00000000..9ff0422b --- /dev/null +++ b/classes/local/exporters/forum.php @@ -0,0 +1,192 @@ +. + +/** + * Hsuforum Exporter. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\exporters; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\exporters\post as post_exporter; +use core\external\exporter; +use \core\output\renderer_base; +use stdClass; + +/** + * Forum class. + * + * @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], + 'name' => ['type' => PARAM_RAW], + 'state' => [ + 'type' => [ + 'groupmode' => ['type' => PARAM_INT], + 'gradingenabled' => ['type' => PARAM_BOOL], + ], + ], + 'userstate' => [ + 'type' => [ + 'tracked' => ['type' => PARAM_INT], + ], + ], + 'capabilities' => [ + 'type' => [ + 'viewdiscussions' => ['type' => PARAM_BOOL], + 'create' => ['type' => PARAM_BOOL], + 'subscribe' => ['type' => PARAM_BOOL], + 'grade' => ['type' => PARAM_BOOL], + ] + ], + 'urls' => [ + 'type' => [ + 'create' => ['type' => PARAM_URL], + 'markasread' => ['type' => PARAM_URL], + 'view' => ['type' => PARAM_URL], + 'sortrepliesasc' => ['type' => PARAM_URL], + 'sortrepliesdesc' => ['type' => PARAM_URL], + 'sortlastpostasc' => ['type' => PARAM_URL], + 'sortlastpostdesc' => ['type' => PARAM_URL], + 'sortcreatedasc' => ['type' => PARAM_URL], + 'sortcreateddesc' => ['type' => PARAM_URL], + 'sortdiscussionasc' => ['type' => PARAM_URL], + 'sortdiscussiondesc' => ['type' => PARAM_URL], + 'sortstarterasc' => ['type' => PARAM_URL], + 'sortstarterdesc' => ['type' => PARAM_URL], + 'sortgroupasc' => ['type' => PARAM_URL], + 'sortgroupdesc' => ['type' => PARAM_URL], + ], + ], + ]; + } + + /** + * Get the additional values to inject while exporting. + * + * @param \core\output\renderer_base $output The renderer. + * @return array Keys are the property names, values are their values. + */ + protected function get_other_values(\core\output\renderer_base $output) { + $capabilitymanager = $this->related['capabilitymanager']; + $urlfactory = $this->related['urlfactory']; + $user = $this->related['user']; + $currentgroup = $this->related['currentgroup']; + $vaultfactory = $this->related['vaultfactory']; + $discussionvault = $vaultfactory->get_discussions_in_forum_vault(); + + return [ + 'id' => $this->forum->get_id(), + 'name' => $this->forum->get_name(), + 'state' => [ + 'groupmode' => $this->forum->get_effective_group_mode(), + 'gradingenabled' => $this->forum->is_grading_enabled() + ], + '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), + 'selfenrol' => $capabilitymanager->can_self_enrol($user), + 'subscribe' => $capabilitymanager->can_subscribe_to_forum($user), + 'grade' => $capabilitymanager->can_grade($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), + 'view' => $urlfactory->get_forum_view_url_from_forum($this->forum)->out(false), + 'sortrepliesasc' => $urlfactory->get_forum_view_url_from_forum($this->forum, null, + $discussionvault::SORTORDER_REPLIES_ASC)->out(false), + 'sortrepliesdesc' => $urlfactory->get_forum_view_url_from_forum($this->forum, null, + $discussionvault::SORTORDER_REPLIES_DESC)->out(false), + 'sortlastpostasc' => $urlfactory->get_forum_view_url_from_forum($this->forum, null, + $discussionvault::SORTORDER_LASTPOST_ASC)->out(false), + 'sortlastpostdesc' => $urlfactory->get_forum_view_url_from_forum($this->forum, null, + $discussionvault::SORTORDER_LASTPOST_DESC)->out(false), + 'sortcreatedasc' => $urlfactory->get_forum_view_url_from_forum($this->forum, null, + $discussionvault::SORTORDER_CREATED_ASC)->out(false), + 'sortcreateddesc' => $urlfactory->get_forum_view_url_from_forum($this->forum, null, + $discussionvault::SORTORDER_CREATED_DESC)->out(false), + 'sortdiscussionasc' => $urlfactory->get_forum_view_url_from_forum($this->forum, null, + $discussionvault::SORTORDER_DISCUSSION_ASC)->out(false), + 'sortdiscussiondesc' => $urlfactory->get_forum_view_url_from_forum($this->forum, null, + $discussionvault::SORTORDER_DISCUSSION_DESC)->out(false), + 'sortstarterasc' => $urlfactory->get_forum_view_url_from_forum($this->forum, null, + $discussionvault::SORTORDER_STARTER_ASC)->out(false), + 'sortstarterdesc' => $urlfactory->get_forum_view_url_from_forum($this->forum, null, + $discussionvault::SORTORDER_STARTER_DESC)->out(false), + 'sortgroupasc' => $urlfactory->get_forum_view_url_from_forum($this->forum, null, + $discussionvault::SORTORDER_GROUP_ASC)->out(false), + 'sortgroupdesc' => $urlfactory->get_forum_view_url_from_forum($this->forum, null, + $discussionvault::SORTORDER_GROUP_DESC)->out(false), + ], + ]; + } + + /** + * Returns a list of objects that are related. + * + * @return array + */ + protected static function define_related() { + return [ + 'legacydatamapperfactory' => 'mod_hsuforum\local\factories\legacy_data_mapper', + 'capabilitymanager' => 'mod_hsuforum\local\managers\capability', + 'urlfactory' => 'mod_hsuforum\local\factories\url', + 'user' => 'stdClass', + 'currentgroup' => 'int?', + 'vaultfactory' => 'mod_hsuforum\local\factories\vault' + ]; + } + + /** + * 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); + } +} diff --git a/classes/local/exporters/group.php b/classes/local/exporters/group.php new file mode 100644 index 00000000..5af085d2 --- /dev/null +++ b/classes/local/exporters/group.php @@ -0,0 +1,107 @@ +. + +/** + * Course Group exporter. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\exporters; + +defined('MOODLE_INTERNAL') || die(); + +use core\external\exporter; +use \core\output\renderer_base; +use stdClass; + +require_once($CFG->dirroot . '/mod/hsuforum/lib.php'); + +/** + * Group exporter. + * + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class group extends exporter { + /** @var stdClass $group Group */ + private $group; + + /** + * Constructor. + * + * @param stdClass $group The group to export + * @param array $related The related data for the export. + */ + public function __construct(stdClass $group, array $related = []) { + $this->group = $group; + 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 + ], + 'urls' => [ + 'type' => [ + 'image' => [ + 'description' => 'The URL for the group image', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ] + ], + ], + ]; + } + + /** + * Get the additional values to inject while exporting. + * + * @param \core\output\renderer_base $output The renderer. + * @return array Keys are the property names, values are their values. + */ + protected function get_other_values(\core\output\renderer_base $output) { + return [ + 'id' => $group->id, + 'urls' => [ + 'image' => $imageurl ? $imageurl->out(false) : null + ] + ]; + } + + /** + * Returns a list of objects that are related. + * + * @return array + */ + protected static function define_related() { + return [ + 'urlmanager' => 'mod_hsuforum\local\managers\url', + 'context' => 'context' + ]; + } +} diff --git a/classes/local/exporters/post.php b/classes/local/exporters/post.php new file mode 100644 index 00000000..a4f3c60b --- /dev/null +++ b/classes/local/exporters/post.php @@ -0,0 +1,701 @@ +. + +/** + * Post exporter class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\exporters; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\post as post_entity; +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\exporters\author as author_exporter; +use mod_hsuforum\local\factories\exporter as exporter_factory; +use core\external\exporter; +use core_files\external\stored_file_exporter; +use context; +use core_tag_tag; +use \core\output\renderer_base; +use stdClass; + +require_once($CFG->dirroot . '/mod/hsuforum/lib.php'); + +/** + * Post exporter class. + * + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class post extends exporter { + /** @var post_entity $post The post to export */ + private $post; + + /** + * Constructor. + * + * @param post_entity $post The post to export + * @param array $related List of related data + */ + public function __construct(post_entity $post, array $related = []) { + $this->post = $post; + return parent::__construct([], $related); + } + + /** + * Return the list of additional properties. + * + * @return array + */ + protected static function define_other_properties() { + $attachmentdefinition = stored_file_exporter::read_properties_definition(); + $attachmentdefinition['urls'] = [ + 'type' => [ + 'export' => [ + 'type' => PARAM_URL, + 'description' => 'The URL used to export the attachment', + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ] + ] + ]; + $attachmentdefinition['html'] = [ + 'type' => [ + 'plagiarism' => [ + 'type' => PARAM_RAW, + 'description' => 'The HTML source for the Plagiarism Response', + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + ] + ]; + + return [ + 'id' => ['type' => PARAM_INT], + 'subject' => ['type' => PARAM_TEXT], + 'replysubject' => ['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, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'timemodified' => [ + 'type' => PARAM_INT, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'unread' => [ + 'type' => PARAM_BOOL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'isdeleted' => ['type' => PARAM_BOOL], + 'isprivatereply' => ['type' => PARAM_BOOL], + 'haswordcount' => ['type' => PARAM_BOOL], + 'wordcount' => [ + 'type' => PARAM_INT, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'charcount' => [ + 'type' => PARAM_INT, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'capabilities' => [ + 'type' => [ + 'view' => [ + 'type' => PARAM_BOOL, + 'null' => NULL_ALLOWED, + 'description' => 'Whether the user can view the post', + ], + 'edit' => [ + 'type' => PARAM_BOOL, + 'null' => NULL_ALLOWED, + 'description' => 'Whether the user can edit the post', + ], + 'delete' => [ + 'type' => PARAM_BOOL, + 'null' => NULL_ALLOWED, + 'description' => 'Whether the user can delete the post', + ], + 'split' => [ + 'type' => PARAM_BOOL, + 'null' => NULL_ALLOWED, + 'description' => 'Whether the user can split the post', + ], + 'reply' => [ + 'type' => PARAM_BOOL, + 'null' => NULL_ALLOWED, + 'description' => 'Whether the user can reply to the post', + ], + 'selfenrol' => [ + 'type' => PARAM_BOOL, + 'null' => NULL_ALLOWED, + 'description' => 'Whether the user can self enrol into the course', + ], + 'export' => [ + 'type' => PARAM_BOOL, + 'null' => NULL_ALLOWED, + 'description' => 'Whether the user can export the post', + ], + 'controlreadstatus' => [ + 'type' => PARAM_BOOL, + 'null' => NULL_ALLOWED, + 'description' => 'Whether the user can control the read status of the post', + ], + 'canreplyprivately' => [ + 'type' => PARAM_BOOL, + 'null' => NULL_ALLOWED, + 'description' => 'Whether the user can post a private reply', + ] + ] + ], + 'urls' => [ + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED, + 'type' => [ + 'view' => [ + 'description' => 'The URL used to view the post', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'viewisolated' => [ + 'description' => 'The URL used to view the post in isolation', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'viewparent' => [ + 'description' => 'The URL used to view the parent of the post', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'edit' => [ + 'description' => 'The URL used to edit the post', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'delete' => [ + 'description' => 'The URL used to delete the post', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'split' => [ + 'description' => 'The URL used to split the discussion ' . + 'with the selected post being the first post in the new discussion', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'reply' => [ + 'description' => 'The URL used to reply to the post', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'export' => [ + 'description' => 'The URL used to export the post', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'markasread' => [ + 'description' => 'The URL used to mark the post as read', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'markasunread' => [ + 'description' => 'The URL used to mark the post as unread', + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], + 'discuss' => [ + 'type' => PARAM_URL, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ] + ] + ], + 'attachments' => [ + 'multiple' => true, + 'type' => $attachmentdefinition + ], + 'messageinlinefiles' => [ + 'optional' => true, + 'multiple' => true, + 'type' => stored_file_exporter::read_properties_definition(), + ], + 'tags' => [ + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED, + 'multiple' => true, + 'type' => [ + 'id' => [ + 'type' => PARAM_INT, + 'description' => 'The ID of the Tag', + 'null' => NULL_NOT_ALLOWED, + ], + 'tagid' => [ + 'type' => PARAM_INT, + 'description' => 'The tagid', + 'null' => NULL_NOT_ALLOWED, + ], + 'isstandard' => [ + 'type' => PARAM_BOOL, + 'description' => 'Whether this is a standard tag', + 'null' => NULL_NOT_ALLOWED, + ], + 'displayname' => [ + 'type' => PARAM_TEXT, + 'description' => 'The display name of the tag', + 'null' => NULL_NOT_ALLOWED, + ], + 'flag' => [ + 'type' => PARAM_BOOL, + 'description' => 'Wehther this tag is flagged', + 'null' => NULL_NOT_ALLOWED, + ], + 'urls' => [ + 'description' => 'URLs associated with the tag', + 'null' => NULL_NOT_ALLOWED, + 'type' => [ + 'view' => [ + 'type' => PARAM_URL, + 'description' => 'The URL to view the tag', + 'null' => NULL_NOT_ALLOWED, + ], + ] + ] + ] + ], + 'html' => [ + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED, + 'type' => [ + 'rating' => [ + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED, + 'type' => PARAM_RAW, + 'description' => 'The HTML source to rate the post', + ], + 'taglist' => [ + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED, + 'type' => PARAM_RAW, + 'description' => 'The HTML source to view the list of tags', + ], + 'authorsubheading' => [ + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED, + 'type' => PARAM_RAW, + 'description' => 'The HTML source to view the author details', + ], + ] + ] + ]; + } + + /** + * Get the additional values to inject while exporting. + * + * @param \core\output\renderer_base $output The renderer. + * @return array Keys are the property names, values are their values. + */ + protected function get_other_values(\core\output\renderer_base $output) { + $post = $this->post; + $authorgroups = $this->related['authorgroups']; + $forum = $this->related['forum']; + $discussion = $this->related['discussion']; + $author = $this->related['author']; + $authorcontextid = $this->related['authorcontextid']; + $user = $this->related['user']; + $readreceiptcollection = $this->related['readreceiptcollection']; + $rating = $this->related['rating']; + $tags = $this->related['tags']; + $attachments = $this->related['attachments']; + $inlineattachments = $this->related['messageinlinefiles']; + $includehtml = $this->related['includehtml']; + $isdeleted = $post->is_deleted(); + $isprivatereply = $post->is_private_reply(); + $hasrating = $rating != null; + $hastags = !empty($tags); + $discussionid = $post->get_discussion_id(); + $parentid = $post->get_parent_id(); + + $capabilitymanager = $this->related['capabilitymanager']; + $canview = $capabilitymanager->can_view_post($user, $discussion, $post); + $canedit = $capabilitymanager->can_edit_post($user, $discussion, $post); + $candelete = $capabilitymanager->can_delete_post($user, $discussion, $post); + $cansplit = $capabilitymanager->can_split_post($user, $discussion, $post); + $canreply = $capabilitymanager->can_reply_to_post($user, $discussion, $post); + $canexport = $capabilitymanager->can_export_post($user, $post); + $cancontrolreadstatus = $capabilitymanager->can_manually_control_post_read_status($user); + $canselfenrol = $capabilitymanager->can_self_enrol($user); + $canreplyprivately = $capabilitymanager->can_reply_privately_to_post($user, $post); + + $urlfactory = $this->related['urlfactory']; + $viewurl = $canview ? $urlfactory->get_view_post_url_from_post($post) : null; + $viewisolatedurl = $canview ? $urlfactory->get_view_isolated_post_url_from_post($post) : null; + $viewparenturl = $post->has_parent() ? $urlfactory->get_view_post_url_from_post_id($discussionid, $parentid) : null; + $editurl = $canedit ? $urlfactory->get_edit_post_url_from_post($forum, $post) : null; + $deleteurl = $candelete ? $urlfactory->get_delete_post_url_from_post($post) : null; + $spliturl = $cansplit ? $urlfactory->get_split_discussion_at_post_url_from_post($post) : null; + $replyurl = $canreply || $canselfenrol ? $urlfactory->get_reply_to_post_url_from_post($post) : null; + $exporturl = $canexport ? $urlfactory->get_export_post_url_from_post($post) : null; + $markasreadurl = $cancontrolreadstatus ? $urlfactory->get_mark_post_as_read_url_from_post($post) : null; + $markasunreadurl = $cancontrolreadstatus ? $urlfactory->get_mark_post_as_unread_url_from_post($post) : null; + $discussurl = $canview ? $urlfactory->get_discussion_view_url_from_post($post) : null; + + $authorexporter = new author_exporter( + $author, + $authorcontextid, + $authorgroups, + $canview, + $this->related + ); + $exportedauthor = $authorexporter->export($output); + // Only bother loading the content if the user can see it. + $loadcontent = $canview && !$isdeleted; + $exportattachments = $loadcontent && !empty($attachments); + $exportinlineattachments = $loadcontent && !empty($inlineattachments); + + if ($loadcontent) { + $subject = $post->get_subject(); + $timecreated = $this->get_start_time($discussion, $post); + $message = $this->get_message($post); + } else { + $subject = $isdeleted ? get_string('forumsubjectdeleted', 'hsuforum') : get_string('forumsubjecthidden', 'hsuforum'); + $message = $isdeleted ? get_string('forumbodydeleted', 'hsuforum') : get_string('forumbodyhidden', 'hsuforum'); + $timecreated = null; + } + + $replysubject = $subject; + $strre = get_string('re', 'hsuforum'); + if (!(substr($replysubject, 0, strlen($strre)) == $strre)) { + $replysubject = "{$strre} {$replysubject}"; + } + + $showwordcount = $forum->should_display_word_count(); + if ($showwordcount) { + $wordcount = $post->get_wordcount() ?? count_words($message); + $charcount = $post->get_charcount() ?? count_letters($message); + } else { + $wordcount = null; + $charcount = null; + } + + return [ + 'id' => $post->get_id(), + 'subject' => $subject, + 'replysubject' => $replysubject, + '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, + 'timemodified' => $post->get_time_modified(), + 'unread' => ($loadcontent && $readreceiptcollection) ? !$readreceiptcollection->has_user_read_post($user, $post) : null, + 'isdeleted' => $isdeleted, + 'isprivatereply' => $isprivatereply, + 'haswordcount' => $showwordcount, + 'wordcount' => $wordcount, + 'charcount' => $charcount, + 'capabilities' => [ + 'view' => $canview, + 'edit' => $canedit, + 'delete' => $candelete, + 'split' => $cansplit, + 'reply' => $canreply, + 'export' => $canexport, + 'controlreadstatus' => $cancontrolreadstatus, + 'canreplyprivately' => $canreplyprivately, + 'selfenrol' => $canselfenrol + ], + 'urls' => [ + 'view' => $viewurl ? $viewurl->out(false) : null, + 'viewisolated' => $viewisolatedurl ? $viewisolatedurl->out(false) : null, + 'viewparent' => $viewparenturl ? $viewparenturl->out(false) : null, + 'edit' => $editurl ? $editurl->out(false) : null, + 'delete' => $deleteurl ? $deleteurl->out(false) : null, + 'split' => $spliturl ? $spliturl->out(false) : null, + 'reply' => $replyurl ? $replyurl->out(false) : null, + 'export' => $exporturl && $exporturl ? $exporturl->out(false) : null, + 'markasread' => $markasreadurl ? $markasreadurl->out(false) : null, + 'markasunread' => $markasunreadurl ? $markasunreadurl->out(false) : null, + 'discuss' => $discussurl ? $discussurl->out(false) : null, + ], + 'attachments' => ($exportattachments) ? $this->export_attachments($attachments, $post, $output, $canexport) : [], + 'messageinlinefiles' => ($exportinlineattachments) ? $this->export_inline_attachments($inlineattachments, + $post, $output) : [], + 'tags' => ($loadcontent && $hastags) ? $this->export_tags($tags) : [], + 'html' => $includehtml ? [ + 'rating' => ($loadcontent && $hasrating) ? $output->render($rating) : null, + 'taglist' => ($loadcontent && $hastags) ? $output->tag_list($tags) : null, + 'authorsubheading' => ($loadcontent) ? $this->get_author_subheading_html($exportedauthor, $timecreated) : null + ] : null + ]; + } + + /** + * Returns a list of objects that are related. + * + * @return array + */ + protected static function define_related() { + return [ + 'capabilitymanager' => 'mod_hsuforum\local\managers\capability', + 'readreceiptcollection' => 'mod_hsuforum\local\entities\post_read_receipt_collection?', + 'urlfactory' => 'mod_hsuforum\local\factories\url', + 'forum' => 'mod_hsuforum\local\entities\forum', + 'discussion' => 'mod_hsuforum\local\entities\discussion', + 'author' => 'mod_hsuforum\local\entities\author', + 'authorcontextid' => 'int?', + 'user' => 'stdClass', + 'context' => 'context', + 'authorgroups' => 'stdClass[]', + 'attachments' => '\stored_file[]?', + 'messageinlinefiles' => '\stored_file[]?', + 'tags' => '\core_tag_tag[]?', + 'rating' => 'rating?', + 'includehtml' => 'bool' + ]; + } + + /** + * This method returns the parameters for the post's message to + * use with the function \core_external\util::format_text(). + * + * @return array + */ + protected function get_format_parameters_for_message() { + return [ + 'component' => 'mod_hsuforum', + 'filearea' => 'post', + 'itemid' => $this->post->get_id(), + 'options' => [ + 'para' => false, + 'trusted' => $this->post->is_message_trusted() + ] + ]; + } + + /** + * Get the message text from a post. + * + * @param post_entity $post The post + * @return string + */ + private function get_message(post_entity $post): string { + global $CFG; + + $message = $post->get_message(); + + if (!empty($CFG->enableplagiarism)) { + require_once($CFG->libdir . '/plagiarismlib.php'); + $forum = $this->related['forum']; + $message .= plagiarism_get_links([ + 'userid' => $post->get_author_id(), + 'content' => $message, + 'cmid' => $forum->get_course_module_record()->id, + 'course' => $forum->get_course_id(), + 'forum' => $forum->get_id() + ]); + } + + return $message; + } + + /** + * Get the exported attachments for a post. + * + * @param stored_file[] $attachments The list of attachments for the post + * @param post_entity $post The post being exported + * @param \core\output\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, \core\output\renderer_base $output, bool $canexport): array { + global $CFG; + + $urlfactory = $this->related['urlfactory']; + $enableplagiarism = $CFG->enableplagiarism; + $forum = $this->related['forum']; + $context = $this->related['context']; + + if ($enableplagiarism) { + require_once($CFG->libdir . '/plagiarismlib.php' ); + } + + return array_map(function($attachment) use ( + $output, + $enableplagiarism, + $canexport, + $context, + $forum, + $post, + $urlfactory + ) { + $exporter = new stored_file_exporter($attachment, ['context' => $context]); + $exportedattachment = $exporter->export($output); + $exporturl = $canexport ? $urlfactory->get_export_attachment_url_from_post_and_attachment($post, $attachment) : null; + + if ($enableplagiarism) { + $plagiarismhtml = plagiarism_get_links([ + 'userid' => $post->get_author_id(), + 'file' => $attachment, + 'cmid' => $forum->get_course_module_record()->id, + 'course' => $forum->get_course_id(), + 'forum' => $forum->get_id() + ]); + } else { + $plagiarismhtml = null; + } + + $exportedattachment->urls = [ + 'export' => $exporturl ? $exporturl->out(false) : null + ]; + $exportedattachment->html = [ + 'plagiarism' => $plagiarismhtml + ]; + + return $exportedattachment; + }, $attachments); + } + + /** + * Get the exported inline attachments for a post. + * + * @param array $inlineattachments The list of inline attachments for the post + * @param post_entity $post The post being exported + * @param \core\output\renderer_base $output Renderer base + * @return array + */ + private function export_inline_attachments(array $inlineattachments, post_entity $post, \core\output\renderer_base $output): array { + + return array_map(function($attachment) use ( + $output, + $post + ) { + $exporter = new stored_file_exporter($attachment, ['context' => $this->related['context']]); + return $exporter->export($output);; + }, $inlineattachments); + } + + /** + * Export the list of tags. + * + * @param core_tag_tag[] $tags List of tags to export + * @return array + */ + private function export_tags(array $tags): array { + $user = $this->related['user']; + $context = $this->related['context']; + $capabilitymanager = $this->related['capabilitymanager']; + $canmanagetags = $capabilitymanager->can_manage_tags($user); + + return array_values(array_map(function($tag) use ($context, $canmanagetags) { + $viewurl = core_tag_tag::make_url($tag->tagcollid, $tag->rawname, 0, $context->id); + return [ + 'id' => $tag->taginstanceid, + 'tagid' => $tag->id, + 'isstandard' => $tag->isstandard, + 'displayname' => $tag->get_display_name(), + 'flag' => $canmanagetags && !empty($tag->flag), + 'urls' => [ + 'view' => $viewurl->out(false) + ] + ]; + }, $tags)); + } + + /** + * Get the HTML to display as a subheading in a post. + * + * @param stdClass $exportedauthor The exported author object + * @param int $timecreated The post time created timestamp if it's to be displayed + * @return string + */ + private function get_author_subheading_html(stdClass $exportedauthor, int $timecreated): string { + $fullname = $exportedauthor->fullname; + $profileurl = $exportedauthor->urls['profile'] ?? null; + $name = $profileurl ? "{$fullname}" : $fullname; + $date = userdate_htmltime($timecreated, get_string('strftimedaydatetime', 'core_langconfig')); + return get_string('bynameondate', 'mod_hsuforum', ['name' => $name, 'date' => $date]); + } + + /** + * Get the start time for a post. + * + * @param discussion_entity $discussion entity + * @param post_entity $post entity + * @return int The start time (timestamp) for a post + */ + private function get_start_time(discussion_entity $discussion, post_entity $post) { + global $CFG; + + $posttime = $post->get_time_created(); + $discussiontime = $discussion->get_time_start(); + if (!empty($CFG->forum_enabletimedposts) && ($discussiontime > $posttime)) { + return $discussiontime; + } + return $posttime; + } +} diff --git a/classes/local/exporters/posts.php b/classes/local/exporters/posts.php new file mode 100644 index 00000000..31125f90 --- /dev/null +++ b/classes/local/exporters/posts.php @@ -0,0 +1,180 @@ +. + +/** + * Posts exporter class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\exporters; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\author as author_entity; +use mod_hsuforum\local\entities\post as post_entity; +use mod_hsuforum\local\exporters\post as post_exporter; +use core\external\exporter; +use \core\output\renderer_base; + +require_once($CFG->dirroot . '/mod/hsuforum/lib.php'); + +/** + * Posts exporter class. + * + * @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 int[] $authorcontextids List of authors context ids indexed by author id */ + private $authorcontextids; + /** @var array $attachmentsbypostid List of attachments indexed by post id */ + private $attachmentsbypostid; + /** @var array $inlineattachmentsbypostid List of inline attachments indexed by post id */ + private $inlineattachmentsbypostid; + /** @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 int[] $authorcontextids List of authors context ids 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 + * @param array $inlineattachmentsbypostid List of inline attachments indexed by post id + */ + public function __construct( + array $posts, + array $authorsbyid = [], + array $authorcontextids = [], + array $attachmentsbypostid = [], + array $groupsbyauthorid = [], + array $tagsbypostid = [], + array $ratingbypostid = [], + array $related = [], + array $inlineattachmentsbypostid = [] + ) { + $this->posts = $posts; + $this->authorsbyid = $authorsbyid; + $this->authorcontextids = $authorcontextids; + $this->attachmentsbypostid = $attachmentsbypostid; + $this->inlineattachmentsbypostid = $inlineattachmentsbypostid; + $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 \core\output\renderer_base $output The renderer. + * @return array Keys are the property names, values are their values. + */ + protected function get_other_values(\core\output\renderer_base $output) { + $related = $this->related; + $authorsbyid = $this->authorsbyid; + $authorcontextids = $this->authorcontextids; + $attachmentsbypostid = $this->attachmentsbypostid; + $inlineattachmentsbypostid = $this->inlineattachmentsbypostid; + $groupsbyauthorid = $this->groupsbyauthorid; + $tagsbypostid = $this->tagsbypostid; + $ratingbypostid = $this->ratingbypostid; + $exportedposts = array_map( + function($post) use ( + $related, + $authorsbyid, + $authorcontextids, + $attachmentsbypostid, + $groupsbyauthorid, + $tagsbypostid, + $ratingbypostid, + $output, + $inlineattachmentsbypostid + ) { + $authorid = $post->get_author_id(); + $postid = $post->get_id(); + $author = isset($authorsbyid[$authorid]) ? $authorsbyid[$authorid] : []; + $authorcontextid = isset($authorcontextids[$authorid]) ? $authorcontextids[$authorid] : null; + $attachments = isset($attachmentsbypostid[$postid]) ? $attachmentsbypostid[$postid] : []; + $inlineattachments = isset($inlineattachmentsbypostid[$postid]) ? $inlineattachmentsbypostid[$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, + 'authorcontextid' => $authorcontextid, + 'attachments' => $attachments, + 'messageinlinefiles' => $inlineattachments, + '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 [ + 'capabilitymanager' => 'mod_hsuforum\local\managers\capability', + 'urlfactory' => 'mod_hsuforum\local\factories\url', + 'forum' => 'mod_hsuforum\local\entities\forum', + 'discussion' => 'mod_hsuforum\local\entities\discussion', + 'readreceiptcollection' => 'mod_hsuforum\local\entities\post_read_receipt_collection?', + 'user' => 'stdClass', + 'context' => 'context', + 'includehtml' => 'bool' + ]; + } +} diff --git a/classes/local/factories/builder.php b/classes/local/factories/builder.php new file mode 100644 index 00000000..8cf28fa0 --- /dev/null +++ b/classes/local/factories/builder.php @@ -0,0 +1,125 @@ +. + +/** + * Builder factory. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\factories; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\builders\exported_posts as exported_posts_builder; +use mod_hsuforum\local\builders\exported_discussion_summaries as exported_discussion_summaries_builder; +use mod_hsuforum\local\builders\exported_discussion as exported_discussion_builder; +use mod_hsuforum\local\factories\vault as vault_factory; +use mod_hsuforum\local\factories\legacy_data_mapper as legacy_data_mapper_factory; +use mod_hsuforum\local\factories\exporter as exporter_factory; +use mod_hsuforum\local\factories\manager as manager_factory; +use \core\output\renderer_base; + +/** + * Builder factory to construct any builders for hsuforum. + * + * See: + * https://designpatternsphp.readthedocs.io/en/latest/Creational/SimpleFactory/README.html + * + * @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 \core\output\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 \core\output\renderer_base $rendererbase Renderer base + */ + public function __construct( + legacy_data_mapper_factory $legacydatamapperfactory, + exporter_factory $exporterfactory, + vault_factory $vaultfactory, + manager_factory $managerfactory, + \core\output\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 an instance of the exported discussion summaries builder. + * + * @return exported_discussion_summaries_builder + */ + public function get_exported_discussion_summaries_builder(): exported_discussion_summaries_builder { + return new exported_discussion_summaries_builder( + $this->rendererbase, + $this->legacydatamapperfactory, + $this->exporterfactory, + $this->vaultfactory, + $this->managerfactory + ); + } + + /** + * Get an instance of the exported discussion builder. + * + * @return exported_discussion_summaries_builder + */ + public function get_exported_discussion_builder(): exported_discussion_builder { + return new exported_discussion_builder( + $this->rendererbase, + $this->legacydatamapperfactory, + $this->exporterfactory, + $this->vaultfactory, + $this->managerfactory->get_rating_manager() + ); + } +} diff --git a/classes/local/factories/entity.php b/classes/local/factories/entity.php new file mode 100644 index 00000000..e2439993 --- /dev/null +++ b/classes/local/factories/entity.php @@ -0,0 +1,257 @@ +. + +/** + * Entity factory. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\factories; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\author as author_entity; +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\entities\discussion_summary as discussion_summary_entity; +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\entities\post as post_entity; +use mod_hsuforum\local\entities\post_read_receipt_collection as post_read_receipt_collection_entity; +use mod_hsuforum\local\entities\sorter as sorter_entity; +use stdClass; +use context; +use cm_info; +use \core\output\user_picture; +use \core\url; + +/** + * Entity factory to create the hsuforum entities. + * + * See: + * https://designpatternsphp.readthedocs.io/en/latest/Creational/SimpleFactory/README.html + * + * @copyright 2019 Ryan Wyllie + * @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->grade_forum, + $record->grade_forum_notify, + $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, + $record->duedate, + $record->cutoffdate + ); + } + + /** + * 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, + $record->timelocked + ); + } + + /** + * 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, + $record->privatereplyto, + $record->wordcount, + $record->charcount + ); + } + + /** + * 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->deleted, + $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; + } + ); + } +} diff --git a/classes/local/factories/exporter.php b/classes/local/factories/exporter.php new file mode 100644 index 00000000..a0fa6509 --- /dev/null +++ b/classes/local/factories/exporter.php @@ -0,0 +1,275 @@ +. + +/** + * Hsuforum Exporter factory. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\factories; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\entities\post as post_entity; +use mod_hsuforum\local\entities\post_read_receipt_collection as post_read_receipt_collection_entity; +use mod_hsuforum\local\factories\author_entity; +use mod_hsuforum\local\factories\discussion_summary_entity; +use mod_hsuforum\local\factories\legacy_data_mapper as legacy_data_mapper_factory; +use mod_hsuforum\local\factories\manager as manager_factory; +use mod_hsuforum\local\factories\url as url_factory; +use mod_hsuforum\local\factories\vault as vault_factory; +use mod_hsuforum\local\exporters\forum as forum_exporter; +use mod_hsuforum\local\exporters\discussion as discussion_exporter; +use mod_hsuforum\local\exporters\discussion_summaries as discussion_summaries_exporter; +use mod_hsuforum\local\exporters\post as post_exporter; +use mod_hsuforum\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 + * + * @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; + + /** @var vault_factory The vault factory */ + private $vaultfactory; + + /** + * 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 + * @param vault_factory $vaultfactory The vault factory + */ + public function __construct(legacy_data_mapper_factory $legacydatamapperfactory, manager_factory $managerfactory, + url_factory $urlfactory, vault_factory $vaultfactory) { + $this->legacydatamapperfactory = $legacydatamapperfactory; + $this->managerfactory = $managerfactory; + $this->urlfactory = $urlfactory; + $this->vaultfactory = $vaultfactory; + } + + /** + * 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, + 'vaultfactory' => $this->vaultfactory, + ]); + } + + /** + * 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 = [], + array $favouriteids = [] + ): 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, + 'favouriteids' => $favouriteids + ]); + } + + /** + * 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_summary_entity[] $discussions The set of discussion summaries to export + * @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 + * @param int[] $postauthorcontextids The context ids for the first and last post authors (indexed by author id) + * @param int[] $favourites The list of discussion ids that have been favourited + * @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 $latestpostids = [], + array $postauthorcontextids = [], + array $favourites = [], + array $latestauthors = [] + ): discussion_summaries_exporter { + return new discussion_summaries_exporter( + $discussions, + $groupsbyid, + $groupsbyauthorid, + $discussionreplycount, + $discussionunreadcount, + $latestpostids, + $postauthorcontextids, + [ + 'legacydatamapperfactory' => $this->legacydatamapperfactory, + 'context' => $forum->get_context(), + 'forum' => $forum, + 'capabilitymanager' => $this->managerfactory->get_capability_manager($forum), + 'urlfactory' => $this->urlfactory, + 'user' => $user, + 'favouriteids' => $favourites, + 'latestauthors' => $latestauthors + ] + ); + } + + /** + * 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 int[] $authorcontextids List of authors context ids 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 + * @param array $inlineattachmentsbypostid List of attachments for each post indexed by post id + * @return posts_exporter + */ + public function get_posts_exporter( + stdClass $user, + forum_entity $forum, + discussion_entity $discussion, + array $posts, + array $authorsbyid = [], + array $authorcontextids = [], + array $attachmentsbypostid = [], + array $groupsbyauthorid = [], + post_read_receipt_collection_entity $readreceiptcollection = null, + array $tagsbypostid = [], + array $ratingbypostid = [], + bool $includehtml = false, + array $inlineattachmentsbypostid = [] + ): posts_exporter { + return new posts_exporter( + $posts, + $authorsbyid, + $authorcontextids, + $attachmentsbypostid, + $groupsbyauthorid, + $tagsbypostid, + $ratingbypostid, + [ + 'capabilitymanager' => $this->managerfactory->get_capability_manager($forum), + 'urlfactory' => $this->urlfactory, + 'forum' => $forum, + 'discussion' => $discussion, + 'user' => $user, + 'context' => $forum->get_context(), + 'readreceiptcollection' => $readreceiptcollection, + 'includehtml' => $includehtml + ], + $inlineattachmentsbypostid + ); + } + + /** + * Fetch the structure of the posts exporter. + * + * @return array + */ + public static function get_posts_export_structure() { + return posts_exporter::read_properties_definition(); + } +} diff --git a/classes/local/factories/legacy_data_mapper.php b/classes/local/factories/legacy_data_mapper.php new file mode 100644 index 00000000..e3e88286 --- /dev/null +++ b/classes/local/factories/legacy_data_mapper.php @@ -0,0 +1,99 @@ +. + +/** + * Legacy data mapper factory. + * + * @package mod_hsuforum + * @copyright 2019 Ryan Wyllie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\factories; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\data_mappers\legacy\author as author_data_mapper; +use mod_hsuforum\local\data_mappers\legacy\discussion as discussion_data_mapper; +use mod_hsuforum\local\data_mappers\legacy\forum as forum_data_mapper; +use mod_hsuforum\local\data_mappers\legacy\post as post_data_mapper; +use mod_hsuforum\local\entities\forum; + +/** + * Legacy data mapper factory. + * + * See: + * https://designpatternsphp.readthedocs.io/en/latest/Creational/SimpleFactory/README.html + * + * @copyright 2019 Ryan Wyllie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class legacy_data_mapper { + /** + * Create a legacy hsuforum 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(); + } + + /** + * Get the corresponding entity based on the supplied value + * + * @param string $entity + * @return author_data_mapper|discussion_data_mapper|forum_data_mapper|post_data_mapper + */ + public function get_legacy_data_mapper_for_vault($entity) { + switch($entity) { + case 'forum': + return $this->get_forum_data_mapper(); + case 'discussion': + return $this->get_discussion_data_mapper(); + case 'post': + return $this->get_post_data_mapper(); + case 'author': + return $this->get_author_data_mapper(); + } + } +} diff --git a/classes/local/factories/manager.php b/classes/local/factories/manager.php new file mode 100644 index 00000000..1e401fa9 --- /dev/null +++ b/classes/local/factories/manager.php @@ -0,0 +1,79 @@ +. + +/** + * Managers factory. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\factories; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/rating/lib.php'); + +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\managers\capability as capability_manager; +use rating_manager; + +/** + * Managers factory. + * + * See: + * https://designpatternsphp.readthedocs.io/en/latest/Creational/SimpleFactory/README.html + * + * @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 hsuforum. + * + * @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(); + } +} diff --git a/classes/local/factories/renderer.php b/classes/local/factories/renderer.php new file mode 100644 index 00000000..4817b91e --- /dev/null +++ b/classes/local/factories/renderer.php @@ -0,0 +1,638 @@ +. + +/** + * Renderer factory. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\factories; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\grades\forum_gradeitem; +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\factories\renderer_base; +use mod_hsuforum\local\factories\vault as vault_factory; +use mod_hsuforum\local\factories\legacy_data_mapper as legacy_data_mapper_factory; +use mod_hsuforum\local\factories\entity as entity_factory; +use mod_hsuforum\local\factories\exporter as exporter_factory; +use mod_hsuforum\local\factories\manager as manager_factory; +use mod_hsuforum\local\factories\builder as builder_factory; +use mod_hsuforum\local\factories\url as url_factory; +use mod_hsuforum\local\renderers\discussion as discussion_renderer; +use mod_hsuforum\local\renderers\discussion_list as discussion_list_renderer; +use mod_hsuforum\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 + * @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 \core\output\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_hsuforum'); + } + + /** + * Create a discussion renderer for the given hsuforum 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_hsuforum/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 HSUFORUM_MODE_THREADED: + $template = 'mod_hsuforum/forum_discussion_threaded_posts'; + break; + case HSUFORUM_MODE_NESTED: + $template = 'mod_hsuforum/forum_discussion_nested_posts'; + break; + case HSUFORUM_MODE_NESTED_V2: + $template = 'mod_hsuforum/forum_discussion_nested_v2_posts'; + break; + default; + $template = 'mod_hsuforum/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 === HSUFORUM_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 === HSUFORUM_MODE_NESTED || + $displaymode === HSUFORUM_MODE_THREADED || + $displaymode === HSUFORUM_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_hsuforum'); + } + 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 === HSUFORUM_MODE_FLATNEWEST || $displaymode === HSUFORUM_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 hsuforum 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_hsuforum/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, + '', + '' + ); + + foreach ($searchterms as $searchterm) { + if ( + preg_match("/$searchterm/i", $exportedpost->message) && + !preg_match('/' . $searchterm . '<\/fgw9sdpq4>/i', $exportedpost->message) + ) { + $missingterms .= " $searchterm"; + } + } + + $exportedpost->message = str_replace('', '', $exportedpost->message); + $exportedpost->message = str_replace('', '', $exportedpost->message); + + if ($missingterms) { + $strmissingsearchterms = get_string('missingsearchterms', 'hsuforum'); + $exportedpost->message = '

' . $strmissingsearchterms . ' ' + . $missingterms . '

' . $exportedpost->message; + } + + return $exportedpost; + }, + $exportedposts + ); + } + ); + } + + /** + * Create a posts renderer to render posts in mod/hsuforum/user.php. + * + * @param bool $addlinkstocontext Should links to the course, hsuforum, 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_hsuforum/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_hsuforum/frontpage_news_discussion_list'; + } else { + $template = 'mod_hsuforum/news_discussion_list'; + } + break; + case 'qanda': + $template = 'mod_hsuforum/qanda_discussion_list'; + break; + default: + $template = 'mod_hsuforum/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_hsuforum/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_hsuforum/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_hsuforum/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', 'hsuforum'))) + ->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 + ); + } +} diff --git a/classes/local/factories/url.php b/classes/local/factories/url.php new file mode 100644 index 00000000..b628f55d --- /dev/null +++ b/classes/local/factories/url.php @@ -0,0 +1,505 @@ +. + +/** + * A URL factory for the hsuforum of today. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\factories; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\author as author_entity; +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\entities\post as post_entity; +use mod_hsuforum\local\factories\legacy_data_mapper as legacy_data_mapper_factory; +use \core\url as moodle_url; +use stored_file; +use \core\output\user_picture; + +require_once($CFG->dirroot . '/mod/hsuforum/lib.php'); + +/** + * A URL factory for the hsuforum. + * + * @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/hsuforum/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 + * @param int|null $sortorder The sorting order + * @return moodle_url + */ + public function get_forum_view_url_from_forum(forum_entity $forum, ?int $pageno = null, + ?int $sortorder = null): moodle_url { + + return $this->get_forum_view_url_from_course_module_id($forum->get_course_module_record()->id, $pageno, $sortorder); + } + + /** + * 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 + * @param int|null $sortorder The sorting order + * @return moodle_url + */ + public function get_forum_view_url_from_course_module_id(int $coursemoduleid, ?int $pageno = null, + ?int $sortorder = null): moodle_url { + + $url = new moodle_url('/mod/hsuforum/view.php', [ + 'id' => $coursemoduleid, + ]); + + if (null !== $pageno) { + $url->param('p', $pageno); + } + + if (null !== $sortorder) { + $url->param('o', $sortorder); + } + + 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/hsuforum/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' && !$post->has_parent()) { + return new moodle_url('/course/modedit.php', [ + 'update' => $forum->get_course_module_record()->id, + 'sesskey' => sesskey(), + 'return' => 1 + ]); + } else { + return new moodle_url('/mod/hsuforum/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/hsuforum/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/hsuforum/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/hsuforum/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_hsuforum'); + 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 = HSUFORUM_MODE_THREADED): moodle_url { + $params = [ + 'd' => $post->get_discussion_id(), + 'postid' => $post->get_id(), + 'mark' => 'read' + ]; + + $url = new moodle_url('/mod/hsuforum/discuss.php', $params); + + if ($displaymode == HSUFORUM_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 hsuforum in after marking as unread + * @return moodle_url + */ + public function get_mark_post_as_unread_url_from_post(post_entity $post, int $displaymode = HSUFORUM_MODE_THREADED): moodle_url { + $params = [ + 'd' => $post->get_discussion_id(), + 'postid' => $post->get_id(), + 'mark' => 'unread' + ]; + + $url = new moodle_url('/mod/hsuforum/discuss.php', $params); + + if ($displaymode == HSUFORUM_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_hsuforum' + ); + $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 + * @param int $courseid The course id + * @return moodle_url + */ + public function get_author_profile_url(author_entity $author, int $courseid): moodle_url { + return new moodle_url('/user/view.php', [ + 'id' => $author->get_id(), + 'course' => $courseid + ]); + } + + /** + * Get the url to view the author's profile image. The author's context id should be + * provided to prevent the code from needing to load it. + * + * @param author_entity $author The author + * @param int|null $authorcontextid The author context id + * @param int $size The size of the image to return + * @return moodle_url + */ + public function get_author_profile_image_url( + author_entity $author, + int $authorcontextid = null, + int $size = 100 + ): moodle_url { + global $PAGE; + + $datamapper = $this->legacydatamapperfactory->get_author_data_mapper(); + $record = $datamapper->to_legacy_object($author); + $record->contextid = $authorcontextid; + $userpicture = new \core\output\user_picture($record); + $userpicture->size = $size; + + return $userpicture->get_url($PAGE); + } + + /** + * Get the url to view an author's group. + * + * @param \stdClass $group The group + * @return moodle_url + */ + public function get_author_group_url(\stdClass $group): moodle_url { + return new moodle_url('/user/index.php', [ + 'id' => $group->courseid, + 'group' => $group->id + ]); + } + /** + * 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/hsuforum/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/hsuforum/markposts.php', [ + 'f' => $forum->get_id(), + 'mark' => 'read', + 'sesskey' => sesskey(), + 'return' => $this->get_forum_view_url_from_forum($forum)->out(), + ]); + } + + /** + * Get the url to subscribe to a discussion. + * + * @param discussion_entity $discussion The discussion + * @return moodle_url + */ + public function get_discussion_subscribe_url(discussion_entity $discussion): moodle_url { + return new moodle_url('/mod/hsuforum/subscribe.php', [ + 'sesskey' => sesskey(), + 'id' => $discussion->get_forum_id(), + 'd' => $discussion->get_id() + ]); + } + + /** + * Generate the pinned discussion link + * + * @param discussion_entity $discussion + * @return moodle_url + * @throws \core\exception\moodle_exception + */ + public function get_pin_discussion_url_from_discussion(discussion_entity $discussion): moodle_url { + return new moodle_url('discuss.php', [ + 'sesskey' => sesskey(), + 'd' => $discussion->get_id(), + 'pin' => $discussion->is_pinned() ? HSUFORUM_DISCUSSION_UNPINNED : HSUFORUM_DISCUSSION_PINNED + ]); + } +} diff --git a/classes/local/factories/vault.php b/classes/local/factories/vault.php new file mode 100644 index 00000000..71f68fbf --- /dev/null +++ b/classes/local/factories/vault.php @@ -0,0 +1,163 @@ +. + +/** + * Vault factory. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\factories; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\factories\entity as entity_factory; +use mod_hsuforum\local\factories\legacy_data_mapper; +use mod_hsuforum\local\vaults\author as author_vault; +use mod_hsuforum\local\vaults\discussion as discussion_vault; +use mod_hsuforum\local\vaults\discussion_list as discussion_list_vault; +use mod_hsuforum\local\vaults\forum as forum_vault; +use mod_hsuforum\local\vaults\post as post_vault; +use mod_hsuforum\local\vaults\post_attachment as post_attachment_vault; +use mod_hsuforum\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 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class vault { + /** @var entity_factory $entityfactory Entity factory */ + private $entityfactory; + /** @var legacy_data_mapper $legacymapper Entity factory */ + private $legacymapper; + /** @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 + * @param legacy_data_mapper $legacyfactory Datamapper + */ + public function __construct(moodle_database $db, entity_factory $entityfactory, + file_storage $filestorage, legacy_data_mapper $legacyfactory) { + $this->db = $db; + $this->entityfactory = $entityfactory; + $this->filestorage = $filestorage; + $this->legacymapper = $legacyfactory; + } + + /** + * Create a hsuforum vault. + * + * @return forum_vault + */ + public function get_forum_vault(): forum_vault { + return new forum_vault( + $this->db, + $this->entityfactory, + $this->legacymapper->get_legacy_data_mapper_for_vault('forum') + ); + } + + /** + * Create a discussion vault. + * + * @return discussion_vault + */ + public function get_discussion_vault(): discussion_vault { + return new discussion_vault( + $this->db, + $this->entityfactory, + $this->legacymapper->get_legacy_data_mapper_for_vault('discussion') + ); + } + + /** + * 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, + $this->legacymapper->get_legacy_data_mapper_for_vault('discussion') + ); + } + + /** + * Create a post vault. + * + * @return post_vault + */ + public function get_post_vault(): post_vault { + return new post_vault( + $this->db, + $this->entityfactory, + $this->legacymapper->get_legacy_data_mapper_for_vault('post') + ); + } + + /** + * Create an author vault. + * + * @return author_vault + */ + public function get_author_vault(): author_vault { + return new author_vault( + $this->db, + $this->entityfactory, + $this->legacymapper->get_legacy_data_mapper_for_vault('author') + ); + } + + /** + * 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, + $this->legacymapper->get_legacy_data_mapper_for_vault('post') + ); + } + + /** + * 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 + ); + } +} diff --git a/classes/local/managers/capability.php b/classes/local/managers/capability.php new file mode 100644 index 00000000..5cf91c8d --- /dev/null +++ b/classes/local/managers/capability.php @@ -0,0 +1,730 @@ +. + +/** + * Capability manager for the hsuforum. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\managers; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\data_mappers\legacy\forum as legacy_forum_data_mapper; +use mod_hsuforum\local\data_mappers\legacy\discussion as legacy_discussion_data_mapper; +use mod_hsuforum\local\data_mappers\legacy\post as legacy_post_data_mapper; +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\entities\post as post_entity; +use mod_hsuforum\subscriptions; +use context; +use context_system; +use stdClass; +use \core\exception\moodle_exception; + +require_once($CFG->dirroot . '/mod/hsuforum/lib.php'); + +/** + * Capability manager for the forum. + * + * Defines all the business rules for what a user can and can't do in the forum. + * + * @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; + /** @var array $canviewpostcache Cache of discussion posts that can be viewed by a user. */ + protected $canviewpostcache = []; + + /** + * 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 hsuforum? + * + * @param stdClass $user The user to check + * @return bool + */ + public function can_subscribe_to_forum(stdClass $user): bool { + if ($this->forum->get_type() == 'single') { + return false; + } + + 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; + } + + if ($this->forum->is_cutoff_date_reached()) { + if (!has_capability('mod/hsuforum:canoverridecutoff', $this->get_context())) { + return false; + } + } + + // If the user reaches the number of posts equal to warning/blocking setting then return the value of canpost in $warningobj. + $cmrecord = $this->forum->get_course_module_record(); + if ($warningobj = hsuforum_check_throttling($this->forumrecord, $cmrecord)) { + return $warningobj->canpost; + } + + switch ($this->forum->get_type()) { + case 'news': + $capability = 'mod/hsuforum:addnews'; + break; + case 'qanda': + $capability = 'mod/hsuforum:addquestion'; + break; + default: + $capability = 'mod/hsuforum:startdiscussion'; + } + + if (!has_capability($capability, $this->forum->get_context(), $user)) { + return false; + } + + if ($this->forum->get_type() == 'eachuser') { + if (hsuforum_user_has_posted_discussion($this->forum->get_id(), $user->id, $groupid)) { + 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): bool { + 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): bool { + 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 post to their groups? + * + * @param stdClass $user The user to check + * @return bool + */ + public function can_post_to_my_groups(stdClass $user): bool { + return has_capability('mod/hsuforum:canposttomygroups', $this->get_context(), $user); + } + + /** + * 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/hsuforum: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/hsuforum: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 $this->forum->get_type() !== 'single' && + has_capability('mod/hsuforum: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/hsuforum: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/hsuforum: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/hsuforum:viewqandawithoutposting', $this->get_context(), $user) && + !hsuforum_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(); + + $status = hsuforum_user_can_post($forumrecord, $discussionrecord, $user, $coursemodule, $course, $context); + + // If the user reaches the number of posts equal to warning/blocking setting then logically and canpost value with $status. + if ($warningobj = hsuforum_check_throttling($forumrecord, $coursemodule)) { + return $status && $warningobj->canpost; + } + return $status; + } + + /** + * Can the user favourite the discussion + * + * @param stdClass $user The user to check + * @return bool + */ + public function can_favourite_discussion(stdClass $user): bool { + $context = $this->get_context(); + return has_capability('mod/hsuforum:cantogglefavourite', $context, $user); + } + + /** + * Can the user view the content of a discussion? + * + * @param stdClass $user The user to check + * @param discussion_entity $discussion The discussion to check + * @return bool + */ + public function can_view_discussion(stdClass $user, discussion_entity $discussion): bool { + $forumrecord = $this->get_forum_record(); + $discussionrecord = $this->get_discussion_record($discussion); + $context = $this->get_context(); + + return hsuforum_user_can_see_discussion($forumrecord, $discussionrecord, $context, $user); + } + + /** + * Can the user view the content of 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 { + if (!$this->can_view_post_shell($user, $post)) { + return false; + } + + // Return cached can view if possible. + if (isset($this->canviewpostcache[$user->id][$post->get_id()])) { + return $this->canviewpostcache[$user->id][$post->get_id()]; + } + + // Otherwise, check if the user can see this post. + $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(); + $canviewpost = hsuforum_user_can_see_post($forumrecord, $discussionrecord, $postrecord, $user, $coursemodule, false); + + // Then cache the result before returning. + $this->canviewpostcache[$user->id][$post->get_id()] = $canviewpost; + + return $canviewpost; + } + + /** + * Can the user view the post at all? + * In some situations the user can view the shell of a post without being able to view its content. + * + * @param stdClass $user The user to check + * @param post_entity $post The post the user wants to view + * @return bool + * + */ + public function can_view_post_shell(stdClass $user, post_entity $post): bool { + if ($post->is_owned_by_user($user)) { + return true; + } + + if (!$post->is_private_reply()) { + return true; + } + + if ($post->is_private_reply_intended_for_user($user)) { + return true; + } + + return $this->can_view_any_private_reply($user); + } + + /** + * Whether the user can view any private reply in the forum. + * + * @param stdClass $user The user to check + * @return bool + */ + public function can_view_any_private_reply(stdClass $user): bool { + return has_capability('mod/hsuforum:readprivatereplies', $this->get_context(), $user); + } + + /** + * 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; + $mailnow = $post->should_mail_now(); + + 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': + if ($discussion->is_first_post($post)) { + return has_capability('moodle/course:manageactivities', $context, $user); + } + break; + } + + return ($ownpost && $ineditingtime && !$mailnow) || has_capability('mod/hsuforum:editanypost', $context, $user); + } + + /** + * Verifies is the given user can delete a post. + * + * @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 + * @param bool $hasreplies Whether the post has replies + * @return bool + * @throws moodle_exception + */ + public function validate_delete_post(stdClass $user, discussion_entity $discussion, post_entity $post, + bool $hasreplies = false): void { + 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. + throw new moodle_exception('cannotdeletepost', 'hsuforum'); + } + + $context = $this->get_context(); + $ownpost = $post->is_owned_by_user($user); + $ineditingtime = $post->get_age() < $CFG->maxeditingtime; + $mailnow = $post->should_mail_now(); + + if (!($ownpost && $ineditingtime && has_capability('mod/hsuforum:deleteownpost', $context, $user) && !$mailnow || + has_capability('mod/hsuforum:deleteanypost', $context, $user))) { + + throw new moodle_exception('cannotdeletepost', 'hsuforum'); + } + + if ($post->get_total_score()) { + throw new moodle_exception('couldnotdeleteratings', 'rating'); + } + + if ($hasreplies && !has_capability('mod/hsuforum:deleteanypost', $context, $user)) { + throw new moodle_exception('couldnotdeletereplies', 'hsuforum'); + } + } + + + /** + * 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 + * @param bool $hasreplies Whether the post has replies + * @return bool + */ + public function can_delete_post(stdClass $user, discussion_entity $discussion, post_entity $post, + bool $hasreplies = false): bool { + + try { + $this->validate_delete_post($user, $discussion, $post, $hasreplies); + return true; + } catch (moodle_exception $e) { + return false; + } + } + + /** + * 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 { + if ($post->is_private_reply()) { + // It is not possible to create a private discussion. + return false; + } + + 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 { + if ($post->is_private_reply()) { + // It is not possible to reply to a private reply. + return false; + } else if (!$this->can_view_post($user, $discussion, $post)) { + // If the user cannot view the post in the first place, the user should not be able to reply to the post. + return false; + } + + return $this->can_post_in_discussion($user, $discussion); + } + + /** + * Can the user reply privately to the specified post? + * + * @param stdClass $user The user to check + * @param post_entity $post The post the user wants to reply to + * @return bool + */ + public function can_reply_privately_to_post(stdClass $user, post_entity $post): bool { + if ($post->is_private_reply()) { + // You cannot reply privately to a post which is, itself, a private reply. + return false; + } + + return has_capability('mod/hsuforum:postprivatereply', $this->get_context(), $user); + } + + /** + * 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/hsuforum:exportpost', $context, $user) || + ($post->is_owned_by_user($user) && has_capability('mod/hsuforum:exportownpost', $context, $user))); + } + + /** + * Get the hsuforum 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/hsuforum: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); + } + + /** + * Checks whether the user can self enrol into the course. + * Mimics the checks on the add button in deprecatedlib/forum_print_latest_discussions + * + * @param stdClass $user + * @return bool + */ + public function can_self_enrol(stdClass $user): bool { + $canstart = false; + + if ($this->forum->get_type() != 'news') { + if (isguestuser($user) or !isloggedin()) { + $canstart = true; + } + + if (!is_enrolled($this->context) and !is_viewing($this->context)) { + // Allow guests and not-logged-in to see the button - they are prompted to log in after clicking the link, + // Normal users with temporary guest access see this button too, they are asked to enrol instead, + // Do not show the button to users with suspended enrolments here. + $canstart = enrol_selfenrol_available($this->forum->get_course_id()); + } + } + + return $canstart; + } + + /** + * Checks whether the user can export the whole forum (discussions and posts). + * + * @param stdClass $user The user object. + * @return bool True if the user can export the forum or false otherwise. + */ + public function can_export_forum(stdClass $user): bool { + return has_capability('mod/hsuforum:exportforum', $this->get_context(), $user); + } + + /** + * Check whether the supplied grader can grade the gradee. + * + * @param stdClass $grader The user grading + * @param stdClass $gradee The user being graded + * @return bool + */ + public function can_grade(stdClass $grader, stdClass $gradee = null): bool { + if (!has_capability('mod/hsuforum:grade', $this->get_context(), $grader)) { + return false; + } + + return true; + } +} diff --git a/classes/local/renderers/discussion.php b/classes/local/renderers/discussion.php new file mode 100644 index 00000000..0a822dc9 --- /dev/null +++ b/classes/local/renderers/discussion.php @@ -0,0 +1,459 @@ +. + +/** + * Discussion renderer. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\renderers; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\entities\post as post_entity; +use mod_hsuforum\local\entities\sorter as sorter_entity; +use mod_hsuforum\local\factories\entity as entity_factory; +use mod_hsuforum\local\factories\legacy_data_mapper as legacy_data_mapper_factory; +use mod_hsuforum\local\factories\exporter as exporter_factory; +use mod_hsuforum\local\factories\url as url_factory; +use mod_hsuforum\local\factories\vault as vault_factory; +use mod_hsuforum\local\managers\capability as capability_manager; +use mod_hsuforum\local\renderers\posts as posts_renderer; +use forum_portfolio_caller; +use core\output\notification; +use context; +use context_module; +use \core\output\html_writer; +use \core\exception\moodle_exception; +use moodle_page; +use \core\url as moodle_url; +use rating_manager; +use \core\output\renderer_base; +use \core\output\single_button; +use \core\output\single_select; +use stdClass; +use \core\output\url_select; + +require_once($CFG->dirroot . '/mod/hsuforum/lib.php'); +require_once($CFG->dirroot . '/mod/hsuforum/locallib.php'); + +/** + * Discussion renderer class. + * + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class discussion { + /** @var forum_entity $forum The forum that the discussion belongs to */ + private $forum; + /** @var discussion_entity $discussion The discussion entity */ + private $discussion; + /** @var stdClass $discussionrecord Legacy discussion record */ + private $discussionrecord; + /** @var stdClass $forumrecord Legacy forum record */ + private $forumrecord; + /** @var int $displaymode The display mode to render the discussion in */ + private $displaymode; + /** @var \core\output\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 url_factory $urlfactory URL factory */ + private $urlfactory; + /** @var entity_factory $entityfactory Entity factory */ + private $entityfactory; + /** @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; + /** @var callable $postprocessfortemplate Function to process exported posts before template rendering */ + private $postprocessfortemplate; + + /** + * Constructor. + * + * @param forum_entity $forum The forum that the discussion belongs to + * @param discussion_entity $discussion The discussion entity + * @param int $displaymode The display mode to render the discussion in + * @param \core\output\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 url_factory $urlfactory URL factory + * @param entity_factory $entityfactory Entity 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 + * @param callable|null $postprocessfortemplate Post processing for template callback + */ + public function __construct( + forum_entity $forum, + discussion_entity $discussion, + int $displaymode, + \core\output\renderer_base $renderer, + posts_renderer $postsrenderer, + moodle_page $page, + legacy_data_mapper_factory $legacydatamapperfactory, + exporter_factory $exporterfactory, + vault_factory $vaultfactory, + url_factory $urlfactory, + entity_factory $entityfactory, + capability_manager $capabilitymanager, + rating_manager $ratingmanager, + sorter_entity $exportedpostsorter, + moodle_url $baseurl, + array $notifications = [], + callable $postprocessfortemplate = null + ) { + $this->forum = $forum; + $this->discussion = $discussion; + $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->urlfactory = $urlfactory; + $this->entityfactory = $entityfactory; + $this->capabilitymanager = $capabilitymanager; + $this->ratingmanager = $ratingmanager; + $this->notifications = $notifications; + + $this->exportedpostsorter = $exportedpostsorter; + $this->postprocessfortemplate = $postprocessfortemplate; + + $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; + $urlfactory = $this->urlfactory; + $entityfactory = $this->entityfactory; + + // Make sure we can render. + if (!$capabilitymanager->can_view_discussions($user)) { + throw new moodle_exception('noviewdiscussionspermission', 'mod_hsuforum'); + } + + $posts = array_merge([$firstpost], array_values($replies)); + + if ($this->postprocessfortemplate !== null) { + $exporteddiscussion = ($this->postprocessfortemplate) ($this->discussion, $user, $this->forum); + } else { + $exporteddiscussion = $this->get_exported_discussion($user); + } + + $hasanyactions = false; + $hasanyactions = $hasanyactions || $capabilitymanager->can_favourite_discussion($user); + $hasanyactions = $hasanyactions || $capabilitymanager->can_pin_discussions($user); + $hasanyactions = $hasanyactions || $capabilitymanager->can_manage_forum($user); + + $exporteddiscussion = array_merge($exporteddiscussion, [ + 'notifications' => $this->get_notifications($user), + 'html' => [ + 'hasanyactions' => $hasanyactions, + 'posts' => $this->postsrenderer->render($user, [$this->forum], [$this->discussion], $posts), + 'modeselectorform' => $this->get_display_mode_selector_html($displaymode, $user), + 'subscribe' => null, + 'movediscussion' => null, + 'pindiscussion' => null, + 'neighbourlinks' => $this->get_neighbour_links_html(), + 'exportdiscussion' => !empty($CFG->enableportfolios) ? $this->get_export_discussion_html($user) : null + ], + 'settingsselector' => true, + ]); + + $capabilities = (array) $exporteddiscussion['capabilities']; + + if ($capabilities['move']) { + $exporteddiscussion['html']['movediscussion'] = $this->get_move_discussion_html(); + } + + if (!empty($user->id)) { + $loggedinuser = $entityfactory->get_author_from_stdClass($user); + $exporteddiscussion['loggedinuser'] = [ + 'firstname' => $loggedinuser->get_first_name(), + 'fullname' => $loggedinuser->get_full_name(), + 'profileimageurl' => ($urlfactory->get_author_profile_image_url($loggedinuser, null))->out(false) + ]; + } + + $exporteddiscussion['throttlingwarningmsg'] = ''; + $cmrecord = $this->forum->get_course_module_record(); + if (($warningobj = hsuforum_check_throttling($this->forumrecord, $cmrecord)) && $warningobj->canpost) { + $throttlewarnnotification = (new notification( + get_string($warningobj->errorcode, $warningobj->module, $warningobj->additional) + ))->set_show_closebutton(); + $exporteddiscussion['throttlingwarningmsg'] = $throttlewarnnotification->get_message(); + } + + if ($this->displaymode === HSUFORUM_MODE_NESTED_V2) { + $template = 'mod_hsuforum/forum_discussion_nested_v2'; + } else { + $template = 'mod_hsuforum/forum_discussion'; + } + + return $this->renderer->render_from_template($template, $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 + * @param stdClass $user The current user + * @return string + */ + private function get_display_mode_selector_html(int $displaymode, stdClass $user): string { + $baseurl = $this->baseurl; + $select = new \core\output\single_select( + $baseurl, + 'mode', + hsuforum_get_layout_modes(get_user_preferences('forum_useexperimentalui', false, $user)), + $displaymode, + null, + 'mode' + ); + $select->set_label(get_string('displaymode', 'hsuforum'), ['class' => 'accesshide']); + + return $this->renderer->render($select); + } + + /** + * 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['hsuforum'])) { + $forummenu = []; + // Check forum types and eliminate simple discussions. + $forumcheck = $DB->get_records('hsuforum', ['course' => $courseid], '', 'id, type'); + foreach ($modinfo->instances['hsuforum'] as $forumcm) { + if (!$forumcm->uservisible || !has_capability('mod/hsuforum: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/hsuforum/discuss.php?d={$discussion->get_id()}&move=$forumcm->instance&sesskey=".sesskey(); + $forummenu[$section][$sectionname][$url] = format_string($forumcm->name); + } + } + if (!empty($forummenu)) { + $html = '
'; + + $movebutton = get_string('move'); + if ($this->displaymode === HSUFORUM_MODE_NESTED_V2) { + // Move discussion selector will be rendered on the settings drawer. We won't output the button in this mode. + $movebutton = null; + } + $select = new \core\output\url_select($forummenu, '', + ['/mod/hsuforum/discuss.php?d=' . $discussion->get_id() => get_string("movethisdiscussionto", "hsuforum")], + 'forummenu', $movebutton); + $select->set_label(get_string('movethisdiscussionlabel', 'mod_hsuforum'), [ + 'class' => 'sr-only', + ]); + $html .= $this->renderer->render($select); + $html .= "
"; + return $html; + } + } + + return null; + } + + /** + * Get the HTML to render the export discussion button. + * + * @param stdClass $user The user viewing the discussion + * @return string|null + */ + private function get_export_discussion_html(stdClass $user): ?string { + global $CFG; + + if (!$this->capabilitymanager->can_export_discussions($user)) { + return null; + } + + $button = new \portfolio_add_button(); + $button->set_callback_options('forum_portfolio_caller', ['discussionid' => $this->discussion->get_id()], 'mod_hsuforum'); + $button = $button->to_html(PORTFOLIO_ADD_FULL_FORM, get_string('exportdiscussion', 'mod_hsuforum')); + return $button ?: null; + } + + /** + * Get a list of notification HTML to render in the page. + * + * @param stdClass $user The user viewing the discussion + * @return string[] + */ + private function get_notifications($user): array { + $notifications = $this->notifications; + $discussion = $this->discussion; + $forum = $this->forum; + + if ($forum->is_cutoff_date_reached()) { + $notifications[] = (new notification( + get_string('cutoffdatereached', 'hsuforum'), + notification::NOTIFY_INFO + ))->set_show_closebutton(); + } else if ($forum->get_type() != 'single') { + // Due date is already shown at the top of the page for single simple discussion forums. + if ($forum->is_due_date_reached()) { + $notifications[] = (new notification( + get_string('thisforumisdue', 'hsuforum', userdate($forum->get_due_date())), + notification::NOTIFY_INFO + ))->set_show_closebutton(); + } else if ($forum->has_due_date()) { + $notifications[] = (new notification( + get_string('thisforumhasduedate', 'hsuforum', userdate($forum->get_due_date())), + notification::NOTIFY_INFO + ))->set_show_closebutton(); + } + } + + if ($forum->is_discussion_locked($discussion)) { + $notifications[] = (new notification( + get_string('discussionlocked', 'hsuforum'), + notification::NOTIFY_INFO + )) + ->set_extra_classes(['discussionlocked']) + ->set_show_closebutton(); + } + + if ($forum->get_type() == 'qanda') { + if ($this->capabilitymanager->must_post_before_viewing_discussion($user, $discussion)) { + $notifications[] = (new notification( + get_string('qandanotify', 'hsuforum') + ))->set_show_closebutton(true)->set_extra_classes(['mt-3']); + } + } + + if ($forum->has_blocking_enabled()) { + $notifications[] = (new notification( + get_string('thisforumisthrottled', 'hsuforum', [ + 'blockafter' => $forum->get_block_after(), + 'blockperiod' => get_string('secondstotime' . $forum->get_block_period()) + ]), + notification::NOTIFY_INFO + ))->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 = hsuforum_get_discussion_neighbours($coursemodule, $this->discussionrecord, $this->forumrecord); + return $this->renderer->neighbouring_discussion_navigation($neighbours['prev'], $neighbours['next']); + } +} diff --git a/classes/local/renderers/discussion_list.php b/classes/local/renderers/discussion_list.php new file mode 100644 index 00000000..21db256f --- /dev/null +++ b/classes/local/renderers/discussion_list.php @@ -0,0 +1,425 @@ +. + +/** + * Discussion list renderer. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\renderers; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\grades\forum_gradeitem; +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\factories\legacy_data_mapper as legacy_data_mapper_factory; +use mod_hsuforum\local\factories\exporter as exporter_factory; +use mod_hsuforum\local\factories\vault as vault_factory; +use mod_hsuforum\local\factories\url as url_factory; +use mod_hsuforum\local\managers\capability as capability_manager; +use mod_hsuforum\local\renderers\cm_info; +use mod_hsuforum\local\renderers\gradeitem; +use mod_hsuforum\local\vaults\discussion_list as discussion_list_vault; +use \core\output\renderer_base; +use stdClass; +use core\output\notification; +use mod_hsuforum\local\data_mappers\legacy\forum; +use mod_hsuforum\local\factories\builder as builder_factory; + +require_once($CFG->dirroot . '/mod/hsuforum/lib.php'); + +/** + * The discussion list renderer. + * + * @package mod_hsuforum + * @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 \core\output\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; + + /** @var builder_factory $builderfactory Builder factory */ + private $builderfactory; + + /** @var callable $postprocessfortemplate Function to process exported posts before template rendering */ + private $postprocessfortemplate; + + /** @var string $template The template to use when displaying */ + private $template; + + /** @var gradeitem The gradeitem instance associated with this forum */ + private $forumgradeitem; + + /** + * Constructor for a new discussion list renderer. + * + * @param forum_entity $forum The forum entity to be rendered + * @param \core\output\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 builder_factory $builderfactory The factory used to fetch the builder 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 string $template + * @param notification[] $notifications A list of any notifications to be displayed within the page + * @param callable|null $postprocessfortemplate Callback function to process discussion lists for templates + */ + public function __construct( + forum_entity $forum, + \core\output\renderer_base $renderer, + legacy_data_mapper_factory $legacydatamapperfactory, + exporter_factory $exporterfactory, + vault_factory $vaultfactory, + builder_factory $builderfactory, + capability_manager $capabilitymanager, + url_factory $urlfactory, + forum_gradeitem $forumgradeitem, + string $template, + array $notifications = [], + callable $postprocessfortemplate = null + ) { + $this->forum = $forum; + $this->renderer = $renderer; + $this->legacydatamapperfactory = $legacydatamapperfactory; + $this->exporterfactory = $exporterfactory; + $this->vaultfactory = $vaultfactory; + $this->builderfactory = $builderfactory; + $this->capabilitymanager = $capabilitymanager; + + $this->urlfactory = $urlfactory; + $this->notifications = $notifications; + $this->postprocessfortemplate = $postprocessfortemplate; + $this->template = $template; + $this->forumgradeitem = $forumgradeitem; + + $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 + * @param int $displaymode The discussion display mode + * @param bool $enablediscussioncreation To show the discussion button. + * @return string The rendered content for display + */ + public function render( + stdClass $user, + \cm_info $cm, + ?int $groupid, + ?int $sortorder, + ?int $pageno, + ?int $pagesize, + int $displaymode = null, + bool $enablediscussioncreation = true + ): string { + global $PAGE; + + $forum = $this->forum; + $course = $forum->get_course_record(); + + $forumexporter = $this->exporterfactory->get_forum_exporter( + $user, + $this->forum, + $groupid + ); + + $pagesize = $this->get_page_size($pagesize); + $pageno = $this->get_page_number($pageno); + + // Count all forum discussion posts. + $alldiscussionscount = mod_hsuforum_count_all_discussions($forum, $user, $groupid); + + // Get all forum discussion summaries. + $discussions = mod_hsuforum_get_discussion_summaries($forum, $user, $groupid, $sortorder, $pageno, $pagesize); + + $capabilitymanager = $this->capabilitymanager; + $hasanyactions = false; + $hasanyactions = $hasanyactions || $capabilitymanager->can_favourite_discussion($user); + $hasanyactions = $hasanyactions || $capabilitymanager->can_pin_discussions($user); + $hasanyactions = $hasanyactions || $capabilitymanager->can_manage_forum($user); + + $forumview = [ + 'forum' => (array) $forumexporter->export($this->renderer), + 'contextid' => $forum->get_context()->id, + 'cmid' => $cm->id, + 'groupid' => $groupid, + 'name' => format_string($forum->get_name()), + 'courseid' => $course->id, + 'coursename' => format_string($course->shortname), + 'experimentaldisplaymode' => $displaymode == HSUFORUM_MODE_NESTED_V2, + 'gradingcomponent' => $this->forumgradeitem->get_grading_component_name(), + 'gradingcomponentsubtype' => $this->forumgradeitem->get_grading_component_subtype(), + 'sendstudentnotifications' => $forum->should_notify_students_default_when_grade_for_forum(), + 'gradeonlyactiveusers' => $this->forumgradeitem->should_grade_only_active_users(), + 'hasanyactions' => $hasanyactions, + 'groupchangemenu' => groups_print_activity_menu( + $cm, + $this->urlfactory->get_forum_view_url_from_forum($forum), + true + ), + 'hasmore' => ($alldiscussionscount > $pagesize), + 'notifications' => $this->get_notifications($user, $groupid), + 'settings' => [ + 'excludetext' => true, + 'togglemoreicon' => true, + 'excludesubscription' => true + ], + 'totaldiscussioncount' => $alldiscussionscount, + 'userid' => $user->id, + 'visiblediscussioncount' => count($discussions), + 'enablediscussioncreation' => $enablediscussioncreation, + ]; + + if ($forumview['forum']['capabilities']['create']) { + $forumview['newdiscussionhtml'] = $this->get_discussion_form($user, $cm, $groupid); + } + + if (!$discussions) { + return $this->renderer->render_from_template($this->template, $forumview); + } + + if ($this->postprocessfortemplate !== null) { + // We've got some post processing to do! + $exportedposts = ($this->postprocessfortemplate) ($discussions, $user, $forum); + } + + $baseurl = new \core\url($PAGE->url, ['o' => $sortorder, 's' => $pagesize]); + + $forumview = array_merge( + $forumview, + [ + 'pagination' => $this->renderer->render(new \core\output\paging_bar($alldiscussionscount, $pageno, $pagesize, $baseurl, 'p')), + ], + $exportedposts + ); + + $firstdiscussion = reset($discussions); + $forumview['firstgradeduserid'] = $firstdiscussion->get_latest_post_author()->get_id(); + + return $this->renderer->render_from_template($this->template, $forumview); + } + + /** + * Add new discussion button to the action bar for tertiary nav. + * + * @param stdClass $user The user object. + * @param int|null $groupid The group id. + * @return string rendered HTML string + */ + public function render_new_discussion(stdClass $user, ?int $groupid): string { + $forumexporter = $this->exporterfactory->get_forum_exporter( + $user, + $this->forum, + $groupid + ); + + $forumview = [ + 'forum' => (array) $forumexporter->export($this->renderer), + ]; + + return $this->renderer->render_from_template('mod_hsuforum/forum_new_discussion_actionbar', $forumview); + } + + /** + * Get the mod_hsuforum_post_form. This is the default boiler plate from mod_hsuforum/post_form.php with the inpage flag caveat + * + * @param stdClass $user The user the form is being generated for + * @param \cm_info $cm + * @param int $groupid The groupid if any + * + * @return string The rendered html + */ + private function get_discussion_form(stdClass $user, \cm_info $cm, ?int $groupid) { + $forum = $this->forum; + $forumrecord = $this->legacydatamapperfactory->get_forum_data_mapper()->to_legacy_object($forum); + $modcontext = \context_module::instance($cm->id); + $coursecontext = \context_course::instance($forum->get_course_id()); + $post = (object) [ + 'course' => $forum->get_course_id(), + 'forum' => $forum->get_id(), + 'discussion' => 0, // Ie discussion # not defined yet. + 'parent' => 0, + 'subject' => '', + 'userid' => $user->id, + 'message' => '', + 'messageformat' => editors_get_preferred_format(), + 'messagetrust' => 0, + 'groupid' => $groupid, + ]; + $thresholdwarning = hsuforum_check_throttling($forumrecord, $cm); + + $formparams = array( + 'course' => $forum->get_course_record(), + 'cm' => $cm, + 'coursecontext' => $coursecontext, + 'modcontext' => $modcontext, + 'forum' => $forumrecord, + 'post' => $post, + 'subscribe' => \mod_hsuforum\subscriptions::is_subscribed($user->id, $forumrecord, + null, $cm), + 'thresholdwarning' => $thresholdwarning, + 'inpagereply' => true, + 'edit' => 0 + ); + $posturl = new \core\url('/mod/hsuforum/post.php'); + $mformpost = new \mod_hsuforum_post_form($posturl, $formparams, 'post', '', array('id' => 'mformforum')); + $discussionsubscribe = \mod_hsuforum\subscriptions::get_user_default_subscription($forumrecord, $coursecontext, $cm, null); + + $params = array('reply' => 0, 'forum' => $forumrecord->id, 'edit' => 0) + + (isset($post->groupid) ? array('groupid' => $post->groupid) : array()) + + array( + 'userid' => $post->userid, + 'parent' => $post->parent, + 'discussion' => $post->discussion, + 'course' => $forum->get_course_id(), + 'discussionsubscribe' => $discussionsubscribe + ); + $mformpost->set_data($params); + + return $mformpost->render(); + } + + /** + * 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; + } + + /** + * 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; + $capabilitymanager = $this->capabilitymanager; + + if ($forum->is_cutoff_date_reached()) { + $notifications[] = (new notification( + get_string('cutoffdatereached', 'hsuforum'), + notification::NOTIFY_INFO + ))->set_show_closebutton(); + } + + if ($forum->has_blocking_enabled()) { + $notifications[] = (new notification( + get_string('thisforumisthrottled', 'hsuforum', [ + 'blockafter' => $forum->get_block_after(), + 'blockperiod' => get_string('secondstotime' . $forum->get_block_period()) + ]), + notification::NOTIFY_INFO + ))->set_show_closebutton(); + } + + if ($forum->is_in_group_mode() && !$capabilitymanager->can_access_all_groups($user)) { + if ($groupid === null) { + if (!$capabilitymanager->can_post_to_my_groups($user)) { + $notifications[] = (new notification( + get_string('cannotadddiscussiongroup', 'mod_hsuforum'), + \core\output\notification::NOTIFY_WARNING + ))->set_show_closebutton(); + } else { + $notifications[] = (new notification( + get_string('cannotadddiscussionall', 'mod_hsuforum'), + \core\output\notification::NOTIFY_WARNING + ))->set_show_closebutton(); + } + } else if (!$capabilitymanager->can_access_group($user, $groupid)) { + $notifications[] = (new notification( + get_string('cannotadddiscussion', 'mod_hsuforum'), + \core\output\notification::NOTIFY_WARNING + ))->set_show_closebutton(); + } + } + + if ('qanda' === $forum->get_type() && !$capabilitymanager->can_manage_forum($user)) { + $notifications[] = (new notification( + get_string('qandanotify', 'hsuforum'), + notification::NOTIFY_INFO + ))->set_show_closebutton()->set_extra_classes(['mt-3']); + } + + if ('eachuser' === $forum->get_type()) { + $notifications[] = (new notification( + get_string('allowsdiscussions', 'hsuforum'), + notification::NOTIFY_INFO) + )->set_show_closebutton(); + } + + return array_map(function($notification) { + return $notification->export_for_template($this->renderer); + }, $notifications); + } +} diff --git a/classes/local/renderers/posts.php b/classes/local/renderers/posts.php new file mode 100644 index 00000000..f45c3e88 --- /dev/null +++ b/classes/local/renderers/posts.php @@ -0,0 +1,109 @@ +. + +/** + * Posts renderer. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\renderers; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\builders\exported_posts as exported_posts_builder; +use \core\output\renderer_base; +use stdClass; + +/** + * Posts renderer class. + * + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class posts { + /** @var \core\output\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 \core\output\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( + \core\output\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)] + ); + } +} diff --git a/classes/local/vaults/author.php b/classes/local/vaults/author.php new file mode 100644 index 00000000..49bf2847 --- /dev/null +++ b/classes/local/vaults/author.php @@ -0,0 +1,118 @@ +. + +/** + * Author vault class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\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 + * + * @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 + * @param int|null $userid The user ID + * @return string + */ + protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null, ?int $userid = 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); + } + + /** + * Get the context ids for a set of author ids. The results are indexed + * by the author id. + * + * @param int[] $authorids The list of author ids to fetch. + * @return int[] Results indexed by author id. + */ + public function get_context_ids_for_author_ids(array $authorids): array { + $db = $this->get_db(); + [$insql, $params] = $db->get_in_or_equal($authorids); + $sql = "SELECT instanceid, id FROM {context} WHERE contextlevel = ? AND instanceid {$insql}"; + $records = $db->get_records_sql($sql, array_merge([CONTEXT_USER], $params)); + return array_reduce($authorids, function($carry, $id) use ($records) { + $carry[$id] = isset($records[$id]) ? (int) $records[$id]->id : null; + return $carry; + }, []); + } +} diff --git a/classes/local/vaults/db_table_vault.php b/classes/local/vaults/db_table_vault.php new file mode 100644 index 00000000..cf634f15 --- /dev/null +++ b/classes/local/vaults/db_table_vault.php @@ -0,0 +1,187 @@ +. + +/** + * Abstract class for loading records from the DB. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\vaults; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\factories\entity as entity_factory; +use moodle_database; + +/** + * Abstract class for loading records from the DB. + * + * @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; + /** @var object $legacyfactory Entity->legacy factory */ + private $legacyfactory; + + /** + * Constructor. + * + * @param moodle_database $db A moodle database + * @param entity_factory $entityfactory Entity factory + * @param object $legacyfactory Legacy factory + */ + public function __construct( + moodle_database $db, + entity_factory $entityfactory, + $legacyfactory + ) { + $this->db = $db; + $this->entityfactory = $entityfactory; + $this->legacyfactory = $legacyfactory; + } + + /** + * 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 + * @param object|null $user The user object + * @return string + */ + abstract protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null, + ?int $userid = 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; + } + + /** + * Get the legacy factory + * + * @return object + */ + protected function get_legacy_factory() { + return $this->legacyfactory; + } + + /** + * 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); + } +} diff --git a/classes/local/vaults/discussion.php b/classes/local/vaults/discussion.php new file mode 100644 index 00000000..3847486d --- /dev/null +++ b/classes/local/vaults/discussion.php @@ -0,0 +1,175 @@ +. + +/** + * Discussion vault class. + * + * @package mod_hsuforum + + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\vaults; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\container; +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\entities\discussion as discussion_entity; +use mod_hsuforum\local\vaults\db_table_vault; + +/** + * 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 + * + * @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 = 'hsuforum_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 + * @param int|null $userid The user ID + * @return string + */ + protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null, ?int $userid = 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 all discussions in the specified hsuforum. + * + * @param forum_entity $forum + * @return array + */ + public function get_all_discussions_in_forum(forum_entity $forum, string $sort = null): ?array { + global $USER; + $options = ['forum' => $forum->get_id()]; + + $managerfactory = container::get_manager_factory(); + $capabilitymanager = $managerfactory->get_capability_manager($forum); + + $select = "forum = :forum"; + + if ($forum->is_in_group_mode() && !$capabilitymanager->can_access_all_groups($USER)) { + $allowedgroups = groups_get_activity_allowed_groups($forum->get_course_module_record()); + $allowedgroups = implode(",", array_keys($allowedgroups)); + if (!$allowedgroups) { + return []; + } + $select .= " AND groupid IN ($allowedgroups)"; + } + $records = $this->get_db()->get_records_select(self::TABLE, $select, $options, $sort ?? ''); + + return $this->transform_db_records_to_entities($records); + } + + /** + * 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; + } + + /** + * Get the last discussion in the specified forum. + * + * @param forum_entity $forum + * @return discussion_entity|null + */ + public function get_last_discussion_in_forum(forum_entity $forum): ?discussion_entity { + $records = $this->get_db()->get_records(self::TABLE, [ + 'forum' => $forum->get_id(), + ], 'timemodified DESC', '*', 0, 1); + + $records = $this->transform_db_records_to_entities($records); + return count($records) ? array_shift($records) : null; + } + + /** + * Get the count of the discussions in the specified forum. + * + * @param forum_entity $forum + * @return int + */ + public function get_count_discussions_in_forum(forum_entity $forum): ?int { + return $this->get_db()->count_records(self::TABLE, [ + 'forum' => $forum->get_id()]); + } + + /** + * Update the discussion + * + * @param discussion_entity $discussion + * @return discussion_entity|null + */ + public function update_discussion(discussion_entity $discussion): ?discussion_entity { + $discussionrecord = $this->get_legacy_factory()->to_legacy_object($discussion); + if ($this->get_db()->update_record('hsuforum_discussions', $discussionrecord)) { + $records = $this->transform_db_records_to_entities([$discussionrecord]); + + return count($records) ? array_shift($records) : null; + } + + return null; + } +} diff --git a/classes/local/vaults/discussion_list.php b/classes/local/vaults/discussion_list.php new file mode 100644 index 00000000..4451fed3 --- /dev/null +++ b/classes/local/vaults/discussion_list.php @@ -0,0 +1,563 @@ +. + +/** + * Vault class for a discussion list. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\vaults; + +defined('MOODLE_INTERNAL') || die(); + +use core_group\output\group_details; +use mod_hsuforum\local\vaults\db_table_vault; +use mod_hsuforum\local\vaults\preprocessors\extract_record as extract_record_preprocessor; +use mod_hsuforum\local\vaults\preprocessors\extract_user as extract_user_preprocessor; +use mod_hsuforum\local\renderers\discussion_list as discussion_list_renderer; +use core\dml\table as dml_table; +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_hsuforum + * @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 = 'hsuforum_discussions'; + /** Alias for first author id */ + private const FIRST_AUTHOR_ID_ALIAS = 'fauserpictureid'; + /** Alias for author fields */ + private const FIRST_AUTHOR_ALIAS = 'fauserrecord'; + /** Alias for last author id */ + private const LATEST_AUTHOR_ID_ALIAS = 'lauserpictureid'; + /** Alias for last author fields */ + private const LATEST_AUTHOR_ALIAS = 'lauserrecord'; + /** Default limit */ + public const PAGESIZE_DEFAULT = 100; + + /** Sort by newest first */ + public const SORTORDER_LASTPOST_DESC = 1; + /** Sort by oldest first */ + public const SORTORDER_LASTPOST_ASC = 2; + /** Sort by created desc */ + public const SORTORDER_CREATED_DESC = 3; + /** Sort by created asc */ + public const SORTORDER_CREATED_ASC = 4; + /** Sort by number of replies desc */ + public const SORTORDER_REPLIES_DESC = 5; + /** Sort by number of replies desc */ + public const SORTORDER_REPLIES_ASC = 6; + /** Sort by discussion name desc */ + public const SORTORDER_DISCUSSION_DESC = 7; + /** Sort by discussion name asc */ + public const SORTORDER_DISCUSSION_ASC = 8; + /** Sort by discussion starter's name desc */ + public const SORTORDER_STARTER_DESC = 9; + /** Sort by discussion starter's name asc */ + public const SORTORDER_STARTER_ASC = 10; + /** Sort by group name desc */ + public const SORTORDER_GROUP_DESC = 11; + /** Sort by group name asc */ + public const SORTORDER_GROUP_ASC = 12; + + /** + * Get the table alias. + * + * @return string + */ + protected function get_table_alias(): string { + return 'd'; + } + + /** + * Get the favourite table alias + * + * @return string + */ + protected function get_favourite_alias(): string { + return 'favalias'; + } + + /** + * 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 + * @param int|null $userid The ID of the user we are performing this query for + * + * @return string + */ + protected function generate_get_records_sql(string $wheresql = null, ?string $sortsql = null, ?int $userid = null): string { + $alias = $this->get_table_alias(); + + $includefavourites = $userid ? true : false; + + $favsql = ''; + if ($includefavourites) { + list($favsql, $favparams) = $this->get_favourite_sql($userid); + foreach ($favparams as $key => $param) { + $favsql = str_replace(":$key", "'$param'", $favsql); + } + } + + // Fetch: + // - Discussion + // - First post + // - Author + // - Most recent editor. + $thistable = new dml_table(self::TABLE, $alias, $alias); + $posttable = new dml_table('hsuforum_posts', 'fp', 'p_'); + $userfieldsapi = \core_user\fields::for_userpic()->including('deleted'); + $firstauthorfields = $userfieldsapi->get_sql('fa', false, + self::FIRST_AUTHOR_ALIAS, self::FIRST_AUTHOR_ID_ALIAS, false)->selects; + $latestuserfields = $userfieldsapi->get_sql('la', false, + self::LATEST_AUTHOR_ALIAS, self::LATEST_AUTHOR_ID_ALIAS, false)->selects; + + $fields = implode(', ', [ + $thistable->get_field_select(), + $posttable->get_field_select(), + $firstauthorfields, + $latestuserfields, + ]); + + $sortkeys = [ + $this->get_sort_order(self::SORTORDER_REPLIES_DESC, $includefavourites), + $this->get_sort_order(self::SORTORDER_REPLIES_ASC, $includefavourites) + ]; + $issortbyreplies = in_array($sortsql, $sortkeys); + + $tables = $thistable->get_from_sql(); + $tables .= ' JOIN ' . $posttable->get_from_sql() . ' ON fp.id = ' . $alias . '.firstpost'; + $tables .= ' JOIN {user} fa ON fa.id = fp.userid'; + $tables .= ' JOIN {user} la ON la.id = ' . $alias . '.usermodified'; + $tables .= $favsql; + + if ($issortbyreplies) { + // Join the discussion replies. + $tables .= ' JOIN ( + SELECT rd.id, COUNT(rp.id) as replycount + FROM {hsuforum_discussions} rd + LEFT JOIN {hsuforum_posts} rp + ON rp.discussion = rd.id AND rp.id != rd.firstpost + GROUP BY rd.id + ) r ON d.id = r.id'; + } + + $groupsortorders = [ + $this->get_sort_order(self::SORTORDER_GROUP_DESC, $includefavourites), + $this->get_sort_order(self::SORTORDER_GROUP_ASC, $includefavourites) + ]; + $sortbygroup = in_array($sortsql, $groupsortorders); + if ($sortbygroup) { + $groupstable = new dml_table('groups', 'g', 'g'); + $fields .= ', ' . $groupstable->get_field_select(); + // Join groups. + $tables .= 'LEFT JOIN {groups} g ON g.id = d.groupid'; + } + + $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(self::TABLE, $this->get_table_alias()), + 'firstpost' => new extract_record_preprocessor('hsuforum_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 field to sort by. + * + * @param int|null $sortmethod + * @return string + */ + protected function get_keyfield(?int $sortmethod): string { + global $CFG; + + switch ($sortmethod) { + case self::SORTORDER_CREATED_DESC: + case self::SORTORDER_CREATED_ASC: + return 'fp.created'; + case self::SORTORDER_REPLIES_DESC: + case self::SORTORDER_REPLIES_ASC: + return 'replycount'; + case self::SORTORDER_DISCUSSION_DESC: + case self::SORTORDER_DISCUSSION_ASC: + return 'dname'; + case self::SORTORDER_STARTER_DESC: + case self::SORTORDER_STARTER_ASC: + // We'll sort by the first name field of the discussion starter's name. + + // Let's get the full name display config first. + $nameformat = $CFG->fullnamedisplay; + if ($CFG->fullnamedisplay === 'language') { + $nameformat = get_string('fullnamedisplay', '', (object)['firstname' => 'firstname', 'lastname' => 'lastname']); + } + // Fetch all the available user name fields. + $availablefields = order_in_string(\core_user\fields::get_name_fields(), $nameformat); + // We'll default to the first name if there's no available name field. + $returnfield = 'firstname'; + if (!empty($availablefields)) { + // Use the first name field. + $returnfield = reset($availablefields); + } + return 'fauserrecord' . $returnfield; + case self::SORTORDER_GROUP_DESC: + case self::SORTORDER_GROUP_ASC: + return 'gname'; + default: + global $CFG; + $alias = $this->get_table_alias(); + $field = "{$alias}.timemodified"; + if (!empty($CFG->forum_enabletimedposts)) { + return "CASE WHEN {$field} < {$alias}.timestart THEN {$alias}.timestart ELSE {$field} END"; + } + return $field; + } + } + + /** + * Get the sort direction. + * + * @param int|null $sortmethod + * @return string + */ + protected function get_sort_direction(?int $sortmethod): string { + switch ($sortmethod) { + case self::SORTORDER_LASTPOST_ASC: + case self::SORTORDER_CREATED_ASC: + case self::SORTORDER_REPLIES_ASC: + case self::SORTORDER_DISCUSSION_ASC: + case self::SORTORDER_STARTER_ASC: + case self::SORTORDER_GROUP_ASC: + return "ASC"; + case self::SORTORDER_LASTPOST_DESC: + case self::SORTORDER_CREATED_DESC: + case self::SORTORDER_REPLIES_DESC: + case self::SORTORDER_DISCUSSION_DESC: + case self::SORTORDER_STARTER_DESC: + case self::SORTORDER_GROUP_DESC: + default: + return "DESC"; + } + } + + /** + * Get the sort order SQL for a sort method. + * + * @param int|null $sortmethod + * @param bool|null $includefavourites + * @return string + */ + private function get_sort_order(?int $sortmethod, bool $includefavourites = true): string { + + $alias = $this->get_table_alias(); + // TODO consider user favourites... + $keyfield = $this->get_keyfield($sortmethod); + $direction = $this->get_sort_direction($sortmethod); + + $favouritesort = ''; + if ($includefavourites) { + $favalias = $this->get_favourite_alias(); + // Since we're joining on the favourite table any discussion that isn't favourited will have + // null in the favourite columns. Nulls behave differently in the sorting for different databases. + // We can ensure consistency between databases by explicitly deprioritising any null favourite field + // using a case statement. + $favouritesort = ", CASE WHEN {$favalias}.id IS NULL THEN 0 ELSE 1 END DESC"; + // After the null favourite fields are deprioritised and appear below the favourited discussions we + // need to order the favourited discussions by id so that the most recently favourited discussions + // appear at the top of the list. + $favouritesort .= ", {$favalias}.itemtype DESC"; + } + + return "{$alias}.pinned DESC $favouritesort , {$keyfield} {$direction}, {$alias}.id {$direction}"; + } + + /** + * Fetch any required SQL to respect timed posts. + * + * @param bool $includehiddendiscussions Whether to include hidden discussions or not + * @param int|null $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, + ]); + + $includefavourites = $includepostsforuser ? true : false; + $sql = $this->generate_get_records_sql($wheresql, $this->get_sort_order($sortorder, $includefavourites), + $includepostsforuser); + $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, + ]); + + $includefavourites = $includepostsforuser ? true : false; + $sql = $this->generate_get_records_sql($wheresql, $this->get_sort_order($sortorder, $includefavourites), + $includepostsforuser); + $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}.hsuforum = :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}.hsuforum = :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); + } + + /** + * Get the standard favouriting sql. + * + * @param int $userid The ID of the user we are getting the sql for + * @return [$sql, $params] An array comprising of the sql and any associated params + */ + private function get_favourite_sql(int $userid): array { + + $usercontext = \context_user::instance($userid); + $alias = $this->get_table_alias(); + $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext); + list($favsql, $favparams) = $ufservice->get_join_sql_by_type('mod_hsuforum', 'discussions', + $this->get_favourite_alias(), "$alias.id"); + + return [$favsql, $favparams]; + } +} diff --git a/classes/local/vaults/forum.php b/classes/local/vaults/forum.php new file mode 100644 index 00000000..200fdbd7 --- /dev/null +++ b/classes/local/vaults/forum.php @@ -0,0 +1,198 @@ +. + +/** + * Hsuforum vault class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\vaults; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\vaults\db_table_vault; +use mod_hsuforum\local\vaults\preprocessors\extract_context as extract_context_preprocessor; +use mod_hsuforum\local\vaults\preprocessors\extract_record as extract_record_preprocessor; +use core\dml\table as dml_table; +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 + * + * @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 = 'hsuforum'; + + /** + * 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 + * @param int|null $userid The user ID + * @return string + */ + protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null, ?int $userid = null): string { + $db = $this->get_db(); + $alias = $this->get_table_alias(); + + $thistable = new dml_table(self::TABLE, $alias, $alias); + $cmtable = new dml_table('course_modules', 'cm', 'cm_'); + $coursetable = new dml_table('course', 'c', 'c_'); + + $fields = implode(', ', [ + $thistable->get_field_select(), + context_helper::get_preload_record_columns_sql('ctx'), + $cmtable->get_field_select(), + $coursetable->get_field_select(), + ]); + + $tables = $thistable->get_from_sql(); + $tables .= " JOIN {modules} m ON m.name = 'hsuforum'"; + $tables .= " JOIN " . $cmtable->get_from_sql() . " 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 " . $coursetable->get_from_sql() . " 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(self::TABLE, $this->get_table_alias()), + 'course_module' => new extract_record_preprocessor('course_modules', 'cm_'), + 'course' => new extract_record_preprocessor('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); + } + + /** + * Get the forum entity for the given post id. + * + * @param int $id The course module id + * @return forum_entity|null + */ + public function get_from_post_id(int $id): ?forum_entity { + $alias = $this->get_table_alias(); + $thistable = new dml_table(self::TABLE, $alias, $alias); + $coursemoduletable = new dml_table('course_modules', 'cm', 'cm_'); + $coursetable = new dml_table('course', 'c', 'c_'); + + $tablefields = $thistable->get_field_select(); + $coursemodulefields = $coursemoduletable->get_field_select(); + $coursefields = $coursetable->get_field_select(); + + $fields = implode(', ', [ + $tablefields, + context_helper::get_preload_record_columns_sql('ctx'), + $coursemodulefields, + $coursefields, + ]); + + $tables = "{hsuforum_posts} p"; + $tables .= " JOIN {hsuforum_discussions} d ON d.id = p.discussion"; + $tables .= ' JOIN {' . self::TABLE . "} {$alias} ON {$alias}.id = d.forum"; + $tables .= " JOIN {modules} m ON m.name = 'hsuforum'"; + $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"; + + $sql = "SELECT {$fields} FROM {$tables} WHERE p.id = :postid"; + $records = $this->get_db()->get_records_sql($sql, [ + 'postid' => $id, + ]); + + $records = $this->transform_db_records_to_entities($records); + return count($records) ? array_shift($records) : null; + } +} diff --git a/classes/local/vaults/post.php b/classes/local/vaults/post.php new file mode 100644 index 00000000..f8d475ed --- /dev/null +++ b/classes/local/vaults/post.php @@ -0,0 +1,550 @@ +. + +/** + * Post vault class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\vaults; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\entities\forum as forum_entity; +use mod_hsuforum\local\entities\post as post_entity; +use mod_hsuforum\local\factories\entity as entity_factory; +use mod_hsuforum\local\vaults\db_table_vault; +use stdClass; + +/** + * Post vault class. + * + * This should be the only place that accessed the database. + * + * This class should not return any objects other than post_entity objects. The class + * may contain some utility count methods which return integers. + * + * This uses the repository pattern. See: + * https://designpatternsphp.readthedocs.io/en/latest/More/Repository/README.html + * + * @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 = 'hsuforum_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 + * @param int|null $userid The user ID + * @return string + */ + protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null, ?int $userid = 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 stdClass $user The user to check the unread count for + * @param int $discussionid The discussion to load posts for + * @param bool $canseeprivatereplies Whether this user can see all private replies or not + * @param string $orderby Order the results + * @return post_entity[] + */ + public function get_from_discussion_id( + stdClass $user, + int $discussionid, + bool $canseeprivatereplies, + string $orderby = 'created ASC' + ): array { + return $this->get_from_discussion_ids($user, [$discussionid], $canseeprivatereplies, $orderby); + } + + /** + * Get the list of posts for the given discussions. + * + * @param stdClass $user The user to load posts for. + * @param int[] $discussionids The list of discussion ids to load posts for + * @param bool $canseeprivatereplies Whether this user can see all private replies or not + * @param string $orderby Order the results + * @return post_entity[] + */ + public function get_from_discussion_ids( + stdClass $user, + array $discussionids, + bool $canseeprivatereplies, + string $orderby = '' + ): array { + if (empty($discussionids)) { + return []; + } + + return $this->get_from_filters($user, ['discussionids' => $discussionids], $canseeprivatereplies, $orderby); + } + + /** + * The method returns posts based on a set of filters. + * + * @param stdClass $user Only used when restricting private replies + * @param array $filters Export filters, valid filters are: + * + * 'discussionids' => array of discussion ids eg [1,2,3] + * 'userids' => array of user ids eg [1,2,3] + * 'from' => timestamp to filter posts from this date. + * 'to' => timestamp to filter posts till this date. + * + * @param bool $canseeprivatereplies Whether this user can see all private replies or not + * @param string $orderby Order the results + * @return post_entity[] + */ + public function get_from_filters( + stdClass $user, + array $filters, + bool $canseeprivatereplies, + string $orderby = '' + ): array { + if (count($filters) == 0) { + return []; + } + $wheresql = []; + $params = []; + $alias = $this->get_table_alias(); + + // Filter by discussion ids. + if (!empty($filters['discussionids'])) { + list($indiscussionssql, $indiscussionsparams) = $this->get_db()->get_in_or_equal($filters['discussionids'], + SQL_PARAMS_NAMED); + $wheresql[] = "{$alias}.discussion {$indiscussionssql}"; + $params += $indiscussionsparams; + } + + // Filter by user ids. + if (!empty($filters['userids'])) { + list($inuserssql, $inusersparams) = $this->get_db()->get_in_or_equal($filters['userids'], + SQL_PARAMS_NAMED); + $wheresql[] = "{$alias}.userid {$inuserssql}"; + $params += $inusersparams; + } + + // Filter posts by from and to dates. + if (isset($filters['from'])) { + $wheresql[] = "{$alias}.created >= :from"; + $params['from'] = $filters['from']; + } + + if (isset($filters['to'])) { + $wheresql[] = "{$alias}.created < :to"; + $params['to'] = $filters['to']; + } + + // We need to build the WHERE here, because get_private_reply_sql returns the query with the AND clause. + $wheresql = implode(' AND ', $wheresql); + + // Build private replies sql. + [ + 'where' => $privatewhere, + 'params' => $privateparams, + ] = $this->get_private_reply_sql($user, $canseeprivatereplies); + $wheresql .= "{$privatewhere}"; + $params += $privateparams; + + if ($orderby) { + $orderbysql = $alias . '.' . $orderby; + } else { + $orderbysql = ''; + } + + $sql = $this->generate_get_records_sql($wheresql, $orderbysql); + $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 stdClass $user The user to check the unread count for + * @param post_entity $post The post to load replies for + * @param bool $canseeprivatereplies Whether this user can see all private replies or not + * @param string $orderby How to order the replies + * @return post_entity[] + */ + public function get_replies_to_post( + stdClass $user, + post_entity $post, + bool $canseeprivatereplies, + string $orderby = 'created ASC' + ): array { + $alias = $this->get_table_alias(); + + [ + 'where' => $privatewhere, + 'params' => $privateparams, + ] = $this->get_private_reply_sql($user, $canseeprivatereplies); + + $params = array_merge([ + 'discussionid' => $post->get_discussion_id(), + 'created' => $post->get_time_created(), + 'excludepostid' => $post->get_id(), + ], $privateparams); + + // 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). + // We also filter to remove private replies if the user cannot vie them. + $wheresql = "{$alias}.discussion = :discussionid + AND {$alias}.created >= :created {$privatewhere} + AND {$alias}.id != :excludepostid"; + $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 stdClass $user The user to check the unread count for + * @param int[] $discussionids The list of discussions to fetch counts for + * @param bool $canseeprivatereplies Whether this user can see all private replies or not + * @return int[] The number of replies for each discussion returned in an associative array + */ + public function get_reply_count_for_discussion_ids(stdClass $user, array $discussionids, bool $canseeprivatereplies): array { + if (empty($discussionids)) { + return []; + } + + list($insql, $params) = $this->get_db()->get_in_or_equal($discussionids, SQL_PARAMS_NAMED); + + [ + 'where' => $privatewhere, + 'params' => $privateparams, + ] = $this->get_private_reply_sql($user, $canseeprivatereplies); + + $sql = "SELECT discussion, COUNT(1) + FROM {" . self::TABLE . "} p + WHERE p.discussion {$insql} AND p.parent > 0 {$privatewhere} + GROUP BY discussion"; + + return $this->get_db()->get_records_sql_menu($sql, array_merge($params, $privateparams)); + } + + /** + * Get a mapping of replies to the specified discussions. + * + * @param stdClass $user The user to check the unread count for + * @param int $postid The post to collect replies to + * @param int $discussionid The list of discussions to fetch counts for + * @param bool $canseeprivatereplies Whether this user can see all private replies or not + * @return int The number of replies for each discussion returned in an associative array + */ + public function get_reply_count_for_post_id_in_discussion_id( + stdClass $user, int $postid, int $discussionid, bool $canseeprivatereplies): int { + [ + 'where' => $privatewhere, + 'params' => $privateparams, + ] = $this->get_private_reply_sql($user, $canseeprivatereplies); + + $alias = $this->get_table_alias(); + $table = self::TABLE; + + $sql = "SELECT {$alias}.id, {$alias}.parent + FROM {{$table}} {$alias} + WHERE p.discussion = :discussionid {$privatewhere}"; + + $postparents = $this->get_db()->get_records_sql_menu($sql, array_merge([ + 'discussionid' => $discussionid, + ], $privateparams)); + + return $this->count_children_from_parent_recursively($postparents, $postid); + } + + /** + * Count the children whose parent matches the current record recursively. + * + * @param array $postparents The full mapping of posts. + * @param int $postid The ID to check for + * @return int $count + */ + private function count_children_from_parent_recursively(array $postparents, int $postid): int { + if (!isset($postparents[$postid])) { + // Post not found at all. + return 0; + } + + $count = 0; + foreach ($postparents as $pid => $parentid) { + if ($postid == $parentid) { + $count += $this->count_children_from_parent_recursively($postparents, $pid) + 1; + } + } + + return $count; + } + + /** + * 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 + * @param bool $canseeprivatereplies Whether this user can see all private replies or not + * @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, bool $canseeprivatereplies): array { + global $CFG; + + if (empty($discussionids)) { + return []; + } + + [ + 'where' => $privatewhere, + 'params' => $privateparams, + ] = $this->get_private_reply_sql($user, $canseeprivatereplies); + + $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 {hsuforum_read} r ON r.postid = p.id AND r.userid = :userid + WHERE p.discussion {$insql} AND p.modified > :cutofftime AND r.id IS NULL {$privatewhere} + 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, array_merge($params, $privateparams)); + } + + /** + * Get a mapping of the most recent post record in each discussion based on post creation time. + * + * @param stdClass $user + * @param array $discussionids + * @param bool $canseeprivatereplies + * @return array + * @throws \core\exception\coding_exception + * @throws \dml_exception + */ + public function get_latest_posts_for_discussion_ids( + stdClass $user, array $discussionids, bool $canseeprivatereplies): array { + + if (empty($discussionids)) { + return []; + } + + list($insql, $params) = $this->get_db()->get_in_or_equal($discussionids, SQL_PARAMS_NAMED); + + [ + 'where' => $privatewhere, + 'params' => $privateparams, + ] = $this->get_private_reply_sql($user, $canseeprivatereplies, "mp"); + + $sql = " + SELECT posts.* + FROM {" . self::TABLE . "} posts + JOIN ( + SELECT p.discussion, MAX(p.id) as latestpostid + FROM {" . self::TABLE . "} p + JOIN ( + SELECT mp.discussion, MAX(mp.created) AS created + FROM {" . self::TABLE . "} mp + WHERE mp.discussion {$insql} {$privatewhere} + GROUP BY mp.discussion + ) lp ON lp.discussion = p.discussion AND lp.created = p.created + GROUP BY p.discussion + ) plp on plp.discussion = posts.discussion AND plp.latestpostid = posts.id"; + + $records = $this->get_db()->get_records_sql($sql, array_merge($params, $privateparams)); + $entities = $this->transform_db_records_to_entities($records); + + return array_reduce($entities, function($carry, $entity) { + $carry[$entity->get_discussion_id()] = $entity; + return $carry; + }, []); + } + + /** + * Get the SQL where and additional parameters to use to restrict posts to private reply posts. + * + * @param stdClass $user The user to fetch counts for + * @param bool $canseeprivatereplies Whether this user can see all private replies or not + * @return array The SQL WHERE clause, and parameters to use in the SQL. + */ + private function get_private_reply_sql(stdClass $user, bool $canseeprivatereplies, $posttablealias = "p") { + $params = []; + $privatewhere = ''; + if (!$canseeprivatereplies) { + $privatewhere = " AND ({$posttablealias}.privatereplyto = :privatereplyto OR " . + "{$posttablealias}.userid = :privatereplyfrom OR {$posttablealias}.privatereplyto = 0)"; + $params['privatereplyto'] = $user->id; + $params['privatereplyfrom'] = $user->id; + } + + return [ + 'where' => $privatewhere, + 'params' => $params, + ]; + } + + /** + * Get a mapping of the first post in each discussion based on post creation time. + * + * @param int[] $discussionids The list of discussions to fetch counts for + * @return post_entity[] The post object of the first post for each discussions returned in an associative array + */ + public function get_first_post_for_discussion_ids(array $discussionids): array { + + if (empty($discussionids)) { + return []; + } + + list($insql, $params) = $this->get_db()->get_in_or_equal($discussionids, SQL_PARAMS_NAMED); + + $sql = " + SELECT p.* + FROM {" . self::TABLE . "} p + JOIN ( + SELECT mp.discussion, MIN(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"; + + $records = $this->get_db()->get_records_sql($sql, $params); + return $this->transform_db_records_to_entities($records); + } + + /** + * Get the posts for the given user. + * + * @param int $discussionid The discussion to fetch posts for + * @param int $userid The user to fetch posts for + * @param bool $canseeprivatereplies Whether this user can see all private replies or not + * @param string $orderby Order the results + * @return post_entity[] + */ + public function get_posts_in_discussion_for_user_id( + int $discussionid, + int $userid, + bool $canseeprivatereplies, + string $orderby = 'created ASC' + ): array { + $user = $this->get_db()->get_record('user', ['id' => (int)$userid], '*', IGNORE_MISSING); + + $alias = $this->get_table_alias(); + [ + 'where' => $privatewhere, + 'params' => $privateparams, + ] = $this->get_private_reply_sql($user, $canseeprivatereplies); + + $wheresql = "{$alias}.userid = :authorid AND + {$alias}.discussion = :discussionid {$privatewhere}"; + $orderbysql = $alias . '.' . $orderby; + + $sql = $this->generate_get_records_sql($wheresql, $orderbysql); + $records = $this->get_db()->get_records_sql($sql, array_merge([ + 'authorid' => $userid, + 'discussionid' => $discussionid + ], $privateparams)); + + return $this->transform_db_records_to_entities($records); + } +} diff --git a/classes/local/vaults/post_attachment.php b/classes/local/vaults/post_attachment.php new file mode 100644 index 00000000..893a5010 --- /dev/null +++ b/classes/local/vaults/post_attachment.php @@ -0,0 +1,118 @@ +. + +/** + * Post attachment vault class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\vaults; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\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 + * + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class post_attachment { + /** The component for attachments */ + private const COMPONENT = 'mod_hsuforum'; + /** 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 (hsuforum) context that the posts are in + * @param post_entity[] $posts The list of posts to load attachments for + * @param string $area The file storage area, can be 'attachment' or 'post' for inline attachments. + * @return array Post attachments indexed by post id + */ + private function get_area_attachments_for_posts(context $context, array $posts, string $area) { + $itemids = array_map(function($post) { + return $post->get_id(); + }, $posts); + + $files = $this->filestorage->get_area_files( + $context->id, + self::COMPONENT, + $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); + } + + /** + * Get attachment for posts. + * + * @param context $context The (hsuforum) 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) { + return $this->get_area_attachments_for_posts($context, $posts, 'attachment'); + } + + /** + * Get inline attachments for posts. + * + * @param context $context The (hsuforum) 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_inline_attachments_for_posts(context $context, array $posts) { + return $this->get_area_attachments_for_posts($context, $posts, 'post'); + } + +} diff --git a/classes/local/vaults/post_read_receipt_collection.php b/classes/local/vaults/post_read_receipt_collection.php new file mode 100644 index 00000000..0aa5a53c --- /dev/null +++ b/classes/local/vaults/post_read_receipt_collection.php @@ -0,0 +1,121 @@ +. + +/** + * Post read receipt collection class. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\vaults; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\vaults\db_table_vault; +use mod_hsuforum\local\vaults\post_entity; +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 + * + * @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 = 'hsuforum_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 + * @param int|null $userid The user ID + * @return string + */ + protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null, ?int $userid = 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); + } +} diff --git a/classes/local/vaults/preprocessors/extract_context.php b/classes/local/vaults/preprocessors/extract_context.php new file mode 100644 index 00000000..7b9f3997 --- /dev/null +++ b/classes/local/vaults/preprocessors/extract_context.php @@ -0,0 +1,52 @@ +. + +/** + * Extract context vault preprocessor. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\vaults\preprocessors; + +defined('MOODLE_INTERNAL') || die(); + +use context; +use context_helper; +use mod_hsuforum\local\vaults\preprocessors\stdClass; + +/** + * Extract context vault preprocessor. + * + * @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); + } +} diff --git a/classes/local/vaults/preprocessors/extract_record.php b/classes/local/vaults/preprocessors/extract_record.php new file mode 100644 index 00000000..61849feb --- /dev/null +++ b/classes/local/vaults/preprocessors/extract_record.php @@ -0,0 +1,66 @@ +. + +/** + * Extract record vault preprocessor. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\vaults\preprocessors; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\vaults\preprocessors\stdClass; +use moodle_database; +use core\dml\table as dml_table; + +/** + * Extract record vault preprocessor. + * + * Extract record vault preprocessor. Typically used to separate out records + * when two tables have been joined together in a query. + * + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class extract_record { + /** @var \core\dml\table $table The table object relating to the table that the records were loaded from */ + private $table; + + /** + * Constructor. + * + * @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(string $table, string $alias) { + $this->table = new dml_table($table, $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 { + return array_map(function($record) { + return $this->table->extract_from_result($record); + }, $records); + } +} diff --git a/classes/local/vaults/preprocessors/extract_user.php b/classes/local/vaults/preprocessors/extract_user.php new file mode 100644 index 00000000..d451b899 --- /dev/null +++ b/classes/local/vaults/preprocessors/extract_user.php @@ -0,0 +1,70 @@ +. + +/** + * Extract user vault preprocessor. + * + * @package mod_hsuforum + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hsuforum\local\vaults\preprocessors; + +defined('MOODLE_INTERNAL') || die(); + +use mod_hsuforum\local\vaults\preprocessors\stdClass; +use \core\output\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. + * + * @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 \core\output\user_picture::unalias($record, ['deleted'], $idalias, $alias); + }, $records); + } +} \ No newline at end of file diff --git a/classes/message/inbound/reply_handler.php b/classes/message/inbound/reply_handler.php index 0fea5118..5262ee46 100644 --- a/classes/message/inbound/reply_handler.php +++ b/classes/message/inbound/reply_handler.php @@ -103,7 +103,7 @@ public function process_message(\stdClass $record, \stdClass $messagedata) { } else { $groupmode = $course->groupmode; } - if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $modcontext)) { + if ($groupmode == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $modcontext)) { if ($discussion->groupid == -1) { $canpost = false; } else { @@ -237,7 +237,7 @@ public function process_message(\stdClass $record, \stdClass $messagedata) { $this->process_attachment('*', $usercontext, $addpost->itemid, $attachment); // Convert the contentid link in the message. - $draftfile = \moodle_url::make_draftfile_url($addpost->itemid, '/', $attachment->filename); + $draftfile = \core\url::make_draftfile_url($addpost->itemid, '/', $attachment->filename); $addpost->message = preg_replace('/cid:' . $attachment->contentid . '/', $draftfile, $addpost->message); } } @@ -253,7 +253,7 @@ public function process_message(\stdClass $record, \stdClass $messagedata) { 'discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type, - ) + ), ); $event = \mod_hsuforum\event\post_created::create($params); $event->add_record_snapshot('hsuforum_posts', $addpost); @@ -320,7 +320,7 @@ protected function process_attachment($acceptedtypes, \context_user $context, $i public function get_success_message(\stdClass $messagedata, $handlerresult) { $a = new \stdClass(); $a->subject = $handlerresult->subject; - $discussionurl = new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $handlerresult->discussion)); + $discussionurl = new \core\url('/mod/hsuforum/discuss.php', array('d' => $handlerresult->discussion)); $discussionurl->set_anchor('p' . $handlerresult->id); $a->discussionurl = $discussionurl->out(); diff --git a/classes/output/big_search_form.php b/classes/output/big_search_form.php index 33f559f9..ee595411 100644 --- a/classes/output/big_search_form.php +++ b/classes/output/big_search_form.php @@ -25,12 +25,12 @@ namespace mod_hsuforum\output; defined('MOODLE_INTERNAL') || die(); -use html_writer; -use moodle_url; -use renderable; -use renderer_base; +use \core\output\html_writer; +use \core\url as moodle_url; +use \core\output\renderable; +use \core\output\renderer_base; use stdClass; -use templatable; +use \core\output\templatable; /** * Big search form class. @@ -39,7 +39,7 @@ * @copyright 2016 Frédéric Massart - FMCorz.net * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class big_search_form implements renderable, templatable { +class big_search_form implements \core\output\renderable, \core\output\templatable { public $course; public $datefrom; @@ -53,6 +53,7 @@ class big_search_form implements renderable, templatable { public $user; public $words; public $tags; + public $forumid; /** @var string The URL of the search form. */ public $actionurl; @@ -168,7 +169,7 @@ public function set_forumid($forumid) { $this->forumid = $forumid; } - public function export_for_template(renderer_base $output) { + public function export_for_template(\core\output\renderer_base $output) { global $DB, $CFG, $PAGE; $data = new stdClass(); @@ -214,17 +215,17 @@ public function export_for_template(renderer_base $output) { $dateto = time() + HOURSECS; } - $data->datefromfields = html_writer::select_time('days', 'fromday', $datefrom) - . html_writer::select_time('months', 'frommonth', $datefrom) - . html_writer::select_time('years', 'fromyear', $datefrom) - . html_writer::select_time('hours', 'fromhour', $datefrom) - . html_writer::select_time('minutes', 'fromminute', $datefrom); - - $data->datetofields = html_writer::select_time('days', 'today', $dateto) - . html_writer::select_time('months', 'tomonth', $dateto) - . html_writer::select_time('years', 'toyear', $dateto) - . html_writer::select_time('hours', 'tohour', $dateto) - . html_writer::select_time('minutes', 'tominute', $dateto); + $data->datefromfields = \core\output\html_writer::select_time('days', 'fromday', $datefrom) + . \core\output\html_writer::select_time('months', 'frommonth', $datefrom) + . \core\output\html_writer::select_time('years', 'fromyear', $datefrom) + . \core\output\html_writer::select_time('hours', 'fromhour', $datefrom) + . \core\output\html_writer::select_time('minutes', 'fromminute', $datefrom); + + $data->datetofields = \core\output\html_writer::select_time('days', 'today', $dateto) + . \core\output\html_writer::select_time('months', 'tomonth', $dateto) + . \core\output\html_writer::select_time('years', 'toyear', $dateto) + . \core\output\html_writer::select_time('hours', 'tohour', $dateto) + . \core\output\html_writer::select_time('minutes', 'tominute', $dateto); if ($this->forumid && !empty($this->forumoptions)) { foreach ($this->forumoptions as $index => $option) { diff --git a/classes/output/hsuforum_post.php b/classes/output/hsuforum_post.php index 9bf04f2d..54be61ac 100644 --- a/classes/output/hsuforum_post.php +++ b/classes/output/hsuforum_post.php @@ -34,7 +34,7 @@ * * @property boolean $viewfullnames Whether to override fullname() */ -class hsuforum_post implements \renderable, \templatable { +class hsuforum_post implements \core\output\renderable, \core\output\templatable { /** * The course that the forum post is in. @@ -137,7 +137,7 @@ public function __construct($course, $cm, $forum, $discussion, $post, $author, $ * @param bool $plaintext Whethe the target is a plaintext target * @return array Data ready for use in a mustache template */ - public function export_for_template(\renderer_base $renderer, $plaintext = false) { + public function export_for_template(\core\output\renderer_base $renderer, $plaintext = false) { if ($plaintext) { return $this->export_for_template_text($renderer); } else { @@ -252,7 +252,7 @@ public function __set($key, $value) { } // Throw an error rather than fail silently. - throw new \coding_exception('Tried to set unknown property "' . $key . '"'); + throw new \core\exception\coding_exception('Tried to set unknown property "' . $key . '"'); } /** @@ -270,7 +270,7 @@ public function get_is_firstpost() { * @return string */ public function get_courselink() { - $link = new \moodle_url( + $link = new \core\url( // Posts are viewed on the topic. '/course/view.php', array( 'id' => $this->course->id, @@ -286,7 +286,7 @@ public function get_courselink() { * @return string */ public function get_forumindexlink() { - $link = new \moodle_url( + $link = new \core\url( // Posts are viewed on the topic. '/mod/hsuforum/index.php', array( 'id' => $this->course->id, @@ -302,7 +302,7 @@ public function get_forumindexlink() { * @return string */ public function get_forumviewlink() { - $link = new \moodle_url( + $link = new \core\url( // Posts are viewed on the topic. '/mod/hsuforum/view.php', array( 'f' => $this->forum->id, @@ -318,7 +318,7 @@ public function get_forumviewlink() { * @return string */ protected function _get_discussionlink() { - return new \moodle_url( + return new \core\url( // Posts are viewed on the topic. '/mod/hsuforum/discuss.php', array( // Within a discussion. @@ -368,7 +368,7 @@ public function get_parentpostlink() { * @return string */ public function get_authorlink() { - $link = new \moodle_url( + $link = new \core\url( '/user/view.php', array( 'id' => $this->author->id, 'course' => $this->course->id, @@ -384,7 +384,7 @@ public function get_authorlink() { * @return string */ public function get_unsubscribeforumlink() { - $link = new \moodle_url( + $link = new \core\url( '/mod/hsuforum/subscribe.php', array( 'id' => $this->forum->id, ) @@ -399,7 +399,7 @@ public function get_unsubscribeforumlink() { * @return string */ public function get_unsubscribediscussionlink() { - $link = new \moodle_url( + $link = new \core\url( '/mod/hsuforum/subscribe.php', array( 'id' => $this->forum->id, 'd' => $this->discussion->id, @@ -415,7 +415,7 @@ public function get_unsubscribediscussionlink() { * @return string */ public function get_replylink() { - return new \moodle_url( + return new \core\url( '/mod/hsuforum/post.php', array( 'reply' => $this->post->id, ) @@ -544,10 +544,10 @@ public function get_postdate() { /** * The HTML for the author's user picture. * - * @param \renderer_base $renderer + * @param \core\output\renderer_base $renderer * @return string */ - public function get_author_picture(\renderer_base $renderer) { + public function get_author_picture(\core\output\renderer_base $renderer) { $link = !(guest_user()->id == $this->author->id); return $renderer->user_picture($this->author, array('courseid' => $this->course->id, 'link' => $link)); @@ -556,10 +556,10 @@ public function get_author_picture(\renderer_base $renderer) { /** * The HTML for a group picture. * - * @param \renderer_base $renderer + * @param \core\output\renderer_base $renderer * @return string */ - public function get_group_picture(\renderer_base $renderer) { + public function get_group_picture(\core\output\renderer_base $renderer) { if (isset($this->userfrom->groups)) { $groups = $this->userfrom->groups[$this->forum->id]; } else { diff --git a/classes/output/quick_search_form.php b/classes/output/quick_search_form.php index 0c56519c..921a5aec 100644 --- a/classes/output/quick_search_form.php +++ b/classes/output/quick_search_form.php @@ -25,11 +25,11 @@ namespace mod_hsuforum\output; defined('MOODLE_INTERNAL') || die(); -use help_icon; -use moodle_url; -use renderable; -use renderer_base; -use templatable; +use \core\output\help_icon; +use \core\url as moodle_url; +use \core\output\renderable; +use \core\output\renderer_base; +use \core\output\templatable; /** * Quick search form renderable class. @@ -38,7 +38,7 @@ * @copyright 2016 Frédéric Massart - FMCorz.net * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class quick_search_form implements renderable, templatable { +class quick_search_form implements \core\output\renderable, \core\output\templatable { /** @var int The course ID. */ protected $courseid; @@ -62,7 +62,7 @@ public function __construct($courseid, $query = '') { $this->helpicon = new help_icon('search', 'core'); } - public function export_for_template(renderer_base $output) { + public function export_for_template(\core\output\renderer_base $output) { $data = [ 'actionurl' => $this->actionurl->out(false), 'courseid' => $this->courseid, diff --git a/classes/post_form.php b/classes/post_form.php index 68d5885d..bd2d687e 100644 --- a/classes/post_form.php +++ b/classes/post_form.php @@ -1,5 +1,4 @@ $maxbytes, 'maxfiles' => $forum->maxattachments, 'accepted_types' => '*', - 'return_types' => FILE_INTERNAL | FILE_CONTROLLED_LINK + 'return_types' => FILE_INTERNAL | FILE_CONTROLLED_LINK, ); } @@ -72,7 +71,7 @@ public static function editor_options(context_module $context, $postid) { 'maxbytes' => $maxbytes, 'trusttext'=> true, 'return_types'=> FILE_INTERNAL | FILE_EXTERNAL, - 'subdirs' => file_area_contains_subdirs($context, 'mod_hsuforum', 'post', $postid) + 'subdirs' => file_area_contains_subdirs($context, 'mod_hsuforum', 'post', $postid), ); } @@ -149,7 +148,7 @@ function definition() { $mform->addHelpButton('pinned', 'discussionpinned', 'hsuforum'); } - if (!empty($forum->anonymous) and $post->userid == $USER->id and has_capability('mod/hsuforum:revealpost', $modcontext)) { + if (!empty($forum->anonymous) && $post->userid == $USER->id && has_capability('mod/hsuforum:revealpost', $modcontext)) { $mform->addElement('advcheckbox', 'reveal', get_string('reveal', 'hsuforum')); $mform->addHelpButton('reveal', 'reveal', 'hsuforum'); } @@ -158,7 +157,7 @@ function definition() { $mform->addElement('checkbox', 'mailnow', get_string('mailnow', 'hsuforum')); } - if (!empty($forum->allowprivatereplies) and !empty($post->parent) and has_capability('mod/hsuforum:allowprivate', $modcontext)) { + if (!empty($forum->allowprivatereplies) && !empty($post->parent) && has_capability('mod/hsuforum:allowprivate', $modcontext)) { if ($post->userid != $USER->id) { $mform->addElement('hidden', 'privatereply', 0); $mform->setType('privatereply', PARAM_INT); diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index cc509659..768458d6 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -98,7 +98,7 @@ public static function get_metadata(collection $items) : collection { 'userid' => 'privacy:metadata:hsuforum_queue:userid', 'discussionid' => 'privacy:metadata:hsuforum_queue:discussionid', 'postid' => 'privacy:metadata:hsuforum_queue:postid', - 'timemodified' => 'privacy:metadata:hsuforum_queue:timemodified' + 'timemodified' => 'privacy:metadata:hsuforum_queue:timemodified', ], 'privacy:metadata:hsuforum_queue'); // The 'hsuforum_read' table stores data about which forum posts have been read by each user. @@ -122,6 +122,20 @@ public static function get_metadata(collection $items) : collection { 'forumid' => 'privacy:metadata:hsuforum_track_prefs:forumid', ], 'privacy:metadata:hsuforum_track_prefs'); + // The 'hsuforum_discussion_subs' table stores information about which discussions a user is subscribed to. + $items->add_database_table('hsuforum_discussion_subs', [ + 'discussionid' => 'privacy:metadata:hsuforum_discussion_subs:discussionid', + 'preference' => 'privacy:metadata:hsuforum_discussion_subs:preference', + 'userid' => 'privacy:metadata:hsuforum_discussion_subs:userid', + ], 'privacy:metadata:hsuforum_discussion_subs'); + + // The 'hsuforum_grades' table stores grade data. + $items->add_database_table('hsuforum_grades', [ + 'userid' => 'privacy:metadata:hsuforum_grades:userid', + 'forum' => 'privacy:metadata:hsuforum_grades:forum', + 'grade' => 'privacy:metadata:hsuforum_grades:grade', + ], 'privacy:metadata:hsuforum_grades'); + // Forum posts can be tagged and rated. $items->link_subsystem('core_tag', 'privacy:metadata:core_tag'); $items->link_subsystem('core_rating', 'privacy:metadata:core_rating'); @@ -561,7 +575,7 @@ protected static function export_discussion_data(int $userid, array $mappings) { 'pinned' => transform::yesno((bool)$discussion->pinned), 'timemodified' => transform::datetime($discussion->timemodified), 'usermodified' => transform::datetime($discussion->usermodified), - 'creator_was_you' => transform::yesno($discussion->userid == $userid) + 'creator_was_you' => transform::yesno($discussion->userid == $userid), ]; $discussionarea = static::get_discussion_area($discussion); @@ -943,7 +957,7 @@ protected static function export_read_data(int $userid, \context_module $context * Delete all data for all users in the specified context. * * @param \context|context $context $context The specific context to delete data for. - * @throws \coding_exception + * @throws \core\exception\coding_exception */ public static function delete_data_for_all_users_in_context(\context $context) { global $DB; diff --git a/classes/renderables/advanced_editor.php b/classes/renderables/advanced_editor.php index 0f6dda0e..5653a01d 100644 --- a/classes/renderables/advanced_editor.php +++ b/classes/renderables/advanced_editor.php @@ -27,7 +27,7 @@ defined('MOODLE_INTERNAL') || die(); -class advanced_editor implements \renderable { +class advanced_editor implements \core\output\renderable { /** * @var \context_module @@ -110,7 +110,7 @@ public function get_data($draftid = 0){ 'editor' => $editor, 'options' => $options, 'fpoptions' => $fpoptions, - 'draftitemid' => $draftitemid + 'draftitemid' => $draftitemid, ]; } } diff --git a/classes/response/json_response.php b/classes/response/json_response.php index 7b94f378..d5df8a46 100644 --- a/classes/response/json_response.php +++ b/classes/response/json_response.php @@ -56,10 +56,10 @@ function __construct($data) { /** * Convert data attribute to a string * - * @param \core_renderer $output + * @param \core\output\core_renderer $output * @return string */ - protected function data_to_string(\core_renderer $output) { + protected function data_to_string(\core\output\core_renderer $output) { if ($this->data instanceof \Exception) { $info = get_exception_info($this->data); return $output->fatal_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo); @@ -76,7 +76,7 @@ protected function data_to_string(\core_renderer $output) { public function send() { global $PAGE; - /** @var \core_renderer $output */ + /** @var \core\output\core_renderer $output */ $output = $PAGE->get_renderer('core', null, RENDERER_TARGET_AJAX); echo $output->header(); diff --git a/classes/search/post.php b/classes/search/post.php index e8558d1f..4bfd6798 100644 --- a/classes/search/post.php +++ b/classes/search/post.php @@ -144,7 +144,7 @@ public function uses_file_indexing() { public function get_search_fileareas() { $fileareas = array( 'attachment', - 'post' + 'post', ); return $fileareas; @@ -235,23 +235,23 @@ public function check_access($id) { * Link to the forum post discussion * * @param \core_search\document $doc - * @return \moodle_url + * @return \core\url */ public function get_doc_url(\core_search\document $doc) { // The post is already in static cache, we fetch it in self::search_access. $post = $this->get_post($doc->get('itemid')); - return new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $post->discussion)); + return new \core\url('/mod/hsuforum/discuss.php', array('d' => $post->discussion)); } /** * Link to the forum. * * @param \core_search\document $doc - * @return \moodle_url + * @return \core\url */ public function get_context_url(\core_search\document $doc) { $contextmodule = \context::instance_by_id($doc->get('contextid')); - return new \moodle_url('/mod/hsuforum/view.php', array('id' => $contextmodule->instanceid)); + return new \core\url('/mod/hsuforum/view.php', array('id' => $contextmodule->instanceid)); } /** @@ -315,7 +315,7 @@ protected function get_discussion($discussionid) { protected function get_contexts_to_reindex_extra_sql() { return [ 'JOIN {hsuforum_discussions} fd ON fd.course = cm.course AND fd.forum = cm.instance', - 'MAX(fd.timemodified) DESC' + 'MAX(fd.timemodified) DESC', ]; } diff --git a/classes/service/discussion_service.php b/classes/service/discussion_service.php index f864111d..da714f93 100644 --- a/classes/service/discussion_service.php +++ b/classes/service/discussion_service.php @@ -29,7 +29,7 @@ use mod_hsuforum\response\json_response; use mod_hsuforum\upload_file; use mod_hsuforum\local; -use moodle_exception; +use \core\exception\moodle_exception; defined('MOODLE_INTERNAL') || die(); @@ -160,7 +160,7 @@ public function create_discussion_object($forum, $context, array $options = arra public function validate_discussion($cm, $forum, $context, $discussion, upload_file $uploader, $posttomygroups = false) { $errors = array(); if (!hsuforum_user_can_post_discussion($forum, $discussion->groupid, -1, $cm, $context)) { - $errors[] = new \moodle_exception('nopostforum', 'hsuforum'); + $errors[] = new moodle_exception('nopostforum', 'hsuforum'); } if (!empty($posttomygroups)) { @@ -173,22 +173,22 @@ public function validate_discussion($cm, $forum, $context, $discussion, upload_f $thresholdwarning = hsuforum_check_throttling($forum, $cm); if ($thresholdwarning !== false && $thresholdwarning->canpost === false) { - $errors[] = new \moodle_exception($thresholdwarning->errorcode, $thresholdwarning->module, $thresholdwarning->additional); + $errors[] = new moodle_exception($thresholdwarning->errorcode, $thresholdwarning->module, $thresholdwarning->additional); } $subject = trim($discussion->subject); if (empty($subject)) { - $errors[] = new \moodle_exception('subjectisrequired', 'hsuforum'); + $errors[] = new moodle_exception('subjectisrequired', 'hsuforum'); } if (hsuforum_str_empty($discussion->message)) { - $errors[] = new \moodle_exception('messageisrequired', 'hsuforum'); + $errors[] = new moodle_exception('messageisrequired', 'hsuforum'); } // Check restriction times. list ($start, $end) = local::get_form_discussion_times(); if ($start && $end && $start > $end) { - $errors[] = new \moodle_exception('errortimestartgreater', 'hsuforum'); + $errors[] = new moodle_exception('errortimestartgreater', 'hsuforum'); } if ($uploader->was_file_uploaded()) { @@ -242,7 +242,7 @@ public function trigger_discussion_created($course, \context_module $context, $c 'objectid' => $discussion->id, 'other' => array( 'forumid' => $forum->id, - ) + ), ); $event = discussion_created::create($params); $event->add_record_snapshot('hsuforum_discussions', $discussion); @@ -269,14 +269,14 @@ public function get_posts($discussionid) { || $discussion->timestart <= time()) && ($discussion->timeend == 0 || $discussion->timeend > time()))) ) { - throw new \moodle_exception('invaliddiscussionid', 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); + throw new moodle_exception('invaliddiscussionid', 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); } } if (!$post = hsuforum_get_post_full($discussion->firstpost)) { - throw new \moodle_exception("notexists", 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); + throw new moodle_exception("notexists", 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); } if (!hsuforum_user_can_see_post($forum, $discussion, $post, null, $cm)) { - throw new \moodle_exception('nopermissiontoview', 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); + throw new moodle_exception('nopermissiontoview', 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); } $posts = hsuforum_get_all_discussion_posts($discussion->id); @@ -293,7 +293,7 @@ public function get_posts($discussionid) { * * @param int $discussionid * @return string - * @throws \coding_exception + * @throws \core\exception\coding_exception */ public function render_discussion($discussionid, $fullthread = false) { global $PAGE; @@ -303,7 +303,7 @@ public function render_discussion($discussionid, $fullthread = false) { list($cm, $discussion, $posts, $canreply) = $this->get_posts($discussionid); if (!array_key_exists($discussion->firstpost, $posts)) { - throw new \coding_exception('Failed to find discussion post'); + throw new \core\exception\coding_exception('Failed to find discussion post'); } return $renderer->discussion($cm, $discussion, $posts[$discussion->firstpost], $fullthread, $posts, $canreply); } @@ -313,7 +313,7 @@ public function render_discussion($discussionid, $fullthread = false) { * * @param int $discussionid * @return string - * @throws \coding_exception + * @throws \core\exception\coding_exception */ public function render_full_thread($discussionid) { return $this->render_discussion($discussionid, true); diff --git a/classes/service/form_service.php b/classes/service/form_service.php index 6a8de072..81fbe3ab 100644 --- a/classes/service/form_service.php +++ b/classes/service/form_service.php @@ -40,7 +40,7 @@ class form_service { /** * Lazy load renderer * - * @return \mod_hsuforum_renderer|\renderer_base + * @return \mod_hsuforum_renderer|\core\output\renderer_base */ protected function get_renderer() { global $PAGE; @@ -88,14 +88,14 @@ protected function file_prepare_draft_area(&$draftitemid, $contextid, $component $draftitemid = file_get_unused_draft_itemid(); } $file_record = array('contextid'=>$usercontext->id, 'component'=>'user', 'filearea'=>'draft', 'itemid'=>$draftitemid); - if (!is_null($itemid) and $files = $fs->get_area_files($contextid, $component, $filearea, $itemid)) { + if (!is_null($itemid) && $files = $fs->get_area_files($contextid, $component, $filearea, $itemid)) { foreach ($files as $file) { - if ($file->is_directory() and $file->get_filepath() === '/') { + if ($file->is_directory() && $file->get_filepath() === '/') { // we need a way to mark the age of each draft area, // by not copying the root dir we force it to be created automatically with current timestamp continue; } - if (!$options['subdirs'] and ($file->is_directory() or $file->get_filepath() !== '/')) { + if (!$options['subdirs'] && ($file->is_directory() or $file->get_filepath() !== '/')) { continue; } @@ -215,7 +215,7 @@ public function edit_discussion_form($cm, $discussion, $post, $draftid) { 'groupid' => ($discussion->groupid == -1) ? 0 : $discussion->groupid, 'itemid' => $itemid, 'timestart' => $discussion->timestart, - 'timeend' => $discussion->timeend + 'timeend' => $discussion->timeend, )); } } diff --git a/classes/service/post_service.php b/classes/service/post_service.php index a1fb0715..ae9ad959 100644 --- a/classes/service/post_service.php +++ b/classes/service/post_service.php @@ -29,7 +29,7 @@ use mod_hsuforum\event\post_updated; use mod_hsuforum\response\json_response; use mod_hsuforum\upload_file; -use moodle_exception; +use \core\exception\moodle_exception; defined('MOODLE_INTERNAL') || die(); @@ -193,14 +193,14 @@ public function require_can_edit_post($forum, \context_module $context, $discuss global $CFG, $USER; if (!($forum->type == 'news' && !$post->parent && $discussion->timestart > time())) { - if (((time() - $post->created) > $CFG->maxeditingtime) and + if (((time() - $post->created) > $CFG->maxeditingtime) && !has_capability('mod/hsuforum:editanypost', $context) ) { - throw new \moodle_exception('maxtimehaspassed', 'hsuforum', '', format_time($CFG->maxeditingtime)); + throw new moodle_exception('maxtimehaspassed', 'hsuforum', '', format_time($CFG->maxeditingtime)); } } if (($post->userid <> $USER->id) && !has_capability('mod/hsuforum:editanypost', $context)) { - throw new \moodle_exception('cannoteditposts', 'hsuforum'); + throw new moodle_exception('cannoteditposts', 'hsuforum'); } } @@ -265,38 +265,38 @@ public function validate_post($course, $cm, $forum, $context, $discussion, $post $errors = array(); if (!hsuforum_user_can_post($forum, $discussion, null, $cm, $course, $context)) { - $errors[] = new \moodle_exception('nopostforum', 'hsuforum'); + $errors[] = new moodle_exception('nopostforum', 'hsuforum'); } if (!empty($post->id)) { if (!(($post->userid == $USER->id && (has_capability('mod/hsuforum:replypost', $context) || has_capability('mod/hsuforum:startdiscussion', $context))) || has_capability('mod/hsuforum:editanypost', $context)) ) { - $errors[] = new \moodle_exception('cannotupdatepost', 'hsuforum'); + $errors[] = new moodle_exception('cannotupdatepost', 'hsuforum'); } } if (empty($post->id)) { $thresholdwarning = hsuforum_check_throttling($forum, $cm); if ($thresholdwarning !== false && $thresholdwarning->canpost === false) { - $errors[] = new \moodle_exception($thresholdwarning->errorcode, $thresholdwarning->module, $thresholdwarning->additional); + $errors[] = new moodle_exception($thresholdwarning->errorcode, $thresholdwarning->module, $thresholdwarning->additional); } } if (hsuforum_str_empty($post->subject)) { - $errors[] = new \moodle_exception('subjectisrequired', 'hsuforum'); + $errors[] = new moodle_exception('subjectisrequired', 'hsuforum'); } if (hsuforum_str_empty($post->message)) { - $errors[] = new \moodle_exception('messageisrequired', 'hsuforum'); + $errors[] = new moodle_exception('messageisrequired', 'hsuforum'); } if ($discussion->timestart && $discussion->timeend && $discussion->timestart > $discussion->timeend) { - $errors[] = new \moodle_exception('errortimestartgreater', 'hsuforum'); + $errors[] = new moodle_exception('errortimestartgreater', 'hsuforum'); } if ($post->privatereply) { if (!has_capability('mod/hsuforum:allowprivate', $context) || !$forum->allowprivatereplies ) { - $errors[] = new \moodle_exception('cannotmakeprivatereplies', 'hsuforum'); + $errors[] = new moodle_exception('cannotmakeprivatereplies', 'hsuforum'); } } @@ -374,7 +374,7 @@ public function trigger_post_created($course, \context_module $context, $cm, $fo 'discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type, - ) + ), ); $event = post_created::create($params); $event->add_record_snapshot('hsuforum_posts', $post); @@ -400,7 +400,7 @@ public function trigger_post_updated(\context_module $context, $forum, $discussi 'discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type, - ) + ), ); if ($post->userid !== $USER->id) { diff --git a/classes/subscriptions.php b/classes/subscriptions.php index 07b829f0..7ed3145f 100644 --- a/classes/subscriptions.php +++ b/classes/subscriptions.php @@ -152,7 +152,7 @@ public static function is_forcesubscribed($forum) { * @return bool */ public static function subscription_disabled($forum) { - return ($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE); + return ($forum->forcesubscribe == HSUFORUM_DISALLOWSUBSCRIBE); } /** diff --git a/classes/upload_file.php b/classes/upload_file.php index daef4f37..ad24fe1c 100644 --- a/classes/upload_file.php +++ b/classes/upload_file.php @@ -26,7 +26,7 @@ require_once(__DIR__.'/attachments.php'); -use moodle_exception; +use \core\exception\moodle_exception; defined('MOODLE_INTERNAL') || die(); @@ -94,11 +94,11 @@ public function __construct(attachments $attachments, array $options, $stub = fa * Returns an array of files * * @return array - * @throws \coding_exception + * @throws \core\exception\coding_exception */ public function get_files() { if (empty($_FILES) || !isset($_FILES[$this->element])) { - throw new \coding_exception('Cannot use this method when no files were uploaded'); + throw new \core\exception\coding_exception('Cannot use this method when no files were uploaded'); } $files = array(); foreach ($_FILES[$this->element] as $name => $values) { @@ -151,7 +151,7 @@ public function was_file_uploaded() { /** * Validate all of the uploaded files * - * @throws \moodle_exception + * @throws moodle_exception */ public function validate_files($postid = 0) { if (!isset($_FILES[$this->element])) { @@ -177,7 +177,7 @@ public function validate_files($postid = 0) { * Validate the uploaded file * * @param array $file - * @throws \moodle_exception + * @throws moodle_exception * @throws \file_exception */ protected function validate_file(array $file) { diff --git a/db/access.php b/db/access.php index f7abe9b4..780b0bb8 100644 --- a/db/access.php +++ b/db/access.php @@ -35,9 +35,9 @@ 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW + 'manager' => CAP_ALLOW, ), - 'clonepermissionsfrom' => 'moodle/course:manageactivities' + 'clonepermissionsfrom' => 'moodle/course:manageactivities', ), 'mod/hsuforum:viewdiscussion' => array( @@ -50,8 +50,8 @@ 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:viewhiddentimedposts' => array( @@ -61,8 +61,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:startdiscussion' => array( @@ -75,8 +75,8 @@ 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:replypost' => array( @@ -89,8 +89,8 @@ 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:addnews' => array( @@ -102,8 +102,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:replynews' => array( @@ -115,8 +115,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:viewrating' => array( @@ -127,8 +127,8 @@ 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:viewanyrating' => array( @@ -139,8 +139,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:viewallratings' => array( @@ -151,13 +151,23 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW + 'manager' => CAP_ALLOW, ), - 'clonepermissionsfrom' => 'mod/hsuforum:viewanyrating' + 'clonepermissionsfrom' => 'mod/hsuforum:viewanyrating', ), 'mod/hsuforum:rate' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array( + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW, + ), + ), + + 'mod/hsuforum:postprivatereply' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( @@ -167,6 +177,16 @@ ) ), + 'mod/hsuforum:readprivatereplies' => array( + 'captype' => 'read', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array( + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ) + ), + 'mod/hsuforum:createattachment' => array( 'riskbitmask' => RISK_SPAM, @@ -177,8 +197,8 @@ 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:deleteownpost' => array( @@ -189,8 +209,8 @@ 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:deleteanypost' => array( @@ -200,8 +220,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:splitdiscussions' => array( @@ -211,8 +231,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:movediscussions' => array( @@ -222,8 +242,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:pindiscussions' => array( @@ -233,8 +253,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:editanypost' => array( @@ -246,8 +266,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:revealpost' => array( @@ -258,8 +278,8 @@ 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:viewqandawithoutposting' => array( @@ -269,8 +289,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:viewsubscribers' => array( @@ -280,8 +300,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:managesubscriptions' => array( @@ -293,8 +313,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:postwithoutthrottling' => array( @@ -306,8 +326,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:exportdiscussion' => array( @@ -319,8 +339,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:exportpost' => array( @@ -331,8 +351,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:exportownpost' => array( @@ -345,7 +365,7 @@ 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW, 'student' => CAP_ALLOW, - ) + ), ), 'mod/hsuforum:addquestion' => array( @@ -356,8 +376,8 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:viewposters' => array( @@ -371,8 +391,8 @@ 'student' => CAP_PREVENT, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:viewflags'=> array( @@ -384,8 +404,8 @@ 'student' => CAP_PREVENT, 'teacher' => CAP_PREVENT, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:allowprivate' => array( @@ -396,8 +416,8 @@ 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'teacher' => CAP_ALLOW - ) + 'teacher' => CAP_ALLOW, + ), ), 'mod/hsuforum:allowforcesubscribe' => array( @@ -407,8 +427,8 @@ 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'frontpage' => CAP_ALLOW - ) + 'frontpage' => CAP_ALLOW, + ), ), 'mod/hsuforum:canposttomygroups' => array( @@ -417,17 +437,43 @@ 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) + 'manager' => CAP_ALLOW, + ), ), 'mod/hsuforum:canoverridediscussionlock' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array( + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW, + ), + ), + 'mod/hsuforum:canoverridecutoff' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW + ), + 'clonepermissionsfrom' => 'mod/hsuforum:canoverridediscussionlock' + ), + 'mod/hsuforum:cantogglefavourite' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array( + 'user' => CAP_ALLOW ) ), + 'mod/hsuforum:grade' => [ + 'captype' => 'write', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => [ + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW, + ], + ], ); diff --git a/db/events.php b/db/events.php index ab1629ca..614b2608 100644 --- a/db/events.php +++ b/db/events.php @@ -35,7 +35,7 @@ array( 'eventname' => '\core\event\role_assigned', - 'callback' => 'mod_hsuforum_observer::role_assigned' + 'callback' => 'mod_hsuforum_observer::role_assigned', ), array( diff --git a/db/install.xml b/db/install.xml index 1f862836..fc8cb9c8 100644 --- a/db/install.xml +++ b/db/install.xml @@ -12,13 +12,18 @@ + + + + + @@ -59,6 +64,7 @@ + @@ -88,6 +94,9 @@ + + + @@ -195,5 +204,40 @@ + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
diff --git a/db/log.php b/db/log.php index 89d61592..fd43770c 100644 --- a/db/log.php +++ b/db/log.php @@ -1,5 +1,4 @@ 'read', 'capabilities' => 'mod/hsuforum:viewdiscussion', - 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'mod_hsuforum_get_forum_discussion_posts' => array( @@ -43,7 +43,7 @@ 'description' => 'Returns a list of forum posts for a discussion.', 'type' => 'read', 'capabilities' => 'mod/hsuforum:viewdiscussion, mod/hsuforum:viewqandawithoutposting', - 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'mod_hsuforum_get_forum_discussions_paginated' => array( @@ -53,6 +53,16 @@ 'description' => 'Returns a list of forum discussions optionally sorted and paginated.', 'type' => 'read', 'capabilities' => 'mod/hsuforum:viewdiscussion, mod/hsuforum:viewqandawithoutposting', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + ), + + 'mod_hsuforum_get_forum_discussions' => array( + 'classname' => 'mod_hsuforum_external', + 'methodname' => 'get_forum_discussions', + 'classpath' => 'mod/hsuforum/externallib.php', + 'description' => 'Returns a list of forum discussions optionally sorted and paginated.', + 'type' => 'read', + 'capabilities' => 'mod/hsuforum:viewdiscussion, mod/hsuforum:viewqandawithoutposting', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) ), @@ -63,7 +73,7 @@ 'description' => 'Trigger the course module viewed event and update the module completion status.', 'type' => 'write', 'capabilities' => 'mod/hsuforum:viewdiscussion', - 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'mod_hsuforum_view_forum_discussion' => array( @@ -73,7 +83,7 @@ 'description' => 'Trigger the forum discussion viewed event.', 'type' => 'write', 'capabilities' => 'mod/hsuforum:viewdiscussion', - 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'mod_hsuforum_add_discussion_post' => array( @@ -83,7 +93,7 @@ 'description' => 'Create new posts into an existing discussion.', 'type' => 'write', 'capabilities' => 'mod/hsuforum:replypost', - 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'mod_hsuforum_add_discussion' => array( @@ -93,7 +103,7 @@ 'description' => 'Add a new discussion into an existing forum.', 'type' => 'write', 'capabilities' => 'mod/hsuforum:startdiscussion', - 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'mod_hsuforum_can_add_discussion' => array( @@ -102,7 +112,7 @@ 'classpath' => 'mod/hsuforum/externallib.php', 'description' => 'Check if the current user can add discussions in the given forum (and optionally for the given group).', 'type' => 'read', - 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), ); diff --git a/db/tasks.php b/db/tasks.php index c5399816..79ca6727 100644 --- a/db/tasks.php +++ b/db/tasks.php @@ -33,6 +33,6 @@ 'hour' => '*', 'day' => '*', 'month' => '*', - 'dayofweek' => '*' - ) + 'dayofweek' => '*', + ), ); diff --git a/db/upgrade.php b/db/upgrade.php index a60d6d73..d692b8c4 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -55,7 +55,7 @@ function xmldb_hsuforum_upgrade($oldversion) { $replacements = array( 11 => 20, 12 => 50, - 13 => 100 + 13 => 100, ); // Run the replacements. @@ -164,7 +164,7 @@ function xmldb_hsuforum_upgrade($oldversion) { 'showsubstantive', 'trackingtype', 'trackreadposts', - 'usermarksread' + 'usermarksread', ); // Migrate legacy configs to plugin configs. @@ -343,6 +343,92 @@ function xmldb_hsuforum_upgrade($oldversion) { upgrade_mod_savepoint(true, 2018120301, 'hsuforum'); } + if ($oldversion < 2024091701) { + $table = new xmldb_table('hsuforum'); + $field1 = new xmldb_field('duedate', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'introformat'); + $field2 = new xmldb_field('cutoffdate', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'duedate'); + $field3 = new xmldb_field('grade_forum', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'scale'); + $field4 = new xmldb_field('grade_forum_notify', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'grade_forum'); + $field5 = new xmldb_field('trackingtype', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1', 'forcesubscribe'); + + // Add whichever is missing. + if (!$dbman->field_exists($table, $field1)) { + $dbman->add_field($table, $field1); + } + if (!$dbman->field_exists($table, $field2)) { + $dbman->add_field($table, $field2); + } + if (!$dbman->field_exists($table, $field3)) { + $dbman->add_field($table, $field3); + } + if (!$dbman->field_exists($table, $field4)) { + $dbman->add_field($table, $field4); + } + if (!$dbman->field_exists($table, $field5)) { + $dbman->add_field($table, $field5); + } + + $table = new xmldb_table('hsuforum_discussions'); + $field = new xmldb_field('timelocked', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'pinned'); + + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + $table = new xmldb_table('hsuforum_posts'); + $field1 = new xmldb_field('privatereplyto', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'deleted'); + $field2 = new xmldb_field('wordcount', XMLDB_TYPE_INTEGER, '20', null, null, null, null, 'privatereplyto'); + $field3 = new xmldb_field('charcount', XMLDB_TYPE_INTEGER, '20', null, null, null, null, 'wordcount'); + + if (!$dbman->field_exists($table, $field1)) { + $dbman->add_field($table, $field1); + } + if (!$dbman->field_exists($table, $field2)) { + $dbman->add_field($table, $field2); + } + if (!$dbman->field_exists($table, $field3)) { + $dbman->add_field($table, $field3); + } + + $table = new xmldb_table('hsuforum_discussion_subs'); + if (!$dbman->table_exists($table)) { + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('forum', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id'); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'forum'); + $table->add_field('discussion', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'userid'); + $table->add_field('preference', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '1', 'discussion'); + + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + $table->add_key('forum', XMLDB_KEY_FOREIGN, ['forum'], 'hsuforum', ['id']); + $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']); + $table->add_key('discussion', XMLDB_KEY_FOREIGN, ['discussion'], 'hsuforum_discussions', ['id']); + $table->add_key('user_discussions', XMLDB_KEY_UNIQUE, ['userid', 'discussion']); + + $dbman->create_table($table); + } + + $table = new xmldb_table('hsuforum_grades'); + if (!$dbman->table_exists($table)) { + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('forum', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id'); + $table->add_field('itemnumber', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'forum'); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'itemnumber'); + $table->add_field('grade', XMLDB_TYPE_NUMBER, '10,5', null, null, null, null, 'itemnumber'); + $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'grade'); + $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'timecreated'); + + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + $table->add_key('forum', XMLDB_KEY_FOREIGN, ['forum'], 'hsuforum', ['id']); + + $table->add_index('userid', XMLDB_INDEX_NOTUNIQUE, ['userid']); + $table->add_index('forumusergrade', XMLDB_INDEX_UNIQUE, ['forum', 'itemnumber', 'userid']); + + $dbman->create_table($table); + } + + upgrade_mod_savepoint(true, 2024091701, 'hsuforum'); + } + return true; } diff --git a/discuss.php b/discuss.php index d3037fa9..5c82c138 100644 --- a/discuss.php +++ b/discuss.php @@ -40,7 +40,7 @@ $d = optional_param('id', null, PARAM_INT); if ($d === null) { - throw new \moodle_exception('missingparameter'); + throw new \core\exception\moodle_exception('missingparameter'); } } @@ -53,7 +53,7 @@ $config = get_config('hsuforum'); - $url = new moodle_url('/mod/hsuforum/discuss.php', array('d'=>$d)); + $url = new \core\url('/mod/hsuforum/discuss.php', array('d'=>$d)); if ($root !== 0) { $url->param('root', $root); } @@ -79,7 +79,7 @@ $params = array( 'context' => $modcontext, - 'objectid' => $forum->id + 'objectid' => $forum->id, ); $event = \mod_hsuforum\event\course_module_viewed::create($params); $event->add_record_snapshot('course_modules', $cm); @@ -98,32 +98,32 @@ } /// move discussion if requested - if ($move > 0 and confirm_sesskey()) { + if ($move > 0 && confirm_sesskey()) { $return = $CFG->wwwroot.'/mod/hsuforum/discuss.php?d='.$discussion->id; require_capability('mod/hsuforum:movediscussions', $modcontext); if ($forum->type == 'single') { - throw new \moodle_exception('cannotmovefromsingleforum', 'hsuforum', $return); + throw new \core\exception\moodle_exception('cannotmovefromsingleforum', 'hsuforum', $return); } if (!$forumto = $DB->get_record('hsuforum', array('id' => $move))) { - throw new \moodle_exception('cannotmovetonotexist', 'hsuforum', $return); + throw new \core\exception\moodle_exception('cannotmovetonotexist', 'hsuforum', $return); } if ($forumto->type == 'single') { - throw new \moodle_exception('cannotmovetosingleforum', 'hsuforum', $return); + throw new \core\exception\moodle_exception('cannotmovetosingleforum', 'hsuforum', $return); } // Get target forum cm and check it is visible to current user. $modinfo = get_fast_modinfo($course); $forums = $modinfo->get_instances_of('hsuforum'); if (!array_key_exists($forumto->id, $forums)) { - throw new \moodle_exception('cannotmovetonotfound', 'hsuforum', $return); + throw new \core\exception\moodle_exception('cannotmovetonotfound', 'hsuforum', $return); } $cmto = $forums[$forumto->id]; if (!$cmto->uservisible) { - throw new \moodle_exception('cannotmovenotvisible', 'hsuforum', $return); + throw new \core\exception\moodle_exception('cannotmovenotvisible', 'hsuforum', $return); } $destinationctx = context_module::instance($cmto->id); @@ -142,7 +142,7 @@ 'other' => array( 'fromforumid' => $forum->id, 'toforumid' => $forumto->id, - ) + ), ); $event = \mod_hsuforum\event\discussion_moved::create($params); $event->add_record_snapshot('hsuforum_discussions', $discussion); @@ -179,7 +179,7 @@ break; } - redirect(new moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id))); + redirect(new \core\url('/mod/hsuforum/discuss.php', array('d' => $discussion->id))); } // Trigger discussion viewed event. @@ -192,11 +192,11 @@ } if (! $post = hsuforum_get_post_full($root)) { - throw new \moodle_exception("notexists", 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); + throw new \core\exception\moodle_exception("notexists", 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?f=$forum->id"); } if (!hsuforum_user_can_see_post($forum, $discussion, $post, null, $cm, false)) { - throw new \moodle_exception('noviewdiscussionspermission', 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?id=$forum->id"); + throw new \core\exception\moodle_exception('noviewdiscussionspermission', 'hsuforum', "$CFG->wwwroot/mod/hsuforum/view.php?id=$forum->id"); } if ($mark == 'read') { @@ -210,7 +210,7 @@ } else { $forumnode->make_active(); } - $node = $forumnode->add(format_string($discussion->name), new moodle_url('/mod/hsuforum/discuss.php', array('d'=>$discussion->id))); + $node = $forumnode->add(format_string($discussion->name), new \core\url('/mod/hsuforum/discuss.php', array('d'=>$discussion->id))); $node->display = false; if ($node && $post->id != $discussion->firstpost) { $node->add(format_string($post->subject), $PAGE->url); @@ -236,11 +236,11 @@ /// Also, if we know they should be able to reply, then explicitly set $canreply for performance reasons $canreply = hsuforum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext); - if (!$canreply and $forum->type !== 'news') { + if (!$canreply && $forum->type !== 'news') { if (isguestuser() or !isloggedin()) { $canreply = true; } - if (!is_enrolled($modcontext) and !is_viewing($modcontext)) { + if (!is_enrolled($modcontext) && !is_viewing($modcontext)) { // allow guests and not-logged-in to see the link - they are prompted to log in after clicking the link // normal users with temporary guest access see this link too, they are asked to enrol instead $canreply = enrol_selfenrol_available($course->id); @@ -249,11 +249,11 @@ // Print Notice of Warning if Moving this Discussion - if ($move > 0 and confirm_sesskey()) { + if ($move > 0 && confirm_sesskey()) { echo $OUTPUT->confirm( get_string('anonymouswarning', 'hsuforum'), - new moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id, 'move' => $move, 'warned' => 1)), - new moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id)) + new \core\url('/mod/hsuforum/discuss.php', array('d' => $discussion->id, 'move' => $move, 'warned' => 1)), + new \core\url('/mod/hsuforum/discuss.php', array('d' => $discussion->id)) ); } @@ -274,7 +274,7 @@ echo $OUTPUT->notification(get_string('qandanotify', 'hsuforum')); } - if ($move == -1 and confirm_sesskey()) { + if ($move == -1 && confirm_sesskey()) { echo $OUTPUT->notification(get_string('discussionmoved', 'hsuforum', format_string($forum->name,true)), 'success'); } @@ -294,7 +294,7 @@ $button = ' '; $buttonextraclass = ' noavailable'; } - echo html_writer::tag('div', $button, array('class' => 'discussioncontrol exporttoportfolio'.$buttonextraclass)); + echo \core\output\html_writer::tag('div', $button, array('class' => 'discussioncontrol exporttoportfolio'.$buttonextraclass)); } if ($course->format !='singleactivity' && $forum->type != 'single' @@ -319,7 +319,7 @@ } $forumidcompare = $forumcm->instance != $forum->id; $forumtypecheck = $forumcheck[$forumcm->instance]->type !== 'single'; - if ($forumidcompare and $forumtypecheck) { + if ($forumidcompare && $forumtypecheck) { $url = "/mod/hsuforum/discuss.php?d=$discussion->id&move=$forumcm->instance&sesskey=".sesskey(); $forummenu[$section][$sectionname][$url] = format_string($forumcm->name); } @@ -329,7 +329,7 @@ } if (!empty($forummenu)) { echo '
'; - $select = new url_select($forummenu, '', + $select = new \core\output\url_select($forummenu, '', array('/mod/hsuforum/discuss.php?d=' . $discussion->id => get_string("movethisdiscussionto", "hsuforum")), 'forummenu'); echo $OUTPUT->render($select); @@ -344,8 +344,8 @@ if (isloggedin() && !isguestuser()) { if (get_config('core', 'theme') == 'snap') { - echo \html_writer::div(html_writer::link( - new \moodle_url( + echo \core\output\html_writer::div(html_writer::link( + new \core\url( '/mod/hsuforum/index.php', ['id' => $course->id] ), @@ -354,7 +354,7 @@ )); echo \html_writer::div(html_writer::link( - new \moodle_url( + new \core\url( '/mod/hsuforum/route.php', ['contextid' => $context->id, 'action' => 'export'] ), @@ -362,8 +362,8 @@ ['class' => 'exportdiscussionslink'] )); - echo \html_writer::div(html_writer::link( - new \moodle_url( + echo \core\output\html_writer::div(\core\output\html_writer::link( + new \core\url( '/mod/hsuforum/route.php', ['contextid' => $context->id, 'action' => 'viewposters'] ), @@ -377,8 +377,8 @@ $subscribe = get_string('unsubscribe', 'hsuforum'); } - echo \html_writer::div(html_writer::link( - new \moodle_url( + echo \core\output\html_writer::div(\core\output\html_writer::link( + new \core\url( '/mod/hsuforum/subscribe.php', ['id' => $forum->id, 'sesskey' => sesskey()] ), @@ -396,7 +396,7 @@ $gradingcontrollerpreview = $controller->render_preview($PAGE); if ($gradingcontrollerpreview) { - echo \html_writer::div(html_writer::link( + echo \core\output\html_writer::div(\core\output\html_writer::link( '#hsuforum_gradingcriteria', get_string('gradingmethodpreview', 'hsuforum'), ['class' => 'hsuforum_gradingcriteria', @@ -406,10 +406,10 @@ 'aria-controls' => 'hsuforum_gradingcriteria'] )); - echo \html_writer::div( - html_writer::div( - html_writer::div( - html_writer::div( + echo \core\output\html_writer::div( + \core\output\html_writer::div( + \core\output\html_writer::div( + \core\output\html_writer::div( $gradingcontrollerpreview, 'card card-body' ), 'collapse multi-collapse', ['id' => 'hsuforum_gradingcriteria'] ), 'col' diff --git a/externallib.php b/externallib.php index 5deface1..ea4b5e3b 100644 --- a/externallib.php +++ b/externallib.php @@ -1,5 +1,4 @@ libdir/externallib.php"); +use core_external\external_api; +use core_external\external_value; +use core_external\external_function_parameters; +use core_external\external_single_structure; +use core_external\external_multiple_structure; +use core_external\util as external_util; +use core_external\external_files; +use core_external\external_format_value; +use core_external\external_warnings; + class mod_hsuforum_external extends external_api { @@ -88,10 +96,10 @@ public static function get_forums_by_courses($courseids = array()) { continue; } - $forum->name = external_format_string($forum->name, $context->id); + $forum->name = \core_external\util::format_string($forum->name, $context->id); // Format the intro before being returning using the format setting. - list($forum->intro, $forum->introformat) = external_format_text($forum->intro, $forum->introformat, - $context->id, 'mod_hsuforum', 'intro', 0); + list($forum->intro, $forum->introformat) = \core_external\util::format_text($forum->intro, $forum->introformat, + $context, 'mod_hsuforum', 'intro', 0); $forum->introfiles = external_util::get_area_files($context->id, 'mod_hsuforum', 'intro', false, false); // Discussions count. This function does static request cache. $forum->numdiscussions = hsuforum_count_discussions($forum, $cm, $course); @@ -125,14 +133,19 @@ public static function get_forums_by_courses_returns() { 'name' => new external_value(PARAM_RAW, 'Forum name'), 'intro' => new external_value(PARAM_RAW, 'The forum intro'), 'introformat' => new external_format_value('intro'), + 'duedate' => new external_value(PARAM_INT, 'A due date to show in the calendar. Not used for grading.'), + 'cutoffdate' => new external_value(PARAM_INT, 'The final date after which forum posts will no longer be accepted for this forum.'), 'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL), 'assessed' => new external_value(PARAM_INT, 'Aggregate type'), 'assesstimestart' => new external_value(PARAM_INT, 'Assess start time'), 'assesstimefinish' => new external_value(PARAM_INT, 'Assess finish time'), 'scale' => new external_value(PARAM_INT, 'Scale'), + 'grade_forum' => new external_value(PARAM_INT, 'Grade forum'), + 'grade_forum_notify' => new external_value(PARAM_INT, 'Grade forum notify'), 'maxbytes' => new external_value(PARAM_INT, 'Maximum attachment size'), 'maxattachments' => new external_value(PARAM_INT, 'Maximum number of attachments'), 'forcesubscribe' => new external_value(PARAM_INT, 'Force users to subscribe'), + 'trackingtype' => new external_value(PARAM_INT, 'Tracking type'), 'rsstype' => new external_value(PARAM_INT, 'RSS feed for this activity'), 'rssarticles' => new external_value(PARAM_INT, 'Number of RSS recent articles'), 'timemodified' => new external_value(PARAM_INT, 'Time modified'), @@ -171,7 +184,7 @@ public static function get_forum_discussion_posts_parameters() { return new external_function_parameters ( array( 'discussionid' => new external_value(PARAM_INT, 'discussion ID', VALUE_REQUIRED), - ) + ), ); } @@ -214,12 +227,12 @@ public static function get_forum_discussion_posts($discussionid) { require_capability('mod/hsuforum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'hsuforum'); if (! $post = hsuforum_get_post_full($discussion->firstpost)) { - throw new moodle_exception('notexists', 'hsuforum'); + throw new \core\exception\moodle_exception('notexists', 'hsuforum'); } // This function check groups, qanda, timed discussions, etc. if (!hsuforum_user_can_see_post($forum, $discussion, $post, null, $cm)) { - throw new moodle_exception('noviewdiscussionspermission', 'hsuforum'); + throw new \core\exception\moodle_exception('noviewdiscussionspermission', 'hsuforum'); } $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext); @@ -280,14 +293,14 @@ public static function get_forum_discussion_posts($discussionid) { $user = username_load_fields_from_object($user, $post, null, array('picture', 'imagealt', 'email')); $post->userfullname = fullname($user, $canviewfullname); - $userpicture = new user_picture($user); + $userpicture = new \core\output\user_picture($user); $userpicture->size = 1; // Size f1. $post->userpictureurl = $userpicture->get_url($PAGE)->out(false); } // Rewrite embedded images URLs. list($post->message, $post->messageformat) = - external_format_text($post->message, $post->messageformat, $modcontext->id, 'mod_hsuforum', 'post', $post->id); + \core_external\util::format_text($post->message, $post->messageformat, $modcontext, 'mod_hsuforum', 'post', $post->id); // List attachments. if (!empty($post->attachment)) { @@ -341,12 +354,12 @@ public static function get_forum_discussion_posts_returns() { 'postread' => new external_value(PARAM_BOOL, 'The post was read'), 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'), 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.', VALUE_OPTIONAL), - 'deleted' => new external_value(PARAM_BOOL, 'This post has been removed.') + 'deleted' => new external_value(PARAM_BOOL, 'This post has been removed.'), ), 'post' ) ), 'ratinginfo' => \core_rating\external\util::external_ratings_structure(), - 'warnings' => new external_warnings() + 'warnings' => new external_warnings(), ) ); } @@ -397,7 +410,7 @@ public static function get_forum_discussions_paginated($forumid, $sortby = 'time 'sortby' => $sortby, 'sortdirection' => $sortdirection, 'page' => $page, - 'perpage' => $perpage + 'perpage' => $perpage, ) ); @@ -410,14 +423,14 @@ public static function get_forum_discussions_paginated($forumid, $sortby = 'time $sortallowedvalues = array('id', 'timemodified', 'timestart', 'timeend'); if (!in_array($sortby, $sortallowedvalues)) { - throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' . + throw new \core\exception\invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' . 'allowed values are: ' . implode(',', $sortallowedvalues)); } $sortdirection = strtoupper($sortdirection); $directionallowedvalues = array('ASC', 'DESC'); if (!in_array($sortdirection, $directionallowedvalues)) { - throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' . + throw new \core\exception\invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues)); } @@ -485,8 +498,8 @@ public static function get_forum_discussions_paginated($forumid, $sortby = 'time // Rewrite embedded images URLs. list($discussion->message, $discussion->messageformat) = - external_format_text($discussion->message, $discussion->messageformat, - $modcontext->id, 'mod_hsuforum', 'post', $discussion->id); + \core_external\util::format_text($discussion->message, $discussion->messageformat, + $modcontext, 'mod_hsuforum', 'post', $discussion->id); // List attachments. if (!empty($discussion->attachment)) { @@ -520,7 +533,7 @@ public static function get_forum_discussions_paginated($forumid, $sortby = 'time $user->id = $discussion->userid; $discussion->userfullname = fullname($user, $canviewfullname); - $userpicture = new user_picture($user); + $userpicture = new \core\output\user_picture($user); $userpicture->size = 1; // Size f1. $discussion->userpictureurl = $userpicture->get_url($PAGE)->out(false); @@ -531,7 +544,7 @@ public static function get_forum_discussions_paginated($forumid, $sortby = 'time $usermodified->id = $discussion->usermodified; $discussion->usermodifiedfullname = fullname($usermodified, $canviewfullname); - $userpicture = new user_picture($usermodified); + $userpicture = new \core\output\user_picture($usermodified); $userpicture->size = 1; // Size f1. $discussion->usermodifiedpictureurl = $userpicture->get_url($PAGE)->out(false); } @@ -593,10 +606,371 @@ public static function get_forum_discussions_paginated_returns() { ), 'post' ) ), + 'warnings' => new external_warnings(), + ) + ); + } + + /** + * Describes the parameters for get_forum_discussions. + * + * @return external_function_parameters + * @since Moodle 3.7 + */ + public static function get_forum_discussions_parameters() { + return new external_function_parameters ( + array( + 'forumid' => new external_value(PARAM_INT, 'forum instance id', VALUE_REQUIRED), + 'sortorder' => new external_value(PARAM_INT, + 'sort by this element: numreplies, , created or timemodified', VALUE_DEFAULT, -1), + 'page' => new external_value(PARAM_INT, 'current page', VALUE_DEFAULT, -1), + 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0), + 'groupid' => new external_value(PARAM_INT, 'group id', VALUE_DEFAULT, 0), + ) + ); + } + + /** + * Returns a list of hsuforum discussions optionally sorted and paginated. + * + * @param int $forumid the forum instance id + * @param int $sortorder The sort order + * @param int $page page number + * @param int $perpage items per page + * @param int $groupid the user course group + * + * + * @return array the hsuforum discussion details including warnings + * @since Moodle 3.7 + */ + public static function get_forum_discussions(int $forumid, ?int $sortorder = -1, ?int $page = -1, + ?int $perpage = 0, ?int $groupid = 0) { + + global $CFG, $DB, $USER; + + require_once($CFG->dirroot . "/mod/hsuforum/lib.php"); + + $warnings = array(); + $discussions = array(); + + $params = self::validate_parameters(self::get_forum_discussions_parameters(), + array( + 'forumid' => $forumid, + 'sortorder' => $sortorder, + 'page' => $page, + 'perpage' => $perpage, + 'groupid' => $groupid + ) + ); + + // Compact/extract functions are not recommended. + $forumid = $params['forumid']; + $sortorder = $params['sortorder']; + $page = $params['page']; + $perpage = $params['perpage']; + $groupid = $params['groupid']; + + $vaultfactory = \mod_hsuforum\local\container::get_vault_factory(); + $discussionlistvault = $vaultfactory->get_discussions_in_forum_vault(); + + $sortallowedvalues = array( + $discussionlistvault::SORTORDER_LASTPOST_DESC, + $discussionlistvault::SORTORDER_LASTPOST_ASC, + $discussionlistvault::SORTORDER_CREATED_DESC, + $discussionlistvault::SORTORDER_CREATED_ASC, + $discussionlistvault::SORTORDER_REPLIES_DESC, + $discussionlistvault::SORTORDER_REPLIES_ASC + ); + + // If sortorder not defined set a default one. + if ($sortorder == -1) { + $sortorder = $discussionlistvault::SORTORDER_LASTPOST_DESC; + } + + if (!in_array($sortorder, $sortallowedvalues)) { + throw new \core\exception\invalid_parameter_exception('Invalid value for sortorder parameter (value: ' . $sortorder . '),' . + ' allowed values are: ' . implode(',', $sortallowedvalues)); + } + + $managerfactory = \mod_hsuforum\local\container::get_manager_factory(); + $urlfactory = \mod_hsuforum\local\container::get_url_factory(); + $legacydatamapperfactory = mod_hsuforum\local\container::get_legacy_data_mapper_factory(); + + $forumvault = $vaultfactory->get_forum_vault(); + $forum = $forumvault->get_from_id($forumid); + if (!$forum) { + throw new \core\exception\moodle_exception("Unable to find hsuforum with id {$forumid}"); + } + $forumdatamapper = $legacydatamapperfactory->get_forum_data_mapper(); + $forumrecord = $forumdatamapper->to_legacy_object($forum); + + $capabilitymanager = $managerfactory->get_capability_manager($forum); + + $course = $DB->get_record('course', array('id' => $forum->get_course_id()), '*', MUST_EXIST); + $cm = get_coursemodule_from_instance('hsuforum', $forum->get_id(), $course->id, false, MUST_EXIST); + + // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..). + $modcontext = context_module::instance($cm->id); + self::validate_context($modcontext); + + $canseeanyprivatereply = $capabilitymanager->can_view_any_private_reply($USER); + + // Check they have the view hsuforum capability. + if (!$capabilitymanager->can_view_discussions($USER)) { + throw new \core\exception\moodle_exception('noviewdiscussionspermission', 'hsuforum'); + } + + $alldiscussions = mod_hsuforum_get_discussion_summaries($forum, $USER, $groupid, $sortorder, $page, $perpage); + + if ($alldiscussions) { + $discussionids = array_keys($alldiscussions); + + $postvault = $vaultfactory->get_post_vault(); + $postdatamapper = $legacydatamapperfactory->get_post_data_mapper(); + // Return the reply count for each discussion in a given hsuforum. + $replies = $postvault->get_reply_count_for_discussion_ids($USER, $discussionids, $canseeanyprivatereply); + // Return the first post for each discussion in a given hsuforum. + $firstposts = $postvault->get_first_post_for_discussion_ids($discussionids); + + // Get the unreads array, this takes a forum id and returns data for all discussions. + $unreads = array(); + if ($cantrack = forum_tp_can_track_forums($forumrecord)) { + if ($forumtracked = forum_tp_is_tracked($forumrecord)) { + $unreads = $postvault->get_unread_count_for_discussion_ids($USER, $discussionids, $canseeanyprivatereply); + } + } + + $canlock = $capabilitymanager->can_manage_forum($USER); + + $usercontext = context_user::instance($USER->id); + $ufservice = core_favourites\service_factory::get_service_for_user_context($usercontext); + + $canfavourite = has_capability('mod/hsuforum:cantogglefavourite', $modcontext, $USER); + + foreach ($alldiscussions as $discussionsummary) { + $discussion = $discussionsummary->get_discussion(); + $firstpostauthor = $discussionsummary->get_first_post_author(); + $latestpostauthor = $discussionsummary->get_latest_post_author(); + + // This function checks for qanda hsuforums. + $canviewdiscussion = $capabilitymanager->can_view_discussion($USER, $discussion); + if (!$canviewdiscussion) { + $warning = array(); + // Function forum_get_discussions returns forum_posts ids not forum_discussions ones. + $warning['item'] = 'post'; + $warning['itemid'] = $discussion->get_id(); + $warning['warningcode'] = '1'; + $warning['message'] = 'You can\'t see this discussion'; + $warnings[] = $warning; + continue; + } + + $firstpost = $firstposts[$discussion->get_first_post_id()]; + $discussionobject = $postdatamapper->to_legacy_object($firstpost); + // Fix up the types for these properties. + $discussionobject->mailed = $discussionobject->mailed ? 1 : 0; + $discussionobject->messagetrust = $discussionobject->messagetrust ? 1 : 0; + $discussionobject->mailnow = $discussionobject->mailnow ? 1 : 0; + $discussionobject->groupid = $discussion->get_group_id(); + $discussionobject->timemodified = $discussion->get_time_modified(); + $discussionobject->usermodified = $discussion->get_user_modified(); + $discussionobject->timestart = $discussion->get_time_start(); + $discussionobject->timeend = $discussion->get_time_end(); + $discussionobject->pinned = $discussion->is_pinned(); + + $discussionobject->numunread = 0; + if ($cantrack && $forumtracked) { + if (isset($unreads[$discussion->get_id()])) { + $discussionobject->numunread = (int) $unreads[$discussion->get_id()]; + } + } + + $discussionobject->numreplies = 0; + if (!empty($replies[$discussion->get_id()])) { + $discussionobject->numreplies = (int) $replies[$discussion->get_id()]; + } + + $discussionobject->name = \core_external\util::format_string($discussion->get_name(), $modcontext); + $discussionobject->subject = \core_external\util::format_string($discussionobject->subject, $modcontext); + // Rewrite embedded images URLs. + $options = array('trusted' => $discussionobject->messagetrust); + list($discussionobject->message, $discussionobject->messageformat) = + \core_external\util::format_text($discussionobject->message, $discussionobject->messageformat, + $modcontext, 'mod_hsuforum', 'post', $discussionobject->id, $options); + + // List attachments. + if (!empty($discussionobject->attachment)) { + $discussionobject->attachments = external_util::get_area_files($modcontext->id, 'mod_hsuforum', + 'attachment', $discussionobject->id); + } + $messageinlinefiles = external_util::get_area_files($modcontext->id, 'mod_hsuforum', 'post', + $discussionobject->id); + if (!empty($messageinlinefiles)) { + $discussionobject->messageinlinefiles = $messageinlinefiles; + } + + $discussionobject->locked = $forum->is_discussion_locked($discussion); + $discussionobject->canlock = $canlock; + $discussionobject->starred = !empty($ufservice) ? $ufservice->favourite_exists('mod_hsuforum', 'discussions', + $discussion->get_id(), $modcontext) : false; + $discussionobject->canreply = $capabilitymanager->can_post_in_discussion($USER, $discussion); + $discussionobject->canfavourite = $canfavourite; + + if (forum_is_author_hidden($discussionobject, $forumrecord)) { + $discussionobject->userid = null; + $discussionobject->userfullname = null; + $discussionobject->userpictureurl = null; + + $discussionobject->usermodified = null; + $discussionobject->usermodifiedfullname = null; + $discussionobject->usermodifiedpictureurl = null; + + } else { + $discussionobject->userfullname = $firstpostauthor->get_full_name(); + $discussionobject->userpictureurl = $urlfactory->get_author_profile_image_url($firstpostauthor, null, 2) + ->out(false); + + $discussionobject->usermodifiedfullname = $latestpostauthor->get_full_name(); + $discussionobject->usermodifiedpictureurl = $urlfactory->get_author_profile_image_url( + $latestpostauthor, null, 2)->out(false); + } + + $discussions[] = (array) $discussionobject; + } + } + $result = array(); + $result['discussions'] = $discussions; + $result['warnings'] = $warnings; + + return $result; + } + + /** + * Describes the get_forum_discussions return value. + * + * @return external_single_structure + * @since Moodle 3.7 + */ + public static function get_forum_discussions_returns() { + return new external_single_structure( + array( + 'discussions' => new external_multiple_structure( + new external_single_structure( + array( + 'id' => new external_value(PARAM_INT, 'Post id'), + 'name' => new external_value(PARAM_RAW, 'Discussion name'), + 'groupid' => new external_value(PARAM_INT, 'Group id'), + 'timemodified' => new external_value(PARAM_INT, 'Time modified'), + 'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'), + 'timestart' => new external_value(PARAM_INT, 'Time discussion can start'), + 'timeend' => new external_value(PARAM_INT, 'Time discussion ends'), + 'discussion' => new external_value(PARAM_INT, 'Discussion id'), + 'parent' => new external_value(PARAM_INT, 'Parent id'), + 'userid' => new external_value(PARAM_INT, 'User who started the discussion id'), + 'created' => new external_value(PARAM_INT, 'Creation time'), + 'modified' => new external_value(PARAM_INT, 'Time modified'), + 'mailed' => new external_value(PARAM_INT, 'Mailed?'), + 'subject' => new external_value(PARAM_RAW, 'The post subject'), + 'message' => new external_value(PARAM_RAW, 'The post message'), + 'messageformat' => new external_format_value('message'), + 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'), + 'messageinlinefiles' => new external_files('post message inline files', VALUE_OPTIONAL), + 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'), + 'attachments' => new external_files('attachments', VALUE_OPTIONAL), + 'totalscore' => new external_value(PARAM_INT, 'The post message total score'), + 'mailnow' => new external_value(PARAM_INT, 'Mail now?'), + 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'), + 'usermodifiedfullname' => new external_value(PARAM_TEXT, 'Post modifier full name'), + 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.'), + 'usermodifiedpictureurl' => new external_value(PARAM_URL, 'Post modifier picture.'), + 'numreplies' => new external_value(PARAM_INT, 'The number of replies in the discussion'), + 'numunread' => new external_value(PARAM_INT, 'The number of unread discussions.'), + 'pinned' => new external_value(PARAM_BOOL, 'Is the discussion pinned'), + 'locked' => new external_value(PARAM_BOOL, 'Is the discussion locked'), + 'starred' => new external_value(PARAM_BOOL, 'Is the discussion starred'), + 'canreply' => new external_value(PARAM_BOOL, 'Can the user reply to the discussion'), + 'canlock' => new external_value(PARAM_BOOL, 'Can the user lock the discussion'), + 'canfavourite' => new external_value(PARAM_BOOL, 'Can the user star the discussion'), + ), 'post' + ) + ), 'warnings' => new external_warnings() ) ); } + + /** + * Toggle the favouriting value for the discussion provided + * + * @param int $discussionid The discussion we need to favourite + * @param bool $targetstate The state of the favourite value + * @return array The exported discussion + */ + public static function toggle_favourite_state($discussionid, $targetstate) { + global $DB, $PAGE, $USER; + + $params = self::validate_parameters(self::toggle_favourite_state_parameters(), [ + 'discussionid' => $discussionid, + 'targetstate' => $targetstate + ]); + + $vaultfactory = mod_hsuforum\local\container::get_vault_factory(); + // Get the discussion vault and the corresponding discussion entity. + $discussionvault = $vaultfactory->get_discussion_vault(); + $discussion = $discussionvault->get_from_id($params['discussionid']); + + $forumvault = $vaultfactory->get_forum_vault(); + $forum = $forumvault->get_from_id($discussion->get_forum_id()); + $forumcontext = $forum->get_context(); + self::validate_context($forumcontext); + + $managerfactory = mod_hsuforum\local\container::get_manager_factory(); + $capabilitymanager = $managerfactory->get_capability_manager($forum); + + // Does the user have the ability to favourite the discussion? + if (!$capabilitymanager->can_favourite_discussion($USER)) { + throw new \core\exception\moodle_exception('cannotfavourite', 'hsuforum'); + } + $usercontext = context_user::instance($USER->id); + $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext); + $isfavourited = $ufservice->favourite_exists('mod_hsuforum', 'discussions', $discussion->get_id(), $forumcontext); + + $favouritefunction = $targetstate ? 'create_favourite' : 'delete_favourite'; + if ($isfavourited != (bool) $params['targetstate']) { + $ufservice->{$favouritefunction}('mod_hsuforum', 'discussions', $discussion->get_id(), $forumcontext); + } + + $exporterfactory = mod_hsuforum\local\container::get_exporter_factory(); + $builder = mod_hsuforum\local\container::get_builder_factory()->get_exported_discussion_builder(); + $favourited = ($builder->is_favourited($discussion, $forumcontext, $USER) ? [$discussion->get_id()] : []); + $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion, [], $favourited); + return $exporter->export($PAGE->get_renderer('mod_hsuforum')); + } + + /** + * Returns description of method result value + * + * @return \core_external\external_description + * @since Moodle 3.0 + */ + public static function toggle_favourite_state_returns() { + return discussion_exporter::get_read_structure(); + } + + /** + * Defines the parameters for the toggle_favourite_state method + * + * @return external_function_parameters + */ + public static function toggle_favourite_state_parameters() { + return new external_function_parameters( + [ + 'discussionid' => new external_value(PARAM_INT, 'The discussion to subscribe or unsubscribe'), + 'targetstate' => new external_value(PARAM_BOOL, 'The target state') + ] + ); + } + /** * Returns description of method parameters * @@ -606,7 +980,7 @@ public static function get_forum_discussions_paginated_returns() { public static function view_forum_parameters() { return new external_function_parameters( array( - 'forumid' => new external_value(PARAM_INT, 'forum instance id') + 'forumid' => new external_value(PARAM_INT, 'forum instance id'), ) ); } @@ -617,7 +991,7 @@ public static function view_forum_parameters() { * @param int $forumid the forum instance id * @return array of warnings and status result * @since Moodle 2.9 - * @throws moodle_exception + * @throws \core\exception\moodle_exception */ public static function view_forum($forumid) { global $DB, $CFG; @@ -625,7 +999,7 @@ public static function view_forum($forumid) { $params = self::validate_parameters(self::view_forum_parameters(), array( - 'forumid' => $forumid + 'forumid' => $forumid, )); $warnings = array(); $discussions = array(); @@ -658,7 +1032,7 @@ public static function view_forum_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'status: true if success'), - 'warnings' => new external_warnings() + 'warnings' => new external_warnings(), ) ); } @@ -672,8 +1046,8 @@ public static function view_forum_returns() { public static function view_forum_discussion_parameters() { return new external_function_parameters( array( - 'discussionid' => new external_value(PARAM_INT, 'discussion id') - ) + 'discussionid' => new external_value(PARAM_INT, 'discussion id'), + ), ); } @@ -683,7 +1057,7 @@ public static function view_forum_discussion_parameters() { * @param int $discussionid the discussion id * @return array of warnings and status result * @since Moodle 2.9 - * @throws moodle_exception + * @throws \core\exception\moodle_exception */ public static function view_forum_discussion($discussionid) { global $DB, $CFG, $USER; @@ -691,7 +1065,7 @@ public static function view_forum_discussion($discussionid) { $params = self::validate_parameters(self::view_forum_discussion_parameters(), array( - 'discussionid' => $discussionid + 'discussionid' => $discussionid, )); $warnings = array(); @@ -726,7 +1100,7 @@ public static function view_forum_discussion_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'status: true if success'), - 'warnings' => new external_warnings() + 'warnings' => new external_warnings(), ) ); } @@ -755,9 +1129,9 @@ public static function add_discussion_post_parameters() { '), 'value' => new external_value(PARAM_RAW, 'the value of the option, this param is validated in the external function.' - ) + ), ) - ), 'Options', VALUE_DEFAULT, array()) + ), 'Options', VALUE_DEFAULT, array()), ) ); } @@ -771,7 +1145,7 @@ public static function add_discussion_post_parameters() { * @param array $options optional settings * @return array of warnings and the new post id * @since Moodle 3.0 - * @throws moodle_exception + * @throws \core\exception\moodle_exception */ public static function add_discussion_post($postid, $subject, $message, $options = array()) { global $DB, $CFG, $USER; @@ -782,17 +1156,17 @@ public static function add_discussion_post($postid, $subject, $message, $options 'postid' => $postid, 'subject' => $subject, 'message' => $message, - 'options' => $options + 'options' => $options, ) ); $warnings = array(); if (!$parent = hsuforum_get_post_full($params['postid'])) { - throw new moodle_exception('invalidparentpostid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidparentpostid', 'hsuforum'); } if (!$discussion = $DB->get_record("hsuforum_discussions", array("id" => $parent->discussion))) { - throw new moodle_exception('notpartofdiscussion', 'hsuforum'); + throw new \core\exception\moodle_exception('notpartofdiscussion', 'hsuforum'); } // Request and permission validation. @@ -806,7 +1180,10 @@ public static function add_discussion_post($postid, $subject, $message, $options $options = array( 'discussionsubscribe' => true, 'inlineattachmentsid' => 0, - 'attachmentsid' => null + 'attachmentsid' => null, + 'privatereplyto' => 0, + 'wordcount' => null, + 'charcount' => null, ); foreach ($params['options'] as $option) { $name = trim($option['name']); @@ -815,6 +1192,9 @@ public static function add_discussion_post($postid, $subject, $message, $options $value = clean_param($option['value'], PARAM_BOOL); break; case 'inlineattachmentsid': + case 'privatereplyto': + case 'wordcount': + case 'charcount': $value = clean_param($option['value'], PARAM_INT); break; case 'attachmentsid': @@ -825,13 +1205,13 @@ public static function add_discussion_post($postid, $subject, $message, $options } break; default: - throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); + throw new \core\exception\moodle_exception('errorinvalidparam', 'webservice', '', $name); } $options[$name] = $value; } if (!hsuforum_user_can_post($forum, $discussion, $USER, $cm, $course, $context)) { - throw new moodle_exception('nopostforum', 'hsuforum'); + throw new \core\exception\moodle_exception('nopostforum', 'hsuforum'); } $thresholdwarning = hsuforum_check_throttling($forum, $cm); @@ -848,7 +1228,9 @@ public static function add_discussion_post($postid, $subject, $message, $options $post->reveal = 0; $post->flags = 0; $post->privatereply = 0; - + $post->privatereplyto = $options['privatereplyto']; + $post->wordcount = $options['wordcount']; + $post->charcount = $options['charcount']; $post->itemid = $options['inlineattachmentsid']; $post->attachments = $options['attachmentsid']; $post->deleted = 0; @@ -865,7 +1247,7 @@ public static function add_discussion_post($postid, $subject, $message, $options 'discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type, - ) + ), ); $event = \mod_hsuforum\event\post_created::create($params); $event->add_record_snapshot('hsuforum_posts', $post); @@ -883,7 +1265,7 @@ public static function add_discussion_post($postid, $subject, $message, $options $settings->discussionsubscribe = $options['discussionsubscribe']; hsuforum_post_subscription($settings, $forum, $discussion); } else { - throw new moodle_exception('couldnotadd', 'hsuforum'); + throw new \core\exception\moodle_exception('couldnotadd', 'hsuforum'); } $result = array(); @@ -902,7 +1284,7 @@ public static function add_discussion_post_returns() { return new external_single_structure( array( 'postid' => new external_value(PARAM_INT, 'new post id'), - 'warnings' => new external_warnings() + 'warnings' => new external_warnings(), ) ); } @@ -932,9 +1314,9 @@ public static function add_discussion_parameters() { '), 'value' => new external_value(PARAM_RAW, 'The value of the option, This param is validated in the external function.' - ) + ), ) - ), 'Options', VALUE_DEFAULT, array()) + ), 'Options', VALUE_DEFAULT, array()), ) ); } @@ -949,7 +1331,7 @@ public static function add_discussion_parameters() { * @param array $options optional settings * @return array of warnings and the new discussion id * @since Moodle 3.0 - * @throws moodle_exception + * @throws \core\exception\moodle_exception */ public static function add_discussion($forumid, $subject, $message, $groupid = 0, $options = array()) { global $DB, $CFG; @@ -961,7 +1343,7 @@ public static function add_discussion($forumid, $subject, $message, $groupid = 0 'subject' => $subject, 'message' => $message, 'groupid' => $groupid, - 'options' => $options + 'options' => $options, )); $warnings = array(); @@ -978,7 +1360,8 @@ public static function add_discussion($forumid, $subject, $message, $groupid = 0 'discussionsubscribe' => true, 'discussionpinned' => false, 'inlineattachmentsid' => 0, - 'attachmentsid' => null + 'attachmentsid' => null, + 'timelocked' => 0, ); foreach ($params['options'] as $option) { $name = trim($option['name']); @@ -990,6 +1373,7 @@ public static function add_discussion($forumid, $subject, $message, $groupid = 0 $value = clean_param($option['value'], PARAM_BOOL); break; case 'inlineattachmentsid': + case 'timelocked': $value = clean_param($option['value'], PARAM_INT); break; case 'attachmentsid': @@ -1000,7 +1384,7 @@ public static function add_discussion($forumid, $subject, $message, $groupid = 0 } break; default: - throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); + throw new \core\exception\moodle_exception('errorinvalidparam', 'webservice', '', $name); } $options[$name] = $value; } @@ -1021,7 +1405,7 @@ public static function add_discussion($forumid, $subject, $message, $groupid = 0 } if (!hsuforum_user_can_post_discussion($forum, $groupid, -1, $cm, $context)) { - throw new moodle_exception('cannotcreatediscussion', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotcreatediscussion', 'hsuforum'); } $thresholdwarning = hsuforum_check_throttling($forum, $cm); @@ -1043,6 +1427,7 @@ public static function add_discussion($forumid, $subject, $message, $groupid = 0 $discussion->timeend = 0; $discussion->reveal = 0; $discussion->attachments = $options['attachmentsid']; + $discussion->timelocked = $options['timelocked']; if (has_capability('mod/hsuforum:pindiscussions', $context) && $options['discussionpinned']) { $discussion->pinned = HSUFORUM_DISCUSSION_PINNED; @@ -1061,7 +1446,7 @@ public static function add_discussion($forumid, $subject, $message, $groupid = 0 'objectid' => $discussion->id, 'other' => array( 'forumid' => $forum->id, - ) + ), ); $event = \mod_hsuforum\event\discussion_created::create($params); $event->add_record_snapshot('hsuforum_discussions', $discussion); @@ -1077,7 +1462,7 @@ public static function add_discussion($forumid, $subject, $message, $groupid = 0 $settings->discussionsubscribe = $options['discussionsubscribe']; hsuforum_post_subscription($settings, $forum, $discussion); } else { - throw new moodle_exception('couldnotadd', 'hsuforum'); + throw new \core\exception\moodle_exception('couldnotadd', 'hsuforum'); } $result = array(); @@ -1096,8 +1481,8 @@ public static function add_discussion_returns() { return new external_single_structure( array( 'discussionid' => new external_value(PARAM_INT, 'New Discussion ID'), - 'warnings' => new external_warnings() - ) + 'warnings' => new external_warnings(), + ), ); } @@ -1112,7 +1497,7 @@ public static function can_add_discussion_parameters() { array( 'forumid' => new external_value(PARAM_INT, 'Forum instance ID'), 'groupid' => new external_value(PARAM_INT, 'The group to check, default to active group. - Use -1 to check if the user can post in all the groups.', VALUE_DEFAULT, null) + Use -1 to check if the user can post in all the groups.', VALUE_DEFAULT, null), ) ); } @@ -1124,7 +1509,7 @@ public static function can_add_discussion_parameters() { * @param int $groupid the group to check, default to active group. Use -1 to check if the user can post in all the groups. * @return array of warnings and the status (true if the user can add discussions) * @since Moodle 3.1 - * @throws moodle_exception + * @throws \core\exception\moodle_exception */ public static function can_add_discussion($forumid, $groupid = null) { global $DB, $CFG; @@ -1168,7 +1553,7 @@ public static function can_add_discussion_returns() { VALUE_OPTIONAL), 'cancreateattachment' => new external_value(PARAM_BOOL, 'True if the user can add attachments, false otherwise.', VALUE_OPTIONAL), - 'warnings' => new external_warnings() + 'warnings' => new external_warnings(), ) ); } diff --git a/index.php b/index.php index c61d9475..f74d13bc 100644 --- a/index.php +++ b/index.php @@ -1,5 +1,4 @@ $id)); +$url = new \core\url('/mod/hsuforum/index.php', array('id' => $id)); if ($subscribe !== null) { require_sesskey(); $url->param('subscribe', $subscribe); @@ -42,7 +41,7 @@ if ($id) { if (!$course = $DB->get_record('course', array('id' => $id))) { - throw new \moodle_exception('invalidcourseid'); + throw new \core\exception\moodle_exception('invalidcourseid'); } } else { $course = get_site(); @@ -55,7 +54,7 @@ unset($SESSION->fromdiscussion); $params = array( - 'context' => context_course::instance($course->id) + 'context' => context_course::instance($course->id), ); $event = \mod_hsuforum\event\course_module_instance_list_viewed::create($params); $event->add_record_snapshot('course', $course); @@ -79,7 +78,7 @@ // Retrieve the list of forum digest options for later. $digestoptions = hsuforum_get_user_digest_options(); -$digestoptions_selector = new single_select(new moodle_url('/mod/hsuforum/maildigest.php', +$digestoptions_selector = new \core\output\single_select(new \core\url('/mod/hsuforum/maildigest.php', array( 'backtoindex' => 1, )), @@ -90,7 +89,7 @@ $digestoptions_selector->method = 'post'; // Start of the table for General Forums. -$generaltable = new html_table(); +$generaltable = new \core_table\output\html_table(); $generaltable->head = array ($strforum, $strdescription, $strdiscussions); $generaltable->align = array ('left', 'left', 'center'); @@ -117,7 +116,7 @@ $usesections = course_format_uses_sections($course->format); -$table = new html_table(); +$table = new \core_table\output\html_table(); // Parse and organise all the forums. Most forums are course modules but // some special ones are not. These get placed in the general forums @@ -173,7 +172,7 @@ } /// Do course wide subscribe/unsubscribe -if (!is_null($subscribe) and !isguestuser()) { +if (!is_null($subscribe) && !isguestuser()) { foreach ($modinfo->get_instances_of('hsuforum') as $forumid=>$cm) { $forum = $forums[$forumid]; $modcontext = context_module::instance($cm->id); @@ -196,7 +195,7 @@ } } } - $returnto = hsuforum_go_back_to(new moodle_url('/mod/hsuforum/index.php', array('id' => $course->id))); + $returnto = hsuforum_go_back_to(new \core\url('/mod/hsuforum/index.php', array('id' => $course->id))); $shortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id))); if ($subscribe) { redirect( @@ -249,7 +248,7 @@ // If this forum has RSS activated, calculate it. if ($show_rss) { - if ($forum->rsstype and $forum->rssarticles) { + if ($forum->rsstype && $forum->rssarticles) { //Calculate the tooltip text if ($forum->rsstype == 1) { $tooltiptext = get_string('rsssubscriberssdiscussions', 'hsuforum'); @@ -275,7 +274,7 @@ // Start of the table for Learning Forums -$learningtable = new html_table(); +$learningtable = new \core_table\output\html_table(); $learningtable->head = array ($strforum, $strdescription, $strdiscussions); $learningtable->align = array ('left', 'left', 'center'); @@ -360,7 +359,7 @@ //If this forum has RSS activated, calculate it if ($show_rss) { - if ($forum->rsstype and $forum->rssarticles) { + if ($forum->rsstype && $forum->rssarticles) { //Calculate the tolltip text if ($forum->rsstype == 1) { $tooltiptext = get_string('rsssubscriberssdiscussions', 'hsuforum'); @@ -389,12 +388,12 @@ if (!isguestuser() && isloggedin()) { echo $OUTPUT->box_start('subscription'); - echo html_writer::tag('div', - html_writer::link(new moodle_url('/mod/hsuforum/index.php', array('id'=>$course->id, 'subscribe'=>1, 'sesskey'=>sesskey())), + echo \core\output\html_writer::tag('div', + \core\output\html_writer::link(new \core\url('/mod/hsuforum/index.php', array('id'=>$course->id, 'subscribe'=>1, 'sesskey'=>sesskey())), get_string('allsubscribe', 'hsuforum')), array('class'=>'helplink')); - echo html_writer::tag('div', - html_writer::link(new moodle_url('/mod/hsuforum/index.php', array('id'=>$course->id, 'subscribe'=>0, 'sesskey'=>sesskey())), + echo \core\output\html_writer::tag('div', + \core\output\html_writer::link(new \core\url('/mod/hsuforum/index.php', array('id'=>$course->id, 'subscribe'=>0, 'sesskey'=>sesskey())), get_string('allunsubscribe', 'hsuforum')), array('class'=>'helplink')); echo $OUTPUT->box_end(); @@ -403,12 +402,12 @@ if ($generalforums) { echo $OUTPUT->heading(get_string('generalforums', 'hsuforum'), 2); - echo html_writer::table($generaltable); + echo \core\output\html_writer::table($generaltable); } if ($learningforums) { echo $OUTPUT->heading(get_string('learningforums', 'hsuforum'), 2); - echo html_writer::table($learningtable); + echo \core\output\html_writer::table($learningtable); } echo $OUTPUT->footer(); diff --git a/lang/en/hsuforum.php b/lang/en/hsuforum.php index 1921b30a..3fd164fd 100644 --- a/lang/en/hsuforum.php +++ b/lang/en/hsuforum.php @@ -188,6 +188,11 @@ * Digest - complete posts - you will receive one digest e-mail per day containing the complete contents of each forum post; * Digest - subjects only - you will receive one digest e-mail per day containing just the subject of each forum post. '; +$string['emaildigestupdated'] = 'The e-mail digest option was changed to \'{$a->maildigesttitle}\' for the forum \'{$a->forum}\'. {$a->maildigestdescription}'; +$string['emaildigestupdated_default'] = 'Your default profile setting of \'{$a->maildigesttitle}\' was used for the forum \'{$a->forum}\'. {$a->maildigestdescription}.'; +$string['emaildigest_0'] = 'You will receive one e-mail per forum post.'; +$string['emaildigest_1'] = 'You will receive one digest e-mail per day containing the complete contents of each forum post.'; +$string['emaildigest_2'] = 'You will receive one digest e-mail per day containing the subject of each forum post.'; $string['emptymessage'] = 'Something was wrong with your post. Perhaps you left it blank or the attachment was too big. Your changes have NOT been saved.'; $string['erroremptymessage'] = 'Post message cannot be empty'; $string['erroremptysubject'] = 'Post subject cannot be empty.'; @@ -203,6 +208,7 @@ $string['exportdiscussion'] = 'Export whole discussion to portfolio'; $string['forcessubscribe'] = 'This forum forces everyone to be subscribed'; $string['forum'] = 'Forum'; +$string['from'] = 'From'; $string['gradingmethodpreview'] = 'Grading criteria'; $string['hsuforum:addinstance'] = 'Add a new forum'; $string['hsuforum:allowforcesubscribe'] = 'Allow force subscribe'; @@ -467,6 +473,16 @@ $string['privacy:metadata:hsuforum_track_prefs:forumid'] = 'The Open Forum that has read tracking enabled.'; $string['privacy:metadata:hsuforum_track_prefs'] = 'Information about which forums the user has chosen to track post reads for.'; +$string['privacy:metadata:hsuforum_discussion_subs:discussionid'] = 'The ID of the discussion that was subscribed to.'; +$string['privacy:metadata:hsuforum_discussion_subs:preference'] = 'The start time of the subscription.'; +$string['privacy:metadata:hsuforum_discussion_subs:userid'] = 'The ID of the user with the discussion subscription.'; +$string['privacy:metadata:hsuforum_discussion_subs'] = 'Information about the subscriptions to individual forum discussions'; + +$string['privacy:metadata:hsuforum_grades:forum'] = 'The forum that was graded'; +$string['privacy:metadata:hsuforum_grades:grade'] = 'The grade awarded'; +$string['privacy:metadata:hsuforum_grades:userid'] = 'The user who was graded'; +$string['privacy:metadata:hsuforum_grades'] = 'Grade data for the forum'; + $string['privacy:metadata:core_tag'] = 'The Open Forum makes use of the tag subsystem to support tagging of posts.'; $string['privacy:metadata:core_rating'] = 'The Open Forum makes use of the rating subsystem to support the rating of posts.'; @@ -579,6 +595,7 @@ $string['timedposts'] = 'Timed posts'; $string['timedvisible'] = 'Timed status: Visible to all users'; $string['timestartenderror'] = 'Display end date cannot be earlier than the start date'; +$string['to'] = 'To'; $string['trackforum'] = 'Track unread posts'; $string['trackreadposts_header'] = 'Forum tracking'; $string['unread'] = 'New'; @@ -617,6 +634,11 @@ $string['reveal_help'] = 'If checked, then your name will be shown in the post and you will no longer be anonymous.'; $string['hsuforum:revealpost'] = 'Reveal yourself in an anonymous forum'; $string['hsuforum:viewflags'] = 'View post flags'; +$string['hsuforum:canoverridecutoff'] = 'Post to forums after their cut-off date'; +$string['hsuforum:cantogglefavourite'] = 'Star discussions'; +$string['hsuforum:grade'] = 'Grade forum'; +$string['hsuforum:postprivatereply'] = 'Reply privately to posts'; +$string['hsuforum:readprivatereplies'] = 'View private replies'; $string['viewposters'] = 'View posters'; $string['substantive'] = 'Substantive'; $string['toggle:bookmark'] = 'Bookmark'; diff --git a/lib.php b/lib.php index b5107cd9..6396f96c 100644 --- a/lib.php +++ b/lib.php @@ -30,6 +30,11 @@ use mod_hsuforum\renderables\advanced_editor; /// CONSTANTS /////////////////////////////////////////////////////////// +define('HSUFORUM_MODE_FLATOLDEST', 1); +define('HSUFORUM_MODE_FLATNEWEST', -1); +define('HSUFORUM_MODE_THREADED', 2); +define('HSUFORUM_MODE_NESTED', 3); +define('HSUFORUM_MODE_NESTED_V2', 4); define('HSUFORUM_CHOOSESUBSCRIBE', 0); define('HSUFORUM_FORCESUBSCRIBE', 1); @@ -127,7 +132,7 @@ function hsuforum_add_instance($forum, $mform = null) { $discussion->id = hsuforum_add_discussion($discussion, null, $message); - if ($mform and $draftid = file_get_submitted_draft_itemid('introeditor')) { + if ($mform && $draftid = file_get_submitted_draft_itemid('introeditor')) { // Ugly hack - we need to copy the files somehow. $discussion = $DB->get_record('hsuforum_discussions', array('id'=>$discussion->id), '*', MUST_EXIST); $post = $DB->get_record('hsuforum_posts', array('id'=>$discussion->firstpost), '*', MUST_EXIST); @@ -229,11 +234,11 @@ function hsuforum_update_instance($forum, $mform) { hsuforum_add_discussion($discussion, null, $message); if (! $discussion = $DB->get_record('hsuforum_discussions', array('forum'=>$forum->id))) { - throw new \moodle_exception('cannotadd', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotadd', 'hsuforum'); } } if (! $post = $DB->get_record('hsuforum_posts', array('id'=>$discussion->firstpost))) { - throw new \moodle_exception('cannotfindfirstpost', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotfindfirstpost', 'hsuforum'); } $cm = get_coursemodule_from_instance('hsuforum', $forum->id); @@ -247,7 +252,7 @@ function hsuforum_update_instance($forum, $mform) { $post->modified = $forum->timemodified; $post->userid = $USER->id; // MDL-18599, so that current teacher can take ownership of activities. - if ($mform and $draftid = file_get_submitted_draft_itemid('introeditor')) { + if ($mform && $draftid = file_get_submitted_draft_itemid('introeditor')) { // Ugly hack - we need to copy the files somehow. $options = array('subdirs'=>true); // Use the same options as intro field! $post->message = file_save_draft_area_files($draftid, $modcontext->id, 'mod_hsuforum', 'post', $post->id, $options, $post->message); @@ -636,7 +641,7 @@ function hsuforum_cron() { } $coursecontext = context_course::instance($course->id); - if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext, $userto->id)) { + if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext, $userto->id)) { // The course is hidden and the user does not have access to it. continue; } @@ -698,14 +703,14 @@ function hsuforum_cron() { } // Make sure groups allow this user to see this email. - if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { + if ($discussion->groupid > 0 && $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used. if (!groups_group_exists($discussion->groupid)) { // Can't find group - be safe and don't this message. continue; } - if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $modcontext)) { + if (!groups_is_member($discussion->groupid) && !has_capability('moodle/site:accessallgroups', $modcontext)) { // Do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS. continue; } @@ -831,7 +836,7 @@ function hsuforum_cron() { if (!empty($replyaddress)) { // Add extra text to email messages if they can reply back. $textfooter = "\n\n" . get_string('replytopostbyemail', 'mod_hsuforum'); - $htmlfooter = html_writer::tag('p', get_string('replytopostbyemail', 'mod_hsuforum')); + $htmlfooter = \core\output\html_writer::tag('p', get_string('replytopostbyemail', 'mod_hsuforum')); $additionalcontent = array('fullmessage' => array('footer' => $textfooter), 'fullmessagehtml' => array('footer' => $htmlfooter)); $eventdata->set_additional_content('email', $additionalcontent); @@ -845,7 +850,7 @@ function hsuforum_cron() { // Make sure strings are in message recipients language. $eventdata->smallmessage = get_string_manager()->get_string('smallmessage', 'hsuforum', $smallmessagestrings, $userto->lang); - $contexturl = new moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id), 'p' . $post->id); + $contexturl = new \core\url('/mod/hsuforum/discuss.php', array('d' => $discussion->id), 'p' . $post->id); $eventdata->contexturl = $contexturl->out(); $eventdata->contexturlname = $discussion->name; @@ -910,7 +915,7 @@ function hsuforum_cron() { $DB->delete_records_select('hsuforum_queue', "timemodified < ?", array($weekago)); mtrace ('Cleaned old digest records'); - if ($config->digestmailtimelast < $digesttime and $timenow > $digesttime) { + if ($config->digestmailtimelast < $digesttime && $timenow > $digesttime) { mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone)); @@ -985,7 +990,7 @@ function hsuforum_cron() { // Init user caches - we keep the cache for one cycle only, // otherwise it would unnecessarily consume memory. - if (array_key_exists($userid, $users) and isset($users[$userid]->username)) { + if (array_key_exists($userid, $users) && isset($users[$userid]->username)) { $userto = clone($users[$userid]); } else { $userto = $DB->get_record('user', array('id' => $userid)); @@ -1086,6 +1091,11 @@ function hsuforum_cron() { continue; } + // Avoid sending confidentiary data to unsuspecting eyes. + if ($post->privatereply != '0' && ($post->privatereply != $userto->id && $post->userid != $userto->id)) { + continue; + } + if (!isset($userfrom->groups[$forum->id])) { if (!isset($userfrom->groups)) { $userfrom->groups = array(); @@ -1305,7 +1315,7 @@ function hsuforum_user_complete($course, $user, $mod, $forum) { if ($posts = hsuforum_get_user_posts($forum->id, $user->id)) { if (!$cm = get_coursemodule_from_instance('hsuforum', $forum->id, $course->id)) { - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } $discussions = hsuforum_get_user_involved_discussions($forum->id, $user->id); @@ -1502,7 +1512,7 @@ function hsuforum_print_overview($courses,&$htmlarray) { } } - if (empty($unread) and empty($forumsnewposts)) { + if (empty($unread) && empty($forumsnewposts)) { return; } @@ -1649,8 +1659,8 @@ function hsuforum_recent_activity($course, $viewfullnames, $timestart, $forumid continue; } - if (!empty($config->enabletimedposts) and $USER->id != $post->duserid - and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time())) + if (!empty($config->enabletimedposts) && $USER->id != $post->duserid + && (($post->timestart > 0 && $post->timestart > time()) or ($post->timeend > 0 && $post->timeend < time())) ) { if (!has_capability('mod/hsuforum:viewhiddentimedposts', $context)) { continue; @@ -1661,7 +1671,7 @@ function hsuforum_recent_activity($course, $viewfullnames, $timestart, $forumid if (hsuforum_is_user_group_discussion($cm, $post->groupid)) { $postuser = hsuforum_extract_postuser($post, hsuforum_get_cm_forum($cm), context_module::instance($cm->id)); - $userpicture = new user_picture($postuser); + $userpicture = new \core\output\user_picture($postuser); $userpicture->link = false; $userpicture->alttext = false; $userpicture->size = 100; @@ -1718,7 +1728,7 @@ function hsuforum_media_object($url, $picture, $username, $time, $subject) { */ function hsuforum_get_user_formatted_rating_grade($forum, $userid) { $grades = hsuforum_get_user_rating_grades($forum, $userid); - if (!empty($grades) and array_key_exists($userid, $grades)) { + if (!empty($grades) && array_key_exists($userid, $grades)) { $gradeitem = grade_item::fetch(array( 'courseid' => $forum->course, 'itemtype' => 'mod', @@ -1796,13 +1806,13 @@ function hsuforum_update_grades($forum, $userid=0, $nullifnone=true) { require_once($CFG->libdir.'/gradelib.php'); if ($forum->gradetype == HSUFORUM_GRADETYPE_NONE or $forum->gradetype == HSUFORUM_GRADETYPE_MANUAL or - ($forum->gradetype == HSUFORUM_GRADETYPE_RATING and !$forum->assessed)) { + ($forum->gradetype == HSUFORUM_GRADETYPE_RATING && !$forum->assessed)) { hsuforum_grade_item_update($forum); } else if ($grades = hsuforum_get_user_grades($forum, $userid)) { hsuforum_grade_item_update($forum, $grades); - } else if ($userid and $nullifnone) { + } else if ($userid && $nullifnone) { $grade = new stdClass(); $grade->userid = $userid; $grade->rawgrade = NULL; @@ -1830,7 +1840,7 @@ function hsuforum_upgrade_grades() { WHERE m.name='hsuforum' AND m.id=cm.module AND cm.instance=f.id"; $rs = $DB->get_recordset_sql($sql); if ($rs->valid()) { - $pbar = new progress_bar('forumupgradegrades', 500, true); + $pbar = new \core\output\progress_bar('forumupgradegrades', 500, true); $i=0; foreach ($rs as $forum) { $i++; @@ -1861,7 +1871,7 @@ function hsuforum_grade_item_update($forum, $grades=NULL) { $params = array('itemname'=>$forum->name, 'idnumber'=>$forum->cmidnumber); - if ($forum->gradetype == HSUFORUM_GRADETYPE_NONE or ($forum->gradetype == HSUFORUM_GRADETYPE_RATING and !$forum->assessed) or $forum->scale == 0) { + if ($forum->gradetype == HSUFORUM_GRADETYPE_NONE or ($forum->gradetype == HSUFORUM_GRADETYPE_RATING && !$forum->assessed) or $forum->scale == 0) { $params['gradetype'] = GRADE_TYPE_NONE; } else if ($forum->scale > 0) { @@ -1928,7 +1938,7 @@ function hsuforum_scale_used ($forumid,$scaleid) { */ function hsuforum_scale_used_anywhere($scaleid) { global $DB; - if ($scaleid and $DB->record_exists('hsuforum', array('scale' => -$scaleid))) { + if ($scaleid && $DB->record_exists('hsuforum', array('scale' => -$scaleid))) { return true; } else { return false; @@ -2056,7 +2066,7 @@ function hsuforum_get_readable_forums($userid, $courseid=0, $excludeanonymous = require_once($CFG->dirroot.'/course/lib.php'); if (!$forummod = $DB->get_record('modules', array('name' => 'hsuforum'))) { - throw new \moodle_exception('notinstalled', 'hsuforum'); + throw new \core\exception\moodle_exception('notinstalled', 'hsuforum'); } $config = get_config('hsuforum'); @@ -2104,7 +2114,7 @@ function hsuforum_get_readable_forums($userid, $courseid=0, $excludeanonymous = } /// group access - if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { + if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $context)) { $forum->onlygroups = $modinfo->get_groups($cm->groupingid); $forum->onlygroups[] = -1; @@ -2669,7 +2679,7 @@ function hsuforum_count_discussion_replies($forumid, $forumsort="", $limit=-1, $ $groupby = str_replace('asc', '', $groupby); } - if (($limitfrom == 0 and $limitnum == 0) or $forumsort == "") { + if (($limitfrom == 0 && $limitnum == 0) or $forumsort == "") { $sql = "SELECT p.discussion, COUNT(p.id) AS replies, MAX(p.id) AS lastpostid FROM {hsuforum_posts} p JOIN {hsuforum_discussions} d ON p.discussion = d.id @@ -2960,7 +2970,7 @@ function hsuforum_get_discussions($cm, $forumsort="", $forumselect=true, $unused } // Sort of hacky, but allows for custom select - if (is_string($forumselect) and !empty($forumselect)) { + if (is_string($forumselect) && !empty($forumselect)) { $selectsql = $forumselect; } else { $allnames = $userfieldsapi->get_sql('u', false, '', '', false)->selects; @@ -3022,7 +3032,7 @@ function hsuforum_get_discussion_neighbours($cm, $discussion, $forum) { $config = get_config('hsuforum'); if ($cm->instance != $discussion->forum or $discussion->forum != $forum->id or $forum->id != $cm->instance) { - throw new coding_exception('Discussion is not part of the same forum.'); + throw new \core\exception\coding_exception('Discussion is not part of the same forum.'); } $neighbours = array('prev' => false, 'next' => false); @@ -3567,9 +3577,9 @@ function hsuforum_print_post_start($post, $return = false) { $attributes = [ 'id' => 'p'.$post->id, 'tabindex' => -1, - 'class' => 'relativelink' + 'class' => 'relativelink', ]; - $output .= html_writer::start_tag('article', $attributes); + $output .= \core\output\html_writer::start_tag('article', $attributes); } if ($return) { return $output; @@ -3589,7 +3599,7 @@ function hsuforum_print_post_end($post, $return = false) { $output = ''; if (hsuforum_should_end_post_nesting($post->id)) { - $output .= html_writer::end_tag('article'); + $output .= \core\output\html_writer::end_tag('article'); } if ($return) { return $output; @@ -3616,7 +3626,7 @@ function hsuforum_rating_permissions($contextid, $component, $ratingarea) { 'view' => has_capability('mod/hsuforum:viewrating', $context), 'viewany' => has_capability('mod/hsuforum:viewanyrating', $context), 'viewall' => has_capability('mod/hsuforum:viewallratings', $context), - 'rate' => has_capability('mod/hsuforum:rate', $context) + 'rate' => has_capability('mod/hsuforum:rate', $context), ); } @@ -3701,22 +3711,22 @@ function hsuforum_rating_validate($params) { } // Make sure groups allow this user to see the item they're rating - if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used + if ($discussion->groupid > 0 && $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used if (!groups_group_exists($discussion->groupid)) { // Can't find group throw new rating_exception('cannotfindgroup');//something is wrong } if (!empty($discussion->unread) && $discussion->unread !== '-') { $replystring .= ' / '; - $unreadlink = new moodle_url($discussionlink, null, 'unread'); + $unreadlink = new \core\url($discussionlink, null, 'unread'); if ($discussion->unread == 1) { - $replystring .= html_writer::link($unreadlink, get_string('unreadpostsone', 'hsuforum')); + $replystring .= \core\output\html_writer::link($unreadlink, get_string('unreadpostsone', 'hsuforum')); } else { - $replystring .= html_writer::link($unreadlink, get_string('unreadpostsnumber', 'hsuforum', $discussion->unread)); + $replystring .= \core\output\html_writer::link($unreadlink, get_string('unreadpostsnumber', 'hsuforum', $discussion->unread)); } $replystring .= ''; } - if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) { + if (!groups_is_member($discussion->groupid) && !has_capability('moodle/site:accessallgroups', $context)) { // do not allow rating of posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS throw new rating_exception('notmemberofgroup'); } @@ -3776,7 +3786,7 @@ function hsuforum_set_return() { * itemid => int the ID of the object being rated [required] * scaleid => int scale id [optional] * @return bool - * @throws coding_exception + * @throws \core\exception\coding_exception * @throws rating_exception */ function mod_hsuforum_rating_can_see_item_ratings($params) { @@ -3811,7 +3821,7 @@ function mod_hsuforum_rating_can_see_item_ratings($params) { /** * @global object - * @param string|\moodle_url $default + * @param string|\core\url $default * @return string */ function hsuforum_go_back_to($default) { @@ -3821,7 +3831,7 @@ function hsuforum_go_back_to($default) { && (!defined(AJAX_SCRIPT) || !AJAX_SCRIPT)) { // If we have an ajax fromdiscussion session variable then we need to get rid of it because this is not an // ajax page and we will end up redirecting incorrectly to route.php. - $murl = new moodle_url($SESSION->fromdiscussion); + $murl = new \core\url($SESSION->fromdiscussion); $path = $murl->get_path(); if (strpos($path, '/mod/hsuforum/route.php') === 0) { // OK - this is bad, we are not using AJAX but the redirect url is an AJAX url, so kill it. @@ -4167,7 +4177,7 @@ function hsuforum_pluginfile($course, $cm, $context, $filearea, $args, $forcedow if ($discussion->groupid > 0) { $groupmode = groups_get_activity_groupmode($cm, $course); if ($groupmode == SEPARATEGROUPS) { - if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) { + if (!groups_is_member($discussion->groupid) && !has_capability('moodle/site:accessallgroups', $context)) { return false; } } @@ -4247,6 +4257,7 @@ function hsuforum_add_new_post($post, $mform, $unused=null, \mod_hsuforum\upload $post->mailed = HSUFORUM_MAILED_PENDING; $post->userid = $USER->id; $post->attachment = 0; + $post->privatereplyto = 0; if (!isset($post->totalscore)) { $post->totalscore = 0; } @@ -4262,6 +4273,8 @@ function hsuforum_add_new_post($post, $mform, $unused=null, \mod_hsuforum\upload $post->id = $DB->insert_record("hsuforum_posts", $post); $post->message = file_save_draft_area_files($draftid, $context->id, 'mod_hsuforum', 'post', $post->id, mod_hsuforum_post_form::editor_options($context, $post->id), $post->message); + $post->wordcount = count_words($post->message, $post->messageformat); + $post->charcount = count_letters($post->message, $post->messageformat); $DB->update_record('hsuforum_posts', $post); hsuforum_add_attachment($post, $forum, $cm, $mform, null, $uploader); @@ -4431,6 +4444,7 @@ function hsuforum_add_discussion($discussion, $mform=null, $unused=null, $userid $discussion->usermodified = $post->userid; $discussion->userid = $userid; $discussion->assessed = 0; + $discussion->timelocked = 0; $post->discussion = $DB->insert_record("hsuforum_discussions", $discussion); @@ -4480,20 +4494,20 @@ function hsuforum_verify_and_delete_post($course, $cm, $forum, $modcontext, $dis // Check user capability to delete post. $timepassed = time() - $post->created; if (($timepassed > $CFG->maxeditingtime) && !has_capability('mod/hsuforum:deleteanypost', $modcontext)) { - throw new \moodle_exception("cannotdeletepost", "hsuforum", + throw new \core\exception\moodle_exception("cannotdeletepost", "hsuforum", hsuforum_go_back_to("discuss.php?d=$post->discussion")); } if ($post->totalscore) { - throw new \moodle_exception('couldnotdeleteratings', 'rating', + throw new \core\exception\moodle_exception('couldnotdeleteratings', 'rating', hsuforum_go_back_to("discuss.php?d=$post->discussion")); } if (hsuforum_count_replies($post) && !has_capability('mod/hsuforum:deleteanypost', $modcontext)) { - throw new \moodle_exception("couldnotdeletereplies", "hsuforum", + throw new \core\exception\moodle_exception("couldnotdeletereplies", "hsuforum", hsuforum_go_back_to("discuss.php?d=$post->discussion")); } if (!$post->parent) { // post is a discussion topic as well, so delete discussion if ($forum->type == 'single') { - throw new \moodle_exception('cannnotdeletesinglediscussion', 'hsuforum', + throw new \core\exception\moodle_exception('cannnotdeletesinglediscussion', 'hsuforum', hsuforum_go_back_to("discuss.php?d=$post->discussion")); } hsuforum_delete_discussion($discussion, false, $course, $cm, $forum); @@ -4503,7 +4517,7 @@ function hsuforum_verify_and_delete_post($course, $cm, $forum, $modcontext, $dis 'context' => $modcontext, 'other' => array( 'forumid' => $forum->id, - ) + ), ); $event = \mod_hsuforum\event\discussion_deleted::create($params); @@ -4514,7 +4528,7 @@ function hsuforum_verify_and_delete_post($course, $cm, $forum, $modcontext, $dis } if (!hsuforum_delete_post($post, has_capability('mod/hsuforum:deleteanypost', $modcontext), $course, $cm, $forum)) { - throw new \moodle_exception('errorwhiledelete', 'hsuforum'); + throw new \core\exception\moodle_exception('errorwhiledelete', 'hsuforum'); } if ($forum->type == 'single') { // Single discussion forums are an exception. We show @@ -4532,7 +4546,7 @@ function hsuforum_verify_and_delete_post($course, $cm, $forum, $modcontext, $dis 'discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type, - ) + ), ); if ($post->userid !== $USER->id) { @@ -4669,7 +4683,7 @@ function hsuforum_delete_post($post, $children, $course, $cm, $forum, $skipcompl 'discussionid' => $post->discussion, 'forumid' => $forum->id, 'forumtype' => $forum->type, - ) + ), ); $post->deleted = 1; if ($post->userid !== $USER->id) { @@ -4703,7 +4717,7 @@ function hsuforum_trigger_content_uploaded_event($post, $cm, $name) { 'pathnamehashes' => array_keys($files), 'discussionid' => $post->discussion, 'triggeredfrom' => $name, - ) + ), ); $event = \mod_hsuforum\event\assessable_uploaded::create($params); $event->trigger(); @@ -5032,7 +5046,7 @@ function hsuforum_get_subscribe_link($forum, $context, $messages = array(), $can 'unsubscribed' => get_string('subscribe', 'hsuforum'), 'cantaccessgroup' => get_string('no'), 'forcesubscribed' => get_string('everyoneissubscribed', 'hsuforum'), - 'cantsubscribe' => get_string('disallowsubscribe','hsuforum') + 'cantsubscribe' => get_string('disallowsubscribe','hsuforum'), ); $messages = $messages + $defaultmessages; @@ -5066,7 +5080,7 @@ function hsuforum_get_subscribe_link($forum, $context, $messages = array(), $can $options['id'] = $forum->id; $options['sesskey'] = sesskey(); - $url = new moodle_url('/mod/hsuforum/subscribe.php', $options); + $url = new \core\url('/mod/hsuforum/subscribe.php', $options); return $OUTPUT->single_button($url, $linktext, 'get', array('title' => $linktitle)); } } @@ -5178,7 +5192,7 @@ function hsuforum_user_can_post_discussion($forum, $currentgroup=null, $unused=- if (!$cm) { debugging('missing cm', DEBUG_DEVELOPER); if (!$cm = get_coursemodule_from_instance('hsuforum', $forum->id, $forum->course)) { - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } } @@ -5264,14 +5278,14 @@ function hsuforum_user_can_post($forum, $discussion, $user=NULL, $cm=NULL, $cour if (!$cm) { debugging('missing cm', DEBUG_DEVELOPER); if (!$cm = get_coursemodule_from_instance('hsuforum', $forum->id, $forum->course)) { - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } } if (!$course) { debugging('missing course', DEBUG_DEVELOPER); if (!$course = $DB->get_record('course', array('id' => $forum->course))) { - throw new \moodle_exception('invalidcourseid'); + throw new \core\exception\moodle_exception('invalidcourseid'); } } @@ -5287,7 +5301,7 @@ function hsuforum_user_can_post($forum, $discussion, $user=NULL, $cm=NULL, $cour } // normal users with temporary guest access can not post, suspended users can not post either - if (!is_viewing($context, $user->id) and !is_enrolled($context, $user->id, '', true)) { + if (!is_viewing($context, $user->id) && !is_enrolled($context, $user->id, '', true)) { return false; } @@ -5403,7 +5417,7 @@ function hsuforum_user_can_see_discussion($forum, $discussion, $context, $user=N } } if (!$cm = get_coursemodule_from_instance('hsuforum', $forum->id, $forum->course)) { - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } if (!has_capability('mod/hsuforum:viewdiscussion', $context)) { @@ -5467,7 +5481,7 @@ function hsuforum_user_can_see_post($forum, $discussion, $post, $user=NULL, $cm= if (!$cm) { debugging('missing cm', DEBUG_DEVELOPER); if (!$cm = get_coursemodule_from_instance('hsuforum', $forum->id, $forum->course)) { - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } } @@ -5502,10 +5516,10 @@ function hsuforum_user_can_see_post($forum, $discussion, $post, $user=NULL, $cm= } if (!property_exists($post, 'privatereply')) { - throw new coding_exception('Must set post\'s privatereply property!'); + throw new \core\exception\coding_exception('Must set post\'s privatereply property!'); } if (!empty($post->privatereply)) { - if ($post->userid != $user->id and $post->privatereply != $user->id) { + if ($post->userid != $user->id && $post->privatereply != $user->id) { return false; } } @@ -5549,7 +5563,7 @@ function hsuforum_print_latest_discussions($course, $forum, $maxdiscussions=-1, if (!$cm) { if (!$cm = get_coursemodule_from_instance('hsuforum', $forum->id, $forum->course)) { - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } } $context = context_module::instance($cm->id); @@ -5595,11 +5609,11 @@ function hsuforum_print_latest_discussions($course, $forum, $maxdiscussions=-1, // and the current user is a guest. $canstart = hsuforum_user_can_post_discussion($forum, $currentgroup, $groupmode, $cm, $context); - if (!$canstart and $forum->type !== 'news') { + if (!$canstart && $forum->type !== 'news') { if (isguestuser() or !isloggedin()) { $canstart = true; } - if (!is_enrolled($context) and !is_viewing($context)) { + if (!is_enrolled($context) && !is_viewing($context)) { // allow guests and not-logged-in to see the button - they are prompted to log in after clicking the link // normal users with temporary guest access see this button too, they are asked to enrol instead // do not show the button to users with suspended enrolments here @@ -5617,7 +5631,7 @@ function hsuforum_print_latest_discussions($course, $forum, $maxdiscussions=-1, // Get the number of discussions found. $numdiscussions = hsuforum_get_discussions_count($cm); } else { - if ($maxdiscussions > 0 and $maxdiscussions <= count($discussions)) { + if ($maxdiscussions > 0 && $maxdiscussions <= count($discussions)) { $olddiscussionlink = true; } } @@ -5627,10 +5641,10 @@ function hsuforum_print_latest_discussions($course, $forum, $maxdiscussions=-1, if (!$canstart && (isguestuser() or !isloggedin() or $forum->type == 'news' - or $forum->type == 'qanda' and !has_capability('mod/hsuforum:addquestion', $context) - or $forum->type != 'qanda' and !has_capability('mod/hsuforum:startdiscussion', $context))) { + or $forum->type == 'qanda' && !has_capability('mod/hsuforum:addquestion', $context) + or $forum->type != 'qanda' && !has_capability('mod/hsuforum:startdiscussion', $context))) { // no button and no info - } else if (!$canstart && $groupmode and !has_capability('moodle/site:accessallgroups', $context)) { + } else if (!$canstart && $groupmode && !has_capability('moodle/site:accessallgroups', $context)) { // inform users why they can not post new discussion if (!$currentgroup) { if (!has_capability('mod/hsuforum:canposttomygroups', $context)) { @@ -5668,7 +5682,7 @@ function hsuforum_print_latest_discussions($course, $forum, $maxdiscussions=-1, } // Sort/Filter options - $urlmenu = new moodle_url('/mod/hsuforum/view.php', array('id'=>$cm->id)); + $urlmenu = new \core\url('/mod/hsuforum/view.php', array('id'=>$cm->id)); $groupselect = groups_print_activity_menu($cm, $urlmenu, true); $sortselect = ''; @@ -5945,8 +5959,8 @@ function hsuforum_get_recent_mod_activity(&$activities, &$index, $timestart, $co $printposts = array(); foreach ($posts as $post) { - if (!empty($config->enabletimedposts) and $USER->id != $post->duserid - and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time()))) { + if (!empty($config->enabletimedposts) && $USER->id != $post->duserid + && (($post->timestart > 0 && $post->timestart > time()) or ($post->timeend > 0 && $post->timeend < time()))) { if (!$viewhiddentimed) { continue; } @@ -6002,7 +6016,7 @@ function hsuforum_get_recent_mod_activity(&$activities, &$index, $timestart, $co $tmpactivity->user = hsuforum_anonymize_user($tmpactivity->user, (object) array( 'id' => $post->forum, 'course' => $courseid, - 'anonymous' => $post->forumanonymous + 'anonymous' => $post->forumanonymous, ), $post); $activities[$index++] = $tmpactivity; @@ -6039,10 +6053,10 @@ function hsuforum_print_recent_mod_activity($activity, $courseid, $detail, $modn 'border' => '0', 'cellpadding' => '3', 'cellspacing' => '0', - 'class' => 'forum-recent' + 'class' => 'forum-recent', ]; - $output = html_writer::start_tag('table', $tableoptions); - $output .= html_writer::start_tag('tr'); + $output = \core\output\html_writer::start_tag('table', $tableoptions); + $output .= \core\output\html_writer::start_tag('tr'); $post = (object) ['parent' => $content->parent]; $forum = (object) ['type' => $content->forumtype]; @@ -6056,39 +6070,39 @@ function hsuforum_print_recent_mod_activity($activity, $courseid, $detail, $modn 'alttext' => $authorhidden, ]; $picture = $OUTPUT->user_picture($activity->user, $pictureoptions); - $output .= html_writer::tag('td', $picture, ['class' => 'userpicture', 'valign' => 'top']); + $output .= \core\output\html_writer::tag('td', $picture, ['class' => 'userpicture', 'valign' => 'top']); } // Discussion title and author. - $output .= html_writer::start_tag('td', ['class' => $class]); + $output .= \core\output\html_writer::start_tag('td', ['class' => $class]); - $output .= html_writer::start_div($class); + $output .= \core\output\html_writer::start_div($class); echo '
'; if ($detail) { $aname = s($activity->name); $output .= $OUTPUT->image_icon('icon', $aname, $activity->type); } - $discussionurl = new moodle_url('/mod/hsuforum/discuss.php', ['d' => $content->discussion]); + $discussionurl = new \core\url('/mod/hsuforum/discuss.php', ['d' => $content->discussion]); $discussionurl->set_anchor('p' . $activity->content->id); - $output .= html_writer::link($discussionurl, $content->subject); - $output .= html_writer::end_div(); + $output .= \core\output\html_writer::link($discussionurl, $content->subject); + $output .= \core\output\html_writer::end_div(); $timestamp = userdate_htmltime($activity->timestamp); if ($authorhidden) { $authornamedate = $timestamp; } else { $fullname = fullname($activity->user, $viewfullnames); - $userurl = new moodle_url('/user/view.php'); + $userurl = new \core\url('/user/view.php'); $userurl->params(['id' => $activity->user->id, 'course' => $courseid]); $by = new stdClass(); - $by->name = html_writer::link($userurl, $fullname); + $by->name = \core\output\html_writer::link($userurl, $fullname); $by->date = $timestamp; $authornamedate = get_string('bynameondate', 'hsuforum', $by); } - $output .= html_writer::div($authornamedate, 'user'); - $output .= html_writer::end_tag('td'); - $output .= html_writer::end_tag('tr'); - $output .= html_writer::end_tag('table'); + $output .= \core\output\html_writer::div($authornamedate, 'user'); + $output .= \core\output\html_writer::end_tag('td'); + $output .= \core\output\html_writer::end_tag('tr'); + $output .= \core\output\html_writer::end_tag('table'); echo $output; } @@ -6133,13 +6147,13 @@ function hsuforum_update_subscriptions_button($courseid, $forumid) { $edit = "on"; } - $subscribers = html_writer::start_tag('form', array('action' => $CFG->wwwroot . '/mod/hsuforum/subscribers.php', + $subscribers = \core\output\html_writer::start_tag('form', array('action' => $CFG->wwwroot . '/mod/hsuforum/subscribers.php', 'method' => 'get', 'class' => 'form-inline')); - $subscribers .= html_writer::empty_tag('input', array('type' => 'submit', 'value' => $string, + $subscribers .= \core\output\html_writer::empty_tag('input', array('type' => 'submit', 'value' => $string, 'class' => 'btn btn-secondary')); - $subscribers .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'id', 'value' => $forumid)); - $subscribers .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'edit', 'value' => $edit)); - $subscribers .= html_writer::end_tag('form'); + $subscribers .= \core\output\html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'id', 'value' => $forumid)); + $subscribers .= \core\output\html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'edit', 'value' => $edit)); + $subscribers .= \core\output\html_writer::end_tag('form'); return $subscribers; } @@ -6437,6 +6451,141 @@ function hsuforum_tp_get_course_unread_posts($userid, $courseid) { return array(); } +/** + * Fetch the data used to display the discussions on the current page. + * + * @param \mod_hsuforum\local\entities\forum $forum The forum entity + * @param stdClass $user The user to render for + * @param int[]|null $groupid The group to render + * @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 array The data to use for display + */ +function mod_hsuforum_get_discussion_summaries(\mod_hsuforum\local\entities\forum $forum, stdClass $user, ?int $groupid, ?int $sortorder, + ?int $pageno = 0, ?int $pagesize = 0) { + + $vaultfactory = mod_hsuforum\local\container::get_vault_factory(); + $discussionvault = $vaultfactory->get_discussions_in_forum_vault(); + $managerfactory = mod_hsuforum\local\container::get_manager_factory(); + $capabilitymanager = $managerfactory->get_capability_manager($forum); + + $groupids = mod_hsuforum_get_groups_from_groupid($forum, $user, $groupid); + + if (null === $groupids) { + return $discussions = $discussionvault->get_from_forum_id( + $forum->get_id(), + $capabilitymanager->can_view_hidden_posts($user), + $user->id, + $sortorder, + $pagesize, + $pageno * $pagesize); + } else { + return $discussions = $discussionvault->get_from_forum_id_and_group_id( + $forum->get_id(), + $groupids, + $capabilitymanager->can_view_hidden_posts($user), + $user->id, + $sortorder, + $pagesize, + $pageno * $pagesize); + } +} + +/** + * Get the list of groups to show based on the current user and requested groupid. + * + * @param \mod_hsuforum\local\entities\forum $forum The forum entity + * @param stdClass $user The user viewing + * @param int $groupid The groupid requested + * @return array The list of groups to show + */ +function mod_hsuforum_get_groups_from_groupid(\mod_hsuforum\local\entities\forum $forum, stdClass $user, ?int $groupid): ?array { + + $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) { + $managerfactory = mod_hsuforum\local\container::get_manager_factory(); + $capabilitymanager = $managerfactory->get_capability_manager($forum); + // No group was specified. + $showallgroups = (VISIBLEGROUPS == $effectivegroupmode); + $showallgroups = $showallgroups || $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]; + } +} + +/** + * Returns array of hsuforum layout modes + * + * @param bool $useexperimentalui use experimental layout modes or not + * @return array + */ +function hsuforum_get_layout_modes(bool $useexperimentalui = false) { + $modes = [ + HSUFORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'hsuforum'), + HSUFORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'hsuforum'), + HSUFORUM_MODE_THREADED => get_string('modethreaded', 'hsuforum') + ]; + + if ($useexperimentalui) { + $modes[HSUFORUM_MODE_NESTED_V2] = get_string('modenestedv2', 'hsuforum'); + } else { + $modes[HSUFORUM_MODE_NESTED] = get_string('modenested', 'hsuforum'); + } + + return $modes; +} + +/** + * Get a count of all discussions in a forum. + * + * @param \mod_hsuforum\local\entities\forum $forum The forum entity + * @param stdClass $user The user to render for + * @param int $groupid The group to render + * @return int The number of discussions in a forum + */ +function mod_hsuforum_count_all_discussions(\mod_hsuforum\local\entities\forum $forum, stdClass $user, ?int $groupid) { + + $managerfactory = mod_hsuforum\local\container::get_manager_factory(); + $capabilitymanager = $managerfactory->get_capability_manager($forum); + $vaultfactory = mod_hsuforum\local\container::get_vault_factory(); + $discussionvault = $vaultfactory->get_discussions_in_forum_vault(); + + $groupids = mod_hsuforum_get_groups_from_groupid($forum, $user, $groupid); + + if (null === $groupids) { + return $discussionvault->get_total_discussion_count_from_forum_id( + $forum->get_id(), + $capabilitymanager->can_view_hidden_posts($user), + $user->id); + } else { + return $discussionvault->get_total_discussion_count_from_forum_id_and_group_id( + $forum->get_id(), + $groupids, + $capabilitymanager->can_view_hidden_posts($user), + $user->id); + } +} + /** * Returns the count of records for the provided user and forum and [optionally] group. * @@ -6758,7 +6907,7 @@ function hsuforum_check_throttling($forum, $cm = null) { */ function hsuforum_check_blocking_threshold($thresholdwarning) { if (!empty($thresholdwarning) && !$thresholdwarning->canpost) { - throw new \moodle_exception($thresholdwarning->errorcode, + throw new \core\exception\moodle_exception($thresholdwarning->errorcode, $thresholdwarning->module, $thresholdwarning->link, $thresholdwarning->additional); @@ -7060,7 +7209,7 @@ function hsuforum_get_grading_types(){ return array( HSUFORUM_GRADETYPE_NONE => get_string('gradetypenone', 'hsuforum'), HSUFORUM_GRADETYPE_MANUAL => get_string('gradetypemanual', 'hsuforum'), - HSUFORUM_GRADETYPE_RATING => get_string('gradetyperating', 'hsuforum') + HSUFORUM_GRADETYPE_RATING => get_string('gradetyperating', 'hsuforum'), ); } @@ -7098,54 +7247,54 @@ function hsuforum_extend_settings_navigation(settings_navigation $settingsnav, n $cansubscribe = ($activeenrolled && $subscriptionmode != HSUFORUM_FORCESUBSCRIBE && ($subscriptionmode != HSUFORUM_DISALLOWSUBSCRIBE || $canmanage)); $discussionid = optional_param('d', 0, PARAM_INT); - $viewingdiscussion = ($settingsnav->get_page()->url->compare(new moodle_url('/mod/hsuforum/discuss.php'), URL_MATCH_BASE) and $discussionid); + $viewingdiscussion = ($settingsnav->get_page()->url->compare(new \core\url('/mod/hsuforum/discuss.php'), URL_MATCH_BASE) && $discussionid); if (!is_guest($settingsnav->get_page()->cm->context)) { $forumnode->add( get_string('export', 'hsuforum'), - new moodle_url('/mod/hsuforum/route.php', array('contextid' => $settingsnav->get_page()->cm->context->id, 'action' => 'export')), + new \core\url('/mod/hsuforum/route.php', array('contextid' => $settingsnav->get_page()->cm->context->id, 'action' => 'export')), navigation_node::TYPE_SETTING, null, null, - new pix_icon('i/export', get_string('export', 'hsuforum'))); + new \core\output\pix_icon('i/export', get_string('export', 'hsuforum'))); } $forumnode->add( get_string('viewposters', 'hsuforum'), - new moodle_url('/mod/hsuforum/route.php', array('contextid' => $settingsnav->get_page()->cm->context->id, 'action' => 'viewposters')), + new \core\url('/mod/hsuforum/route.php', array('contextid' => $settingsnav->get_page()->cm->context->id, 'action' => 'viewposters')), navigation_node::TYPE_SETTING, null, null, - new pix_icon('t/preview', get_string('viewposters', 'hsuforum'))); + new \core\output\pix_icon('t/preview', get_string('viewposters', 'hsuforum'))); if ($canmanage) { $mode = $forumnode->add(get_string('subscriptionmode', 'hsuforum'), null, navigation_node::TYPE_CONTAINER); $mode->add_class('subscriptionmode'); - $allowchoice = $mode->add(get_string('subscriptionoptional', 'hsuforum'), new moodle_url('/mod/hsuforum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>HSUFORUM_CHOOSESUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING); - $forceforever = $mode->add(get_string("subscriptionforced", "hsuforum"), new moodle_url('/mod/hsuforum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>HSUFORUM_FORCESUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING); - $forceinitially = $mode->add(get_string("subscriptionauto", "hsuforum"), new moodle_url('/mod/hsuforum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>HSUFORUM_INITIALSUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING); - $disallowchoice = $mode->add(get_string('subscriptiondisabled', 'hsuforum'), new moodle_url('/mod/hsuforum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>HSUFORUM_DISALLOWSUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING); + $allowchoice = $mode->add(get_string('subscriptionoptional', 'hsuforum'), new \core\url('/mod/hsuforum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>HSUFORUM_CHOOSESUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING); + $forceforever = $mode->add(get_string("subscriptionforced", "hsuforum"), new \core\url('/mod/hsuforum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>HSUFORUM_FORCESUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING); + $forceinitially = $mode->add(get_string("subscriptionauto", "hsuforum"), new \core\url('/mod/hsuforum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>HSUFORUM_INITIALSUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING); + $disallowchoice = $mode->add(get_string('subscriptiondisabled', 'hsuforum'), new \core\url('/mod/hsuforum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>HSUFORUM_DISALLOWSUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING); switch ($subscriptionmode) { case HSUFORUM_CHOOSESUBSCRIBE : // 0 $allowchoice->action = null; $allowchoice->add_class('activesetting'); - $allowchoice->icon = new pix_icon('t/selected', '', 'mod_hsuforum'); + $allowchoice->icon = new \core\output\pix_icon('t/selected', '', 'mod_hsuforum'); break; case HSUFORUM_FORCESUBSCRIBE : // 1 $forceforever->action = null; $forceforever->add_class('activesetting'); - $forceforever->icon = new pix_icon('t/selected', '', 'mod_hsuforum'); + $forceforever->icon = new \core\output\pix_icon('t/selected', '', 'mod_hsuforum'); break; case HSUFORUM_INITIALSUBSCRIBE : // 2 $forceinitially->action = null; $forceinitially->add_class('activesetting'); - $forceinitially->icon = new pix_icon('t/selected', '', 'mod_hsuforum'); + $forceinitially->icon = new \core\output\pix_icon('t/selected', '', 'mod_hsuforum'); break; case HSUFORUM_DISALLOWSUBSCRIBE : // 3 $disallowchoice->action = null; $disallowchoice->add_class('activesetting'); - $disallowchoice->icon = new pix_icon('t/selected', '', 'mod_hsuforum'); + $disallowchoice->icon = new \core\output\pix_icon('t/selected', '', 'mod_hsuforum'); break; } @@ -7173,7 +7322,7 @@ function hsuforum_extend_settings_navigation(settings_navigation $settingsnav, n } else { $linktext = get_string('subscribe', 'hsuforum'); } - $url = new moodle_url('/mod/hsuforum/subscribe.php', array('id'=>$forumobject->id, 'sesskey'=>sesskey())); + $url = new \core\url('/mod/hsuforum/subscribe.php', array('id'=>$forumobject->id, 'sesskey'=>sesskey())); $forumnode->add($linktext, $url, navigation_node::TYPE_SETTING); } @@ -7182,7 +7331,7 @@ function hsuforum_extend_settings_navigation(settings_navigation $settingsnav, n $subscribe = new hsuforum_lib_discussion_subscribe($forumobject, $settingsnav->get_page()->cm->context); if ($subscribe->can_subscribe()) { - $subscribeurl = new moodle_url('/mod/hsuforum/route.php', array( + $subscribeurl = new \core\url('/mod/hsuforum/route.php', array( 'contextid' => $settingsnav->get_page()->cm->context->id, 'action' => 'subscribedisc', 'discussionid' => $discussionid, @@ -7201,14 +7350,14 @@ function hsuforum_extend_settings_navigation(settings_navigation $settingsnav, n if (has_capability('mod/hsuforum:viewsubscribers', $settingsnav->get_page()->cm->context)){ - $url = new moodle_url('/mod/hsuforum/subscribers.php', array('id'=>$forumobject->id)); + $url = new \core\url('/mod/hsuforum/subscribers.php', array('id'=>$forumobject->id)); $forumnode->add(get_string('showsubscribers', 'hsuforum'), $url, navigation_node::TYPE_SETTING); $discsubscribers = ($viewingdiscussion or (optional_param('action', '', PARAM_ALPHA) == 'discsubscribers')); if ($discsubscribers && !hsuforum_is_forcesubscribed($forumobject) && $discussionid) { - $url = new moodle_url('/mod/hsuforum/route.php', array( + $url = new \core\url('/mod/hsuforum/route.php', array( 'contextid' => $settingsnav->get_page()->cm->context->id, 'action' => 'discsubscribers', 'discussionid' => $discussionid, @@ -7238,8 +7387,8 @@ function hsuforum_extend_settings_navigation(settings_navigation $settingsnav, n $string = get_string('rsssubscriberssposts','hsuforum'); } - $url = new moodle_url(rss_get_url($settingsnav->get_page()->cm->context->id, $userid, "mod_hsuforum", $forumobject->id)); - $forumnode->add($string, $url, settings_navigation::TYPE_SETTING, null, null, new pix_icon('i/rss', '')); + $url = new \core\url(rss_get_url($settingsnav->get_page()->cm->context->id, $userid, "mod_hsuforum", $forumobject->id)); + $forumnode->add($string, $url, settings_navigation::TYPE_SETTING, null, null, new \core\output\pix_icon('i/rss', '')); } } @@ -7513,7 +7662,7 @@ function hsuforum_page_type_list($pagetype, $parentcontext, $currentcontext) { $hsuforum_pagetype = array( 'mod-hsuforum-*'=>get_string('page-mod-hsuforum-x', 'hsuforum'), 'mod-hsuforum-view'=>get_string('page-mod-hsuforum-view', 'hsuforum'), - 'mod-hsuforum-discuss'=>get_string('page-mod-hsuforum-discuss', 'hsuforum') + 'mod-hsuforum-discuss'=>get_string('page-mod-hsuforum-discuss', 'hsuforum'), ); return $hsuforum_pagetype; } @@ -7694,7 +7843,7 @@ function hsuforum_get_posts_by_user($user, array $courses, $musthaveaccess = fal if (!is_viewing($coursecontext, $user) && !is_enrolled($coursecontext, $user)) { // Need to have full access to a course to see the rest of own info if ($musthaveaccess) { - throw new \moodle_exception('errorenrolmentrequired', 'hsuforum'); + throw new \core\exception\moodle_exception('errorenrolmentrequired', 'hsuforum'); } continue; } @@ -7703,7 +7852,7 @@ function hsuforum_get_posts_by_user($user, array $courses, $musthaveaccess = fal // if they don't we immediately have a problem. if (!can_access_course($course)) { if ($musthaveaccess) { - throw new \moodle_exception('errorenrolmentrequired', 'hsuforum'); + throw new \core\exception\moodle_exception('errorenrolmentrequired', 'hsuforum'); } continue; } @@ -7733,7 +7882,7 @@ function hsuforum_get_posts_by_user($user, array $courses, $musthaveaccess = fal // But they're not... if it was a specific course throw an error otherwise // just skip this course so that it is not searched. if ($musthaveaccess) { - throw new \moodle_exception("groupnotamember", '', $CFG->wwwroot."/course/view.php?id=$course->id"); + throw new \core\exception\moodle_exception("groupnotamember", '', $CFG->wwwroot."/course/view.php?id=$course->id"); } continue; } @@ -7753,7 +7902,7 @@ function hsuforum_get_posts_by_user($user, array $courses, $musthaveaccess = fal // user doesn't have access to any courses is which the requested user has posted. // Although we do know at this point that the requested user has posts. if ($musthaveaccess) { - throw new \moodle_exception('permissiondenied'); + throw new \core\exception\moodle_exception('permissiondenied'); } else { return $return; } @@ -7809,7 +7958,7 @@ function hsuforum_get_posts_by_user($user, array $courses, $musthaveaccess = fal $forumsearchselect = array(); if (!$iscurrentuser && !$hascapsonuser) { // Make sure we check group access - if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $cm->context)) { + if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $cm->context)) { $groups = $modinfo->get_groups($cm->groupingid); $groups[] = -1; list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'grps'.$forumid.'_'); @@ -7961,7 +8110,7 @@ function hsuforum_get_postuser($user, $post, $forum, context_module $context) { $postuser = hsuforum_anonymize_user($user, $forum, $post); if (property_exists($user, 'picture')) { - $postuser->user_picture = new user_picture($postuser); + $postuser->user_picture = new \core\output\user_picture($postuser); $postuser->user_picture->courseid = $forum->course; $postuser->user_picture->link = (!hsuforum_is_anonymous_user($postuser)); } @@ -7979,7 +8128,7 @@ function hsuforum_get_postuser($user, $post, $forum, context_module $context) { * @param object $user * @param object $forum * @param object $post - * @throws coding_exception + * @throws \core\exception\coding_exception * @return stdClass * @author Mark Nielsen */ @@ -7988,10 +8137,10 @@ function hsuforum_anonymize_user($user, $forum, $post) { static $anonymous = null; if (!isset($forum->anonymous) or !isset($forum->course)) { - throw new coding_exception('Must pass the forum\'s anonymous and course fields'); + throw new \core\exception\coding_exception('Must pass the forum\'s anonymous and course fields'); } if (!isset($post->reveal)) { - throw new coding_exception('Must pass the post\'s reveal field'); + throw new \core\exception\coding_exception('Must pass the post\'s reveal field'); } if (empty($forum->anonymous) or !empty($post->reveal) @@ -8014,8 +8163,8 @@ function hsuforum_anonymize_user($user, $forum, $post) { 'picture' => 0, 'email' => $CFG->noreplyaddress, 'imagealt' => '', - 'profilelink' => new moodle_url('/user/view.php', array('id'=>$guest->id, 'course'=>$forum->course)), - 'anonymous' => true + 'profilelink' => new \core\url('/user/view.php', array('id'=>$guest->id, 'course'=>$forum->course)), + 'anonymous' => true, ); $anonymous->fullname = fullname($anonymous, true); $anonymous->imagealt = $anonymous->fullname; @@ -8277,7 +8426,7 @@ function mod_hsuforum_comment_message(stdClass $comment, stdClass $options) { $recipients = get_users_by_capability($context, 'local/joulegrader:grade'); // Add the item user if they are different from commenter. - if ($comment->userid != $user->id and has_capability('mod/hsuforum:replypost', $context, $user)) { + if ($comment->userid != $user->id && has_capability('mod/hsuforum:replypost', $context, $user)) { $recipients[$user->id] = $user; } @@ -8290,7 +8439,7 @@ function mod_hsuforum_comment_message(stdClass $comment, stdClass $options) { if (\core_component::get_plugin_directory('local', 'joulegrader') !== null) { // Joule Grader is installed and control panel enabled. $gareaid = component_callback('local_joulegrader', 'area_from_context', array($context, 'hsuforum')); - $contexturl = new moodle_url('/local/joulegrader/view.php', array('courseid' => $cm->course, + $contexturl = new \core\url('/local/joulegrader/view.php', array('courseid' => $cm->course, 'garea' => $gareaid, 'guser' => $user->id)); } else { $contexturl = $context->get_url(); @@ -8331,7 +8480,7 @@ function hsuforum_set_user_maildigest($forum, $maildigest, $user = null) { $digestoptions = hsuforum_get_user_digest_options($user); if (!isset($digestoptions[$maildigest])) { - throw new moodle_exception('invaliddigestsetting', 'mod_hsuforum'); + throw new \core\exception\moodle_exception('invaliddigestsetting', 'mod_hsuforum'); } // Attempt to retrieve any existing forum digest record. @@ -8439,11 +8588,11 @@ function hsuforum_simpler_time($seconds) { * @param int $timeinpast * @param null|array $attributes Tag attributes * @return string - * @throws coding_exception + * @throws \core\exception\coding_exception */ function hsuforum_relative_time($timeinpast, $attributes = null) { if (!is_numeric($timeinpast)) { - throw new coding_exception('Relative times must be calculated from the raw timestamp'); + throw new \core\exception\coding_exception('Relative times must be calculated from the raw timestamp'); } $precisedatetime = userdate($timeinpast); @@ -8474,7 +8623,7 @@ function hsuforum_relative_time($timeinpast, $attributes = null) { } } - return html_writer::tag('time', $displaytime, $defaultatts); + return \core\output\html_writer::tag('time', $displaytime, $defaultatts); } /** @@ -8514,7 +8663,7 @@ function hsuforum_str_empty($str) { 'param', 'source', 'track', - 'wbr' + 'wbr', ); foreach ($voidtags as $check) { if (stripos($str, $check) !== false) { @@ -8573,7 +8722,7 @@ function hsuforum_view($forum, $course, $cm, $context) { $params = array( 'context' => $context, - 'objectid' => $forum->id + 'objectid' => $forum->id, ); $event = \mod_hsuforum\event\course_module_viewed::create($params); @@ -8619,7 +8768,7 @@ function hsuforum_discussion_pin($modcontext, $forum, $discussion) { $params = array( 'context' => $modcontext, 'objectid' => $discussion->id, - 'other' => array('forumid' => $forum->id) + 'other' => array('forumid' => $forum->id), ); $event = \mod_hsuforum\event\discussion_pinned::create($params); @@ -8643,7 +8792,7 @@ function hsuforum_discussion_unpin($modcontext, $forum, $discussion) { $params = array( 'context' => $modcontext, 'objectid' => $discussion->id, - 'other' => array('forumid' => $forum->id) + 'other' => array('forumid' => $forum->id), ); $event = \mod_hsuforum\event\discussion_unpinned::create($params); @@ -8667,7 +8816,7 @@ function mod_hsuforum_myprofile_navigation(core_user\output\myprofile\tree $tree // May as well just bail aggressively here. return false; } - $postsurl = new moodle_url('/mod/hsuforum/user.php', array('id' => $user->id)); + $postsurl = new \core\url('/mod/hsuforum/user.php', array('id' => $user->id)); if (!empty($course)) { $postsurl->param('course', $course->id); } @@ -8675,7 +8824,7 @@ function mod_hsuforum_myprofile_navigation(core_user\output\myprofile\tree $tree $node = new core_user\output\myprofile\node('miscellaneous', 'hsuforumposts', $string, null, $postsurl); $tree->add_node($node); - $discussionssurl = new moodle_url('/mod/hsuforum/user.php', array('id' => $user->id, 'mode' => 'discussions')); + $discussionssurl = new \core\url('/mod/hsuforum/user.php', array('id' => $user->id, 'mode' => 'discussions')); if (!empty($course)) { $discussionssurl->param('course', $course->id); } @@ -8707,14 +8856,14 @@ function mod_hsuforum_output_fragment_editor($args) { * @param object $post The forum post. * @param object $forum The forum object. * @return bool - * @throws coding_exception + * @throws \core\exception\coding_exception */ function hsuforum_is_author_hidden($post, $forum) { if (!isset($post->parent)) { - throw new coding_exception('$post->parent must be set.'); + throw new \core\exception\coding_exception('$post->parent must be set.'); } if (!isset($forum->type)) { - throw new coding_exception('$forum->type must be set.'); + throw new \core\exception\coding_exception('$forum->type must be set.'); } if ($forum->type === 'single' && empty($post->parent)) { return true; @@ -8815,7 +8964,7 @@ function hsuforum_change_format($messagecontent, $prefilledpostformat, $modconte // Only if there are prefilled contents coming. if (!empty($messagecontent)) { // If the prefilled post is not HTML and the preferred format is HTML, convert to it. - if ($prefilledpostformat != FORMAT_HTML and $preferredformat == FORMAT_HTML) { + if ($prefilledpostformat != FORMAT_HTML && $preferredformat == FORMAT_HTML) { $messagecontent = format_text($messagecontent, $prefilledpostformat, ['context' => $modcontext]); } } @@ -8939,7 +9088,7 @@ function mod_hsuforum_core_calendar_provide_event_action(calendar_event $event, return $factory->create_instance( get_string('view'), - new \moodle_url('/mod/hsuforum/view.php', ['id' => $cm->id]), + new \core\url('/mod/hsuforum/view.php', ['id' => $cm->id]), $itemcount, true ); diff --git a/lib/discussion/sort.php b/lib/discussion/sort.php index 69070cb0..502f3150 100644 --- a/lib/discussion/sort.php +++ b/lib/discussion/sort.php @@ -104,7 +104,7 @@ public static function set_to_session(hsuforum_lib_discussion_sort $sort) { */ public function set_disabled(array $disabled) { if (in_array('lastreply', $disabled)) { - throw new coding_exception('The "lastreply" key is the only key that cannot be disabled'); + throw new \core\exception\coding_exception('The "lastreply" key is the only key that cannot be disabled'); } $this->disabled = $disabled; return $this; @@ -137,7 +137,7 @@ public function get_directionopts() { */ public function set_direction($direction) { if (!in_array($direction, $this->get_directionopts())) { - throw new coding_exception('Invalid sort direction: '.$direction); + throw new \core\exception\coding_exception('Invalid sort direction: '.$direction); } $this->direction = $direction; return $this; @@ -156,10 +156,10 @@ public function get_direction() { */ public function set_key($key) { if (!array_key_exists($key, $this->get_keyopts())) { - throw new coding_exception('Invalid sort key: '.$key); + throw new \core\exception\coding_exception('Invalid sort key: '.$key); } if (in_array($key, $this->get_disabled())) { - throw new coding_exception('Invalid sort key (it has been disabled): '.$key); + throw new \core\exception\coding_exception('Invalid sort key (it has been disabled): '.$key); } $this->key = $key; return $this; diff --git a/lib/discussion/subscribe.php b/lib/discussion/subscribe.php index 3b1b9bac..e0512da0 100644 --- a/lib/discussion/subscribe.php +++ b/lib/discussion/subscribe.php @@ -147,11 +147,11 @@ public function get_userid() { } /** - * @throws moodle_exception + * @throws \core\exception\moodle_exception */ public function require_can_subscribe() { if (!$this->can_subscribe()) { - throw new moodle_exception('cansubscribediscerror', 'hsuforum'); + throw new \core\exception\moodle_exception('cansubscribediscerror', 'hsuforum'); } } diff --git a/lib/flag.php b/lib/flag.php index e093b0e7..41d6527a 100644 --- a/lib/flag.php +++ b/lib/flag.php @@ -95,11 +95,11 @@ public function toggle_flag($value, $flag) { * * @param string $name The flag name * @return string - * @throws coding_exception + * @throws \core\exception\coding_exception */ protected function validate_flag($name) { if (!in_array($name, $this->get_flags())) { - throw new coding_exception("Flag does not exist: $name"); + throw new \core\exception\coding_exception("Flag does not exist: $name"); } return $name; } diff --git a/lib/table/posters.php b/lib/table/posters.php index 1c34c854..dccc4709 100644 --- a/lib/table/posters.php +++ b/lib/table/posters.php @@ -26,7 +26,7 @@ require_once($CFG->libdir.'/tablelib.php'); -class hsuforum_lib_table_posters extends table_sql { +class hsuforum_lib_table_posters extends \core_table\sql_table { public function __construct($uniqueid) { global $PAGE, $USER; diff --git a/locallib.php b/locallib.php index 6bc2e064..9840cdfc 100644 --- a/locallib.php +++ b/locallib.php @@ -1,5 +1,4 @@ format_string($this->discussion->name), 'link' => $CFG->wwwroot . '/mod/hsuforum/discuss.php?d=' . $this->discussion->id, - 'type' => 'title' + 'type' => 'title', ); return array($navlinks, $this->cm); } @@ -653,19 +652,19 @@ function mod_hsuforum_get_tagged_posts($tag, $exclusivemode = false, $fromctx = $cm = $modinfo->get_cm($taggeditem->cmid); $forum = (object)['id' => $taggeditem->forum, 'course' => $taggeditem->courseid, - 'type' => $taggeditem->type + 'type' => $taggeditem->type, ]; $discussion = (object)['id' => $taggeditem->discussion, 'timestart' => $taggeditem->timestart, 'timeend' => $taggeditem->timeend, 'groupid' => $taggeditem->groupid, - 'firstpost' => $taggeditem->firstpost + 'firstpost' => $taggeditem->firstpost, ]; $post = (object)['id' => $taggeditem->id, 'parent' => $taggeditem->parent, 'userid' => $taggeditem->userid, 'groupid' => $taggeditem->groupid, - 'privatereply' => 0 + 'privatereply' => 0, ]; $accessible = hsuforum_user_can_see_post($forum, $discussion, $post, null, $cm); @@ -687,14 +686,14 @@ function mod_hsuforum_get_tagged_posts($tag, $exclusivemode = false, $fromctx = context_helper::preload_from_record($item); $modinfo = get_fast_modinfo($item->courseid); $cm = $modinfo->get_cm($item->cmid); - $pageurl = new moodle_url('/mod/hsuforum/discuss.php', array('d' => $item->discussion), 'p' . $item->id); + $pageurl = new \core\url('/mod/hsuforum/discuss.php', array('d' => $item->discussion), 'p' . $item->id); $pagename = format_string($item->subject, true, array('context' => context_module::instance($item->cmid))); - $pagename = html_writer::link($pageurl, $pagename); + $pagename = \core\output\html_writer::link($pageurl, $pagename); $courseurl = course_get_url($item->courseid, $cm->sectionnum); - $cmname = html_writer::link($cm->url, $cm->get_formatted_name()); + $cmname = \core\output\html_writer::link($cm->url, $cm->get_formatted_name()); $coursename = format_string($item->fullname, true, array('context' => context_course::instance($item->courseid))); - $coursename = html_writer::link($courseurl, $coursename); - $icon = html_writer::link($pageurl, html_writer::empty_tag('img', array('src' => $cm->get_icon_url()))); + $coursename = \core\output\html_writer::link($courseurl, $coursename); + $icon = \core\output\html_writer::link($pageurl, \core\output\html_writer::empty_tag('img', array('src' => $cm->get_icon_url()))); $tagfeed->add($icon, $pagename, $cmname.'
'.$coursename); } diff --git a/maildigest.php b/maildigest.php index cf859b6a..dab301ae 100644 --- a/maildigest.php +++ b/maildigest.php @@ -1,5 +1,4 @@ $id, 'maildigest' => $maildigest, )); diff --git a/mod_form.php b/mod_form.php index dfda5d80..629bb3f4 100644 --- a/mod_form.php +++ b/mod_form.php @@ -357,7 +357,7 @@ protected function standard_hsuforum_coursemodule_elements() { // are enabled. if ($this->_features->groups || $this->_features->groupings) { $mform->addElement('static', 'restrictgroupbutton', '', - html_writer::tag('button', get_string('restrictbygroup', 'availability'), + \core\output\html_writer::tag('button', get_string('restrictbygroup', 'availability'), array('id' => 'restrictbygroup', 'disabled' => 'disabled', 'class' => 'btn btn-secondary'))); } @@ -402,6 +402,16 @@ protected function standard_hsuforum_coursemodule_elements() { $mform->setType('completionunlocked', PARAM_INT); $trackingdefault = COMPLETION_TRACKING_NONE; + // If system and activity default is on, set it. + if (!empty($CFG->completiondefault) && $CFG->completiondefault && $this->_features->defaultcompletion) { + $hasrules = plugin_supports('mod', $this->_modname, FEATURE_COMPLETION_HAS_RULES, true); + $tracksviews = plugin_supports('mod', $this->_modname, FEATURE_COMPLETION_TRACKS_VIEWS, true); + if ($hasrules || $tracksviews) { + $trackingdefault = COMPLETION_TRACKING_AUTOMATIC; + } else { + $trackingdefault = COMPLETION_TRACKING_MANUAL; + } + } $mform->addElement('select', 'completion', get_string('completion', 'completion'), array(COMPLETION_TRACKING_NONE=>get_string('completion_none', 'completion'), @@ -673,11 +683,11 @@ protected function add_hsuforum_rating_settings() { $mform->addElement('checkbox', 'ratingtime', get_string('ratingtime', 'rating')); $mform->hideIf('ratingtime', $assessedfieldname, 'eq', 0); - $mform->addElement('date_time_selector', 'assesstimestart', get_string('from')); + $mform->addElement('date_time_selector', 'assesstimestart', get_string('from', 'mod_hsuforum')); $mform->hideIf('assesstimestart', $assessedfieldname, 'eq', 0); $mform->hideIf('assesstimestart', 'ratingtime'); - $mform->addElement('date_time_selector', 'assesstimefinish', get_string('to')); + $mform->addElement('date_time_selector', 'assesstimefinish', get_string('to', 'mod_hsuforum')); $mform->hideIf('assesstimefinish', $assessedfieldname, 'eq', 0); $mform->hideIf('assesstimefinish', 'ratingtime'); } diff --git a/post.php b/post.php index 21e0808e..ec4ff70f 100644 --- a/post.php +++ b/post.php @@ -59,32 +59,32 @@ if (!isloggedin() or isguestuser()) { - if (!isloggedin() and !get_local_referer()) { + if (!isloggedin() && !get_local_referer()) { // No referer+not logged in - probably coming in via email See MDL-9052. require_login(); } if (!empty($forum)) { // User is starting a new discussion in a forum. if (! $forum = $DB->get_record('hsuforum', array('id' => $forum))) { - throw new \moodle_exception('invalidforumid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidforumid', 'hsuforum'); } } else if (!empty($reply)) { // User is writing a new reply. if (! $parent = hsuforum_get_post_full($reply)) { - throw new \moodle_exception('invalidparentpostid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidparentpostid', 'hsuforum'); } if (! $discussion = $DB->get_record('hsuforum_discussions', array('id' => $parent->discussion))) { - throw new \moodle_exception('notpartofdiscussion', 'hsuforum'); + throw new \core\exception\moodle_exception('notpartofdiscussion', 'hsuforum'); } if (! $forum = $DB->get_record('hsuforum', array('id' => $discussion->forum))) { - throw new \moodle_exception('invalidforumid'); + throw new \core\exception\moodle_exception('invalidforumid'); } } if (! $course = $DB->get_record('course', array('id' => $forum->course))) { - throw new \moodle_exception('invalidcourseid'); + throw new \core\exception\moodle_exception('invalidcourseid'); } if (!$cm = get_coursemodule_from_instance('hsuforum', $forum->id, $course->id)) { // For the logs. - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } else { $modcontext = context_module::instance($cm->id); } @@ -105,13 +105,13 @@ if (!empty($forum)) { // User is starting a new discussion in a forum. if (! $forum = $DB->get_record("hsuforum", array("id" => $forum))) { - throw new \moodle_exception('invalidforumid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidforumid', 'hsuforum'); } if (! $course = $DB->get_record("course", array("id" => $forum->course))) { - throw new \moodle_exception('invalidcourseid'); + throw new \core\exception\moodle_exception('invalidcourseid'); } if (! $cm = get_coursemodule_from_instance("hsuforum", $forum->id, $course->id)) { - throw new \moodle_exception("invalidcoursemodule"); + throw new \core\exception\moodle_exception("invalidcoursemodule"); } // Retrieve the contexts. @@ -125,17 +125,17 @@ if (enrol_selfenrol_available($course->id)) { $SESSION->wantsurl = qualified_me(); $SESSION->enrolcancel = get_local_referer(false); - redirect(new moodle_url('/enrol/index.php', array('id' => $course->id, + redirect(new \core\url('/enrol/index.php', array('id' => $course->id, 'returnurl' => '/mod/hsuforum/view.php?f=' . $forum->id)), get_string('youneedtoenrol')); } } } - throw new \moodle_exception('nopostforum', 'hsuforum'); + throw new \core\exception\moodle_exception('nopostforum', 'hsuforum'); } - if (!$cm->visible and !has_capability('moodle/course:viewhiddenactivities', $modcontext)) { - throw new \moodle_exception("activityiscurrentlyhidden"); + if (!$cm->visible && !has_capability('moodle/course:viewhiddenactivities', $modcontext)) { + throw new \core\exception\moodle_exception("activityiscurrentlyhidden"); } $SESSION->fromurl = get_local_referer(false); @@ -167,19 +167,19 @@ } else if (!empty($reply)) { // User is writing a new reply. if (! $parent = hsuforum_get_post_full($reply)) { - throw new \moodle_exception('invalidparentpostid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidparentpostid', 'hsuforum'); } if (! $discussion = $DB->get_record("hsuforum_discussions", array("id" => $parent->discussion))) { - throw new \moodle_exception('notpartofdiscussion', 'hsuforum'); + throw new \core\exception\moodle_exception('notpartofdiscussion', 'hsuforum'); } if (! $forum = $DB->get_record("hsuforum", array("id" => $discussion->forum))) { - throw new \moodle_exception('invalidforumid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidforumid', 'hsuforum'); } if (! $course = $DB->get_record("course", array("id" => $discussion->course))) { - throw new \moodle_exception('invalidcourseid'); + throw new \core\exception\moodle_exception('invalidcourseid'); } if (! $cm = get_coursemodule_from_instance("hsuforum", $forum->id, $course->id)) { - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } // Ensure lang, theme, etc. is set up properly. MDL-6926. @@ -196,12 +196,12 @@ if (!is_enrolled($coursecontext)) { // User is a guest here! $SESSION->wantsurl = qualified_me(); $SESSION->enrolcancel = get_local_referer(false); - redirect(new moodle_url('/enrol/index.php', array('id' => $course->id, + redirect(new \core\url('/enrol/index.php', array('id' => $course->id, 'returnurl' => '/mod/hsuforum/view.php?f=' . $forum->id)), get_string('youneedtoenrol')); } } - throw new \moodle_exception('nopostforum', 'hsuforum'); + throw new \core\exception\moodle_exception('nopostforum', 'hsuforum'); } // Make sure user can post here. @@ -210,18 +210,18 @@ } else { $groupmode = $course->groupmode; } - if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $modcontext)) { + if ($groupmode == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $modcontext)) { if ($discussion->groupid == -1) { - throw new \moodle_exception('nopostforum', 'hsuforum'); + throw new \core\exception\moodle_exception('nopostforum', 'hsuforum'); } else { if (!groups_is_member($discussion->groupid)) { - throw new \moodle_exception('nopostforum', 'hsuforum'); + throw new \core\exception\moodle_exception('nopostforum', 'hsuforum'); } } } - if (!$cm->visible and !has_capability('moodle/course:viewhiddenactivities', $modcontext)) { - throw new \moodle_exception("activityiscurrentlyhidden"); + if (!$cm->visible && !has_capability('moodle/course:viewhiddenactivities', $modcontext)) { + throw new \core\exception\moodle_exception("activityiscurrentlyhidden"); } $messagecontent = hsuforum_change_format($messagecontent, $prefilledpostformat, $modcontext); @@ -251,25 +251,25 @@ } else if (!empty($edit)) { // User is editing their own post. if (! $post = hsuforum_get_post_full($edit)) { - throw new \moodle_exception('invalidpostid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidpostid', 'hsuforum'); } if ($post->parent) { if (! $parent = hsuforum_get_post_full($post->parent)) { - throw new \moodle_exception('invalidparentpostid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidparentpostid', 'hsuforum'); } } if (! $discussion = $DB->get_record("hsuforum_discussions", array("id" => $post->discussion))) { - throw new \moodle_exception('notpartofdiscussion', 'hsuforum'); + throw new \core\exception\moodle_exception('notpartofdiscussion', 'hsuforum'); } if (! $forum = $DB->get_record("hsuforum", array("id" => $discussion->forum))) { - throw new \moodle_exception('invalidforumid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidforumid', 'hsuforum'); } if (! $course = $DB->get_record("course", array("id" => $discussion->course))) { - throw new \moodle_exception('invalidcourseid'); + throw new \core\exception\moodle_exception('invalidcourseid'); } if (!$cm = get_coursemodule_from_instance("hsuforum", $forum->id, $course->id)) { - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } else { $modcontext = context_module::instance($cm->id); } @@ -279,14 +279,14 @@ $PAGE->requires->js_init_call('M.mod_hsuforum.init', null, false, $renderer->get_js_module()); if (!($forum->type == 'news' && !$post->parent && $discussion->timestart > time())) { - if (((time() - $post->created) > $CFG->maxeditingtime) and + if (((time() - $post->created) > $CFG->maxeditingtime) && !has_capability('mod/hsuforum:editanypost', $modcontext)) { - throw new \moodle_exception('maxtimehaspassed', 'hsuforum', '', format_time($CFG->maxeditingtime)); + throw new \core\exception\moodle_exception('maxtimehaspassed', 'hsuforum', '', format_time($CFG->maxeditingtime)); } } - if (($post->userid <> $USER->id) and + if (($post->userid <> $USER->id) && !has_capability('mod/hsuforum:editanypost', $modcontext)) { - throw new \moodle_exception('cannoteditposts', 'hsuforum'); + throw new \core\exception\moodle_exception('cannoteditposts', 'hsuforum'); } @@ -304,19 +304,19 @@ } else if (!empty($delete)) { // User is deleting a post. if (! $post = hsuforum_get_post_full($delete)) { - throw new \moodle_exception('invalidpostid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidpostid', 'hsuforum'); } if (! $discussion = $DB->get_record("hsuforum_discussions", array("id" => $post->discussion))) { - throw new \moodle_exception('notpartofdiscussion', 'hsuforum'); + throw new \core\exception\moodle_exception('notpartofdiscussion', 'hsuforum'); } if (! $forum = $DB->get_record("hsuforum", array("id" => $discussion->forum))) { - throw new \moodle_exception('invalidforumid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidforumid', 'hsuforum'); } if (!$cm = get_coursemodule_from_instance("hsuforum", $forum->id, $forum->course)) { - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } if (!$course = $DB->get_record('course', array('id' => $forum->course))) { - throw new \moodle_exception('invalidcourseid'); + throw new \core\exception\moodle_exception('invalidcourseid'); } require_login($course, false, $cm); @@ -324,7 +324,7 @@ if ( !(($post->userid == $USER->id && has_capability('mod/hsuforum:deleteownpost', $modcontext)) || has_capability('mod/hsuforum:deleteanypost', $modcontext)) ) { - throw new \moodle_exception('cannotdeletepost', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotdeletepost', 'hsuforum'); } @@ -345,8 +345,8 @@ if ($replycount) { if (!has_capability('mod/hsuforum:deleteanypost', $modcontext)) { - throw new \moodle_exception("couldnotdeletereplies", "hsuforum", - hsuforum_go_back_to(new moodle_url('/mod/hsuforum/discuss.php', array('d' => $post->discussion), 'p'.$post->id))); + throw new \core\exception\moodle_exception("couldnotdeletereplies", "hsuforum", + hsuforum_go_back_to(new \core\url('/mod/hsuforum/discuss.php', array('d' => $post->discussion), 'p'.$post->id))); } echo $OUTPUT->header(); echo $OUTPUT->heading(format_string($forum->name), 2); @@ -373,27 +373,27 @@ } else if (!empty($prune)) { // Pruning. if (!$post = hsuforum_get_post_full($prune)) { - throw new \moodle_exception('invalidpostid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidpostid', 'hsuforum'); } if (!$discussion = $DB->get_record("hsuforum_discussions", array("id" => $post->discussion))) { - throw new \moodle_exception('notpartofdiscussion', 'hsuforum'); + throw new \core\exception\moodle_exception('notpartofdiscussion', 'hsuforum'); } if (!$forum = $DB->get_record("hsuforum", array("id" => $discussion->forum))) { - throw new \moodle_exception('invalidforumid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidforumid', 'hsuforum'); } if ($forum->type == 'single') { - throw new \moodle_exception('cannotsplit', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotsplit', 'hsuforum'); } if (!$post->parent) { - throw new \moodle_exception('alreadyfirstpost', 'hsuforum'); + throw new \core\exception\moodle_exception('alreadyfirstpost', 'hsuforum'); } if (!$cm = get_coursemodule_from_instance("hsuforum", $forum->id, $forum->course)) { // For the logs. - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } else { $modcontext = context_module::instance($cm->id); } if (!has_capability('mod/hsuforum:splitdiscussions', $modcontext)) { - throw new \moodle_exception('cannotsplit', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotsplit', 'hsuforum'); } $PAGE->set_cm($cm); @@ -403,7 +403,7 @@ if ($prunemform->is_cancelled()) { - redirect(hsuforum_go_back_to(new moodle_url("/mod/hsuforum/discuss.php", array('d' => $post->discussion)))); + redirect(hsuforum_go_back_to(new \core\url("/mod/hsuforum/discuss.php", array('d' => $post->discussion)))); } else if ($fromform = $prunemform->get_data()) { // User submits the data. @@ -444,7 +444,7 @@ 'objectid' => $discussion->id, 'other' => array( 'forumid' => $forum->id, - ) + ), ); $event = \mod_hsuforum\event\discussion_updated::create($params); $event->trigger(); @@ -454,7 +454,7 @@ 'objectid' => $newid, 'other' => array( 'forumid' => $forum->id, - ) + ), ); $event = \mod_hsuforum\event\discussion_created::create($params); $event->trigger(); @@ -466,7 +466,7 @@ 'discussionid' => $newid, 'forumid' => $forum->id, 'forumtype' => $forum->type, - ) + ), ); $event = \mod_hsuforum\event\post_updated::create($params); $event->add_record_snapshot('hsuforum_discussions', $discussion); @@ -474,7 +474,7 @@ $message = get_string('discussionsplit', 'hsuforum'); redirect( - hsuforum_go_back_to(new moodle_url("/mod/hsuforum/discuss.php", array('d' => $newid))), + hsuforum_go_back_to(new \core\url("/mod/hsuforum/discuss.php", array('d' => $newid))), $message, null, \core\output\notification::NOTIFY_SUCCESS @@ -487,7 +487,7 @@ $renderer = $PAGE->get_renderer('mod_hsuforum'); $PAGE->requires->js_init_call('M.mod_hsuforum.init', null, false, $renderer->get_js_module()); $subjectstr = format_string($post->subject, true); - $PAGE->navbar->add($subjectstr, new moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id))); + $PAGE->navbar->add($subjectstr, new \core\url('/mod/hsuforum/discuss.php', array('d' => $discussion->id))); $PAGE->navbar->add(get_string("prune", "hsuforum")); $PAGE->set_title("$discussion->name: $post->subject"); $PAGE->set_heading($course->fullname); @@ -509,7 +509,7 @@ echo $OUTPUT->footer(); die; } else { - throw new \moodle_exception('unknowaction'); + throw new \core\exception\moodle_exception('unknowaction'); } @@ -522,14 +522,14 @@ // from now on user must be logged on properly. if (!$cm = get_coursemodule_from_instance('hsuforum', $forum->id, $course->id)) { // For the logs. - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } $modcontext = context_module::instance($cm->id); require_login($course, false, $cm); if (isguestuser()) { // Just in case. - throw new \moodle_exception('noguest'); + throw new \core\exception\moodle_exception('noguest'); } if (!isset($forum->maxattachments)) { // TODO - delete this once we add a field to the forum table. @@ -579,13 +579,13 @@ } else { $formheading = get_string('yournewtopic', 'hsuforum'); // Hide duplicated hsuforum description when creating a new discussion topic, see INT-18928. - $hidehtml .= html_writer::start_tag('style', array('type' => 'text/css')) . "\n"; + $hidehtml .= \core\output\html_writer::start_tag('style', array('type' => 'text/css')) . "\n"; $hidehtml .= ' #page-mod-hsuforum-post .activity-description, #page-mod-hsuforum-post [role="main"] h2 { display: none; };'; - $hidehtml .= html_writer::end_tag('style') . "\n"; + $hidehtml .= \core\output\html_writer::end_tag('style') . "\n"; } } @@ -614,7 +614,7 @@ 'message'=>array( 'text'=>$currenttext, 'format'=>empty($post->messageformat) ? editors_get_preferred_format() : $post->messageformat, - 'itemid'=>$draftideditor + 'itemid'=>$draftideditor, ), 'subscribe'=>$subscribe?1:0, 'mailnow'=>!empty($post->mailnow), @@ -623,7 +623,7 @@ 'reveal'=>$post->reveal, 'privatereply'=>$post->privatereply, 'discussion'=>$post->discussion, - 'course'=>$course->id + 'course'=>$course->id, ) + $pageparams + @@ -677,7 +677,7 @@ if ( !(($realpost->userid == $USER->id && (has_capability('mod/hsuforum:replypost', $modcontext) || has_capability('mod/hsuforum:startdiscussion', $modcontext))) || has_capability('mod/hsuforum:editanypost', $modcontext)) ) { - throw new \moodle_exception('cannotupdatepost', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotupdatepost', 'hsuforum'); } if ($realpost->userid != $USER->id || !has_capability('mod/hsuforum:revealpost', $modcontext)) { @@ -691,7 +691,7 @@ } if (!hsuforum_user_can_post_discussion($forum, $fromform->groupinfo, null, $cm, $modcontext)) { - throw new \moodle_exception('cannotupdatepost', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotupdatepost', 'hsuforum'); } $DB->set_field('hsuforum_discussions', 'groupid', $fromform->groupinfo, array('firstpost' => $fromform->id)); @@ -709,7 +709,7 @@ $updatepost = $fromform; // Realpost. $updatepost->forum = $forum->id; if (!hsuforum_update_post($updatepost, $mformpost)) { - throw new \moodle_exception("couldnotupdate", "hsuforum", $errordestination); + throw new \core\exception\moodle_exception("couldnotupdate", "hsuforum", $errordestination); } // MDL-11818. @@ -738,9 +738,9 @@ // Single discussion forums are an exception. We show // the forum itself since it only has one discussion // thread. - $discussionurl = new moodle_url("/mod/hsuforum/view.php", array('f' => $forum->id)); + $discussionurl = new \core\url("/mod/hsuforum/view.php", array('f' => $forum->id)); } else { - $discussionurl = new moodle_url("/mod/hsuforum/discuss.php", array('d' => $discussion->id), 'p' . $fromform->id); + $discussionurl = new \core\url("/mod/hsuforum/discuss.php", array('d' => $discussion->id), 'p' . $fromform->id); } $params = array( @@ -750,7 +750,7 @@ 'discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type, - ) + ), ); if ($realpost->userid !== $USER->id) { @@ -791,9 +791,9 @@ // Single discussion forums are an exception. We show // the forum itself since it only has one discussion // thread. - $discussionurl = new moodle_url("/mod/hsuforum/view.php", array('f' => $forum->id), 'p'.$fromform->id); + $discussionurl = new \core\url("/mod/hsuforum/view.php", array('f' => $forum->id), 'p'.$fromform->id); } else { - $discussionurl = new moodle_url("/mod/hsuforum/discuss.php", array('d' => $discussion->id), 'p'.$fromform->id); + $discussionurl = new \core\url("/mod/hsuforum/discuss.php", array('d' => $discussion->id), 'p'.$fromform->id); } $post = $DB->get_record('hsuforum_posts', array('id' => $fromform->id), '*', MUST_EXIST); $params = array( @@ -803,7 +803,7 @@ 'discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type, - ) + ), ); $event = \mod_hsuforum\event\post_created::create($params); $event->add_record_snapshot('hsuforum_posts', $post); @@ -825,13 +825,13 @@ ); } else { - throw new \moodle_exception("couldnotadd", "hsuforum", $errordestination); + throw new \core\exception\moodle_exception("couldnotadd", "hsuforum", $errordestination); } exit; } else { // Adding a new discussion. // The location to redirect to after successfully posting. - $redirectto = new moodle_url('/mod/hsuforum/view.php', array('f' => $fromform->forum)); + $redirectto = new \core\url('/mod/hsuforum/view.php', array('f' => $fromform->forum)); $fromform->mailnow = empty($fromform->mailnow) ? 0 : 1; @@ -850,6 +850,7 @@ } $discussion->timestart = $fromform->timestart; $discussion->timeend = $fromform->timeend; + $discussion->timelocked = $fromform->timelocked ?? 0; if (has_capability('mod/hsuforum:pindiscussions', $modcontext) && !empty($fromform->pinned)) { $discussion->pinned = HSUFORUM_DISCUSSION_PINNED; @@ -891,7 +892,7 @@ foreach ($groupstopostto as $group) { if (!hsuforum_user_can_post_discussion($forum, $group, -1, $cm, $modcontext)) { - throw new \moodle_exception('cannotcreatediscussion', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotcreatediscussion', 'hsuforum'); } $discussion->groupid = $group; @@ -903,7 +904,7 @@ 'objectid' => $discussion->id, 'other' => array( 'forumid' => $forum->id, - ) + ), ); $event = \mod_hsuforum\event\discussion_created::create($params); $event->add_record_snapshot('hsuforum_discussions', $discussion); @@ -918,7 +919,7 @@ $subscribemessage = hsuforum_post_subscription($fromform, $forum, $discussion); } else { - throw new \moodle_exception("couldnotadd", "hsuforum", $errordestination); + throw new \core\exception\moodle_exception("couldnotadd", "hsuforum", $errordestination); } } @@ -949,7 +950,7 @@ if ($post->discussion) { if (! $toppost = $DB->get_record("hsuforum_posts", array("discussion" => $post->discussion, "parent" => 0))) { - throw new \moodle_exception('cannotfindparentpost', 'hsuforum', '', $post->id); + throw new \core\exception\moodle_exception('cannotfindparentpost', 'hsuforum', '', $post->id); } } else { $toppost = new stdClass(); @@ -997,17 +998,18 @@ $renderer = $PAGE->get_renderer('mod_hsuforum'); $PAGE->requires->js_init_call('M.mod_hsuforum.init', null, false, $renderer->get_js_module()); echo $OUTPUT->header(); +echo $hidehtml; echo $OUTPUT->heading(format_string($forum->name), 2); // Checkup. if (!empty($parent) && !hsuforum_user_can_see_post($forum, $discussion, $post, null, $cm)) { - throw new \moodle_exception('cannotreply', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotreply', 'hsuforum'); } if (!empty($parent) && !empty($parent->privatereply)) { - throw new \moodle_exception('cannotreply', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotreply', 'hsuforum'); } if (empty($parent) && empty($edit) && !hsuforum_user_can_post_discussion($forum, $groupid, -1, $cm, $modcontext)) { - throw new \moodle_exception('cannotcreatediscussion', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotcreatediscussion', 'hsuforum'); } if ($forum->type == 'qanda' @@ -1025,7 +1027,7 @@ if (!empty($parent)) { if (!$discussion = $DB->get_record('hsuforum_discussions', array('id' => $parent->discussion))) { - throw new \moodle_exception('notpartofdiscussion', 'hsuforum'); + throw new \core\exception\moodle_exception('notpartofdiscussion', 'hsuforum'); } echo $renderer->svg_sprite(); diff --git a/renderer.php b/renderer.php index fae6be1b..dba716ff 100644 --- a/renderer.php +++ b/renderer.php @@ -1,5 +1,4 @@ course); $forums = $modinfo->get_instances_of('hsuforum'); if (!isset($forums[$forum->id])) { - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } $cm = $forums[$forum->id]; @@ -128,18 +127,18 @@ public function render_discussionsview($forum) { if ($id) { if (! $course = $DB->get_record("course", array("id" => $cm->course))) { - throw new \moodle_exception('coursemisconf'); + throw new \core\exception\moodle_exception('coursemisconf'); } } else if ($f) { if (! $course = $DB->get_record("course", array("id" => $forum->course))) { - throw new \moodle_exception('coursemisconf'); + throw new \core\exception\moodle_exception('coursemisconf'); } // move require_course_login here to use forced language for course // fix for MDL-6926 require_course_login($course, true, $cm); } else { - throw new \moodle_exception('missingparameter'); + throw new \core\exception\moodle_exception('missingparameter'); } $context = \context_module::instance($cm->id); @@ -156,7 +155,7 @@ public function render_discussionsview($forum) { $completion->set_module_viewed($cm); /// Some capability checks. - if (empty($cm->visible) and !has_capability('moodle/course:viewhiddenactivities', $context)) { + if (empty($cm->visible) && !has_capability('moodle/course:viewhiddenactivities', $context)) { notice(get_string("activityiscurrentlyhidden")); } @@ -181,10 +180,10 @@ public function render_discussionsview($forum) { $forumobject = $DB->get_record("hsuforum", ["id" => $PAGE->cm->instance]); // Url's for different options in the discussion. - $manageforumsubscriptionsurl = new \moodle_url('/mod/hsuforum/index.php', ['id' => $course->id]); - $exporturl = new \moodle_url('/mod/hsuforum/route.php', ['contextid' => $context->id, 'action' => 'export']); - $viewpostersurl = new \moodle_url('/mod/hsuforum/route.php', ['contextid' => $context->id, 'action' => 'viewposters']); - $subscribeforumurl = new \moodle_url('/mod/hsuforum/subscribe.php', ['id' => $forum->id, 'sesskey' => sesskey()]); + $manageforumsubscriptionsurl = new \core\url('/mod/hsuforum/index.php', ['id' => $course->id]); + $exporturl = new \core\url('/mod/hsuforum/route.php', ['contextid' => $context->id, 'action' => 'export']); + $viewpostersurl = new \core\url('/mod/hsuforum/route.php', ['contextid' => $context->id, 'action' => 'viewposters']); + $subscribeforumurl = new \core\url('/mod/hsuforum/subscribe.php', ['id' => $forum->id, 'sesskey' => sesskey()]); // Strings for the Url's. $manageforumsubscriptions = get_string('manageforumsubscriptions', 'mod_hsuforum'); @@ -200,24 +199,24 @@ public function render_discussionsview($forum) { // We need to verify that these outputs only appears for Snap, Boost will only display the manage forum subscriptions link. if (get_config('core', 'theme') == 'snap') { // Outputs for the Url's inside divs to have a correct position inside the page. - $output .= '

    '; + $output .= '

      '; $output .= '
    • '; - $output .= \html_writer::link($manageforumsubscriptionsurl, $manageforumsubscriptions, ['class' => 'btn btn-link']); + $output .= \core\output\html_writer::link($manageforumsubscriptionsurl, $manageforumsubscriptions, ['class' => 'btn btn-link']); $output .= '
    • '; $output .= '
    • '; - $output .= \html_writer::link($exporturl, $exportdiscussions, ['class' => 'btn btn-link']); + $output .= \core\output\html_writer::link($exporturl, $exportdiscussions, ['class' => 'btn btn-link']); $output .= '
    • '; $output .= '
    • '; - $output .= \html_writer::link($viewpostersurl, $viewposters, ['class' => 'btn btn-link']); + $output .= \core\output\html_writer::link($viewpostersurl, $viewposters, ['class' => 'btn btn-link']); $output .= '
    • '; $output .= '
    • '; - $output .= \html_writer::link($subscribeforumurl, $subscribe, ['class' => 'btn btn-link']); + $output .= \core\output\html_writer::link($subscribeforumurl, $subscribe, ['class' => 'btn btn-link']); $output .= '
    • '; $output .= '
    '; } else { - $output .= '

      '; + $output .= '

        '; $output .= '
      • '; - $output .= \html_writer::link($manageforumsubscriptionsurl, $manageforumsubscriptions, ['class' => 'btn btn-link']); + $output .= \core\output\html_writer::link($manageforumsubscriptionsurl, $manageforumsubscriptions, ['class' => 'btn btn-link']); $output .= '
      • '; $output .= '
      '; } @@ -231,9 +230,9 @@ public function render_discussionsview($forum) { if ($controller->is_form_defined()) { $gradingcontrollerpreview = $controller->render_preview($PAGE); if ($gradingcontrollerpreview) { - $output .= '
      '; - $output .= \html_writer::link('#hsuforum_gradingcriteria', get_string('gradingmethodpreview', 'hsuforum'), - ['class' => 'btn btn-link text-right', 'data-toggle' => 'collapse', 'role' => 'button', 'aria-expanded' => 'false', + $output .= '
      '; + $output .= \core\output\html_writer::link('#hsuforum_gradingcriteria', get_string('gradingmethodpreview', 'hsuforum'), + ['class' => 'btn btn-link text-end', 'data-toggle' => 'collapse', 'role' => 'button', 'aria-expanded' => 'false', 'aria-controls' => 'hsuforum_gradingcriteria']); $output .= '
      '; $output .= '
      @@ -277,7 +276,7 @@ public function discussions($cm, array $discussions, array $options) { // TODO - this is confusing code return $this->notification_area(). $this->output->container('', 'hsuforum-add-discussion-target'). - html_writer::tag('section', $output, array('role' => 'region', 'aria-label' => get_string('discussions', 'hsuforum'), 'class' => 'hsuforum-threads-wrapper', 'tabindex' => '-1')). + \core\output\html_writer::tag('section', $output, array('role' => 'region', 'aria-label' => get_string('discussions', 'hsuforum'), 'class' => 'hsuforum-threads-wrapper', 'tabindex' => '-1')). $this->article_assets($cm); } @@ -389,7 +388,7 @@ public function discussion($cm, $discussion, $post, $fullthread, array $posts = $data->group = $group; $data->imagesrc = $postuser->user_picture->get_url($this->page)->out(); $data->userurl = $this->get_post_user_url($cm, $postuser); - $data->viewurl = new moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id)); + $data->viewurl = new \core\url('/mod/hsuforum/discuss.php', array('d' => $discussion->id)); $data->tools = implode(' ', $this->post_get_commands($post, $discussion, $cm, $canreply)); $data->postflags = implode(' ',$this->post_get_flags($post, $cm, $discussion->id)); $data->subscribe = ''; @@ -406,7 +405,7 @@ public function discussion($cm, $discussion, $post, $fullthread, array $posts = } if ($fullthread && $canreply) { - $data->replyform = html_writer::tag( + $data->replyform = \core\output\html_writer::tag( 'div', $this->simple_edit_post($cm, false, $post->id), array('class' => 'hsuforum-footer-reply') ); } else { @@ -441,12 +440,12 @@ public function article_assets($cm) { if (!isloggedin()) { return ''; } - $output = html_writer::tag( + $output = \core\output\html_writer::tag( 'script', $this->simple_edit_post($cm), array('type' => 'text/template', 'id' => 'hsuforum-reply-template') ); - $output .= html_writer::tag( + $output .= \core\output\html_writer::tag( 'script', $this->simple_edit_discussion($cm), array('type' => 'text/template', 'id' => 'hsuforum-discussion-template') @@ -479,7 +478,7 @@ public function post($cm, $discussion, $post, $canreply = false, $parent = null, } else if (empty($commands)) { $commands = $this->post_get_commands($post, $discussion, $cm, $canreply, false); } else if (!is_array($commands)){ - throw new coding_exception('$commands must be false, empty or populated array'); + throw new \core\exception\coding_exception('$commands must be false, empty or populated array'); } $postuser = hsuforum_extract_postuser($post, $forum, context_module::instance($cm->id)); $postuser->user_picture->size = 100; @@ -497,7 +496,7 @@ public function post($cm, $discussion, $post, $canreply = false, $parent = null, $data->imagesrc = $postuser->user_picture->get_url($this->page)->out(); $data->userurl = $this->get_post_user_url($cm, $postuser); $data->unread = empty($post->postread) ? true : false; - $data->permalink = new moodle_url('/mod/hsuforum/discuss.php#p'.$post->id, array('d' => $discussion->id)); + $data->permalink = new \core\url('/mod/hsuforum/discuss.php#p'.$post->id, array('d' => $discussion->id)); $data->isreply = false; $data->parentfullname = ''; $data->parentuserurl = ''; @@ -555,9 +554,9 @@ public function discussion_template($d, $forumtype) { $replies = "$xreplies"; } if (!empty($d->userurl)) { - $byuser = html_writer::link($d->userurl, $d->fullname); + $byuser = \core\output\html_writer::link($d->userurl, $d->fullname); } else { - $byuser = html_writer::tag('span', $d->fullname); + $byuser = \core\output\html_writer::tag('span', $d->fullname); } $unread = ''; $unreadclass = ''; @@ -616,7 +615,7 @@ public function discussion_template($d, $forumtype) { $revealed = ""; if ($d->revealed) { $nonanonymous = get_string('nonanonymous', 'mod_hsuforum'); - $revealed = ''.$nonanonymous.''; + $revealed = ''.$nonanonymous.''; } $arialabeldiscussion = get_string('discussionforum', 'hsuforum', $d->subject); @@ -669,7 +668,7 @@ public function discussion_template($d, $forumtype) { * @param \stdClass $discussion The discussion for the posts * @param \stdClass[] $posts The posts to render * @param bool $canreply - * @throws coding_exception + * @throws \core\exception\coding_exception * @return string */ public function posts($cm, $discussion, $posts, $canreply = false) { @@ -679,7 +678,7 @@ public function posts($cm, $discussion, $posts, $canreply = false) { $count = 0; if (!empty($posts)) { if (!array_key_exists($discussion->firstpost, $posts)) { - throw new coding_exception('Missing discussion post'); + throw new \core\exception\coding_exception('Missing discussion post'); } $parent = $posts[$discussion->firstpost]; $items .= $this->post_walker($cm, $discussion, $posts, $parent, $canreply, $count); @@ -745,13 +744,13 @@ public function post_template($p) { $byuser = $p->fullname; if (!empty($p->userurl)) { - $byuser = html_writer::link($p->userurl, $p->fullname); + $byuser = \core\output\html_writer::link($p->userurl, $p->fullname); } $byline = get_string('postbyx', 'hsuforum', $byuser); if ($p->isreply) { $parent = $p->parentfullname; if (!empty($p->parentuserurl)) { - $parent = html_writer::link($p->parentuserurl, $p->parentfullname); + $parent = \core\output\html_writer::link($p->parentuserurl, $p->parentfullname); } if (empty($p->parentuserpic)) { $byline = get_string('replybyx', 'hsuforum', $byuser); @@ -759,7 +758,7 @@ public function post_template($p) { $byline = get_string('postbyxinreplytox', 'hsuforum', array( 'parent' => $p->parentuserpic.$parent, 'author' => $byuser, - 'parentpost' => "".get_string('parentofthispost', 'hsuforum')."↑" + 'parentpost' => "".get_string('parentofthispost', 'hsuforum')."↑", )); } if (!empty($p->privatereply)) { @@ -768,7 +767,7 @@ public function post_template($p) { } else { $byline = get_string('postbyxinprivatereplytox', 'hsuforum', array( 'author' => $byuser, - 'parent' => $p->parentuserpic.$parent + 'parent' => $p->parentuserpic.$parent, )); } } @@ -800,7 +799,7 @@ public function post_template($p) { $revealed = ""; if ($p->revealed) { $nonanonymous = get_string('nonanonymous', 'mod_hsuforum'); - $revealed = ''.$nonanonymous.''; + $revealed = ''.$nonanonymous.''; } return <<'hidden', 'name'=>'sesskey', 'value'=>sesskey())); + $output .= \core\output\html_writer::start_tag('form', $formattributes); + $output .= \core\output\html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=>sesskey())); - $existingcell = new html_table_cell(); + $existingcell = new \core_table\output\html_table_cell(); $existingcell->text = $existinguc->display(true); $existingcell->attributes['class'] = 'existing'; - $actioncell = new html_table_cell(); - $actioncell->text = html_writer::start_tag('div', array()); - $actioncell->text .= html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'subscribe', 'value'=>$this->page->theme->larrow.' '.get_string('add'), 'class'=>'actionbutton')); - $actioncell->text .= html_writer::empty_tag('br', array()); - $actioncell->text .= html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'unsubscribe', 'value'=>$this->page->theme->rarrow.' '.get_string('remove'), 'class'=>'actionbutton')); - $actioncell->text .= html_writer::end_tag('div', array()); + $actioncell = new \core_table\output\html_table_cell(); + $actioncell->text = \core\output\html_writer::start_tag('div', array()); + $actioncell->text .= \core\output\html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'subscribe', 'value'=>$this->page->theme->larrow.' '.get_string('add'), 'class'=>'actionbutton')); + $actioncell->text .= \core\output\html_writer::empty_tag('br', array()); + $actioncell->text .= \core\output\html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'unsubscribe', 'value'=>$this->page->theme->rarrow.' '.get_string('remove'), 'class'=>'actionbutton')); + $actioncell->text .= \core\output\html_writer::end_tag('div', array()); $actioncell->attributes['class'] = 'actions'; - $potentialcell = new html_table_cell(); + $potentialcell = new \core_table\output\html_table_cell(); $potentialcell->text = $potentialuc->display(true); $potentialcell->attributes['class'] = 'potential'; - $table = new html_table(); + $table = new \core_table\output\html_table(); $table->attributes['class'] = 'subscribertable boxaligncenter'; - $table->data = array(new html_table_row(array($existingcell, $actioncell, $potentialcell))); - $output .= html_writer::table($table); + $table->data = array(new \core_table\output\html_table_row(array($existingcell, $actioncell, $potentialcell))); + $output .= \core\output\html_writer::table($table); - $output .= html_writer::end_tag('form'); + $output .= \core\output\html_writer::end_tag('form'); return $output; } @@ -895,7 +894,7 @@ public function subscriber_overview($users, $entityname, $forum, $course) { $strparams->name = format_string($forum->name); $strparams->count = count($users); $output .= $this->output->heading(get_string("subscriberstowithcount", "hsuforum", $strparams)); - $table = new html_table(); + $table = new \core_table\output\html_table(); $table->cellpadding = 5; $table->cellspacing = 5; $table->tablealign = 'center'; @@ -907,7 +906,7 @@ public function subscriber_overview($users, $entityname, $forum, $course) { } $table->data[] = $info; } - $output .= html_writer::table($table); + $output .= \core\output\html_writer::table($table); } return $output; } @@ -921,7 +920,7 @@ public function subscriber_overview($users, $entityname, $forum, $course) { */ public function subscribed_users(user_selector_base $existingusers) { $output = $this->output->box_start('subscriberdiv boxaligncenter'); - $output .= html_writer::tag('p', get_string('forcessubscribe', 'hsuforum')); + $output .= \core\output\html_writer::tag('p', get_string('forcessubscribe', 'hsuforum')); $output .= $existingusers->display(true); $output .= $this->output->box_end(); return $output; @@ -1006,9 +1005,9 @@ public function get_js_module() { array('toggled:bookmark', 'hsuforum'), array('toggled:subscribe', 'hsuforum'), array('toggled:substantive', 'hsuforum'), - array('addareply', 'hsuforum') + array('addareply', 'hsuforum'), - ) + ), ); } @@ -1019,7 +1018,7 @@ public function get_js_module() { * @param $cm * @param int $discussion id of parent discussion * @param bool $reply is this the first post in a thread or a reply - * @throws coding_exception + * @throws \core\exception\coding_exception * @return array * @author Mark Nielsen */ @@ -1032,7 +1031,7 @@ public function post_get_flags($post, $cm, $discussion, $reply = false) { return array(); } if (!property_exists($post, 'flags')) { - throw new coding_exception('The post\'s flags property must be set'); + throw new \core\exception\coding_exception('The post\'s flags property must be set'); } require_once(__DIR__.'/lib/flag.php'); @@ -1061,13 +1060,13 @@ public function post_get_flags($post, $cm, $discussion, $reply = false) { $isflagged = $flaglib->is_flagged($post->flags, $flag); - $url = new moodle_url('/mod/hsuforum/route.php', array( + $url = new \core\url('/mod/hsuforum/route.php', array( 'contextid' => $context->id, 'action' => 'flag', 'returnurl' => $returnurl, 'postid' => $post->id, 'flag' => $flag, - 'sesskey' => sesskey() + 'sesskey' => sesskey(), )); // Create appropriate area described by. @@ -1116,7 +1115,7 @@ private function return_url($cmid, $discussionid) { * @param string $type - toggle type * @param string $label - label for toggle element * @param string $describedby - aria described by - * @param moodle_url $url + * @param \core\url $url * @param bool $pressed * @param bool $link * @param null $attributes @@ -1150,9 +1149,9 @@ public function toggle_element($type, $describedby, $url, $pressed = false, $lin $attributes['aria-pressed'] = $pressed ? 'true' : 'false'; $attributes['aria-describedby'] = $describedby; $attributes['title'] = $type; - return (html_writer::link($url, $icon, $attributes)); + return (\core\output\html_writer::link($url, $icon, $attributes)); } else { - return (html_writer::tag('span', $icon, $attributes)); + return (\core\output\html_writer::tag('span', $icon, $attributes)); } } @@ -1173,7 +1172,7 @@ public function discussion_subscribe_link($cm, $discussion, hsuforum_lib_discuss $returnurl = $this->return_url($cm->id, $discussion->id); - $url = new moodle_url('/mod/hsuforum/route.php', array( + $url = new \core\url('/mod/hsuforum/route.php', array( 'contextid' => context_module::instance($cm->id)->id, 'action' => 'subscribedisc', 'discussionid' => $discussion->id, @@ -1199,11 +1198,11 @@ public function discussion_subscribe_link($cm, $discussion, hsuforum_lib_discuss */ public function discussion_sorting($cm, hsuforum_lib_discussion_sort $sort) { - $url = new moodle_url('/mod/hsuforum/view.php'); + $url = new \core\url('/mod/hsuforum/view.php'); $sortbysronlytextstring = get_string('sortdiscussionsbysronlytext', 'hsuforum'); $sortbysronlytext = ''.$sortbysronlytextstring.''; - $sortselect = html_writer::select($sort->get_key_options_menu(), 'dsortkey', $sort->get_key(), false, array('class' => '')); + $sortselect = \core\output\html_writer::select($sort->get_key_options_menu(), 'dsortkey', $sort->get_key(), false, array('class' => '')); $sortform = "
      ".get_string('sortdiscussions', 'hsuforum')." @@ -1317,7 +1316,7 @@ public function user_posts_overview($userid, $cm, $showonlypreference = null) { $forum = hsuforum_get_cm_forum($cm); $showonlypreferencebutton = ''; - if (!empty($showonlypreference) and !empty($showonlypreference->button) and !$forum->anonymous) { + if (!empty($showonlypreference) && !empty($showonlypreference->button) && !$forum->anonymous) { $showonlypreferencebutton = $showonlypreference->button; } @@ -1326,10 +1325,10 @@ public function user_posts_overview($userid, $cm, $showonlypreference = null) { $flaglib = new hsuforum_lib_flag(); if ($posts = hsuforum_get_user_posts($forum->id, $userid, context_module::instance($cm->id))) { $discussions = hsuforum_get_user_involved_discussions($forum->id, $userid); - if (!empty($showonlypreference) and !empty($showonlypreference->preference)) { + if (!empty($showonlypreference) && !empty($showonlypreference->preference)) { foreach ($discussions as $discussion) { - if ($discussion->userid == $userid and array_key_exists($discussion->firstpost, $posts)) { + if ($discussion->userid == $userid && array_key_exists($discussion->firstpost, $posts)) { $discussionpost = $posts[$discussion->firstpost]; $discussioncount++; @@ -1344,16 +1343,16 @@ public function user_posts_overview($userid, $cm, $showonlypreference = null) { if (!$forum->anonymous) { $output .= $this->post($cm, $discussion, $discussionpost, false, null, false); - $output .= html_writer::start_tag('div', array('class' => 'indent')); + $output .= \core\output\html_writer::start_tag('div', array('class' => 'indent')); } foreach ($posts as $post) { - if ($post->discussion == $discussion->id and !empty($post->parent)) { + if ($post->discussion == $discussion->id && !empty($post->parent)) { $postcount++; if ($flaglib->is_flagged($post->flags, 'substantive')) { $flagcount++; } - $command = html_writer::link( - new moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id), 'p'.$post->id), + $command = \core\output\html_writer::link( + new \core\url('/mod/hsuforum/discuss.php', array('d' => $discussion->id), 'p'.$post->id), get_string('postincontext', 'hsuforum'), array('target' => '_blank') ); @@ -1363,7 +1362,7 @@ public function user_posts_overview($userid, $cm, $showonlypreference = null) { } } if (!$forum->anonymous) { - $output .= html_writer::end_tag('div'); + $output .= \core\output\html_writer::end_tag('div'); } } } else { @@ -1377,8 +1376,8 @@ public function user_posts_overview($userid, $cm, $showonlypreference = null) { $flagcount++; } if (!$forum->anonymous) { - $command = html_writer::link( - new moodle_url('/mod/hsuforum/discuss.php', array('d' => $post->discussion), 'p'.$post->id), + $command = \core\output\html_writer::link( + new \core\url('/mod/hsuforum/discuss.php', array('d' => $post->discussion), 'p'.$post->id), get_string('postincontext', 'hsuforum'), array('target' => '_blank') ); @@ -1390,12 +1389,12 @@ public function user_posts_overview($userid, $cm, $showonlypreference = null) { if (!empty($postcount) or !empty($discussioncount)) { if ($forum->anonymous) { - $output = html_writer::tag('h3', get_string('thisisanonymous', 'hsuforum')); + $output = \core\output\html_writer::tag('h3', get_string('thisisanonymous', 'hsuforum')); } $counts = array( get_string('totalpostsanddiscussions', 'hsuforum', ($discussioncount+$postcount)), get_string('totaldiscussions', 'hsuforum', $discussioncount), - get_string('totalreplies', 'hsuforum', $postcount) + get_string('totalreplies', 'hsuforum', $postcount), ); if (!empty($config->showsubstantive)) { @@ -1407,10 +1406,10 @@ public function user_posts_overview($userid, $cm, $showonlypreference = null) { } $countshtml = ''; foreach ($counts as $count) { - $countshtml .= html_writer::tag('div', $count, array('class' => 'hsuforum_count')); + $countshtml .= \core\output\html_writer::tag('div', $count, array('class' => 'hsuforum_count')); } - $output = html_writer::div($countshtml, 'hsuforum_counts').$showonlypreferencebutton.$output; - $output = html_writer::div($output, 'mod-hsuforum-posts-container article'); + $output = \core\output\html_writer::div($countshtml, 'hsuforum_counts').$showonlypreferencebutton.$output; + $output = \core\output\html_writer::div($output, 'mod-hsuforum-posts-container article'); } return $output; } @@ -1431,7 +1430,7 @@ public function validation_errors($errors) { } $message = get_string('validationerrorsx', 'hsuforum', array( 'count' => count($errors), - 'errors' => html_writer::alist($items, null, 'ol'), + 'errors' => \core\output\html_writer::alist($items, null, 'ol'), )); } return $message; @@ -1483,7 +1482,7 @@ public function simple_edit_discussion($cm, $postid = 0, array $data = array()) 'groupid' => 0, 'messageformat' => FORMAT_HTML, ); - $actionurl = new moodle_url('/mod/hsuforum/route.php', array( + $actionurl = new \core\url('/mod/hsuforum/route.php', array( 'action' => (empty($postid)) ? 'add_discussion' : 'update_post', 'sesskey' => sesskey(), 'edit' => $postid, @@ -1495,8 +1494,8 @@ public function simple_edit_discussion($cm, $postid = 0, array $data = array()) $extrahtml = ''; if ($forum->anonymous && $ownpost && has_capability('mod/hsuforum:revealpost', $context)) { - $extrahtml .= html_writer::tag('label', get_string('reveal', 'hsuforum') . ' ' . - html_writer::checkbox('reveal', 1, !empty($post->reveal))); + $extrahtml .= \core\output\html_writer::tag('label', get_string('reveal', 'hsuforum') . ' ' . + \core\output\html_writer::checkbox('reveal', 1, !empty($post->reveal))); } if (groups_get_activity_groupmode($cm)) { @@ -1521,8 +1520,8 @@ public function simple_edit_discussion($cm, $postid = 0, array $data = array()) && has_capability('mod/hsuforum:canposttomygroups', $context); if ($canposttoowngroups) { - $extrahtml .= html_writer::tag('label', get_string('posttomygroups', 'hsuforum') . ' ' . - html_writer::checkbox('posttomygroups', 1, false)); + $extrahtml .= \core\output\html_writer::tag('label', get_string('posttomygroups', 'hsuforum') . ' ' . + \core\output\html_writer::checkbox('posttomygroups', 1, false)); } if (hsuforum_user_can_post_discussion($forum, -1, null, $cm, $context)) { @@ -1542,9 +1541,9 @@ public function simple_edit_discussion($cm, $postid = 0, array $data = array()) $canselectgroup = empty($post->parent) && ($canselectgroupfornew || $canselectgroupformove); if ($canselectgroup) { - $groupselect = html_writer::tag('span', get_string('group') . ' '); - $groupselect .= html_writer::select($groupinfo, 'groupinfo', $data['groupid'], false); - $extrahtml .= html_writer::tag('label', $groupselect); + $groupselect = \core\output\html_writer::tag('span', get_string('group') . ' '); + $groupselect .= \core\output\html_writer::select($groupinfo, 'groupinfo', $data['groupid'], false); + $extrahtml .= \core\output\html_writer::tag('label', $groupselect); } else { $actionurl->param('groupinfo', groups_get_activity_group($cm)); } @@ -1558,7 +1557,7 @@ public function simple_edit_discussion($cm, $postid = 0, array $data = array()) 'class' => 'hsuforum-discussion', 'legend' => $legend, 'extrahtml' => $extrahtml, - 'advancedurl' => new moodle_url('/mod/hsuforum/post.php', $params), + 'advancedurl' => new \core\url('/mod/hsuforum/post.php', $params), ); return $this->simple_edit_template($data); } @@ -1613,7 +1612,7 @@ public function simple_edit_post($cm, $isedit = false, $postid = 0, array $data 'reveal' => 0, 'messageformat' => FORMAT_HTML, ); - $actionurl = new moodle_url('/mod/hsuforum/route.php', array( + $actionurl = new \core\url('/mod/hsuforum/route.php', array( 'action' => ($isedit) ? 'update_post' : 'reply', $param => $postid, 'sesskey' => sesskey(), @@ -1626,12 +1625,12 @@ public function simple_edit_post($cm, $isedit = false, $postid = 0, array $data if (has_capability('mod/hsuforum:allowprivate', $context, $postuser) && $forum->allowprivatereplies !== '0' ) { - $extrahtml .= html_writer::tag('label', get_string('privatereply', 'hsuforum') . ' ' . - html_writer::checkbox('privatereply', 1, !empty($data['privatereply']))); + $extrahtml .= \core\output\html_writer::tag('label', get_string('privatereply', 'hsuforum') . ' ' . + \core\output\html_writer::checkbox('privatereply', 1, !empty($data['privatereply']))); } if ($forum->anonymous && $ownpost && has_capability('mod/hsuforum:revealpost', $context)) { - $extrahtml .= html_writer::tag('label', get_string('reveal', 'hsuforum') . ' ' . - html_writer::checkbox('reveal', 1, !empty($data['reveal']))); + $extrahtml .= \core\output\html_writer::tag('label', get_string('reveal', 'hsuforum') . ' ' . + \core\output\html_writer::checkbox('reveal', 1, !empty($data['reveal']))); } $data += array( 'postid' => ($isedit) ? $postid : 0, @@ -1642,7 +1641,7 @@ public function simple_edit_post($cm, $isedit = false, $postid = 0, array $data 'legend' => $legend, 'extrahtml' => $extrahtml, 'subjectrequired' => $isedit, - 'advancedurl' => new moodle_url('/mod/hsuforum/post.php', array($param => $postid)), + 'advancedurl' => new \core\url('/mod/hsuforum/post.php', array($param => $postid)), ); return $this->simple_edit_template($data); } @@ -1687,7 +1686,7 @@ protected function simple_edit_template($t) { $t = (object) $t; $legend = s($t->legend); $subject = s($t->subject); - $hidden = html_writer::input_hidden_params($t->actionurl); + $hidden = \core\output\html_writer::input_hidden_params($t->actionurl); $actionurl = $t->actionurl->out_omit_querystring(); $advancedurl = s($t->advancedurl); $messagelabel = s($t->messagelabel); @@ -1701,13 +1700,13 @@ protected function simple_edit_template($t) { } if (!empty($t->postid) && $canattach) { foreach ($attachments->get_attachments($t->postid) as $file) { - $checkbox = html_writer::checkbox('deleteattachment[]', $file->get_filename(), false). + $checkbox = \core\output\html_writer::checkbox('deleteattachment[]', $file->get_filename(), false). get_string('deleteattachmentx', 'hsuforum', $file->get_filename()); - $files .= html_writer::tag('label', $checkbox); + $files .= \core\output\html_writer::tag('label', $checkbox); } - $files = html_writer::tag('legend', get_string('deleteattachments', 'hsuforum'), array('class' => 'accesshide')).$files; - $files = html_writer::tag('fieldset', $files); + $files = \core\output\html_writer::tag('legend', get_string('deleteattachments', 'hsuforum'), array('class' => 'accesshide')).$files; + $files = \core\output\html_writer::tag('fieldset', $files); } if ($canattach) { $files .= <<user_picture->link) { return null; } - return new moodle_url('/user/view.php', ['id' => $postuser->id, 'course' => $cm->course]); + return new \core\url('/user/view.php', ['id' => $postuser->id, 'course' => $cm->course]); } protected function notification_area() { @@ -1808,27 +1807,27 @@ protected function notification_area() { * @param stdClass $cm * @param bool $canreply * @return array - * @throws coding_exception + * @throws \core\exception\coding_exception * @author Mark Nielsen */ public function post_get_commands($post, $discussion, $cm, $canreply) { global $CFG, $USER, $OUTPUT; - $discussionlink = new moodle_url('/mod/hsuforum/discuss.php', array('d' => $post->discussion)); - $ownpost = (isloggedin() and $post->userid == $USER->id); + $discussionlink = new \core\url('/mod/hsuforum/discuss.php', array('d' => $post->discussion)); + $ownpost = (isloggedin() && $post->userid == $USER->id); $commands = array(); if (!property_exists($post, 'privatereply')) { - throw new coding_exception('Must set post\'s privatereply property!'); + throw new \core\exception\coding_exception('Must set post\'s privatereply property!'); } $forum = hsuforum_get_cm_forum($cm); - if ($canreply and empty($post->privatereply)) { + if ($canreply && empty($post->privatereply)) { $postuser = hsuforum_extract_postuser($post, $forum, context_module::instance($cm->id)); $replytitle = get_string('replybuttontitle', 'hsuforum', strip_tags($postuser->fullname)); - $commands['reply'] = html_writer::link( - new moodle_url('/mod/hsuforum/post.php', array('reply' => $post->id)), + $commands['reply'] = \core\output\html_writer::link( + new \core\url('/mod/hsuforum/post.php', array('reply' => $post->id)), get_string('reply', 'hsuforum'), array( 'title' => $replytitle, @@ -1843,15 +1842,15 @@ public function post_get_commands($post, $discussion, $cm, $canreply) { $age = 0; } if (($ownpost && $age < $CFG->maxeditingtime) || local::cached_has_capability('mod/hsuforum:editanypost', context_module::instance($cm->id))) { - $commands['edit'] = html_writer::link( - new moodle_url('/mod/hsuforum/post.php', array('edit' => $post->id)), + $commands['edit'] = \core\output\html_writer::link( + new \core\url('/mod/hsuforum/post.php', array('edit' => $post->id)), get_string('edit', 'hsuforum') ); } if (($ownpost && $age < $CFG->maxeditingtime && local::cached_has_capability('mod/hsuforum:deleteownpost', context_module::instance($cm->id))) || local::cached_has_capability('mod/hsuforum:deleteanypost', context_module::instance($cm->id))) { - $commands['delete'] = html_writer::link( - new moodle_url('/mod/hsuforum/post.php', array('delete' => $post->id)), + $commands['delete'] = \core\output\html_writer::link( + new \core\url('/mod/hsuforum/post.php', array('delete' => $post->id)), get_string('delete', 'hsuforum') ); } @@ -1860,8 +1859,8 @@ public function post_get_commands($post, $discussion, $cm, $canreply) { && $post->parent && !$post->privatereply && $forum->type != 'single') { - $commands['split'] = html_writer::link( - new moodle_url('/mod/hsuforum/post.php', array('prune' => $post->id)), + $commands['split'] = \core\output\html_writer::link( + new \core\url('/mod/hsuforum/post.php', array('prune' => $post->id)), get_string('prune', 'hsuforum'), array('title' => get_string('pruneheading', 'hsuforum')) ); @@ -1897,7 +1896,7 @@ public function post_get_commands($post, $discussion, $cm, $canreply) { $pinlink = HSUFORUM_DISCUSSION_PINNED; $pintext = get_string('discussionpin', 'hsuforum'); } - $pinurl = new moodle_url('/mod/hsuforum/discuss.php', [ + $pinurl = new \core\url('/mod/hsuforum/discuss.php', [ 'pin' => $pinlink, 'd' => $discussion->id, 'sesskey' => sesskey(), @@ -1910,29 +1909,29 @@ public function post_get_commands($post, $discussion, $cm, $canreply) { /** * Render ax button for pin/unpin. - * @param moodle_url $url + * @param \core\url $url * @param string $content * @param string $method * @param int $pinlink * @param int $discussion * @return string */ - public function render_ax_button(moodle_url $url, $content, $method, $pinlink, $discussion) { + public function render_ax_button(\core\url $url, $content, $method, $pinlink, $discussion) { global $PAGE; $PAGE->requires->js_call_amd('mod_hsuforum/accessibility', 'init', array()); $url = $method === 'get' ? $url->out_omit_querystring(true) : $url->out_omit_querystring(); - $output = html_writer::start_tag('div', ['class' => 'singlebutton']); - $output .= html_writer::start_tag('form', ['method' => $method, 'action' => $url]); - $output .= html_writer::tag('input', '',['type' => 'hidden', 'name' => 'pin', 'value' => $pinlink]); - $output .= html_writer::tag('input', '',['type' => 'hidden', 'name' => 'd', 'value' => $discussion]); - $output .= html_writer::tag('input', '',['type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()]); - $output .= html_writer::start_tag('button', ['class' => 'pinbutton btn btn-default', 'aria-pressed' => 'false', - 'type' => 'submit', 'id' => html_writer::random_id('single_button')]); + $output = \core\output\html_writer::start_tag('div', ['class' => 'singlebutton']); + $output .= \core\output\html_writer::start_tag('form', ['method' => $method, 'action' => $url]); + $output .= \core\output\html_writer::tag('input', '',['type' => 'hidden', 'name' => 'pin', 'value' => $pinlink]); + $output .= \core\output\html_writer::tag('input', '',['type' => 'hidden', 'name' => 'd', 'value' => $discussion]); + $output .= \core\output\html_writer::tag('input', '',['type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()]); + $output .= \core\output\html_writer::start_tag('button', ['class' => 'pinbutton btn btn-default', 'aria-pressed' => 'false', + 'type' => 'submit', 'id' => \core\output\html_writer::random_id('single_button')]); $output .= $content; - $output .= html_writer::end_tag('button'); - $output .= html_writer::end_tag('form'); - $output .= html_writer::end_tag('div'); + $output .= \core\output\html_writer::end_tag('button'); + $output .= \core\output\html_writer::end_tag('form'); + $output .= \core\output\html_writer::end_tag('div'); return $output; } @@ -1981,7 +1980,7 @@ public function discussion_navigation($prevdiscussion, $nextdiscussion) { $nextlink = ''; if ($prevdiscussion) { - $prevurl = new moodle_url($PAGE->URL); + $prevurl = new \core\url($PAGE->URL); $prevurl->param('d', $prevdiscussion->id); $prevlink = ''; } if ($nextdiscussion) { - $nexturl = new moodle_url($PAGE->URL); + $nexturl = new \core\url($PAGE->URL); $nexturl->param('d', $nextdiscussion->id); $nextlink = ''; echo $OUTPUT->heading("$strsearchresults: $totalcount", 3); -$url = new moodle_url('search.php', array('search' => $search, 'id' => $course->id, 'perpage' => $perpage)); +$url = new \core\url('search.php', array('search' => $search, 'id' => $course->id, 'perpage' => $perpage)); //added to implement highlighting of search terms found only in HTML markup //fiedorow - 9/2/2005 @@ -241,7 +240,7 @@ $strippedsearch = implode(' ', $searchterms); // Rebuild the string echo $OUTPUT->box_start("mod-hsuforum-posts-container article"); -echo html_writer::start_tag('ol', array('class' => 'hsuforum-thread-replies-list')); +echo \core\output\html_writer::start_tag('ol', array('class' => 'hsuforum-thread-replies-list')); $resultnumber = ($page * $perpage) + 1; $modinfo = get_fast_modinfo($course); foreach ($posts as $post) { @@ -250,14 +249,14 @@ // Replace the simple subject with the three items forum name -> thread name -> subject // (if all three are appropriate) each as a link. if (! $discussion = $DB->get_record('hsuforum_discussions', array('id' => $post->discussion))) { - throw new \moodle_exception('invaliddiscussionid', 'hsuforum'); + throw new \core\exception\moodle_exception('invaliddiscussionid', 'hsuforum'); } if (! $forum = $DB->get_record('hsuforum', array('id' => "$discussion->forum"))) { - throw new \moodle_exception('invalidforumid', 'hsuforum'); + throw new \core\exception\moodle_exception('invalidforumid', 'hsuforum'); } if (!$cm = get_coursemodule_from_instance('hsuforum', $forum->id)) { - throw new \moodle_exception('invalidcoursemodule'); + throw new \core\exception\moodle_exception('invalidcoursemodule'); } // TODO actually display if the search result has been read, for now just @@ -302,6 +301,8 @@ $options = new stdClass(); $options->trusted = $post->messagetrust; $modcontext = context_module::instance($cm->id); + $coursecontext = context_course::instance($course->id); + $options->context = $coursecontext; $post->message = highlight($strippedsearch, format_text( file_rewrite_pluginfile_urls( @@ -312,8 +313,7 @@ $post->id ), $post->messageformat, - $options, - $course->id), + $options), 0, '', ''); foreach ($searchterms as $searchterm) { @@ -341,9 +341,9 @@ $commands = array('seeincontext' => $fulllink); $postcm = $modinfo->instances['hsuforum'][$discussion->forum]; $rendereredpost = $renderer->post($postcm, $discussion, $post, false, null, $commands, 0, $strippedsearch); - echo html_writer::tag('li', $rendereredpost, array('class' => 'hsuforum-post', 'data-count' => $resultnumber++)); + echo \core\output\html_writer::tag('li', $rendereredpost, array('class' => 'hsuforum-post', 'data-count' => $resultnumber++)); } -echo html_writer::end_tag('ol'); +echo \core\output\html_writer::end_tag('ol'); echo $OUTPUT->box_end(); // End mod-hsuforum-posts-container $pagingurl = $url; if ($search && $forumid) { diff --git a/settings.php b/settings.php index 8937b369..b832bdba 100644 --- a/settings.php +++ b/settings.php @@ -1,5 +1,4 @@ 10, 20 => 20, 50 => 50, - 100 => 100 + 100 => 100, ); $settings->add(new admin_setting_configselect('hsuforum/maxattachments', get_string('maxattachments', 'hsuforum'), get_string('configmaxattachments', 'hsuforum'), 9, $options)); @@ -107,7 +106,7 @@ $options = array( 0 => get_string('none'), 1 => get_string('discussions', 'hsuforum'), - 2 => get_string('posts', 'hsuforum') + 2 => get_string('posts', 'hsuforum'), ); $settings->add(new admin_setting_configselect('hsuforum_rsstype', get_string('rsstypedefault', 'hsuforum'), get_string('configrsstypedefault', 'hsuforum'), 0, $options)); @@ -125,7 +124,7 @@ 25 => '25', 30 => '30', 40 => '40', - 50 => '50' + 50 => '50', ); $settings->add(new admin_setting_configselect('hsuforum_rssarticles', get_string('rssarticles', 'hsuforum'), get_string('configrssarticlesdefault', 'hsuforum'), 0, $options)); diff --git a/styles.css b/styles.css index ff02f3db..a8336665 100644 --- a/styles.css +++ b/styles.css @@ -933,7 +933,7 @@ the use of id^="" */ font-size: 1em; } -#discussionsview .text-right li { +#discussionsview .text-end li { list-style-type: none; } @@ -1061,12 +1061,16 @@ div.hsuforum-reply-wrapper form fieldset div.hsuforum-post-figure span.useriniti } /** Icon color for the activities. **/ -li.activity.modtype_hsuforum .activityiconcontainer, -.modchoosercontainer div[data-internal="hsuforum"] .modicon_hsuforum { - background-color: #8bc34a; +li.activity.modtype_hsuforum .activityiconcontainer { + filter: invert(64%) sepia(56%) saturate(361%) hue-rotate(41deg) brightness(98%) contrast(93%); } -li.activity.modtype_hsuforum .activityiconcontainer img.activityicon, -.modchoosercontainer div[data-internal="hsuforum"] .modicon_hsuforum img.activityicon { - filter: brightness(0) invert(1); +.hsuforum-post-body .danger-label, +.hsuforum-thread-author .danger-label { + color: white; + padding: 0 0.5em; + border-radius: 0.5rem; +} +.modchoosercontainer div[data-internal="hsuforum"] .modicon_hsuforum img { + height: 25px; } diff --git a/subscribe.php b/subscribe.php index 4d1a0800..ef9b4d73 100644 --- a/subscribe.php +++ b/subscribe.php @@ -1,5 +1,4 @@ $id)); +$url = new \core\url('/mod/hsuforum/subscribe.php', array('id'=>$id)); if (!is_null($mode)) { $url->param('mode', $mode); } @@ -61,7 +60,7 @@ if ($user) { require_sesskey(); if (!has_capability('mod/hsuforum:managesubscriptions', $context)) { - throw new \moodle_exception('nopermissiontosubscribe', 'hsuforum'); + throw new \core\exception\moodle_exception('nopermissiontosubscribe', 'hsuforum'); } $user = $DB->get_record('user', array('id' => $user), '*', MUST_EXIST); } else { @@ -75,25 +74,25 @@ } if ($groupmode && !hsuforum_is_subscribed($user->id, $forum) && !has_capability('moodle/site:accessallgroups', $context)) { if (!groups_get_all_groups($course->id, $USER->id)) { - throw new \moodle_exception('cannotsubscribe', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotsubscribe', 'hsuforum'); } } require_login($course, false, $cm); -if (is_null($mode) and !is_enrolled($context, $USER, '', true)) { // Guests and visitors can't subscribe - only enrolled +if (is_null($mode) && !is_enrolled($context, $USER, '', true)) { // Guests and visitors can't subscribe - only enrolled $PAGE->set_title($course->shortname); $PAGE->set_heading($course->fullname); if (isguestuser()) { echo $OUTPUT->header(); echo $OUTPUT->confirm(get_string('subscribeenrolledonly', 'hsuforum').'

      '.get_string('liketologin'), - get_login_url(), new moodle_url('/mod/hsuforum/view.php', array('f'=>$id))); + get_login_url(), new \core\url('/mod/hsuforum/view.php', array('f'=>$id))); echo $OUTPUT->footer(); exit; } else { // There should not be any links leading to this place, just redirect. redirect( - new moodle_url('/mod/hsuforum/view.php', array('f'=>$id)), + new \core\url('/mod/hsuforum/view.php', array('f'=>$id)), get_string('subscribeenrolledonly', 'hsuforum'), null, \core\output\notification::NOTIFY_ERROR @@ -105,7 +104,7 @@ ? "index.php?id=".$course->id : "view.php?f=$id"; -if (!is_null($mode) and has_capability('mod/hsuforum:managesubscriptions', $context)) { +if (!is_null($mode) && has_capability('mod/hsuforum:managesubscriptions', $context)) { require_sesskey(); switch ($mode) { case HSUFORUM_CHOOSESUBSCRIBE : // 0 @@ -151,7 +150,7 @@ ); break; default: - throw new \moodle_exception(get_string('invalidforcesubscribe', 'hsuforum')); + throw new \core\exception\moodle_exception(get_string('invalidforcesubscribe', 'hsuforum')); } } @@ -174,7 +173,7 @@ $PAGE->set_heading($course->fullname); echo $OUTPUT->header(); echo $OUTPUT->confirm(get_string('confirmunsubscribe', 'hsuforum', format_string($forum->name)), - new moodle_url($PAGE->url, array('sesskey' => sesskey())), new moodle_url('/mod/hsuforum/view.php', array('f' => $id))); + new \core\url($PAGE->url, array('sesskey' => sesskey())), new \core\url('/mod/hsuforum/view.php', array('f' => $id))); echo $OUTPUT->footer(); exit; } @@ -187,23 +186,23 @@ \core\output\notification::NOTIFY_SUCCESS ); } else { - throw new \moodle_exception('cannotunsubscribe', 'hsuforum', get_local_referer(false)); + throw new \core\exception\moodle_exception('cannotunsubscribe', 'hsuforum', get_local_referer(false)); } } else { // subscribe if ($forum->forcesubscribe == HSUFORUM_DISALLOWSUBSCRIBE && !has_capability('mod/hsuforum:managesubscriptions', $context)) { - throw new \moodle_exception('disallowsubscribe', 'hsuforum', get_local_referer(false)); + throw new \core\exception\moodle_exception('disallowsubscribe', 'hsuforum', get_local_referer(false)); } if (!has_capability('mod/hsuforum:viewdiscussion', $context)) { - throw new \moodle_exception('noviewdiscussionspermission', 'hsuforum', get_local_referer(false)); + throw new \core\exception\moodle_exception('noviewdiscussionspermission', 'hsuforum', get_local_referer(false)); } if (is_null($sesskey)) { // we came here via link in email $PAGE->set_title($course->shortname); $PAGE->set_heading($course->fullname); echo $OUTPUT->header(); echo $OUTPUT->confirm(get_string('confirmsubscribe', 'hsuforum', format_string($forum->name)), - new moodle_url($PAGE->url, array('sesskey' => sesskey())), new moodle_url('/mod/hsuforum/view.php', array('f' => $id))); + new \core\url($PAGE->url, array('sesskey' => sesskey())), new \core\url('/mod/hsuforum/view.php', array('f' => $id))); echo $OUTPUT->footer(); exit; } diff --git a/subscribers.php b/subscribers.php index 37a98d4f..e8fde231 100644 --- a/subscribers.php +++ b/subscribers.php @@ -1,5 +1,4 @@ $id)); +$url = new \core\url('/mod/hsuforum/subscribers.php', array('id'=>$id)); if ($group !== 0) { $url->param('group', $group); } @@ -51,7 +50,7 @@ $context = context_module::instance($cm->id); if (!has_capability('mod/hsuforum:viewsubscribers', $context)) { - throw new \moodle_exception('nopermissiontosubscribe', 'hsuforum'); + throw new \core\exception\moodle_exception('nopermissiontosubscribe', 'hsuforum'); } unset($SESSION->fromdiscussion); @@ -76,20 +75,20 @@ $unsubscribe = (bool)optional_param('unsubscribe', false, PARAM_RAW); /** It has to be one or the other, not both or neither */ if (!($subscribe xor $unsubscribe)) { - throw new \moodle_exception('invalidaction'); + throw new \core\exception\moodle_exception('invalidaction'); } if ($subscribe) { $users = $subscriberselector->get_selected_users(); foreach ($users as $user) { if (!hsuforum_subscribe($user->id, $id)) { - throw new \moodle_exception('cannotaddsubscriber', 'hsuforum', '', $user->id); + throw new \core\exception\moodle_exception('cannotaddsubscriber', 'hsuforum', '', $user->id); } } } else if ($unsubscribe) { $users = $existingselector->get_selected_users(); foreach ($users as $user) { if (!hsuforum_unsubscribe($user->id, $id)) { - throw new \moodle_exception('cannotremovesubscriber', 'hsuforum', '', $user->id); + throw new \core\exception\moodle_exception('cannotremovesubscriber', 'hsuforum', '', $user->id); } } } @@ -114,7 +113,7 @@ echo $OUTPUT->header(); echo $OUTPUT->heading(get_string('forum', 'hsuforum').' '.$strsubscribers); if (!empty($updatesubscriptionsbutton)) { - echo \html_writer::div($updatesubscriptionsbutton, 'pull-right'); + echo \core\output\html_writer::div($updatesubscriptionsbutton, 'pull-right'); } if (empty($USER->subscriptionsediting)) { $subscribers = hsuforum_subscribed_users($course, $forum, $currentgroup, $context); diff --git a/tests/backup_hsuforum_activity_test.php b/tests/backup_hsuforum_activity_test.php index 13cc95f7..c18acf19 100644 --- a/tests/backup_hsuforum_activity_test.php +++ b/tests/backup_hsuforum_activity_test.php @@ -40,7 +40,7 @@ * @copyright 2016 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_hsuforum_backup_hsuforum_activity_task_testcase extends advanced_testcase { +class backup_hsuforum_activity_test extends advanced_testcase { /** * Test the encoding of forum content links. diff --git a/tests/behat/accessibility_pin_button.feature b/tests/behat/accessibility_pin_button.feature index 0aba5f07..2090a8b3 100644 --- a/tests/behat/accessibility_pin_button.feature +++ b/tests/behat/accessibility_pin_button.feature @@ -36,7 +36,7 @@ Feature: When creating a new discussion the unpin option should exist as a butto | teacher1 | C1 | editingteacher | And I log in as "teacher1" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Test forum name | | Forum type | Standard forum for general use | | Description | Test forum description | diff --git a/tests/behat/add_forum.feature b/tests/behat/add_forum.feature index cb4c75f4..8ce620d0 100644 --- a/tests/behat/add_forum.feature +++ b/tests/behat/add_forum.feature @@ -19,7 +19,7 @@ Feature: Add Open Forum activities and discussions | student1 | C1 | student | And I log in as "teacher1" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Test forum name | | Forum type | Standard forum for general use | | Description | Test forum description | @@ -59,7 +59,7 @@ Feature: Add Open Forum activities and discussions | teacher1 | C1 | editingteacher | And I log in as "teacher1" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Test forum name | | Forum type | Standard forum for general use | | Description | Test forum description | diff --git a/tests/behat/advanced_editor_pass_data_to_new_page.feature b/tests/behat/advanced_editor_pass_data_to_new_page.feature index 61a01041..e6b3cad1 100644 --- a/tests/behat/advanced_editor_pass_data_to_new_page.feature +++ b/tests/behat/advanced_editor_pass_data_to_new_page.feature @@ -17,13 +17,14 @@ Feature: Users see their typed information in the advanced editor view when clic | enabletimedposts | 1 | hsuforum | And I log in as "admin" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Test forum name | | Forum type | Standard forum for general use | | Description | Test forum description | And I log out @javascript @_switch_iframe + @editor_tiny Scenario: User can continue writing after clicking "Use advanced editor" When I log in as "teacher1" And I am on "Course 1" course homepage @@ -39,7 +40,7 @@ Feature: Users see their typed information in the advanced editor view when clic And I should not see "Add your discussion" And I should see "Your new discussion topic" And I set the field with xpath "//*[@id='id_subject']" to "Test discussion 1 to be cancelled" - And I set the field "Message" to "Test discussion 1 to be cancelled description" + And I set the field with xpath "//*[@id='id_message']" to "Test discussion 1 to be cancelled description" And I press "Post to forum" Then I log out And I log in as "student1" @@ -54,7 +55,7 @@ Feature: Users see their typed information in the advanced editor view when clic And I wait until the page is ready And I should see "Your reply" And I set the field with xpath "//*[@id='id_subject']" to "Test reply subject" - And I set the field "Message" to "Test reply message" + And I set the field with xpath "//*[@id='id_message']" to "Test reply message" And I press "Post to forum" And I log out And I log in as "teacher1" @@ -68,8 +69,8 @@ Feature: Users see their typed information in the advanced editor view when clic And I set editable div with break line ".hsuforum-post.depth0 .hsuforum-textarea" "css_element" to "This is a reply \n This is a new line" And I click on ".hsuforum-post.depth0 .hsuforum-use-advanced" "css_element" And I wait until the page is ready - And I switch to "tox-edit-area__iframe" class iframe + And I switch to the "Message" TinyMCE editor iframe And ".text_to_html br" "css_element" should exist And I switch to the main frame And I should see "Your reply" - And I set the field "Message" to "This is a reply" + And I set the field with xpath "//*[@id='id_message']" to "This is a reply" diff --git a/tests/behat/edit_post_student.feature b/tests/behat/edit_post_student.feature index a7ed2125..ea031667 100644 --- a/tests/behat/edit_post_student.feature +++ b/tests/behat/edit_post_student.feature @@ -51,7 +51,7 @@ Feature: Students can edit or delete their Open Forum posts within a set time li And I log out And I log in as "admin" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Test forum name | | Forum type | Standard forum for general use | | Description | Test forum description | diff --git a/tests/behat/edit_post_teacher.feature b/tests/behat/edit_post_teacher.feature index dedc60ad..37c85b41 100644 --- a/tests/behat/edit_post_teacher.feature +++ b/tests/behat/edit_post_teacher.feature @@ -18,7 +18,7 @@ Feature: Teachers can edit or delete any Open Forum post | student1 | C1 | student | And I log in as "teacher1" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Test forum name | | Description | Test forum description | And I add a new discussion to "Test forum name" Open Forum with: diff --git a/tests/behat/forum_subscriptions_availability.feature b/tests/behat/forum_subscriptions_availability.feature index b8ad6382..eb092a3c 100644 --- a/tests/behat/forum_subscriptions_availability.feature +++ b/tests/behat/forum_subscriptions_availability.feature @@ -25,7 +25,7 @@ Feature: In Open Forums as a teacher I need to see an accurate list of subscribe @javascript Scenario: A forced forum lists all subscribers - When I add a "Open Forum" to section "1" and I fill the form with: + When I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Forced Forum 1 | | Forum type | Standard forum for general use | | Description | Test forum description | @@ -39,7 +39,7 @@ Feature: In Open Forums as a teacher I need to see an accurate list of subscribe @javascript Scenario: A forced forum does not allow to edit the subscribers - When I add a "Open Forum" to section "1" and I fill the form with: + When I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Forced Forum 2 | | Forum type | Standard forum for general use | | Description | Test forum description | @@ -55,7 +55,7 @@ Feature: In Open Forums as a teacher I need to see an accurate list of subscribe @javascript Scenario: A forced and hidden forum lists only teachers - When I add a "Open Forum" to section "1" and I fill the form with: + When I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Forced Forum 2 | | Forum type | Standard forum for general use | | Description | Test forum description | @@ -70,7 +70,7 @@ Feature: In Open Forums as a teacher I need to see an accurate list of subscribe @javascript Scenario: An automatic forum lists all subscribers - When I add a "Open Forum" to section "1" and I fill the form with: + When I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Forced Forum 1 | | Forum type | Standard forum for general use | | Description | Test forum description | diff --git a/tests/behat/grading_settings.feature b/tests/behat/grading_settings.feature index add22ae7..3e65b11e 100644 --- a/tests/behat/grading_settings.feature +++ b/tests/behat/grading_settings.feature @@ -39,7 +39,7 @@ Feature: While creating a new activity, the grade settings should remain in the | Grade category 1 | C1 | And I log in as "teacher1" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Test forum name | | Forum type | Standard forum for general use | | Description | Test forum description | diff --git a/tests/behat/hsuforum_tags.feature b/tests/behat/hsuforum_tags.feature index 5744f2fc..faaffba4 100644 --- a/tests/behat/hsuforum_tags.feature +++ b/tests/behat/hsuforum_tags.feature @@ -37,7 +37,7 @@ Feature: Open forum posts and new discussions handle tags correctly, in order to | student1 | C1 | student | And I log in as "teacher1" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Test forum name | | Description | Test forum description | And I add a new discussion to "Test forum name" Open Forum with: @@ -47,7 +47,7 @@ Feature: Open forum posts and new discussions handle tags correctly, in order to Given I log in as "admin" And I navigate to "Appearance > Manage tags" in site administration And I follow "Default collection" - And I follow "Add standard tags" + And I click on "Add standard tags" "button" And I set the field "Enter comma-separated list of new tags" to "OT1, OT2, OT3" And I press "Continue" And I log out diff --git a/tests/behat/inline_edit.feature b/tests/behat/inline_edit.feature index e8de4003..7247034f 100644 --- a/tests/behat/inline_edit.feature +++ b/tests/behat/inline_edit.feature @@ -17,7 +17,7 @@ Feature: Teachers and students can add discussions inline | enabletimedposts | 1 | hsuforum | And I log in as "admin" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Test forum name | | Forum type | Standard forum for general use | | Description | Test forum description | diff --git a/tests/behat/maintain_start_end_date.feature b/tests/behat/maintain_start_end_date.feature index e9631fdd..75d30dbf 100644 --- a/tests/behat/maintain_start_end_date.feature +++ b/tests/behat/maintain_start_end_date.feature @@ -17,7 +17,7 @@ Feature: In Open Forums users can change start and end date and the changes rema And the following config values are set as admin: | enabletimedposts | 1 | hsuforum | And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Test forum name | | Description | Test forum description | And I add a new discussion to "Test forum name" Open Forum with: diff --git a/tests/behat/posts_ordering_blog.feature b/tests/behat/posts_ordering_blog.feature index ff6ff1b6..30bcc33a 100644 --- a/tests/behat/posts_ordering_blog.feature +++ b/tests/behat/posts_ordering_blog.feature @@ -18,7 +18,7 @@ Feature: In Open Forums, blog posts are always displayed in reverse chronologica | student1 | C1 | student | And I log in as "teacher1" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Course blog forum | | Description | Single discussion forum description | | Forum type | Standard forum displayed in a blog-like format | @@ -65,7 +65,7 @@ Feature: In Open Forums, blog posts are always displayed in reverse chronologica And I follow "Blog post 1" And I follow "Reply" And I follow "Use advanced editor and additional options" - And I set the field "Message" to "Reply to the first post" + And I set the field with xpath "//*[@id='id_message']" to "Reply to the first post" And I press "Post to forum" And I wait to be redirected to open forum And I am on "Course 1" course homepage diff --git a/tests/behat/posts_ordering_general.feature b/tests/behat/posts_ordering_general.feature index 48f56882..2013f81f 100644 --- a/tests/behat/posts_ordering_general.feature +++ b/tests/behat/posts_ordering_general.feature @@ -18,7 +18,7 @@ Feature: New Open discussions and discussions with recently added replies are di | student1 | C1 | student | And I log in as "teacher1" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Course general forum | | Description | Single discussion forum description | | Forum type | Standard forum for general use | diff --git a/tests/behat/rate_posts.feature b/tests/behat/rate_posts.feature index 07af4cbd..22d151a7 100644 --- a/tests/behat/rate_posts.feature +++ b/tests/behat/rate_posts.feature @@ -18,7 +18,7 @@ Feature: Users can rate other users forum posts | student1 | C1 | student | And I log in as "teacher1" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Test forum name | | Description | Test forum description | | Aggregate type | Average of ratings | diff --git a/tests/behat/separate_group_single_group_discussions.feature b/tests/behat/separate_group_single_group_discussions.feature index e3b1d8ec..c7515480 100644 --- a/tests/behat/separate_group_single_group_discussions.feature +++ b/tests/behat/separate_group_single_group_discussions.feature @@ -40,13 +40,13 @@ Feature: In Open Forums, posting to groups in a separate group discussion when r | G2 | G2G1 | And I log in as "admin" And I am on "Course 1" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Multiple groups forum | | Forum type | Standard forum for general use | | Description | Standard forum description | | Group mode | Separate groups | | Grouping | G1 | - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Course 1" section "1" and I fill the form with: | Forum name | Single groups forum | | Forum type | Standard forum for general use | | Description | Standard forum description | diff --git a/tests/behat/split_forum_discussion.feature b/tests/behat/split_forum_discussion.feature index 33ddcc6e..905e4ca4 100644 --- a/tests/behat/split_forum_discussion.feature +++ b/tests/behat/split_forum_discussion.feature @@ -18,7 +18,7 @@ Feature: Open Forum discussions can be split | student1 | C1 | student | And I log in as "teacher1" And I am on "Science 101" course homepage with editing mode on - And I add a "Open Forum" to section "1" and I fill the form with: + And I add a "hsuforum" activity to course "Science 101" section "1" and I fill the form with: | Forum name | Study discussions | | Forum type | Standard forum for general use | | Description | Forum to discuss your coursework. | diff --git a/tests/custom_completion_test.php b/tests/custom_completion_test.php index 4d4be0de..73857822 100644 --- a/tests/custom_completion_test.php +++ b/tests/custom_completion_test.php @@ -28,9 +28,9 @@ use advanced_testcase; use cm_info; -use coding_exception; +use \core\exception\coding_exception; use mod_hsuforum\completion\custom_completion; -use moodle_exception; +use \core\exception\moodle_exception; defined('MOODLE_INTERNAL') || die(); @@ -48,7 +48,7 @@ */ class custom_completion_test extends advanced_testcase { - use \mod_hsuforum_tests_generator_trait; + use \mod_hsuforum\generator_trait; /** * Data provider for get_state(). @@ -58,34 +58,34 @@ class custom_completion_test extends advanced_testcase { public function get_state_provider(): array { return [ 'Undefined rule' => [ - 'somenonexistentrule', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, coding_exception::class + 'somenonexistentrule', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, coding_exception::class, ], 'Completion discussions rule not available' => [ - 'completiondiscussions', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, moodle_exception::class + 'completiondiscussions', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, moodle_exception::class, ], 'Completion discussions rule available, user has not created discussion' => [ - 'completiondiscussions', 0, COMPLETION_TRACKING_AUTOMATIC, 5, 0, 0, COMPLETION_INCOMPLETE, null + 'completiondiscussions', 0, COMPLETION_TRACKING_AUTOMATIC, 5, 0, 0, COMPLETION_INCOMPLETE, null, ], 'Rule available, user has created discussions' => [ - 'completiondiscussions', 5, COMPLETION_TRACKING_AUTOMATIC, 5, 0, 0, COMPLETION_COMPLETE, null + 'completiondiscussions', 5, COMPLETION_TRACKING_AUTOMATIC, 5, 0, 0, COMPLETION_COMPLETE, null, ], 'Completion replies rule not available' => [ - 'completionreplies', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, moodle_exception::class + 'completionreplies', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, moodle_exception::class, ], 'Rule available, user has not replied' => [ - 'completionreplies', 0, COMPLETION_TRACKING_AUTOMATIC, 0, 5, 0, COMPLETION_INCOMPLETE, null + 'completionreplies', 0, COMPLETION_TRACKING_AUTOMATIC, 0, 5, 0, COMPLETION_INCOMPLETE, null, ], 'Rule available, user has created replied' => [ - 'completionreplies', 5, COMPLETION_TRACKING_AUTOMATIC, 0, 5, 0, COMPLETION_COMPLETE, null + 'completionreplies', 5, COMPLETION_TRACKING_AUTOMATIC, 0, 5, 0, COMPLETION_COMPLETE, null, ], 'Completion posts rule not available' => [ - 'completionposts', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, moodle_exception::class + 'completionposts', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, moodle_exception::class, ], 'Rule available, user has not posted' => [ - 'completionposts', 0, COMPLETION_TRACKING_AUTOMATIC, 0, 0, 5, COMPLETION_INCOMPLETE, null + 'completionposts', 0, COMPLETION_TRACKING_AUTOMATIC, 0, 0, 5, COMPLETION_INCOMPLETE, null, ], 'Rule available, user has posted' => [ - 'completionposts', 5, COMPLETION_TRACKING_AUTOMATIC, 0, 0, 5, COMPLETION_COMPLETE, null + 'completionposts', 5, COMPLETION_TRACKING_AUTOMATIC, 0, 0, 5, COMPLETION_COMPLETE, null, ], ]; } @@ -122,7 +122,7 @@ public function test_get_state(string $rule, int $rulecount, int $available, ?in 'completion' => $available, 'completiondiscussions' => $discussions, 'completionreplies' => $replies, - 'completionposts' => $posts + 'completionposts' => $posts, ]; $hsuforum = $this->getDataGenerator()->create_module('hsuforum', $params); @@ -217,22 +217,22 @@ public function test_is_defined() { public function get_available_custom_rules_provider(): array { return [ 'Completion discussions available' => [ - COMPLETION_ENABLED, ['completiondiscussions'] + COMPLETION_ENABLED, ['completiondiscussions'], ], 'Completion discussions not available' => [ - COMPLETION_DISABLED, [] + COMPLETION_DISABLED, [], ], 'Completion replies available' => [ - COMPLETION_ENABLED, ['completionreplies'] + COMPLETION_ENABLED, ['completionreplies'], ], 'Completion replies not available' => [ - COMPLETION_DISABLED, [] + COMPLETION_DISABLED, [], ], 'Completion posts available' => [ - COMPLETION_ENABLED, ['completionposts'] + COMPLETION_ENABLED, ['completionposts'], ], 'Completion posts not available' => [ - COMPLETION_DISABLED, [] + COMPLETION_DISABLED, [], ], ]; } @@ -246,12 +246,12 @@ public function get_available_custom_rules_provider(): array { */ public function test_get_available_custom_rules(int $status, array $expected) { $customdataval = [ - 'customcompletionrules' => [] + 'customcompletionrules' => [], ]; if ($status == COMPLETION_ENABLED) { $rule = $expected[0]; $customdataval = [ - 'customcompletionrules' => [$rule => $status] + 'customcompletionrules' => [$rule => $status], ]; } diff --git a/tests/events_test.php b/tests/events_test.php index e6b533cc..e65d2223 100644 --- a/tests/events_test.php +++ b/tests/events_test.php @@ -23,6 +23,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace mod_hsuforum\tests; + defined('MOODLE_INTERNAL') || die(); /** @@ -33,7 +35,7 @@ * @copyright 2014 Dan Poltawski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_hsuforum_events_testcase extends advanced_testcase { +class events_test extends \advanced_testcase { /** * Tests set up. @@ -47,12 +49,12 @@ public function setUp(): void { */ public function test_course_searched_searchterm_validation() { $course = $this->getDataGenerator()->create_course(); - $coursectx = context_course::instance($course->id); + $coursectx = \context_course::instance($course->id); $params = array( 'context' => $coursectx, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('The \'searchterm\' value must be set in other'); \mod_hsuforum\event\course_searched::create($params); } @@ -63,13 +65,13 @@ public function test_course_searched_searchterm_validation() { public function test_course_searched_context_validation() { $course = $this->getDataGenerator()->create_course(); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, 'other' => array('searchterm' => 'testing'), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_COURSE'); \mod_hsuforum\event\course_searched::create($params); } @@ -81,7 +83,7 @@ public function test_course_searched() { // Setup test data. $course = $this->getDataGenerator()->create_course(); - $coursectx = context_course::instance($course->id); + $coursectx = \context_course::instance($course->id); $searchterm = 'testing123'; $params = array( @@ -113,13 +115,13 @@ public function test_course_searched() { public function test_discussion_created_forumid_validation() { $course = $this->getDataGenerator()->create_course(); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumid' value must be set in other"); \mod_hsuforum\event\discussion_created::create($params); } @@ -132,11 +134,11 @@ public function test_discussion_created_context_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'other' => array('forumid' => $forum->id), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\discussion_created::create($params); } @@ -158,7 +160,7 @@ public function test_discussion_created() { $record['userid'] = $user->id; $discussion = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, @@ -190,13 +192,13 @@ public function test_discussion_created() { public function test_discussion_updated_forumid_validation() { $course = $this->getDataGenerator()->create_course(); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumid' value must be set in other"); \mod_hsuforum\event\discussion_updated::create($params); } @@ -209,11 +211,11 @@ public function test_discussion_updated_context_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'other' => array('forumid' => $forum->id), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\discussion_updated::create($params); } @@ -235,7 +237,7 @@ public function test_discussion_updated() { $record['userid'] = $user->id; $discussion = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, @@ -267,13 +269,13 @@ public function test_discussion_updated() { public function test_discussion_deleted_forumid_validation() { $course = $this->getDataGenerator()->create_course(); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumid' value must be set in other"); \mod_hsuforum\event\discussion_deleted::create($params); } @@ -286,11 +288,11 @@ public function test_discussion_deleted_context_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'other' => array('forumid' => $forum->id), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\discussion_deleted::create($params); } @@ -312,7 +314,7 @@ public function test_discussion_deleted() { $record['userid'] = $user->id; $discussion = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, @@ -344,14 +346,14 @@ public function test_discussion_moved_fromforumid_validation() { $course = $this->getDataGenerator()->create_course(); $toforum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); - $context = context_module::instance($toforum->cmid); + $context = \context_module::instance($toforum->cmid); $params = array( 'context' => $context, - 'other' => array('toforumid' => $toforum->id) + 'other' => array('toforumid' => $toforum->id), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'fromforumid' value must be set in other"); \mod_hsuforum\event\discussion_moved::create($params); } @@ -363,14 +365,14 @@ public function test_discussion_moved_toforumid_validation() { $course = $this->getDataGenerator()->create_course(); $fromforum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $toforum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); - $context = context_module::instance($toforum->cmid); + $context = \context_module::instance($toforum->cmid); $params = array( 'context' => $context, - 'other' => array('fromforumid' => $fromforum->id) + 'other' => array('fromforumid' => $fromforum->id), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'toforumid' value must be set in other"); \mod_hsuforum\event\discussion_moved::create($params); } @@ -392,12 +394,12 @@ public function test_discussion_moved_context_validation() { $discussion = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'objectid' => $discussion->id, - 'other' => array('fromforumid' => $fromforum->id, 'toforumid' => $toforum->id) + 'other' => array('fromforumid' => $fromforum->id, 'toforumid' => $toforum->id), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\discussion_moved::create($params); } @@ -419,12 +421,12 @@ public function test_discussion_moved() { $record['userid'] = $user->id; $discussion = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); - $context = context_module::instance($toforum->cmid); + $context = \context_module::instance($toforum->cmid); $params = array( 'context' => $context, 'objectid' => $discussion->id, - 'other' => array('fromforumid' => $fromforum->id, 'toforumid' => $toforum->id) + 'other' => array('fromforumid' => $fromforum->id, 'toforumid' => $toforum->id), ); $event = \mod_hsuforum\event\discussion_moved::create($params); @@ -461,11 +463,11 @@ public function test_discussion_viewed_context_validation() { $discussion = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'objectid' => $discussion->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\discussion_viewed::create($params); } @@ -486,7 +488,7 @@ public function test_discussion_viewed() { $record['userid'] = $user->id; $discussion = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, @@ -518,11 +520,11 @@ public function test_course_module_viewed_context_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'objectid' => $forum->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\course_module_viewed::create($params); } @@ -535,7 +537,7 @@ public function test_course_module_viewed() { $course = $this->getDataGenerator()->create_course(); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, @@ -554,7 +556,7 @@ public function test_course_module_viewed() { // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\course_module_viewed', $event); $this->assertEquals($context, $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/view.php', array('f' => $forum->id)); + $url = new \core\url('/mod/hsuforum/view.php', array('f' => $forum->id)); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); @@ -570,11 +572,11 @@ public function test_subscription_created_forumid_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'relateduserid' => $user->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumid' value must be set in other"); \mod_hsuforum\event\subscription_created::create($params); } @@ -587,11 +589,11 @@ public function test_subscription_created_relateduserid_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $forum->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'relateduserid' must be set"); \mod_hsuforum\event\subscription_created::create($params); } @@ -605,12 +607,12 @@ public function test_subscription_created_contextlevel_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'other' => array('forumid' => $forum->id), 'relateduserid' => $user->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\subscription_created::create($params); } @@ -624,7 +626,7 @@ public function test_subscription_created() { $course = $this->getDataGenerator()->create_course(); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $user = $this->getDataGenerator()->create_user(); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); // Add a subscription. $record = array(); @@ -652,7 +654,7 @@ public function test_subscription_created() { // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\subscription_created', $event); $this->assertEquals($context, $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/subscribers.php', array('id' => $forum->id)); + $url = new \core\url('/mod/hsuforum/subscribers.php', array('id' => $forum->id)); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); @@ -668,11 +670,11 @@ public function test_subscription_deleted_forumid_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'relateduserid' => $user->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumid' value must be set in other"); \mod_hsuforum\event\subscription_deleted::create($params); } @@ -685,11 +687,11 @@ public function test_subscription_deleted_relateduserid_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $forum->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'relateduserid' must be set"); \mod_hsuforum\event\subscription_deleted::create($params); } @@ -703,12 +705,12 @@ public function test_subscription_deleted_contextlevel_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'other' => array('forumid' => $forum->id), 'relateduserid' => $user->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\subscription_deleted::create($params); } @@ -722,7 +724,7 @@ public function test_subscription_deleted() { $course = $this->getDataGenerator()->create_course(); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $user = $this->getDataGenerator()->create_user(); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); // Add a subscription. $record = array(); @@ -750,7 +752,7 @@ public function test_subscription_deleted() { // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\subscription_deleted', $event); $this->assertEquals($context, $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/subscribers.php', array('id' => $forum->id)); + $url = new \core\url('/mod/hsuforum/subscribers.php', array('id' => $forum->id)); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); @@ -766,11 +768,11 @@ public function test_readtracking_enabled_forumid_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'relateduserid' => $user->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumid' value must be set in other"); \mod_hsuforum\event\readtracking_enabled::create($params); } @@ -783,11 +785,11 @@ public function test_readtracking_enabled_relateduserid_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $forum->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'relateduserid' must be set"); \mod_hsuforum\event\readtracking_enabled::create($params); } @@ -801,12 +803,12 @@ public function test_readtracking_enabled_contextlevel_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'other' => array('forumid' => $forum->id), 'relateduserid' => $user->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\readtracking_enabled::create($params); } @@ -819,7 +821,7 @@ public function test_readtracking_enabled() { $user = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, @@ -839,7 +841,7 @@ public function test_readtracking_enabled() { // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\readtracking_enabled', $event); $this->assertEquals($context, $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/view.php', array('f' => $forum->id)); + $url = new \core\url('/mod/hsuforum/view.php', array('f' => $forum->id)); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); @@ -855,11 +857,11 @@ public function test_readtracking_disabled_forumid_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'relateduserid' => $user->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumid' value must be set in other"); \mod_hsuforum\event\readtracking_disabled::create($params); } @@ -872,11 +874,11 @@ public function test_readtracking_disabled_relateduserid_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $forum->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'relateduserid' must be set"); \mod_hsuforum\event\readtracking_disabled::create($params); } @@ -890,12 +892,12 @@ public function test_readtracking_disabled_contextlevel_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'other' => array('forumid' => $forum->id), 'relateduserid' => $user->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\readtracking_disabled::create($params); } @@ -908,7 +910,7 @@ public function test_readtracking_disabled() { $user = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, @@ -928,7 +930,7 @@ public function test_readtracking_disabled() { // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\readtracking_disabled', $event); $this->assertEquals($context, $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/view.php', array('f' => $forum->id)); + $url = new \core\url('/mod/hsuforum/view.php', array('f' => $forum->id)); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); @@ -944,11 +946,11 @@ public function test_subscribers_viewed_forumid_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'relateduserid' => $user->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumid' value must be set in other"); \mod_hsuforum\event\subscribers_viewed::create($params); } @@ -962,12 +964,12 @@ public function test_subscribers_viewed_contextlevel_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'other' => array('forumid' => $forum->id), 'relateduserid' => $user->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\subscribers_viewed::create($params); } @@ -979,7 +981,7 @@ public function test_subscribers_viewed() { // Setup test data. $course = $this->getDataGenerator()->create_course(); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, @@ -1011,11 +1013,11 @@ public function test_user_report_viewed_reportmode_validation() { $course = $this->getDataGenerator()->create_course(); $params = array( - 'context' => context_course::instance($course->id), + 'context' => \context_course::instance($course->id), 'relateduserid' => $user->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'reportmode' value must be set in other"); \mod_hsuforum\event\user_report_viewed::create($params); } @@ -1029,12 +1031,12 @@ public function test_user_report_viewed_contextlevel_validation() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'other' => array('reportmode' => 'posts'), 'relateduserid' => $user->id, ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be either CONTEXT_SYSTEM, CONTEXT_COURSE or CONTEXT_USER'); \mod_hsuforum\event\user_report_viewed::create($params); } @@ -1045,11 +1047,11 @@ public function test_user_report_viewed_contextlevel_validation() { public function test_user_report_viewed_relateduserid_validation() { $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'other' => array('reportmode' => 'posts'), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'relateduserid' must be set"); \mod_hsuforum\event\user_report_viewed::create($params); } @@ -1061,7 +1063,7 @@ public function test_user_report_viewed() { // Setup test data. $user = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); - $context = context_course::instance($course->id); + $context = \context_course::instance($course->id); $params = array( 'context' => $context, @@ -1102,8 +1104,8 @@ public function test_post_created_postid_validation() { $discussion = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); $params = array( - 'context' => context_module::instance($forum->cmid), - 'other' => array('forumid' => $forum->id, 'forumtype' => $forum->type, 'discussionid' => $discussion->id) + 'context' => \context_module::instance($forum->cmid), + 'other' => array('forumid' => $forum->id, 'forumtype' => $forum->type, 'discussionid' => $discussion->id), ); \mod_hsuforum\event\post_created::create($params); @@ -1131,12 +1133,12 @@ public function test_post_created_discussionid_validation() { $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $post->id, - 'other' => array('forumid' => $forum->id, 'forumtype' => $forum->type) + 'other' => array('forumid' => $forum->id, 'forumtype' => $forum->type), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'discussionid' value must be set in other"); \mod_hsuforum\event\post_created::create($params); } @@ -1163,12 +1165,12 @@ public function test_post_created_forumid_validation() { $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumtype' => $forum->type) + 'other' => array('discussionid' => $discussion->id, 'forumtype' => $forum->type), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumid' value must be set in other"); \mod_hsuforum\event\post_created::create($params); } @@ -1195,12 +1197,12 @@ public function test_post_created_forumtype_validation() { $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id) + 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumtype' value must be set in other"); \mod_hsuforum\event\post_created::create($params); } @@ -1227,12 +1229,12 @@ public function test_post_created_context_validation() { $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type) + 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\post_created::create($params); } @@ -1259,12 +1261,12 @@ public function test_post_created() { $record['userid'] = $user->id; $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type) + 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type), ); $event = \mod_hsuforum\event\post_created::create($params); @@ -1279,7 +1281,7 @@ public function test_post_created() { // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\post_created', $event); $this->assertEquals($context, $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id)); + $url = new \core\url('/mod/hsuforum/discuss.php', array('d' => $discussion->id)); $url->set_anchor('p'.$event->objectid); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); @@ -1309,12 +1311,12 @@ public function test_post_created_single() { $record['userid'] = $user->id; $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type) + 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type), ); $event = \mod_hsuforum\event\post_created::create($params); @@ -1329,7 +1331,7 @@ public function test_post_created_single() { // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\post_created', $event); $this->assertEquals($context, $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/view.php', array('f' => $forum->id)); + $url = new \core\url('/mod/hsuforum/view.php', array('f' => $forum->id)); $url->set_anchor('p'.$event->objectid); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); @@ -1353,8 +1355,8 @@ public function test_post_deleted_postid_validation() { $discussion = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); $params = array( - 'context' => context_module::instance($forum->cmid), - 'other' => array('forumid' => $forum->id, 'forumtype' => $forum->type, 'discussionid' => $discussion->id) + 'context' => \context_module::instance($forum->cmid), + 'other' => array('forumid' => $forum->id, 'forumtype' => $forum->type, 'discussionid' => $discussion->id), ); \mod_hsuforum\event\post_deleted::create($params); @@ -1382,12 +1384,12 @@ public function test_post_deleted_discussionid_validation() { $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $post->id, - 'other' => array('forumid' => $forum->id, 'forumtype' => $forum->type) + 'other' => array('forumid' => $forum->id, 'forumtype' => $forum->type), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'discussionid' value must be set in other"); \mod_hsuforum\event\post_deleted::create($params); } @@ -1414,12 +1416,12 @@ public function test_post_deleted_forumid_validation() { $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumtype' => $forum->type) + 'other' => array('discussionid' => $discussion->id, 'forumtype' => $forum->type), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumid' value must be set in other"); \mod_hsuforum\event\post_deleted::create($params); } @@ -1446,12 +1448,12 @@ public function test_post_deleted_forumtype_validation() { $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id) + 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumtype' value must be set in other"); \mod_hsuforum\event\post_deleted::create($params); } @@ -1478,12 +1480,12 @@ public function test_post_deleted_context_validation() { $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type) + 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\post_deleted::create($params); } @@ -1533,8 +1535,8 @@ public function test_post_deleted() { // Check that the events contain the expected values. $this->assertInstanceOf('\mod_hsuforum\event\post_deleted', $event); - $this->assertEquals(context_module::instance($forum->cmid), $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id)); + $this->assertEquals(\context_module::instance($forum->cmid), $event->get_context()); + $url = new \core\url('/mod/hsuforum/discuss.php', array('d' => $discussion->id)); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); @@ -1552,8 +1554,8 @@ public function test_post_deleted() { // Check that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\post_deleted', $event); - $this->assertEquals(context_module::instance($forum->cmid), $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id)); + $this->assertEquals(\context_module::instance($forum->cmid), $event->get_context()); + $url = new \core\url('/mod/hsuforum/discuss.php', array('d' => $discussion->id)); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); $this->assertNotEmpty($event->get_name()); @@ -1582,12 +1584,12 @@ public function test_post_deleted_single() { $record['userid'] = $user->id; $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type) + 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type), ); $event = \mod_hsuforum\event\post_deleted::create($params); @@ -1602,7 +1604,7 @@ public function test_post_deleted_single() { // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\post_deleted', $event); $this->assertEquals($context, $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/view.php', array('f' => $forum->id)); + $url = new \core\url('/mod/hsuforum/view.php', array('f' => $forum->id)); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); @@ -1631,12 +1633,12 @@ public function test_post_updated_discussionid_validation() { $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $post->id, - 'other' => array('forumid' => $forum->id, 'forumtype' => $forum->type) + 'other' => array('forumid' => $forum->id, 'forumtype' => $forum->type), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'discussionid' value must be set in other"); \mod_hsuforum\event\post_updated::create($params); } @@ -1663,12 +1665,12 @@ public function test_post_updated_forumid_validation() { $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumtype' => $forum->type) + 'other' => array('discussionid' => $discussion->id, 'forumtype' => $forum->type), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumid' value must be set in other"); \mod_hsuforum\event\post_updated::create($params); } @@ -1695,12 +1697,12 @@ public function test_post_updated_forumtype_validation() { $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); $params = array( - 'context' => context_module::instance($forum->cmid), + 'context' => \context_module::instance($forum->cmid), 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id) + 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage("The 'forumtype' value must be set in other"); \mod_hsuforum\event\post_updated::create($params); } @@ -1727,12 +1729,12 @@ public function test_post_updated_context_validation() { $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); $params = array( - 'context' => context_system::instance(), + 'context' => \context_system::instance(), 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type) + 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type), ); - $this->expectException(coding_exception::class); + $this->expectException(\core\exception\coding_exception::class); $this->expectExceptionMessage('Context level must be CONTEXT_MODULE'); \mod_hsuforum\event\post_updated::create($params); } @@ -1759,12 +1761,12 @@ public function test_post_updated() { $record['userid'] = $user->id; $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type) + 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type), ); $event = \mod_hsuforum\event\post_updated::create($params); @@ -1779,7 +1781,7 @@ public function test_post_updated() { // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\post_updated', $event); $this->assertEquals($context, $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id)); + $url = new \core\url('/mod/hsuforum/discuss.php', array('d' => $discussion->id)); $url->set_anchor('p'.$event->objectid); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); @@ -1809,12 +1811,12 @@ public function test_post_updated_single() { $record['userid'] = $user->id; $post = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $params = array( 'context' => $context, 'objectid' => $post->id, - 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type) + 'other' => array('discussionid' => $discussion->id, 'forumid' => $forum->id, 'forumtype' => $forum->type), ); $event = \mod_hsuforum\event\post_updated::create($params); @@ -1829,7 +1831,7 @@ public function test_post_updated_single() { // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\post_updated', $event); $this->assertEquals($context, $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/view.php', array('f' => $forum->id)); + $url = new \core\url('/mod/hsuforum/view.php', array('f' => $forum->id)); $url->set_anchor('p'.$post->id); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); @@ -1857,7 +1859,7 @@ public function test_observers() { // them just in case there are APIs changes in future. $user = $this->getDataGenerator()->create_user(array( 'maildigest' => 1, - 'trackforums' => 1 + 'trackforums' => 1, )); $manplugin = enrol_get_plugin('manual'); diff --git a/tests/form_service_test.php b/tests/form_service_test.php index f652cfe8..0e814c87 100644 --- a/tests/form_service_test.php +++ b/tests/form_service_test.php @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . - + /** * Testing form service prepare draft area * @@ -27,7 +27,7 @@ public function protected_file_prepare_draft_area(&$draftitemid, $contextid, $co } } -class mod_hsuforum_form_service_testcase extends advanced_testcase { +class form_service_test extends advanced_testcase { public function test_prepare_draft_area() { global $DB, $CFG, $USER; @@ -79,13 +79,13 @@ public function test_prepare_draft_area() { 'filearea' => 'draft', 'itemid' => $draftid, 'filepath' => '/', - 'filename' => '' + 'filename' => '', ); // Create some files. $imagefiles = array( 'testgif_small.gif', - 'testgif2_small.gif' + 'testgif2_small.gif', ); // Add files to draft area and make sure they exist! @@ -93,7 +93,7 @@ public function test_prepare_draft_area() { $dummy['filename'] = $filename; $fs->create_file_from_pathname($dummy, $CFG->dirroot.'/mod/hsuforum/tests/fixtures/'.$filename); $this->assertTrue(repository::draftfile_exists($draftid, '/', $filename)); - $draftfileurl = new moodle_url('/draftfile.php/'.$usercontext->id.'/user/draft/'.$draftid.'/'.$filename); + $draftfileurl = new \core\url('/draftfile.php/'.$usercontext->id.'/user/draft/'.$draftid.'/'.$filename); $message .= ''; } @@ -110,7 +110,7 @@ public function test_prepare_draft_area() { $dummy['filename'] = $filename; $fs->create_file_from_pathname($dummy, $CFG->dirroot.'/mod/hsuforum/tests/fixtures/'.$filename); $this->assertTrue(repository::draftfile_exists($draftid, '/', $filename)); - $draftfileurl = new moodle_url('/draftfile.php/'.$usercontext->id.'/user/draft/'.$draftid.'/'.$filename); + $draftfileurl = new \core\url('/draftfile.php/'.$usercontext->id.'/user/draft/'.$draftid.'/'.$filename); $message .= ''; // Now save the items in the draft area diff --git a/tests/generator/lib.php b/tests/generator/lib.php index f6b849ec..d4b63147 100644 --- a/tests/generator/lib.php +++ b/tests/generator/lib.php @@ -106,15 +106,15 @@ public function create_subscription($record = null) { $record = (array)$record; if (!isset($record['course'])) { - throw new coding_exception('course must be present in phpunit_util::create_subscription() $record'); + throw new \core\exception\coding_exception('course must be present in phpunit_util::create_subscription() $record'); } if (!isset($record['forum'])) { - throw new coding_exception('forum must be present in phpunit_util::create_subscription() $record'); + throw new \core\exception\coding_exception('forum must be present in phpunit_util::create_subscription() $record'); } if (!isset($record['userid'])) { - throw new coding_exception('userid must be present in phpunit_util::create_subscription() $record'); + throw new \core\exception\coding_exception('userid must be present in phpunit_util::create_subscription() $record'); } $record = (object)$record; @@ -140,15 +140,15 @@ public function create_discussion($record = null) { $record = (array) $record; if (!isset($record['course'])) { - throw new coding_exception('course must be present in phpunit_util::create_discussion() $record'); + throw new \core\exception\coding_exception('course must be present in phpunit_util::create_discussion() $record'); } if (!isset($record['forum'])) { - throw new coding_exception('forum must be present in phpunit_util::create_discussion() $record'); + throw new \core\exception\coding_exception('forum must be present in phpunit_util::create_discussion() $record'); } if (!isset($record['userid'])) { - throw new coding_exception('userid must be present in phpunit_util::create_discussion() $record'); + throw new \core\exception\coding_exception('userid must be present in phpunit_util::create_discussion() $record'); } if (!isset($record['name'])) { @@ -160,7 +160,7 @@ public function create_discussion($record = null) { } if (!isset($record['message'])) { - $record['message'] = html_writer::tag('p', 'Message for discussion ' . $this->forumdiscussioncount); + $record['message'] = \core\output\html_writer::tag('p', 'Message for discussion ' . $this->forumdiscussioncount); } if (!isset($record['messageformat'])) { @@ -207,6 +207,10 @@ public function create_discussion($record = null) { $mailed = $record['mailed']; } + if (!isset($record['timelocked'])) { + $record['timelocked'] = 0; + } + $record = (object) $record; // Add the discussion. @@ -259,11 +263,11 @@ public function create_post($record = null) { $record = (array) $record; if (!isset($record['discussion'])) { - throw new coding_exception('discussion must be present in phpunit_util::create_post() $record'); + throw new \core\exception\coding_exception('discussion must be present in phpunit_util::create_post() $record'); } if (!isset($record['userid'])) { - throw new coding_exception('userid must be present in phpunit_util::create_post() $record'); + throw new \core\exception\coding_exception('userid must be present in phpunit_util::create_post() $record'); } if (!isset($record['parent'])) { @@ -275,7 +279,7 @@ public function create_post($record = null) { } if (!isset($record['message'])) { - $record['message'] = html_writer::tag('p', 'Forum message post ' . $this->forumpostcount); + $record['message'] = \core\output\html_writer::tag('p', 'Forum message post ' . $this->forumpostcount); } if (!isset($record['created'])) { @@ -322,6 +326,18 @@ public function create_post($record = null) { $record['privatereply'] = 0; } + if (!isset($record['privatereplyto'])) { + $record['privatereplyto'] = 0; + } + + if (!isset($record['wordcount'])) { + $record['wordcount'] = null; + } + + if (!isset($record['charcount'])) { + $record['charcount'] = null; + } + $record = (object) $record; // Add the post. @@ -347,7 +363,7 @@ public function create_content($instance, $record = array()) { $record = (array)$record + array( 'forum' => $instance->id, 'userid' => $USER->id, - 'course' => $instance->course + 'course' => $instance->course, ); if (empty($record['discussion']) && empty($record['parent'])) { // Create discussion. diff --git a/tests/generator_test.php b/tests/generator_test.php index b22ec6c8..7e587001 100644 --- a/tests/generator_test.php +++ b/tests/generator_test.php @@ -34,7 +34,7 @@ * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_hsuforum_generator_testcase extends advanced_testcase { +class generator_test extends advanced_testcase { public function test_generator() { global $DB; diff --git a/tests/generator_trait.php b/tests/generator_trait.php index 5eef51ae..4af2f549 100644 --- a/tests/generator_trait.php +++ b/tests/generator_trait.php @@ -22,9 +22,11 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace mod_hsuforum; + defined('MOODLE_INTERNAL') || die(); -trait mod_hsuforum_tests_generator_trait { +trait generator_trait { /** * Helper to create the required number of users in the specified course. diff --git a/tests/grades_gradeitems_test.php b/tests/grades_gradeitems_test.php index b9cd21cb..08014505 100644 --- a/tests/grades_gradeitems_test.php +++ b/tests/grades_gradeitems_test.php @@ -29,7 +29,7 @@ use advanced_testcase; use core_grades\component_gradeitems; -use coding_exception; +use \core\exception\coding_exception; /** * Unit tests for mod_hsuforum\grades\gradeitems. @@ -39,7 +39,7 @@ * @copyright 2020 Open LMS * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class gradeitems_test extends advanced_testcase +class grades_gradeitems_test extends advanced_testcase { /** diff --git a/tests/helper.php b/tests/helper.php index cfd48e87..dfe53de1 100644 --- a/tests/helper.php +++ b/tests/helper.php @@ -21,6 +21,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace mod_hsuforum; + defined('MOODLE_INTERNAL') || die(); global $CFG; @@ -30,7 +32,7 @@ * * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -trait helper_hsuforums { +trait helper { /** * Helper to create the required number of users in the specified @@ -66,7 +68,7 @@ protected function helper_post_to_forum($forum, $author) { $generator = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum'); // Create a discussion in the forum, and then add a post to that discussion. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $forum->course; $record->userid = $author->id; $record->forum = $forum->id; @@ -105,7 +107,7 @@ protected function helper_post_to_discussion($forum, $discussion, $author) { $generator = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum'); // Add a post to the discussion. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $forum->course; $strre = get_string('re', 'hsuforum'); $record->subject = $strre . ' ' . $discussion->subject; diff --git a/tests/lib_test.php b/tests/lib_test.php index 068790e2..575be783 100644 --- a/tests/lib_test.php +++ b/tests/lib_test.php @@ -22,6 +22,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace mod_hsuforum\tests; + defined('MOODLE_INTERNAL') || die(); use mod_hsuforum\service; @@ -33,7 +35,7 @@ require_once($CFG->dirroot . '/mod/hsuforum/mod_form.php'); require_once($CFG->dirroot . '/course/modlib.php'); -class mod_hsuforum_lib_testcase extends advanced_testcase { +class lib_test extends \advanced_testcase { public function test_hsuforum_trigger_content_uploaded_event() { $this->resetAfterTest(); @@ -41,7 +43,7 @@ public function test_hsuforum_trigger_content_uploaded_event() { $user = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $this->setUser($user->id); $fakepost = (object) array('id' => 123, 'message' => 'Yay!', 'discussion' => 100); @@ -54,11 +56,11 @@ public function test_hsuforum_trigger_content_uploaded_event() { 'filearea' => 'attachment', 'itemid' => $fakepost->id, 'filepath' => '/', - 'filename' => 'myassignmnent.pdf' + 'filename' => 'myassignmnent.pdf', ); $fi = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename); - $data = new stdClass(); + $data = new \stdClass(); $sink = $this->redirectEvents(); hsuforum_trigger_content_uploaded_event($fakepost, $cm, 'some triggered from value'); $events = $sink->get_events(); @@ -87,52 +89,52 @@ public function test_hsuforum_get_courses_user_posted_in() { $course3 = $this->getDataGenerator()->create_course(); // Create 3 forums, one in each course. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course1->id; $forum1 = $this->getDataGenerator()->create_module('hsuforum', $record); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course2->id; $forum2 = $this->getDataGenerator()->create_module('hsuforum', $record); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course3->id; $forum3 = $this->getDataGenerator()->create_module('hsuforum', $record); // Add a second forum in course 1. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course1->id; $forum4 = $this->getDataGenerator()->create_module('hsuforum', $record); // Add discussions to course 1 started by user1. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course1->id; $record->userid = $user1->id; $record->forum = $forum1->id; $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course1->id; $record->userid = $user1->id; $record->forum = $forum4->id; $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); // Add discussions to course2 started by user1. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course2->id; $record->userid = $user1->id; $record->forum = $forum2->id; $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); // Add discussions to course 3 started by user2. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course3->id; $record->userid = $user2->id; $record->forum = $forum3->id; $discussion3 = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); // Add post to course 3 by user1. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course3->id; $record->userid = $user1->id; $record->forum = $forum3->id; @@ -184,14 +186,14 @@ public function test_hsuforum_tp_get_course_unread_posts() { $forumforce = $this->getDataGenerator()->create_module('hsuforum', $options); // Add discussions to the tracking forced forum. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user->id; $record->forum = $forumforce->id; $discussionforce = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); // Add post to the tracking forced discussion. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user->id; $record->forum = $forumforce->id; @@ -331,7 +333,7 @@ public function test_forum_view() { $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id), array('completion' => 2, 'completionview' => 1)); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $cm = get_coursemodule_from_instance('hsuforum', $forum->id); // Trigger and capture the event. @@ -348,13 +350,13 @@ public function test_forum_view() { // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\course_module_viewed', $event); $this->assertEquals($context, $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/view.php', array('f' => $forum->id)); + $url = new \core\url('/mod/hsuforum/view.php', array('f' => $forum->id)); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); $this->assertNotEmpty($event->get_name()); // Check completion status. - $completion = new completion_info($course); + $completion = new \completion_info($course); $completiondata = $completion->get_data($cm); $this->assertEquals(1, $completiondata->completionstate); @@ -373,7 +375,7 @@ public function test_forum_discussion_view() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $discussion = $this->create_single_discussion_with_replies($forum, $USER, 2); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $cm = get_coursemodule_from_instance('hsuforum', $forum->id); // Trigger and capture the event. @@ -415,9 +417,9 @@ public function test_forum_get_neighbours() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $cm = get_coursemodule_from_instance('hsuforum', $forum->id); - $context = context_module::instance($cm->id); + $context = \context_module::instance($cm->id); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user->id; $record->forum = $forum->id; @@ -490,7 +492,7 @@ public function test_forum_get_neighbours() { $past = $now - 600; $future = $now + 600; - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user->id; $record->forum = $forum->id; @@ -667,9 +669,9 @@ public function test_forum_get_neighbours_blog() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id, 'type' => 'blog')); $cm = get_coursemodule_from_instance('hsuforum', $forum->id); - $context = context_module::instance($cm->id); + $context = \context_module::instance($cm->id); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user->id; $record->forum = $forum->id; @@ -737,7 +739,7 @@ public function test_forum_get_neighbours_blog() { $past = $now - 600; $future = $now + 600; - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user->id; $record->forum = $forum->id; @@ -868,11 +870,11 @@ public function test_forum_get_neighbours_with_groups() { $forum2 = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id, 'groupmode' => SEPARATEGROUPS)); $cm1 = get_coursemodule_from_instance('hsuforum', $forum1->id); $cm2 = get_coursemodule_from_instance('hsuforum', $forum2->id); - $context1 = context_module::instance($cm1->id); - $context2 = context_module::instance($cm2->id); + $context1 = \context_module::instance($cm1->id); + $context2 = \context_module::instance($cm2->id); // Creating discussions in both forums. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user1->id; $record->forum = $forum1->id; @@ -1037,7 +1039,7 @@ public function test_forum_get_neighbours_with_groups() { $this->assertEmpty($neighbours['next']); // Querying the neighbours of a discussion passing the wrong CM. - $this->expectException('coding_exception'); + $this->expectException('\\core\\exception\\coding_exception'); hsuforum_get_discussion_neighbours($cm2, $disc11, $forum2); } @@ -1067,11 +1069,11 @@ public function test_forum_get_neighbours_with_groups_blog() { 'groupmode' => SEPARATEGROUPS)); $cm1 = get_coursemodule_from_instance('hsuforum', $forum1->id); $cm2 = get_coursemodule_from_instance('hsuforum', $forum2->id); - $context1 = context_module::instance($cm1->id); - $context2 = context_module::instance($cm2->id); + $context1 = \context_module::instance($cm1->id); + $context2 = \context_module::instance($cm2->id); // Creating blog posts in both forums. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user1->id; $record->forum = $forum1->id; @@ -1237,7 +1239,7 @@ public function test_forum_get_neighbours_with_groups_blog() { $this->assertEmpty($neighbours['next']); // Querying the neighbours of a discussion passing the wrong CM. - $this->expectException('coding_exception'); + $this->expectException('\\core\\exception\\coding_exception'); hsuforum_get_discussion_neighbours($cm2, $disc11, $forum2); } @@ -1333,12 +1335,12 @@ public function test_count_discussion_replies_private() { $user = $generator->create_user(); $otheruser = $generator->create_user(); $course = $generator->create_course(); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $forum = $generator->create_module('hsuforum', $record); $forumgenerator = $generator->get_plugin_generator('mod_hsuforum'); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $forum->course; $record->forum = $forum->id; $record->userid = $user->id; @@ -1348,7 +1350,7 @@ public function test_count_discussion_replies_private() { // Retrieve the first post. $replyto = $DB->get_record('hsuforum_posts', array('discussion' => $discussion->id)); - $post = new stdClass(); + $post = new \stdClass(); $post->userid = $user->id; $post->discussion = $discussion->id; $post->parent = $replyto->id; @@ -1371,7 +1373,7 @@ public function test_hsuforum_view() { $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id), array('completion' => 2, 'completionview' => 1)); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $cm = get_coursemodule_from_instance('hsuforum', $forum->id); // Trigger and capture the event. @@ -1388,13 +1390,13 @@ public function test_hsuforum_view() { // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_hsuforum\event\course_module_viewed', $event); $this->assertEquals($context, $event->get_context()); - $url = new \moodle_url('/mod/hsuforum/view.php', array('f' => $forum->id)); + $url = new \core\url('/mod/hsuforum/view.php', array('f' => $forum->id)); $this->assertEquals($url, $event->get_url()); $this->assertEventContextNotUsed($event); $this->assertNotEmpty($event->get_name()); // Check completion status. - $completion = new completion_info($course); + $completion = new \completion_info($course); $completiondata = $completion->get_data($cm); $this->assertEquals(1, $completiondata->completionstate); @@ -1413,7 +1415,7 @@ public function test_hsuforum_discussion_view() { $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $discussion = $this->create_single_discussion_with_replies($forum, $USER, 2); - $context = context_module::instance($forum->cmid); + $context = \context_module::instance($forum->cmid); $cm = get_coursemodule_from_instance('hsuforum', $forum->id); // Trigger and capture the event. @@ -1448,7 +1450,7 @@ protected function create_multiple_discussions_with_replies($discussioncount, $r // Setup the content. $user = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $forum = $this->getDataGenerator()->create_module('hsuforum', $record); @@ -1480,7 +1482,7 @@ protected function create_single_discussion_with_replies($forum, $user, $replyco $generator = self::getDataGenerator()->get_plugin_generator('mod_hsuforum'); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $forum->course; $record->forum = $forum->id; $record->userid = $user->id; @@ -1490,7 +1492,7 @@ protected function create_single_discussion_with_replies($forum, $user, $replyco $replyto = $DB->get_record('hsuforum_posts', array('discussion' => $discussion->id)); // Create the replies. - $post = new stdClass(); + $post = new \stdClass(); $post->userid = $user->id; $post->discussion = $discussion->id; $post->parent = $replyto->id; @@ -1514,7 +1516,7 @@ protected function create_single_discussion_pinned_with_replies($forum, $user, $ $generator = self::getDataGenerator()->get_plugin_generator('mod_hsuforum'); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $forum->course; $record->forum = $forum->id; $record->userid = $user->id; @@ -1525,7 +1527,7 @@ protected function create_single_discussion_pinned_with_replies($forum, $user, $ $replyto = $DB->get_record('hsuforum_posts', array('discussion' => $discussion->id)); // Create the replies. - $post = new stdClass(); + $post = new \stdClass(); $post->userid = $user->id; $post->discussion = $discussion->id; $post->parent = $replyto->id; @@ -1540,8 +1542,8 @@ protected function create_single_discussion_pinned_with_replies($forum, $user, $ /** * Tests for mod_hsuforum_rating_can_see_item_ratings(). * - * @throws coding_exception - * @throws rating_exception + * @throws \core\exception\coding_exception + * @throws \rating_exception */ public function test_mod_hsuforum_rating_can_see_item_ratings() { global $DB; @@ -1549,14 +1551,14 @@ public function test_mod_hsuforum_rating_can_see_item_ratings() { $this->resetAfterTest(); // Setup test data. - $course = new stdClass(); + $course = new \stdClass(); $course->groupmode = SEPARATEGROUPS; $course->groupmodeforce = true; $course = $this->getDataGenerator()->create_course($course); $forum = $this->getDataGenerator()->create_module('hsuforum', array('course' => $course->id)); $generator = self::getDataGenerator()->get_plugin_generator('mod_hsuforum'); $cm = get_coursemodule_from_instance('hsuforum', $forum->id); - $context = context_module::instance($cm->id); + $context = \context_module::instance($cm->id); // Create users. $user1 = $this->getDataGenerator()->create_user(); @@ -1578,7 +1580,7 @@ public function test_mod_hsuforum_rating_can_see_item_ratings() { groups_add_member($group2, $user3); groups_add_member($group2, $user4); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $forum->course; $record->forum = $forum->id; $record->userid = $user1->id; @@ -1588,14 +1590,14 @@ public function test_mod_hsuforum_rating_can_see_item_ratings() { // Retrieve the first post. $post = $DB->get_record('hsuforum_posts', array('discussion' => $discussion->id)); - $ratingoptions = new stdClass; + $ratingoptions = new \stdClass; $ratingoptions->context = $context; $ratingoptions->ratingarea = 'post'; $ratingoptions->component = 'mod_hsuforum'; $ratingoptions->itemid = $post->id; $ratingoptions->scaleid = 2; $ratingoptions->userid = $user2->id; - $rating = new rating($ratingoptions); + $rating = new \rating($ratingoptions); $rating->update_rating(2); // Now try to access it as various users. @@ -1660,7 +1662,7 @@ public function test_forum_get_discussions_with_groups() { self::getDataGenerator()->enrol_user($user3->id, $course->id, $role->id); // Forum forcing separate gropus. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $forum = self::getDataGenerator()->create_module('hsuforum', $record, array('groupmode' => SEPARATEGROUPS)); $cm = get_coursemodule_from_instance('hsuforum', $forum->id); @@ -1773,11 +1775,11 @@ public function test_forum_user_can_post_discussion() { $this->getDataGenerator()->enrol_user($user->id, $course->id); // Forum forcing separate gropus. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $forum = self::getDataGenerator()->create_module('hsuforum', $record, array('groupmode' => SEPARATEGROUPS)); $cm = get_coursemodule_from_instance('hsuforum', $forum->id); - $context = context_module::instance($cm->id); + $context = \context_module::instance($cm->id); self::setUser($user); @@ -1836,7 +1838,7 @@ public function test_forum_user_can_post_discussion() { $this->assertTrue($can); // Post now. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user->id; $record->forum = $forum->id; @@ -1881,7 +1883,7 @@ public function test_forum_user_has_posted_discussion_no_groups() { $this->assertFalse(hsuforum_user_has_posted_discussion($forum->id, $other->id)); // Post in the forum. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $author->id; $record->forum = $forum->id; @@ -1913,7 +1915,7 @@ public function test_forum_user_has_posted_discussion_multiple_forums() { $this->assertFalse(hsuforum_user_has_posted_discussion($forum2->id, $author->id)); // Post in the forum. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $author->id; $record->forum = $forum1->id; @@ -1953,7 +1955,7 @@ public function test_forum_user_has_posted_discussion_multiple_groups() { $this->assertFalse(hsuforum_user_has_posted_discussion($forum->id, $author->id, $group2->id)); // Post in one group. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $author->id; $record->forum = $forum->id; @@ -1966,7 +1968,7 @@ public function test_forum_user_has_posted_discussion_multiple_groups() { $this->assertFalse(hsuforum_user_has_posted_discussion($forum->id, $author->id, $group2->id)); // Post in the other group. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $author->id; $record->forum = $forum->id; @@ -1996,7 +1998,7 @@ public function test_mod_hsuforum_myprofile_navigation() { // Check the node tree is correct. mod_hsuforum_myprofile_navigation($tree, $user, $iscurrentuser, $course); - $reflector = new ReflectionObject($tree); + $reflector = new \ReflectionObject($tree); $nodes = $reflector->getProperty('nodes'); $nodes->setAccessible(true); $this->assertArrayHasKey('hsuforumposts', $nodes->getValue($tree)); @@ -2021,7 +2023,7 @@ public function test_mod_hsuforum_myprofile_navigation_as_guest() { // Check the node tree is correct. mod_hsuforum_myprofile_navigation($tree, $USER, $iscurrentuser, $course); - $reflector = new ReflectionObject($tree); + $reflector = new \ReflectionObject($tree); $nodes = $reflector->getProperty('nodes'); $nodes->setAccessible(true); $this->assertArrayNotHasKey('hsuforumposts', $nodes->getValue($tree)); @@ -2046,7 +2048,7 @@ public function test_mod_hsuforum_myprofile_navigation_different_user() { // Check the node tree is correct. mod_hsuforum_myprofile_navigation($tree, $user, $iscurrentuser, $course); - $reflector = new ReflectionObject($tree); + $reflector = new \ReflectionObject($tree); $nodes = $reflector->getProperty('nodes'); $nodes->setAccessible(true); $this->assertArrayHasKey('hsuforumposts', $nodes->getValue($tree)); @@ -2073,13 +2075,13 @@ public function test_print_overview() { $this->getDataGenerator()->enrol_user($viewer->id, $course2->id); // Create two forums - one in each course. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course1->id; $forum1 = self::getDataGenerator()->create_module('hsuforum', (object) array('course' => $course1->id)); $forum2 = self::getDataGenerator()->create_module('hsuforum', (object) array('course' => $course2->id)); // A standard post in the forum. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course1->id; $record->userid = $author->id; $record->forum = $forum1->id; @@ -2146,7 +2148,7 @@ public function test_print_overview_groups() { $this->getDataGenerator()->create_group_member(array('userid' => $viewer2->id, 'groupid' => $group2->id)); // Create a forum. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course1->id; $forum1 = self::getDataGenerator()->create_module('hsuforum', (object) array( 'course' => $course1->id, @@ -2154,7 +2156,7 @@ public function test_print_overview_groups() { )); // A post in the forum for group1. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course1->id; $record->userid = $author->id; $record->forum = $forum1->id; @@ -2202,12 +2204,12 @@ public function test_print_overview_timed($config, $hasresult) { $this->getDataGenerator()->enrol_user($viewer->id, $course1->id); // Create a forum. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course1->id; $forum1 = self::getDataGenerator()->create_module('hsuforum', (object) array('course' => $course1->id)); // A timed post with a timestart in the past (24 hours ago). - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course1->id; $record->userid = $author->id; $record->forum = $forum1->id; @@ -2264,7 +2266,7 @@ public function test_print_overview_timed_groups($config, $hasresult) { $this->getDataGenerator()->create_group_member(array('userid' => $viewer2->id, 'groupid' => $group2->id)); // Create a forum. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course1->id; $forum1 = self::getDataGenerator()->create_module('hsuforum', (object) array( 'course' => $course1->id, @@ -2272,7 +2274,7 @@ public function test_print_overview_timed_groups($config, $hasresult) { )); // A post in the forum for group1. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course1->id; $record->userid = $author->id; $record->forum = $forum1->id; @@ -2378,13 +2380,13 @@ public function test_pinned_discussion_with_group() { // Create 4 discussions in all participants group and group1, where the first // discussion is pinned in each group. - $allrecord = new stdClass(); + $allrecord = new \stdClass(); $allrecord->course = $course1->id; $allrecord->userid = $author->id; $allrecord->forum = $forum1->id; $allrecord->pinned = HSUFORUM_DISCUSSION_PINNED; - $group1record = new stdClass(); + $group1record = new \stdClass(); $group1record->course = $course1->id; $group1record->userid = $author->id; $group1record->forum = $forum1->id; @@ -2488,7 +2490,7 @@ public function test_pinned_with_timed_discussions() { $this->getDataGenerator()->enrol_user($user->id, $course->id); // Create a forum. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $forum = $this->getDataGenerator()->create_module('hsuforum', (object) array( 'course' => $course->id, @@ -2500,7 +2502,7 @@ public function test_pinned_with_timed_discussions() { $discussions = array(); $discussiongenerator = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum'); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user->id; $record->forum = $forum->id; @@ -2560,7 +2562,7 @@ public function test_pinned_timed_discussions_with_timed_discussions() { $this->getDataGenerator()->enrol_user($user->id, $course->id); // Create a forum. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $forum = $this->getDataGenerator()->create_module('hsuforum', (object) array( 'course' => $course->id, @@ -2572,7 +2574,7 @@ public function test_pinned_timed_discussions_with_timed_discussions() { $discussions = array(); $discussiongenerator = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum'); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user->id; $record->forum = $forum->id; @@ -2646,7 +2648,7 @@ public function test_forum_get_unmailed_posts($discussiondata, $enabletimedposts // Keep track of the start time of the test. Do not use time() after this point to prevent random failures. $time = time(); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user->id; $record->forum = $forum->id; @@ -2675,7 +2677,7 @@ public function test_forum_get_unmailed_posts($discussiondata, $enabletimedposts // Add a reply just outside the maxeditingtime. $replyto = $DB->get_record('hsuforum_posts', array('discussion' => $discussion->id)); - $reply = new stdClass(); + $reply = new \stdClass(); $reply->userid = $user->id; $reply->discussion = $discussion->id; $reply->parent = $replyto->id; @@ -2700,7 +2702,7 @@ public function test_function_validate_files(){ $user = $generator->create_user(); // Create discussion. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user->id; $record->forum = $forum->id; @@ -2712,12 +2714,12 @@ public function test_function_validate_files(){ $path = $CFG->dirroot . '/mod/hsuforum/tests/fixtures/testgif_small.gif'; $post = $DB->get_record('hsuforum_posts', array('discussion' => $discussion->id)); $filerecord = array( - 'contextid' => context_module::instance($forum->cmid)->id, + 'contextid' => \context_module::instance($forum->cmid)->id, 'component' => 'mod_hsuforum', 'filearea' => 'attachment', 'itemid' => $post->id, 'filepath' => '/', - 'filename' => 'testgif_small.gif' + 'filename' => 'testgif_small.gif', ); $file = (array) $fs->create_file_from_pathname($filerecord, $path); @@ -2728,11 +2730,11 @@ public function test_function_validate_files(){ 'attachment' => array( 'name' => $filerecord['filename'], 'tmp_name' => $path, - 'error' => 0 + 'error' => 0, )); // Get Context - $modcontext = context_module::instance($forum->cmid); + $modcontext = \context_module::instance($forum->cmid); // Reflexion to convert a protected method into a public $class = new \ReflectionClass('mod_hsuforum\upload_file'); @@ -2748,7 +2750,7 @@ public function test_function_validate_files(){ // Define the expected exception with its error message $params = array( 'file' => '\'' . $filerecord['filename'] . '\'', - 'size' => '10B' + 'size' => '10B', ); $errormessage = get_string('maxbytesfile', 'error', $params); $this->expectException('file_exception'); @@ -2773,7 +2775,7 @@ public function test_attachment_field_on_create_discussion() { $forumgen = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum'); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $record->userid = $user->id; $record->forum = $forum->id; @@ -2781,7 +2783,7 @@ public function test_attachment_field_on_create_discussion() { $discussion = $forumgen->create_discussion($record); // Get Context. - $modcontext = context_module::instance($forum->cmid); + $modcontext = \context_module::instance($forum->cmid); // Create Uploader. $uploader = new \mod_hsuforum\upload_file( @@ -2821,13 +2823,13 @@ public function test_forum_is_author_hidden() { $this->assertFalse(hsuforum_is_author_hidden($post, $forum)); // Incorrect parameters: $post. - $this->expectException('coding_exception'); + $this->expectException('\\core\\exception\\coding_exception'); $this->expectExceptionMessage('$post->parent must be set.'); unset($post->parent); hsuforum_is_author_hidden($post, $forum); // Incorrect parameters: $forum. - $this->expectException('coding_exception'); + $this->expectException('\\core\\exception\\coding_exception'); $this->expectExceptionMessage('$forum->type must be set.'); unset($forum->type); hsuforum_is_author_hidden($post, $forum); @@ -2872,7 +2874,7 @@ public function hsuforum_get_unmailed_posts_provider() { 'discussion' => [ 'timecreated' => - WEEKSECS, 'timestart' => - DAYSECS, - 'timeend' => + DAYSECS + 'timeend' => + DAYSECS, ], 'timedposts' => true, 'postcount' => 1, @@ -2891,7 +2893,7 @@ public function hsuforum_get_unmailed_posts_provider() { 'Timed discussion; Single post; Posted 1 week ago; timeend not reached' => [ 'discussion' => [ 'timecreated' => - WEEKSECS, - 'timeend' => + DAYSECS + 'timeend' => + DAYSECS, ], 'timedposts' => true, 'postcount' => 0, @@ -2994,8 +2996,8 @@ public function hsuforum_get_unmailed_posts_provider() { * Test the hsuforum_discussion_is_locked function. * * @dataProvider hsuforum_discussion_is_locked_provider - * @param stdClass $forum - * @param stdClass $discussion + * @param \stdClass $forum + * @param \stdClass $discussion * @param bool $expect */ public function test_forum_discussion_is_locked($forum, $discussion, $expect) { @@ -3012,37 +3014,37 @@ public function hsuforum_discussion_is_locked_provider() { 'Unlocked: lockdiscussionafter is unset' => [ (object) [], (object) [], - false + false, ], 'Unlocked: lockdiscussionafter is false' => [ (object) ['lockdiscussionafter' => false], (object) [], - false + false, ], 'Unlocked: lockdiscussionafter is null' => [ (object) ['lockdiscussionafter' => null], (object) [], - false + false, ], 'Unlocked: lockdiscussionafter is set; forum is of type single; post is recent' => [ (object) ['lockdiscussionafter' => DAYSECS, 'type' => 'single'], (object) ['timemodified' => time()], - false + false, ], 'Unlocked: lockdiscussionafter is set; forum is of type single; post is old' => [ (object) ['lockdiscussionafter' => MINSECS, 'type' => 'single'], (object) ['timemodified' => time() - DAYSECS], - false + false, ], 'Unlocked: lockdiscussionafter is set; forum is of type eachuser; post is recent' => [ (object) ['lockdiscussionafter' => DAYSECS, 'type' => 'eachuser'], (object) ['timemodified' => time()], - false + false, ], 'Locked: lockdiscussionafter is set; forum is of type eachuser; post is old' => [ (object) ['lockdiscussionafter' => MINSECS, 'type' => 'eachuser'], (object) ['timemodified' => time() - DAYSECS], - true + true, ], ]; } @@ -3165,7 +3167,7 @@ public function test_forum_core_calendar_provide_event_action() { // Confirm the event was decorated. $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); $this->assertEquals(get_string('view'), $actionevent->get_name()); - $this->assertInstanceOf('moodle_url', $actionevent->get_url()); + $this->assertInstanceOf('\core\url', $actionevent->get_url()); $this->assertEquals(7, $actionevent->get_item_count()); $this->assertTrue($actionevent->is_actionable()); } @@ -3240,7 +3242,7 @@ public function test_forum_core_calendar_provide_event_action_for_user() { // Confirm the event was decorated. $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); $this->assertEquals(get_string('view'), $actionevent->get_name()); - $this->assertInstanceOf('moodle_url', $actionevent->get_url()); + $this->assertInstanceOf('\core\url', $actionevent->get_url()); $this->assertEquals(7, $actionevent->get_item_count()); $this->assertTrue($actionevent->is_actionable()); } @@ -3294,7 +3296,7 @@ public function test_forum_core_calendar_provide_event_action_already_completed( \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); // Mark the activity as completed. - $completion = new completion_info($course); + $completion = new \completion_info($course); $completion->set_module_viewed($cm); // Create an action factory. @@ -3333,7 +3335,7 @@ public function test_forum_core_calendar_provide_event_action_already_completed_ \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); // Mark the activity as completed for the student. - $completion = new completion_info($course); + $completion = new \completion_info($course); $completion->set_module_viewed($cm, $student->id); // Create an action factory. @@ -3371,7 +3373,7 @@ public function test_mod_hsuforum_get_tagged_posts() { $post23 = $forumgenerator->create_content($forum2, array('tags' => array('mice', 'Cats'))); $post31 = $forumgenerator->create_content($forum3, array('tags' => array('mice', 'Cats'))); - $tag = core_tag_tag::get_by_name(0, 'Cats'); + $tag = \core_tag_tag::get_by_name(0, 'Cats'); // Admin can see everything. $res = mod_hsuforum_get_tagged_posts($tag, /*$exclusivemode = */false, @@ -3409,7 +3411,7 @@ public function test_mod_hsuforum_get_tagged_posts() { $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual'); $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual'); $this->setUser($student); - core_tag_index_builder::reset_caches(); + \core_tag_index_builder::reset_caches(); // User can not see posts in course 3 because he is not enrolled. $res = mod_hsuforum_get_tagged_posts($tag, /*$exclusivemode = */false, @@ -3419,7 +3421,7 @@ public function test_mod_hsuforum_get_tagged_posts() { $this->assertDoesNotMatchRegularExpression('/'.$post31->subject.'/', $res->content); // User can search forum posts inside a course. - $coursecontext = context_course::instance($course1->id); + $coursecontext = \context_course::instance($course1->id); $res = mod_hsuforum_get_tagged_posts($tag, /*$exclusivemode = */false, /*$fromctx = */0, /*$ctx = */$coursecontext->id, /*$rec = */1, /*$post = */0); $this->assertMatchesRegularExpression('/'.$post11->subject.'/', $res->content); @@ -3440,10 +3442,10 @@ public function test_mod_hsuforum_get_tagged_posts() { * @param int $courseid The course id. * @param int $instanceid The instance id. * @param string $eventtype The event type. - * @return bool|calendar_event + * @return bool|\calendar_event */ private function create_action_event($courseid, $instanceid, $eventtype) { - $event = new stdClass(); + $event = new \stdClass(); $event->name = 'Calendar event'; $event->modulename = 'hsuforum'; $event->courseid = $courseid; @@ -3452,7 +3454,7 @@ private function create_action_event($courseid, $instanceid, $eventtype) { $event->eventtype = $eventtype; $event->timestart = time(); - return calendar_event::create($event); + return \calendar_event::create($event); } /** @@ -3471,38 +3473,38 @@ public function test_mod_hsuforum_completion_get_active_rule_descriptions() { 'completion' => 2, 'completiondiscussions' => 3, 'completionreplies' => 3, - 'completionposts' => 3 + 'completionposts' => 3, ]); $forum2 = $this->getDataGenerator()->create_module('hsuforum', [ 'course' => $course->id, 'completion' => 2, 'completiondiscussions' => 0, 'completionreplies' => 0, - 'completionposts' => 0 + 'completionposts' => 0, ]); - $cm1 = cm_info::create(get_coursemodule_from_instance('hsuforum', $forum1->id)); - $cm2 = cm_info::create(get_coursemodule_from_instance('hsuforum', $forum2->id)); + $cm1 = \cm_info::create(get_coursemodule_from_instance('hsuforum', $forum1->id)); + $cm2 = \cm_info::create(get_coursemodule_from_instance('hsuforum', $forum2->id)); // Data for the stdClass input type. // This type of input would occur when checking the default completion rules for an activity type, where we don't have // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info. - $moddefaults = new stdClass(); + $moddefaults = new \stdClass(); $moddefaults->customdata = ['customcompletionrules' => [ 'completiondiscussions' => 3, 'completionreplies' => 3, - 'completionposts' => 3 + 'completionposts' => 3, ]]; $moddefaults->completion = 2; $activeruledescriptions = [ get_string('completiondiscussionsdesc', 'hsuforum', 3), get_string('completionrepliesdesc', 'hsuforum', 3), - get_string('completionpostsdesc', 'hsuforum', 3) + get_string('completionpostsdesc', 'hsuforum', 3), ]; $this->assertEquals(mod_hsuforum_get_completion_active_rule_descriptions($cm1), $activeruledescriptions); $this->assertEquals(mod_hsuforum_get_completion_active_rule_descriptions($cm2), []); $this->assertEquals(mod_hsuforum_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions); - $this->assertEquals(mod_hsuforum_get_completion_active_rule_descriptions(new stdClass()), []); + $this->assertEquals(mod_hsuforum_get_completion_active_rule_descriptions(new \stdClass()), []); } public function test_hsuforum_recent_activity_query() { @@ -3512,14 +3514,14 @@ public function test_hsuforum_recent_activity_query() { $generator = $this->getDataGenerator(); $user = $generator->create_user(); $course = $generator->create_course(); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $forum = $generator->create_module('hsuforum', $record); $now = time(); $forumgenerator = $generator->get_plugin_generator('mod_hsuforum'); //Create 3 discussions. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $forum->course; $record->forum = $forum->id; $record->userid = $user->id; @@ -3557,13 +3559,13 @@ public function test_hsuforum_word_count() { $generator = $this->getDataGenerator(); $user = $generator->create_user(); $course = $generator->create_course(); - $record = new stdClass(); + $record = new \stdClass(); $record->course = $course->id; $forum = $generator->create_module('hsuforum', $record); $forumgenerator = $generator->get_plugin_generator('mod_hsuforum'); //Create a discussion. - $record = new stdClass(); + $record = new \stdClass(); $record->course = $forum->course; $record->forum = $forum->id; $record->userid = $user->id; @@ -3597,12 +3599,12 @@ public function test_hsuforum_scale_dependency_form() { $data->return = 0; $data->sr = 0; $data->add = 'hsuforum'; - $mform = new mod_hsuforum_mod_form($data, 0, null, $course); - $reflection = new ReflectionClass($mform); + $mform = new \mod_hsuforum_mod_form($data, 0, null, $course); + $reflection = new \ReflectionClass($mform); $property = $reflection->getProperty('_form'); $property->setAccessible(true); $form = $property->getValue($mform); - $reflection2 = new ReflectionClass($form); + $reflection2 = new \ReflectionClass($form); $hideifs = $reflection2->getProperty('_hideifs'); $hideifs->setAccessible(true); $value = $hideifs->getValue($form); diff --git a/tests/mail_test.php b/tests/mail_test.php index 33faf02a..ba8bc14e 100644 --- a/tests/mail_test.php +++ b/tests/mail_test.php @@ -27,7 +27,7 @@ global $CFG; -class mod_hsuforum_mail_testcase extends advanced_testcase { +class mail_test extends advanced_testcase { protected $helper; @@ -980,7 +980,7 @@ public function forum_post_email_templates_provider() { 'attachments' => array( array( 'filename' => 'example.txt', - 'filecontents' => 'Basic information about the course' + 'filecontents' => 'Basic information about the course', ), ), ), @@ -995,7 +995,7 @@ public function forum_post_email_templates_provider() { '~&(amp|lt|gt|quot|\#039);(?!course)', 'Attachments example.txt:\r\n' . $CFG->wwwroot.'/pluginfile.php/\d*/mod_hsuforum/attachment/\d*/example.txt\r\n', - 'Hello Moodle', 'Moodle Forum', 'Welcome.*Moodle', 'Love Moodle', '1\d1' + 'Hello Moodle', 'Moodle Forum', 'Welcome.*Moodle', 'Love Moodle', '1\d1', ), ), ), @@ -1173,7 +1173,7 @@ public function test_forum_post_email_templates($data) { 'filearea' => 'attachment', 'itemid' => $post->id, 'filepath' => '/', - 'filename' => $attachment['filename'] + 'filename' => $attachment['filename'], ); $fs->create_file_from_string($filerecord, $attachment['filecontents']); } @@ -1224,7 +1224,7 @@ public function test_forum_post_email_templates($data) { $this->assertNotEmpty($foundexpectation, 'Expectation not found for the mail'); // If we have found the expectation and have contents to match, let's do it. - if (isset($foundexpectation) and isset($foundexpectation['contents'])) { + if (isset($foundexpectation) && isset($foundexpectation['contents'])) { $mail->body = quoted_printable_decode($mail->body); if (!is_array($foundexpectation['contents'])) { // Accept both string and array. $foundexpectation['contents'] = array($foundexpectation['contents']); diff --git a/tests/maildigest_test.php b/tests/maildigest_test.php index ca877e5f..e216fbc6 100644 --- a/tests/maildigest_test.php +++ b/tests/maildigest_test.php @@ -1,5 +1,4 @@ assertEquals($individualcount, $counts->individual); } + public function test_skip_private_replies_on_maildigest() { + $this->resetAfterTest(); + + // Setting up the dynamic learning environment. + $course = $this->getDataGenerator()->create_course(); + $recorduser = new stdClass(); + $recorduser->maildigest = '1'; + $user1 = $this->getDataGenerator()->create_user($recorduser); + $user2 = $this->getDataGenerator()->create_user($recorduser); + $this->getDataGenerator()->enrol_user($user1->id, $course->id); + $this->getDataGenerator()->enrol_user($user2->id, $course->id); + $recordforum = new stdClass(); + $recordforum->course = $course->id; + $recordforum->forcesubscribe = 1; + $forum = $this->getDataGenerator()->create_module('hsuforum', $recordforum); + $recorddiscussion = new stdClass(); + $recorddiscussion->course = $course->id; + $recorddiscussion->userid = $user1->id; + $recorddiscussion->forum = $forum->id; + $discussion = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($recorddiscussion); + $recordpost = new stdClass(); + $recordpost->course = $course->id; + $recordpost->userid = $user1->id; + $recordpost->forum = $forum->id; + $recordpost->discussion = $discussion->id; + $recordpost->privatereply = '0'; + $post1 = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($recordpost); + $recordpost->privatereply = $user1->id; + $post2 = $this->getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($recordpost); + + $this->helper_force_digest_mail_times(); + + $this->helper_run_cron_check_count(2, 0, 2); + } + public function test_set_maildigest() { global $DB; @@ -242,7 +276,7 @@ public function test_set_maildigest() { $this->assertFalse($currentsetting); // Try with an invalid value. - $this->expectException('moodle_exception'); + $this->expectException('\core\exception\moodle_exception'); hsuforum_set_user_maildigest($forum1, 42, $user); } diff --git a/tests/externallib_test.php b/tests/mod_hsuforum_external_test.php similarity index 73% rename from tests/externallib_test.php rename to tests/mod_hsuforum_external_test.php index 9a6a76e3..091e1dc4 100644 --- a/tests/externallib_test.php +++ b/tests/mod_hsuforum_external_test.php @@ -26,14 +26,11 @@ defined('MOODLE_INTERNAL') || die(); global $CFG; - +use core_external\external_api; require_once($CFG->dirroot . '/webservice/tests/helpers.php'); require_once($CFG->dirroot . '/mod/hsuforum/lib.php'); -/** - * @runTestsInSeparateProcesses - */ -class mod_hsuforum_external_testcase extends externallib_advanced_testcase { +class mod_hsuforum_external_test extends externallib_advanced_testcase { /** * Tests set up @@ -292,11 +289,12 @@ public function test_mod_hsuforum_get_forum_discussion_posts() { 'filename' => $filename, 'filepath' => '/', 'filesize' => 27, - 'fileurl' => moodle_url::make_webservice_pluginfile_url($forum1context->id, 'mod_hsuforum', 'post', + 'fileurl' => \core\url::make_webservice_pluginfile_url($forum1context->id, 'mod_hsuforum', 'post', $discussion1reply1->id, '/', $filename)->out(false), 'timemodified' => $timepost, 'mimetype' => 'image/jpeg', 'isexternalfile' => false, + 'icon' => "f/image", ) ), 'totalscore' => $discussion1reply1->totalscore, @@ -338,14 +336,13 @@ public function test_mod_hsuforum_get_forum_discussion_posts() { $this->assertEquals(3, count($posts['posts'])); // Generate here the pictures because we need to wait to the external function to init the theme. - $userpicture = new user_picture($user3); + $userpicture = new \core\output\user_picture($user3); $userpicture->size = 1; // Size f1. $expectedposts['posts'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false); - $userpicture = new user_picture($user2); + $userpicture = new \core\output\user_picture($user2); $userpicture->size = 1; // Size f1. $expectedposts['posts'][1]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false); - // Unset the initial discussion post. array_shift($posts['posts']); $this->assertEquals($expectedposts, $posts); @@ -583,11 +580,11 @@ public function test_mod_hsuforum_get_forum_discussions_paginated() { ); // Wait the theme to be loaded (the external_api call does that) to generate the user profiles. - $userpicture = new user_picture($user1); + $userpicture = new \core\output\user_picture($user1); $userpicture->size = 1; // Size f1. $expectedreturn['discussions'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false); - $userpicture = new user_picture($user4); + $userpicture = new \core\output\user_picture($user4); $userpicture->size = 1; // Size f1. $expectedreturn['discussions'][0]['usermodifiedpictureurl'] = $userpicture->get_url($PAGE)->out(false); @@ -598,7 +595,7 @@ public function test_mod_hsuforum_get_forum_discussions_paginated() { try { mod_hsuforum_external::get_forum_discussions_paginated($forum1->id); $this->fail('Exception expected due to missing capability.'); - } catch (moodle_exception $e) { + } catch (\core\exception\moodle_exception $e) { $this->assertEquals('noviewdiscussionspermission', $e->errorcode); } @@ -609,7 +606,7 @@ public function test_mod_hsuforum_get_forum_discussions_paginated() { try { mod_hsuforum_external::get_forum_discussions_paginated($forum1->id); $this->fail('Exception expected due to being unenrolled from the course.'); - } catch (moodle_exception $e) { + } catch (\core\exception\moodle_exception $e) { $this->assertEquals('requireloginerror', $e->errorcode); } } @@ -658,6 +655,361 @@ public function test_mod_hsuforum_get_forum_discussions_paginated_qanda() { } + /** + * Test get forum discussions + */ + public function test_mod_hsuforum_get_forum_discussions(): void { + global $CFG, $DB, $PAGE; + + $this->resetAfterTest(true); + + // Set the CFG variable to allow track forums. + $CFG->forum_trackreadposts = true; + + // Create a user who can track forums. + $record = new \stdClass(); + $record->trackforums = true; + $user1 = self::getDataGenerator()->create_user($record); + // Create a bunch of other users to post. + $user2 = self::getDataGenerator()->create_user(); + $user3 = self::getDataGenerator()->create_user(); + $user4 = self::getDataGenerator()->create_user(); + + // Set the first created user to the test user. + self::setUser($user1); + + // Create courses to add the modules. + $course1 = self::getDataGenerator()->create_course(); + + // First forum with tracking off. + $record = new \stdClass(); + $record->course = $course1->id; + $record->trackingtype = HSUFORUM_TRACKING_OFF; + $forum1 = self::getDataGenerator()->create_module('hsuforum', $record); + + // Add discussions to the hsuforums. + $record = new \stdClass(); + $record->course = $course1->id; + $record->userid = $user1->id; + $record->forum = $forum1->id; + $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); + + // Add three replies to the discussion 1 from different users. + $record = new \stdClass(); + $record->discussion = $discussion1->id; + $record->parent = $discussion1->firstpost; + $record->userid = $user2->id; + $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); + + $record->parent = $discussion1reply1->id; + $record->userid = $user3->id; + $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); + + $record->userid = $user4->id; + $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); + + // Enrol the user in the first course. + $enrol = enrol_get_plugin('manual'); + + // We don't use the dataGenerator as we need to get the $instance2 to unenrol later. + $enrolinstances = enrol_get_instances($course1->id, true); + foreach ($enrolinstances as $courseenrolinstance) { + if ($courseenrolinstance->enrol == "manual") { + $instance1 = $courseenrolinstance; + break; + } + } + $enrol->enrol_user($instance1, $user1->id); + + // Delete one user. + delete_user($user4); + + // Assign capabilities to view discussions for forum 1. + $cm = get_coursemodule_from_id('hsuforum', $forum1->cmid, 0, false, MUST_EXIST); + $context = \context_module::instance($cm->id); + $newrole = create_role('Role 2', 'role2', 'Role 2 description'); + $this->assignUserCapability('mod/hsuforum:viewdiscussion', $context->id, $newrole); + + // Create what we expect to be returned when querying the hsuforums. + + $post1 = $DB->get_record('hsuforum_posts', array('id' => $discussion1->firstpost), '*', MUST_EXIST); + + // User pictures are initially empty, we should get the links once the external function is called. + $expecteddiscussions = array( + 'id' => $discussion1->firstpost, + 'name' => $discussion1->name, + 'groupid' => (int) $discussion1->groupid, + 'timemodified' => (int) $discussion1reply3->created, + 'usermodified' => (int) $discussion1reply3->userid, + 'timestart' => (int) $discussion1->timestart, + 'timeend' => (int) $discussion1->timeend, + 'discussion' => (int) $discussion1->id, + 'parent' => 0, + 'userid' => (int) $discussion1->userid, + 'created' => (int) $post1->created, + 'modified' => (int) $post1->modified, + 'mailed' => (int) $post1->mailed, + 'subject' => $post1->subject, + 'message' => $post1->message, + 'messageformat' => (int) $post1->messageformat, + 'messagetrust' => (int) $post1->messagetrust, + 'attachment' => $post1->attachment, + 'totalscore' => (int) $post1->totalscore, + 'mailnow' => (int) $post1->mailnow, + 'userfullname' => fullname($user1), + 'usermodifiedfullname' => fullname($user4), + 'userpictureurl' => '', + 'usermodifiedpictureurl' => '', + 'numreplies' => 3, + 'numunread' => 0, + 'pinned' => (bool) HSUFORUM_DISCUSSION_UNPINNED, + 'locked' => false, + 'canreply' => false, + 'canlock' => false, + 'starred' => false, + 'canfavourite' => true + ); + + // Call the external function passing forum id. + $discussions = mod_hsuforum_external::get_forum_discussions($forum1->id); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); + $expectedreturn = array( + 'discussions' => array($expecteddiscussions), + 'warnings' => array() + ); + + // Wait the theme to be loaded (the external_api call does that) to generate the user profiles. + $userpicture = new \core\output\user_picture($user1); + $userpicture->size = 2; // Size f2. + $expectedreturn['discussions'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false); + + $userpicture = new \core\output\user_picture($user4); + $userpicture->size = 2; // Size f2. + $expectedreturn['discussions'][0]['usermodifiedpictureurl'] = $userpicture->get_url($PAGE)->out(false); + + $this->assertEquals($expectedreturn, $discussions); + + // Test the starring functionality return. + $t = mod_hsuforum_external::toggle_favourite_state($discussion1->id, 1); + $expectedreturn['discussions'][0]['starred'] = true; + $discussions = mod_hsuforum_external::get_forum_discussions($forum1->id); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); + $this->assertEquals($expectedreturn, $discussions); + + // Call without required view discussion capability. + $this->unassignUserCapability('mod/hsuforum:viewdiscussion', $context->id, $newrole); + try { + mod_hsuforum_external::get_forum_discussions($forum1->id); + $this->fail('Exception expected due to missing capability.'); + } catch (\core\exception\moodle_exception $e) { + $this->assertEquals('noviewdiscussionspermission', $e->errorcode); + } + + // Unenrol user from second course. + $enrol->unenrol_user($instance1, $user1->id); + + // Call for the second course we unenrolled the user from, make sure exception thrown. + try { + mod_hsuforum_external::get_forum_discussions($forum1->id); + $this->fail('Exception expected due to being unenrolled from the course.'); + } catch (\core\exception\moodle_exception $e) { + $this->assertEquals('requireloginerror', $e->errorcode); + } + + $this->setAdminUser(); + $discussions = mod_hsuforum_external::get_forum_discussions($forum1->id); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); + $this->assertTrue($discussions['discussions'][0]['canlock']); + } + + /** + * Test the sorting in get forum discussions + */ + public function test_mod_hsuforum_get_forum_discussions_sorting(): void { + global $CFG, $DB, $PAGE; + + $this->resetAfterTest(true); + + $clock = $this->mock_clock_with_frozen(); + + // Set the CFG variable to allow track forums. + $CFG->forum_trackreadposts = true; + + // Create a user who can track forums. + $record = new \stdClass(); + $record->trackforums = true; + $user1 = self::getDataGenerator()->create_user($record); + // Create a bunch of other users to post. + $user2 = self::getDataGenerator()->create_user(); + $user3 = self::getDataGenerator()->create_user(); + $user4 = self::getDataGenerator()->create_user(); + + // Set the first created user to the test user. + self::setUser($user1); + + // Create courses to add the modules. + $course1 = self::getDataGenerator()->create_course(); + + // Enrol the user in the first course. + $enrol = enrol_get_plugin('manual'); + + // We don't use the dataGenerator as we need to get the $instance2 to unenrol later. + $enrolinstances = enrol_get_instances($course1->id, true); + foreach ($enrolinstances as $courseenrolinstance) { + if ($courseenrolinstance->enrol == "manual") { + $instance1 = $courseenrolinstance; + break; + } + } + $enrol->enrol_user($instance1, $user1->id); + + // First hsuforum with tracking off. + $record = new \stdClass(); + $record->course = $course1->id; + $record->trackingtype = HSUFORUM_TRACKING_OFF; + $forum1 = self::getDataGenerator()->create_module('hsuforum', $record); + + // Assign capabilities to view discussions for forum 1. + $cm = get_coursemodule_from_id('hsuforum', $forum1->cmid, 0, false, MUST_EXIST); + $context = \context_module::instance($cm->id); + $newrole = create_role('Role 2', 'role2', 'Role 2 description'); + $this->assignUserCapability('mod/hsuforum:viewdiscussion', $context->id, $newrole); + + // Add discussions to the hsuforums. + $record = new \stdClass(); + $record->course = $course1->id; + $record->userid = $user1->id; + $record->forum = $forum1->id; + $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record); + $clock->bump(); + + // Add three replies to the discussion 1 from different users. + $record = new \stdClass(); + $record->discussion = $discussion1->id; + $record->parent = $discussion1->firstpost; + $record->userid = $user2->id; + $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); + $clock->bump(); + + $record->parent = $discussion1reply1->id; + $record->userid = $user3->id; + $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); + $clock->bump(); + + $record->userid = $user4->id; + $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record); + $clock->bump(); + + // Create discussion2. + $record2 = new \stdClass(); + $record2->course = $course1->id; + $record2->userid = $user1->id; + $record2->forum = $forum1->id; + $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record2); + $clock->bump(); + + // Add one reply to the discussion 2. + $record2 = new \stdClass(); + $record2->discussion = $discussion2->id; + $record2->parent = $discussion2->firstpost; + $record2->userid = $user2->id; + $discussion2reply1 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record2); + $clock->bump(); + + // Create discussion 3. + $record3 = new \stdClass(); + $record3->course = $course1->id; + $record3->userid = $user1->id; + $record3->forum = $forum1->id; + $discussion3 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_discussion($record3); + $clock->bump(); + + // Add two replies to the discussion 3. + $record3 = new \stdClass(); + $record3->discussion = $discussion3->id; + $record3->parent = $discussion3->firstpost; + $record3->userid = $user2->id; + $discussion3reply1 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record3); + $clock->bump(); + + $record3->parent = $discussion3reply1->id; + $record3->userid = $user3->id; + $discussion3reply2 = self::getDataGenerator()->get_plugin_generator('mod_hsuforum')->create_post($record3); + + // Call the external function passing forum id. + $discussions = mod_hsuforum_external::get_forum_discussions($forum1->id); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); + // Discussions should be ordered by last post date in descending order by default. + $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion3->id); + $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion2->id); + $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion1->id); + + $vaultfactory = \mod_hsuforum\local\container::get_vault_factory(); + $discussionlistvault = $vaultfactory->get_discussions_in_forum_vault(); + + // Call the external function passing forum id and sort order parameter. + $discussions = mod_hsuforum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_LASTPOST_ASC); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); + // Discussions should be ordered by last post date in ascending order. + $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion1->id); + $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion2->id); + $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion3->id); + + // Call the external function passing forum id and sort order parameter. + $discussions = mod_hsuforum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_CREATED_DESC); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); + // Discussions should be ordered by discussion creation date in descending order. + $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion3->id); + $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion2->id); + $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion1->id); + + // Call the external function passing forum id and sort order parameter. + $discussions = mod_hsuforum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_CREATED_ASC); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); + // Discussions should be ordered by discussion creation date in ascending order. + $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion1->id); + $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion2->id); + $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion3->id); + + // Call the external function passing forum id and sort order parameter. + $discussions = mod_hsuforum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_REPLIES_DESC); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); + // Discussions should be ordered by the number of replies in descending order. + $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion1->id); + $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion3->id); + $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion2->id); + + // Call the external function passing forum id and sort order parameter. + $discussions = mod_hsuforum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_REPLIES_ASC); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); + // Discussions should be ordered by the number of replies in ascending order. + $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion2->id); + $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion3->id); + $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion1->id); + + // Pin discussion2. + $DB->update_record('hsuforum_discussions', + (object) array('id' => $discussion2->id, 'pinned' => HSUFORUM_DISCUSSION_PINNED)); + + // Call the external function passing forum id. + $discussions = mod_hsuforum_external::get_forum_discussions($forum1->id); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); + // Discussions should be ordered by last post date in descending order by default. + // Pinned discussions should be at the top of the list. + $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion2->id); + $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion3->id); + $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion1->id); + + // Call the external function passing forum id and sort order parameter. + $discussions = mod_hsuforum_external::get_forum_discussions($forum1->id, $discussionlistvault::SORTORDER_LASTPOST_ASC); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); + // Discussions should be ordered by last post date in ascending order. + // Pinned discussions should be at the top of the list. + $this->assertEquals($discussions['discussions'][0]['discussion'], $discussion2->id); + $this->assertEquals($discussions['discussions'][1]['discussion'], $discussion1->id); + $this->assertEquals($discussions['discussions'][2]['discussion'], $discussion3->id); + } + /** * Test add_discussion_post */ @@ -693,7 +1045,7 @@ public function test_add_discussion_post() { try { mod_hsuforum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...'); $this->fail('Exception expected due to being unenrolled from the course.'); - } catch (moodle_exception $e) { + } catch (\core\exception\moodle_exception $e) { $this->assertEquals('requireloginerror', $e->errorcode); } @@ -787,7 +1139,7 @@ public function test_add_discussion_post() { try { mod_hsuforum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...'); $this->fail('Exception expected due to invalid permissions for posting.'); - } catch (moodle_exception $e) { + } catch (\core\exception\moodle_exception $e) { $this->assertEquals('nopostforum', $e->errorcode); } @@ -818,7 +1170,7 @@ public function test_add_discussion() { try { mod_hsuforum_external::add_discussion($forum->id, 'the subject', 'some text here...'); $this->fail('Exception expected due to invalid permissions.'); - } catch (moodle_exception $e) { + } catch (\core\exception\moodle_exception $e) { $this->assertEquals('cannotcreatediscussion', $e->errorcode); } @@ -826,8 +1178,8 @@ public function test_add_discussion() { $createddiscussion = mod_hsuforum_external::add_discussion($forum->id, 'the subject', 'some text here...'); $createddiscussion = external_api::clean_returnvalue(mod_hsuforum_external::add_discussion_returns(), $createddiscussion); - $discussions = mod_hsuforum_external::get_forum_discussions_paginated($forum->id); - $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_paginated_returns(), $discussions); + $discussions = mod_hsuforum_external::get_forum_discussions($forum->id); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); $this->assertCount(1, $discussions['discussions']); $this->assertCount(0, $discussions['warnings']); @@ -841,8 +1193,8 @@ public function test_add_discussion() { array('options' => array('name' => 'discussionpinned', 'value' => true))); $discussion3 = mod_hsuforum_external::add_discussion($forum->id, 'the non pinnedsubject', 'some 3 text here...'); - $discussions = mod_hsuforum_external::get_forum_discussions_paginated($forum->id); - $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_paginated_returns(), $discussions); + $discussions = mod_hsuforum_external::get_forum_discussions($forum->id); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); $this->assertCount(3, $discussions['discussions']); $this->assertEquals($discussion2pinned['discussionid'], $discussions['discussions'][0]['discussion']); @@ -886,8 +1238,8 @@ public function test_add_discussion() { $dummytext, -1, $options); $createddiscussion = external_api::clean_returnvalue(mod_hsuforum_external::add_discussion_returns(), $createddiscussion); - $discussions = mod_hsuforum_external::get_forum_discussions_paginated($forum->id); - $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_paginated_returns(), $discussions); + $discussions = mod_hsuforum_external::get_forum_discussions($forum->id); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); $this->assertCount(4, $discussions['discussions']); $this->assertCount(0, $createddiscussion['warnings']); @@ -950,8 +1302,8 @@ public function test_add_discussion_without_attachment_capability() { $createddiscussion = mod_hsuforum_external::add_discussion($forum->id, 'the attachment subject', 'test content', -1, $options); $createddiscussion = external_api::clean_returnvalue(mod_hsuforum_external::add_discussion_returns(), $createddiscussion); - $discussions = mod_hsuforum_external::get_forum_discussions_paginated($forum->id); - $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_paginated_returns(), $discussions); + $discussions = mod_hsuforum_external::get_forum_discussions($forum->id); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); $this->assertCount(1, $discussions['discussions']); $this->assertCount(0, $createddiscussion['warnings']); @@ -962,7 +1314,7 @@ public function test_add_discussion_without_attachment_capability() { } /** - * Test adding discussions in a course with gorups + * Test adding discussions in a course with groups */ public function test_add_discussion_in_course_with_groups() { global $CFG; @@ -986,14 +1338,14 @@ public function test_add_discussion_in_course_with_groups() { try { mod_hsuforum_external::add_discussion($forum->id, 'the subject', 'some text here...'); $this->fail('Exception expected due to invalid group permissions.'); - } catch (moodle_exception $e) { + } catch (\core\exception\moodle_exception $e) { $this->assertEquals('cannotcreatediscussion', $e->errorcode); } try { mod_hsuforum_external::add_discussion($forum->id, 'the subject', 'some text here...', 0); $this->fail('Exception expected due to invalid group permissions.'); - } catch (moodle_exception $e) { + } catch (\core\exception\moodle_exception $e) { $this->assertEquals('cannotcreatediscussion', $e->errorcode); } @@ -1004,7 +1356,7 @@ public function test_add_discussion_in_course_with_groups() { try { mod_hsuforum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id); $this->fail('Exception expected due to invalid group permissions.'); - } catch (moodle_exception $e) { + } catch (\core\exception\moodle_exception $e) { $this->assertEquals('cannotcreatediscussion', $e->errorcode); } @@ -1015,7 +1367,7 @@ public function test_add_discussion_in_course_with_groups() { try { mod_hsuforum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id + 1); $this->fail('Exception expected due to invalid group.'); - } catch (moodle_exception $e) { + } catch (\core\exception\moodle_exception $e) { $this->assertEquals('cannotcreatediscussion', $e->errorcode); } @@ -1023,8 +1375,8 @@ public function test_add_discussion_in_course_with_groups() { $discussion = mod_hsuforum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id); $discussion = external_api::clean_returnvalue(mod_hsuforum_external::add_discussion_returns(), $discussion); - $discussions = mod_hsuforum_external::get_forum_discussions_paginated($forum->id); - $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_paginated_returns(), $discussions); + $discussions = mod_hsuforum_external::get_forum_discussions($forum->id); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); $this->assertCount(1, $discussions['discussions']); $this->assertCount(0, $discussions['warnings']); @@ -1035,8 +1387,8 @@ public function test_add_discussion_in_course_with_groups() { $discussion = mod_hsuforum_external::add_discussion($forum->id, 'the subject', 'some text here...'); $discussion = external_api::clean_returnvalue(mod_hsuforum_external::add_discussion_returns(), $discussion); - $discussions = mod_hsuforum_external::get_forum_discussions_paginated($forum->id); - $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_paginated_returns(), $discussions); + $discussions = mod_hsuforum_external::get_forum_discussions($forum->id); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); $this->assertCount(2, $discussions['discussions']); $this->assertCount(0, $discussions['warnings']); @@ -1051,8 +1403,8 @@ public function test_add_discussion_in_course_with_groups() { $discussion = mod_hsuforum_external::add_discussion($forum->id, 'the subject', 'some text here...'); $discussion = external_api::clean_returnvalue(mod_hsuforum_external::add_discussion_returns(), $discussion); - $discussions = mod_hsuforum_external::get_forum_discussions_paginated($forum->id); - $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_paginated_returns(), $discussions); + $discussions = mod_hsuforum_external::get_forum_discussions($forum->id); + $discussions = external_api::clean_returnvalue(mod_hsuforum_external::get_forum_discussions_returns(), $discussions); $this->assertCount(3, $discussions['discussions']); $this->assertCount(0, $discussions['warnings']); diff --git a/tests/output_email_test.php b/tests/output_email_test.php index ee90b0cd..958de6d1 100644 --- a/tests/output_email_test.php +++ b/tests/output_email_test.php @@ -30,7 +30,7 @@ * @copyright 2016 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_hsuforum_output_email_testcase extends advanced_testcase { +class output_email_test extends advanced_testcase { /** * Data provider for the postdate function tests. */ @@ -219,7 +219,7 @@ public function test_anonymous_author() { $postemail = new \mod_hsuforum\output\hsuforum_post_email($course, $cm, $forum, $discussion, $post, $anonuser, get_admin(), true); - $anonprofileurl = new moodle_url('/user/view.php', ['id' => $anonuser->id, 'course' => $course->id]); + $anonprofileurl = new \core\url('/user/view.php', ['id' => $anonuser->id, 'course' => $course->id]); $anonprofilepic = $OUTPUT->user_picture($anonuser, ['courseid' => $course->id, 'link' => false]); $renderer = $PAGE->get_renderer('mod_hsuforum'); diff --git a/tests/portfolio_caller_test.php b/tests/portfolio_caller_test.php index f3eb52c1..66fec4ac 100644 --- a/tests/portfolio_caller_test.php +++ b/tests/portfolio_caller_test.php @@ -30,7 +30,7 @@ * * Tests behaviour of the hsuforum_portfolio_caller class. */ -class mod_hsuforum_portfolio_caller_testcase extends advanced_testcase { +class portfolio_caller_test extends advanced_testcase { /** * Ensure that a file will be loaded in an instance of the caller when supplied valid and @@ -53,7 +53,7 @@ public function test_file_in_user_post_is_loaded() { 'course' => $course->id, 'forum' => $forum->id, 'userid' => $user->id, - 'attachment' => 1 + 'attachment' => 1, ) ); @@ -64,13 +64,13 @@ public function test_file_in_user_post_is_loaded() { 'filearea' => 'attachment', 'itemid' => $discussion->firstpost, 'filepath' => '/', - 'filename' => 'myassignmnent.pdf' + 'filename' => 'myassignmnent.pdf', ); $firstpostfile = $fs->create_file_from_string($dummy, 'Content of '.$dummy->filename); $caller = new hsuforum_portfolio_caller(array( 'postid' => $discussion->firstpost, - 'attachment' => $firstpostfile->get_id() + 'attachment' => $firstpostfile->get_id(), )); $caller->load_data(); @@ -98,7 +98,7 @@ public function test_file_not_in_user_post_not_loaded() { 'course' => $course->id, 'forum' => $forum->id, 'userid' => $user->id, - 'attachment' => 1 + 'attachment' => 1, ) ); @@ -109,7 +109,7 @@ public function test_file_not_in_user_post_not_loaded() { 'filearea' => 'attachment', 'itemid' => $discussion->firstpost, 'filepath' => '/', - 'filename' => 'myassignmnent.pdf' + 'filename' => 'myassignmnent.pdf', ); $firstpostfile = $fs->create_file_from_string($dummyone, 'Content of '.$dummyone->filename); @@ -118,7 +118,7 @@ public function test_file_not_in_user_post_not_loaded() { array( 'discussion' => $discussion->id, 'userid' => $user->id, - 'attachment' => 1 + 'attachment' => 1, ) ); $dummytwo = (object) array( @@ -127,13 +127,13 @@ public function test_file_not_in_user_post_not_loaded() { 'filearea' => 'attachment', 'itemid' => $secondpost->id, 'filepath' => '/', - 'filename' => 'myotherthing.pdf' + 'filename' => 'myotherthing.pdf', ); $secondpostfile = $fs->create_file_from_string($dummytwo, 'Content of '.$dummytwo->filename); $caller = new hsuforum_portfolio_caller(array( 'postid' => $discussion->firstpost, - 'attachment' => $secondpostfile->get_id() + 'attachment' => $secondpostfile->get_id(), )); $this->expectExceptionMessage('Sorry, the requested file could not be found'); diff --git a/tests/privacy_provider_test.php b/tests/privacy_provider_test.php index a79303c7..2103cbca 100644 --- a/tests/privacy_provider_test.php +++ b/tests/privacy_provider_test.php @@ -22,6 +22,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace mod_hsuforum\tests; + defined('MOODLE_INTERNAL') || die(); global $CFG; @@ -36,14 +38,14 @@ * @copyright 2018 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_hsuforum_privacy_provider_testcase extends \core_privacy\tests\provider_testcase { +class privacy_provider_test extends \core_privacy\tests\provider_testcase { // Include the privacy helper trait. use \mod_hsuforum\privacy\subcontext_info; // Include the mod_hsuforum test helpers. // This includes functions to create forums, users, discussions, and posts. - use helper_hsuforums; + use \mod_hsuforum\helper; // Include the privacy helper trait for the ratings API. use \core_rating\phpunit\privacy_helper; @@ -167,12 +169,12 @@ public function test_user_has_never_posted_subscribed_to_forum() { list($discussion, $post) = $this->helper_post_to_forum($forum, $otheruser); $cm = get_coursemodule_from_instance('hsuforum', $forum->id); $context = \context_module::instance($cm->id); - $record = new stdClass(); + $record = new \stdClass(); $record->userid = $user->id; $record->forum = $forum->id; $DB->insert_record('hsuforum_subscriptions', $record); - $comment = new stdClass(); + $comment = new \stdClass(); $comment->contextid = $context->id; $comment->component = 'mod_hsuforum'; $comment->commentarea = 'userposts_comments'; @@ -346,8 +348,8 @@ public function test_user_has_rated_others() { $context = \context_module::instance($cm->id); // Rate the other users content. - $rm = new rating_manager(); - $ratingoptions = new stdClass; + $rm = new \rating_manager(); + $ratingoptions = new \stdClass; $ratingoptions->context = $context; $ratingoptions->component = 'mod_hsuforum'; $ratingoptions->ratingarea = 'post'; @@ -416,8 +418,8 @@ public function test_user_has_been_rated() { $context = \context_module::instance($cm->id); // Other users rate my content - $rm = new rating_manager(); - $ratingoptions = new stdClass; + $rm = new \rating_manager(); + $ratingoptions = new \stdClass; $ratingoptions->context = $context; $ratingoptions->component = 'mod_hsuforum'; $ratingoptions->ratingarea = 'post'; @@ -914,7 +916,7 @@ public function test_all_users_deleted_from_context() { // Rate the other users content. if ($post->userid != $user->id) { $ratedposts[$post->id] = $post; - $rm = new rating_manager(); + $rm = new \rating_manager(); $ratingoptions = (object) [ 'context' => $context, 'component' => 'mod_hsuforum', @@ -961,7 +963,7 @@ public function test_all_users_deleted_from_context() { $this->assertEmpty($fs->get_area_files($context->id, 'mod_hsuforum', 'attachment', $post->id)); } // All ratings should have been deleted. - $rm = new rating_manager(); + $rm = new \rating_manager(); foreach ($postsinforum as $post) { $ratings = $rm->get_all_ratings_for_item((object) [ 'context' => $context, @@ -997,7 +999,7 @@ public function test_all_users_deleted_from_context() { } } // Ratings should not have been deleted. - $rm = new rating_manager(); + $rm = new \rating_manager(); foreach ($postsinforum as $post) { if (!isset($ratedposts[$post->id])) { continue; @@ -1105,7 +1107,7 @@ public function test_delete_data_for_user() { // Rate the other users content. if ($post->userid != $user->id) { $ratedposts[$post->id] = $post; - $rm = new rating_manager(); + $rm = new \rating_manager(); $ratingoptions = (object) [ 'context' => $context, 'component' => 'mod_hsuforum', @@ -1301,7 +1303,7 @@ public function test_delete_data_for_users() { // Rate the other users content. if ($post->userid != $user->id) { $ratedposts[$post->id] = $post; - $rm = new rating_manager(); + $rm = new \rating_manager(); $ratingoptions = (object) [ 'context' => $context, 'component' => 'mod_hsuforum', diff --git a/tests/restore_date_test.php b/tests/restore_date_test.php index 2e73760d..c3ad4c47 100644 --- a/tests/restore_date_test.php +++ b/tests/restore_date_test.php @@ -35,7 +35,7 @@ * @copyright 2017 onwards Ankit Agarwal * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_hsuforum_restore_date_testcase extends restore_date_testcase { +class restore_date_test extends restore_date_testcase { /** * Test restore dates. diff --git a/tests/rsslib_test.php b/tests/rsslib_test.php index eaf455fc..cf06552f 100644 --- a/tests/rsslib_test.php +++ b/tests/rsslib_test.php @@ -37,10 +37,10 @@ * @copyright 2018 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_hsuforum_rsslib_testcase extends advanced_testcase { +class rsslib_test extends advanced_testcase { // Include the mod_hsuforum test helpers. // This includes functions to create forums, users, discussions, and posts. - use helper_hsuforums; + use \mod_hsuforum\helper; /** * Ensure that deleted posts are not included. diff --git a/tests/search_test.php b/tests/search_test.php index 6e0baec2..bb1720de 100644 --- a/tests/search_test.php +++ b/tests/search_test.php @@ -38,7 +38,7 @@ * @copyright 2015 David Monllao {@link http://www.davidmonllao.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_hsuforum_search_testcase extends advanced_testcase { +class search_test extends advanced_testcase { /** * @var string Area id @@ -402,7 +402,7 @@ public function test_attach_files() { 'filearea' => 'attachment', 'itemid' => $post->id, 'filepath' => '/', - 'filename' => 'myfile1' + 'filename' => 'myfile1', ); $file1 = $fs->create_file_from_string($filerecord, 'Some contents 1'); $filerecord['filename'] = 'myfile2'; @@ -514,7 +514,7 @@ public function test_posts_get_contexts_to_reindex() { $expected = [ \context_module::instance($forum2->cmid), \context_module::instance($forum3->cmid), - \context_module::instance($forum1->cmid) + \context_module::instance($forum1->cmid), ]; $this->assertEquals($expected, $contexts); } diff --git a/tests/user_autosubscription_test.php b/tests/user_autosubscription_test.php index 6a0f2786..b54c3dbc 100644 --- a/tests/user_autosubscription_test.php +++ b/tests/user_autosubscription_test.php @@ -30,7 +30,7 @@ * @copyright Copyright (c) 2018 Open LMS (https://www.openlms.net) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_hsuforum_user_autosubscription_testcase extends advanced_testcase { +class user_autosubscription_test extends advanced_testcase { public function test_hsuforum_optional_subscription() { global $DB; diff --git a/unsubscribeall.php b/unsubscribeall.php index f233d931..30f70125 100644 --- a/unsubscribeall.php +++ b/unsubscribeall.php @@ -1,5 +1,4 @@ header(); echo $OUTPUT->heading($strunsubscribeall); -if (data_submitted() and $confirm and confirm_sesskey()) { +if (data_submitted() && $confirm && confirm_sesskey()) { $forums = hsuforum_get_optional_subscribed_forums(); foreach($forums as $forum) { @@ -66,7 +65,7 @@ if ($a) { $msg = get_string('unsubscribeallconfirm', 'hsuforum', $a); - echo $OUTPUT->confirm($msg, new moodle_url('unsubscribeall.php', array('confirm'=>1)), $return); + echo $OUTPUT->confirm($msg, new \core\url('unsubscribeall.php', array('confirm'=>1)), $return); echo $OUTPUT->footer(); die; diff --git a/user.php b/user.php index e42a94a1..4b2341d8 100644 --- a/user.php +++ b/user.php @@ -1,5 +1,4 @@ id == $userid); -$url = new moodle_url('/mod/hsuforum/user.php', array('id' => $userid)); +$url = new \core\url('/mod/hsuforum/user.php', array('id' => $userid)); if ($isspecificcourse) { $url->param('course', $courseid); } @@ -71,7 +70,7 @@ if (isguestuser($user)) { // The guest user cannot post, so it is not possible to view any posts. // May as well just bail aggressively here. - throw new \moodle_exception('invaliduserid'); + throw new \core\exception\moodle_exception('invaliduserid'); } // Make sure the user has not been deleted if ($user->deleted) { @@ -170,10 +169,10 @@ $newusernode->make_active(); // Check to see if this is a discussion or a post. if ($mode == 'posts') { - $navbar = $PAGE->navbar->add(get_string('posts', 'hsuforum'), new moodle_url('/mod/hsuforum/user.php', + $navbar = $PAGE->navbar->add(get_string('posts', 'hsuforum'), new \core\url('/mod/hsuforum/user.php', array('id' => $user->id, 'course' => $courseid))); } else { - $navbar = $PAGE->navbar->add(get_string('discussions', 'hsuforum'), new moodle_url('/mod/hsuforum/user.php', + $navbar = $PAGE->navbar->add(get_string('discussions', 'hsuforum'), new \core\url('/mod/hsuforum/user.php', array('id' => $user->id, 'course' => $courseid, 'mode' => 'discussions'))); } } @@ -188,10 +187,10 @@ $usernode->make_active(); // Check to see if this is a discussion or a post. if ($mode == 'posts') { - $navbar = $PAGE->navbar->add(get_string('posts', 'hsuforum'), new moodle_url('/mod/hsuforum/user.php', + $navbar = $PAGE->navbar->add(get_string('posts', 'hsuforum'), new \core\url('/mod/hsuforum/user.php', array('id' => $user->id, 'course' => $courseid))); } else { - $navbar = $PAGE->navbar->add(get_string('discussions', 'hsuforum'), new moodle_url('/mod/hsuforum/user.php', + $navbar = $PAGE->navbar->add(get_string('discussions', 'hsuforum'), new \core\url('/mod/hsuforum/user.php', array('id' => $user->id, 'course' => $courseid, 'mode' => 'discussions'))); } } @@ -207,9 +206,9 @@ // the current uesr doesn't have access to. $notification = get_string('cannotviewusersposts', 'hsuforum'); if ($isspecificcourse) { - $url = new moodle_url('/course/view.php', array('id' => $courseid)); + $url = new \core\url('/course/view.php', array('id' => $courseid)); } else { - $url = new moodle_url('/'); + $url = new \core\url('/'); } navigation_node::override_active_url($url); } @@ -230,7 +229,7 @@ $userheading = array( 'heading' => fullname($user), 'user' => $user, - 'usercontext' => $usercontext + 'usercontext' => $usercontext, ); echo $OUTPUT->context_header($userheading, 2); } @@ -273,8 +272,8 @@ $discussion = $discussions[$post->discussion]; $course = $result->courses[$discussion->course]; - $forumurl = new moodle_url('/mod/hsuforum/view.php', array('id' => $cm->id)); - $discussionurl = new moodle_url('/mod/hsuforum/discuss.php', array('d' => $post->discussion)); + $forumurl = new \core\url('/mod/hsuforum/view.php', array('id' => $cm->id)); + $discussionurl = new \core\url('/mod/hsuforum/discuss.php', array('d' => $post->discussion)); // TODO actually display if the search result has been read, for now just // hide the unread status marker for all results. @@ -305,25 +304,25 @@ $fullsubjects = array(); if (!$isspecificcourse && !$hasparentaccess) { - $fullsubjects[] = html_writer::link(new moodle_url('/course/view.php', array('id' => $course->id)), $courseshortname); - $fullsubjects[] = html_writer::link($forumurl, $forumname); + $fullsubjects[] = \core\output\html_writer::link(new \core\url('/course/view.php', array('id' => $course->id)), $courseshortname); + $fullsubjects[] = \core\output\html_writer::link($forumurl, $forumname); } else { - $fullsubjects[] = html_writer::tag('span', $courseshortname); - $fullsubjects[] = html_writer::tag('span', $forumname); + $fullsubjects[] = \core\output\html_writer::tag('span', $courseshortname); + $fullsubjects[] = \core\output\html_writer::tag('span', $forumname); } if ($forum->type != 'single') { $discussionname = format_string($discussion->name, true, array('context' => $cm->context)); if (!$isspecificcourse && !$hasparentaccess) { - $fullsubjects[] .= html_writer::link($discussionurl, $discussionname); + $fullsubjects[] .= \core\output\html_writer::link($discussionurl, $discussionname); } else { - $fullsubjects[] .= html_writer::tag('span', $discussionname); + $fullsubjects[] .= \core\output\html_writer::tag('span', $discussionname); } if ($post->parent != 0) { $postname = format_string($post->subject, true, array('context' => $cm->context)); if (!$isspecificcourse && !$hasparentaccess) { - $fullsubjects[] .= html_writer::link(new moodle_url('/mod/hsuforum/discuss.php', array('d' => $post->discussion, 'parent' => $post->id)), $postname); + $fullsubjects[] .= \core\output\html_writer::link(new \core\url('/mod/hsuforum/discuss.php', array('d' => $post->discussion, 'parent' => $post->id)), $postname); } else { - $fullsubjects[] .= html_writer::tag('span', $postname); + $fullsubjects[] .= \core\output\html_writer::tag('span', $postname); } } } @@ -332,7 +331,7 @@ // we've added will be lost. $post->subjectnoformat = true; $discussionurl->set_anchor('p'.$post->id); - $fulllink = html_writer::link($discussionurl, get_string("postincontext", "hsuforum")); + $fulllink = \core\output\html_writer::link($discussionurl, get_string("postincontext", "hsuforum")); $commands = array('seeincontext' => $fulllink); $postoutput[] = $renderer->post($cm, $discussion, $post, false, null, $commands); @@ -372,22 +371,22 @@ $usernode->make_active(); // Check to see if this is a discussion or a post. if ($mode == 'posts') { - $navbar = $PAGE->navbar->add(get_string('posts', 'hsuforum'), new moodle_url('/mod/hsuforum/user.php', + $navbar = $PAGE->navbar->add(get_string('posts', 'hsuforum'), new \core\url('/mod/hsuforum/user.php', array('id' => $user->id, 'course' => $courseid))); } else { - $navbar = $PAGE->navbar->add(get_string('discussions', 'hsuforum'), new moodle_url('/mod/hsuforum/user.php', + $navbar = $PAGE->navbar->add(get_string('discussions', 'hsuforum'), new \core\url('/mod/hsuforum/user.php', array('id' => $user->id, 'course' => $courseid, 'mode' => 'discussions'))); } } echo $OUTPUT->header(); -echo html_writer::start_tag('div', array('class' => 'user-content')); +echo \core\output\html_writer::start_tag('div', array('class' => 'user-content')); if ($isspecificcourse) { $userheading = array( 'heading' => fullname($user), 'user' => $user, - 'usercontext' => $usercontext + 'usercontext' => $usercontext, ); echo $OUTPUT->context_header($userheading, 2); } else { @@ -405,5 +404,5 @@ echo $OUTPUT->heading(get_string('noposts', 'hsuforum')); } -echo html_writer::end_tag('div'); +echo \core\output\html_writer::end_tag('div'); echo $OUTPUT->footer(); diff --git a/version.php b/version.php index bcd3f3be..3fbda766 100644 --- a/version.php +++ b/version.php @@ -27,8 +27,8 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2024091700; // The current module version (Date: YYYYMMDDXX) -$plugin->requires = 2023100900; // Requires this Moodle version +$plugin->version = 2025060500; // The current module version (Date: YYYYMMDDXX) +$plugin->requires = 2024100700; // Requires this Moodle version $plugin->component = 'mod_hsuforum'; // Full name of the plugin (used for diagnostics). -$plugin->release = '4.3.4'; +$plugin->release = '4.5.4'; $plugin->maturity = MATURITY_STABLE; diff --git a/view.php b/view.php index aea0b16f..ae055e57 100644 --- a/view.php +++ b/view.php @@ -1,5 +1,4 @@ get_record('hsuforum', array('id' => $f)); $params['f'] = $forum->id; } else { if (!$cm = get_coursemodule_from_id('hsuforum', $id)){ - throw new \moodle_exception('missingparameter'); + throw new \core\exception\moodle_exception('missingparameter'); } $forum = $DB->get_record('hsuforum', array('id' => $cm->instance)); $params['id'] = $cm->id; @@ -59,7 +58,7 @@ $course = $DB->get_record('course', array('id' => $forum->course)); if (empty($cm) && !$cm = get_coursemodule_from_instance("hsuforum", $forum->id, $course->id)) { - throw new \moodle_exception('missingparameter'); + throw new \core\exception\moodle_exception('missingparameter'); } $discussion = false; @@ -69,10 +68,10 @@ $discussion = array_pop($discussions); if (empty($discussion)) { - throw new \moodle_exception('cannotfindfirstpost', 'hsuforum'); + throw new \core\exception\moodle_exception('cannotfindfirstpost', 'hsuforum'); } - redirect(new moodle_url('/mod/hsuforum/discuss.php', array('d' => $discussion->id))); + redirect(new \core\url('/mod/hsuforum/discuss.php', array('d' => $discussion->id))); } // move require_course_login here to use forced language for course @@ -95,9 +94,9 @@ echo ('
      '); // Some capability checks. - $courselink = new moodle_url('/course/view.php', ['id' => $cm->course]); + $courselink = new \core\url('/course/view.php', ['id' => $cm->course]); - if (empty($cm->visible) and !has_capability('moodle/course:viewhiddenactivities', $context)) { + if (empty($cm->visible) && !has_capability('moodle/course:viewhiddenactivities', $context)) { notice(get_string("activityiscurrentlyhidden"), $courselink); }