diff --git a/mod/forum/classes/local/managers/capability.php b/mod/forum/classes/local/managers/capability.php index 8a7096b53cb..0af23db8e60 100644 --- a/mod/forum/classes/local/managers/capability.php +++ b/mod/forum/classes/local/managers/capability.php @@ -444,6 +444,7 @@ class capability { $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': @@ -457,7 +458,7 @@ class capability { break; } - return ($ownpost && $ineditingtime) || has_capability('mod/forum:editanypost', $context, $user); + return ($ownpost && $ineditingtime && !$mailnow) || has_capability('mod/forum:editanypost', $context, $user); } /** @@ -484,8 +485,9 @@ class capability { $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/forum:deleteownpost', $context, $user) || + if (!($ownpost && $ineditingtime && has_capability('mod/forum:deleteownpost', $context, $user) && !$mailnow || has_capability('mod/forum:deleteanypost', $context, $user))) { throw new moodle_exception('cannotdeletepost', 'forum'); diff --git a/mod/forum/classes/post_form.php b/mod/forum/classes/post_form.php index 04f41d002d3..ea1e07b406e 100644 --- a/mod/forum/classes/post_form.php +++ b/mod/forum/classes/post_form.php @@ -151,7 +151,9 @@ class mod_forum_post_form extends moodleform { $mform->addHelpButton('pinned', 'discussionpinned', 'forum'); } - if (empty($post->id) && $manageactivities) { + if (empty($post->id) && ($manageactivities || + ($forum->type == 'qanda' && has_capability('mod/forum:canmailnow', $modcontext))) + ) { $mform->addElement('checkbox', 'mailnow', get_string('mailnow', 'forum')); } diff --git a/mod/forum/db/access.php b/mod/forum/db/access.php index 9facbebffa5..89cc9e46304 100644 --- a/mod/forum/db/access.php +++ b/mod/forum/db/access.php @@ -433,5 +433,17 @@ $capabilities = array( 'manager' => CAP_ALLOW, ], ], + + 'mod/forum:canmailnow' => [ + 'riskbitmask' => RISK_SPAM, + 'captype' => 'write', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => [ + 'student' => CAP_ALLOW, + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ] + ], ); diff --git a/mod/forum/lang/en/forum.php b/mod/forum/lang/en/forum.php index 71074e6976c..b2c2a25d7f8 100644 --- a/mod/forum/lang/en/forum.php +++ b/mod/forum/lang/en/forum.php @@ -299,6 +299,7 @@ $string['forum:addinstance'] = 'Add a new forum'; $string['forum:addnews'] = 'Add announcements'; $string['forum:addquestion'] = 'Add question'; $string['forum:allowforcesubscribe'] = 'Allow force subscribe'; +$string['forum:canmailnow'] = 'Can send mail without delay'; $string['forum:canoverridecutoff'] = 'Post to forums after their cut-off date'; $string['forum:canoverridediscussionlock'] = 'Reply to locked discussions'; $string['forum:cantogglefavourite'] = 'Star discussions'; diff --git a/mod/forum/lib.php b/mod/forum/lib.php index 3db30a3615a..c65149bb647 100644 --- a/mod/forum/lib.php +++ b/mod/forum/lib.php @@ -3470,6 +3470,19 @@ function forum_user_has_posted($forumid, $did, $userid) { } } +/** + * Returns true if user posted with mailnow in given discussion + * @param int $did Discussion id + * @param int $userid User id + * @return bool + */ +function forum_get_user_posted_mailnow(int $did, int $userid): bool { + global $DB; + + $postmailnow = $DB->get_field('forum_posts', 'MAX(mailnow)', ['userid' => $userid, 'discussion' => $did]); + return !empty($postmailnow); +} + /** * Returns creation time of the first user's post in given discussion * @global object $DB @@ -3848,6 +3861,7 @@ function forum_user_can_see_post($forum, $discussion, $post, $user = null, $cm = } if ($forum->type == 'qanda') { + if (has_capability('mod/forum:viewqandawithoutposting', $modcontext, $user->id) || $post->userid == $user->id || (isset($discussion->firstpost) && $discussion->firstpost == $post->id)) { return true; @@ -3856,6 +3870,10 @@ function forum_user_can_see_post($forum, $discussion, $post, $user = null, $cm = if ($firstpost->userid == $user->id) { return true; } + $userpostmailnow = forum_get_user_posted_mailnow($discussion->id, $user->id); + if ($userpostmailnow) { + return true; + } $userfirstpost = forum_get_user_posted_time($discussion->id, $user->id); return (($userfirstpost !== false && (time() - $userfirstpost >= $CFG->maxeditingtime))); } diff --git a/mod/forum/tests/externallib_test.php b/mod/forum/tests/externallib_test.php index b7de590b95e..799fce9a5ea 100644 --- a/mod/forum/tests/externallib_test.php +++ b/mod/forum/tests/externallib_test.php @@ -2163,7 +2163,7 @@ class externallib_test extends externallib_advanced_testcase { // Check default values for capabilities. $enabledcaps = array('canviewdiscussion', 'canstartdiscussion', 'canreplypost', 'canviewrating', 'cancreateattachment', - 'canexportownpost', 'cancantogglefavourite', 'candeleteownpost', 'canallowforcesubscribe'); + 'canexportownpost', 'cancantogglefavourite', 'cancanmailnow', 'candeleteownpost', 'canallowforcesubscribe'); unset($result['warnings']); foreach ($result as $capname => $capvalue) { diff --git a/mod/forum/tests/lib_test.php b/mod/forum/tests/lib_test.php index 7f0cd63f43f..4c2295e2465 100644 --- a/mod/forum/tests/lib_test.php +++ b/mod/forum/tests/lib_test.php @@ -2813,6 +2813,40 @@ class lib_test extends \advanced_testcase { $this->assertTrue(forum_user_has_posted_discussion($forum->id, $author->id, $group2->id)); } + /** + * Test the logic for forum_get_user_posted_mailnow where the user can select if qanda forum post should be sent without delay + * + * @covers ::forum_get_user_posted_mailnow + */ + public function test_forum_get_user_posted_mailnow() { + $this->resetAfterTest(); + + // Create a forum. + $course = $this->getDataGenerator()->create_course(); + $forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]); + $author = $this->getDataGenerator()->create_user(); + $authorid = $author->id; + $generator = $this->getDataGenerator()->get_plugin_generator('mod_forum'); + + // Create a discussion. + $record = new \stdClass(); + $record->course = $forum->course; + $record->forum = $forum->id; + $record->userid = $authorid; + $discussion = $generator->create_discussion($record); + $did = $discussion->id; + + // Return False if no post exists with 'mailnow' selected. + $generator->create_post(['userid' => $authorid, 'discussion' => $did, 'forum' => $forum->id, 'mailnow' => 0]); + $result = forum_get_user_posted_mailnow($did, $authorid); + $this->assertFalse($result); + + // Return True only if any post has 'mailnow' selected. + $generator->create_post(['userid' => $authorid, 'discussion' => $did, 'forum' => $forum->id, 'mailnow' => 1]); + $result = forum_get_user_posted_mailnow($did, $authorid); + $this->assertTrue($result); + } + /** * Tests the mod_forum_myprofile_navigation() function. */ diff --git a/mod/forum/tests/managers_capability_test.php b/mod/forum/tests/managers_capability_test.php index 87a9a0bd4fe..5456c2ce657 100644 --- a/mod/forum/tests/managers_capability_test.php +++ b/mod/forum/tests/managers_capability_test.php @@ -668,6 +668,18 @@ class managers_capability_test extends \advanced_testcase { $CFG->maxeditingtime = 200; $this->assertTrue($capabilitymanager->can_edit_post($user, $discussion, $post)); + // Can not edit within editing time if $post->mailnow > 0 (selected). + $CFG->maxeditingtime = 200; + $post = $this->entityfactory->get_post_from_stdClass( + (object) array_merge((array) $this->postrecord, ['mailnow' => 1]) + ); + $this->assertFalse($capabilitymanager->can_edit_post($user, $discussion, $post)); + + // Back to normal - mailnow not selected. + $post = $this->entityfactory->get_post_from_stdClass( + (object) array_merge((array) $this->postrecord, ['mailnow' => 0]) + ); + // 10 seconds to edit. No longer in editing time. $CFG->maxeditingtime = 10; $this->assertFalse($capabilitymanager->can_edit_post($user, $discussion, $post)); @@ -769,6 +781,17 @@ class managers_capability_test extends \advanced_testcase { // 200 second editing time to make sure our post is still within it. $CFG->maxeditingtime = 200; + // Can not delete within editing time if $post->mailnow > 0 (selected). + $post = $this->entityfactory->get_post_from_stdClass( + (object) array_merge((array) $this->postrecord, ['mailnow' => 1]) + ); + $this->assertFalse($capabilitymanager->can_delete_post($user, $discussion, $post)); + + // Back to normal - mailnow not selected. + $post = $this->entityfactory->get_post_from_stdClass( + (object) array_merge((array) $this->postrecord, ['mailnow' => 0]) + ); + // Make the post owned by someone else. $post = $this->entityfactory->get_post_from_stdClass( (object) array_merge((array) $this->postrecord, ['userid' => $user->id - 1]) diff --git a/mod/forum/version.php b/mod/forum/version.php index c26f8ffc0ff..056b4b9b13f 100644 --- a/mod/forum/version.php +++ b/mod/forum/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2023042400; // The current module version (Date: YYYYMMDDXX). +$plugin->version = 2023042401; // The current module version (Date: YYYYMMDDXX). $plugin->requires = 2023041800; // Requires this Moodle version. $plugin->component = 'mod_forum'; // Full name of the plugin (used for diagnostics)