From 50ae1d6429a90cc94ccb4962502ed556efee2952 Mon Sep 17 00:00:00 2001 From: TamaroWalter Date: Wed, 31 May 2023 17:17:58 +0200 Subject: [PATCH 01/24] change pluginfile function --- lib.php | 24 +++++++++++++++++------- locallib.php | 5 +++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib.php b/lib.php index 5ee67061d7..2d6c62da6f 100644 --- a/lib.php +++ b/lib.php @@ -410,7 +410,7 @@ function moodleoverflow_get_file_info($browser, $areas, $course, $cm, $context, * @param array $options additional options affecting the file serving */ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, array $args, $forcedownload, array $options = array()) { - global $DB; + global $DB, $CFG; if ($context->contextlevel != CONTEXT_MODULE) { return false; @@ -439,22 +439,32 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, array $arg return false; } + $itemid = array_shift($args); + $filename = array_pop($args); + if (!$args) { + // Empty path, use root. + $filepath = '/'; + } else { + // Assemble filepath. + $filepath = '/' . implode('/', $args) . '/'; + } + $fs = get_file_storage(); $relativepath = implode('/', $args); - $fullpath = "/$context->id/mod_moodleoverflow/$filearea/$postid/$relativepath"; + /*$fullpath = "/$context->id/mod_moodleoverflow/$filearea/$postid/$relativepath"; if (!$file = $fs->get_file_by_hash(sha1($fullpath))||$file->is_directory()) { return false; - } - + }*/ + $file = $fs->get_file($context->id, 'mod_moodleoverflow', $filearea, $itemid, $filepath, $filename); // Make sure groups allow this user to see this file. - if ($discussion->groupid > 0) { + /*if ($discussion->groupid > 0) { $groupmode = groups_get_activity_groupmode($cm, $course); if ($groupmode == SEPARATEGROUPS) { if (!groups_is_member($discussion->groupid) && !has_capability('moodle/site:accessallgroups', $context)) { return false; } } - } + }*/ // Make sure we're allowed to see it... if (!moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $cm)) { @@ -462,7 +472,7 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, array $arg } // Finally send the file. - send_stored_file($file, 0, 0, true, $options); // Download MUST be forced - security! + send_stored_file($file, 86400, 0, $forcedownload, $options); // Download MUST be forced - security! } /* Navigation API */ diff --git a/locallib.php b/locallib.php index ef1ec7a7b9..0bc6b1b05e 100644 --- a/locallib.php +++ b/locallib.php @@ -1585,6 +1585,7 @@ function get_attachments($post, $cm) { // We retrieve all files according to the time that they were created. In the case that several files were uploaded // at the sametime (e.g. in the case of drag/drop upload) we revert to using the filename. $files = $fs->get_area_files($context->id, 'mod_moodleoverflow', 'attachment', $post->id, "filename", false); + var_dump($files); if ($files) { $i = 0; foreach ($files as $file) { @@ -1595,8 +1596,8 @@ function get_attachments($post, $cm) { $iconimage = $OUTPUT->pix_icon(file_file_icon($file), get_mimetype_description($file), 'moodle', array('class' => 'icon')); - $path = file_encode_url($CFG->wwwroot . '/pluginfile.php', '/' . - $context->id . '/mod_moodleoverflow/attachment/' . $post->id . '/' . $attachments[$i]['filename']); + $path = moodle_url::make_pluginfile_url($file->get_contextid(), $file->get_component(), $file->get_filearea(), + $file->get_itemid(), $file->get_filepath(), $file->get_filename()); $attachments[$i]['icon'] = $iconimage; $attachments[$i]['filepath'] = $path; From f6022608018dd1380f4885b8880d11ff3f26d9f8 Mon Sep 17 00:00:00 2001 From: TamaroWalter Date: Wed, 31 May 2023 17:49:38 +0200 Subject: [PATCH 02/24] var_dumps added --- lib.php | 25 +++++++++++++++++-------- version.php | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib.php b/lib.php index 2d6c62da6f..29e426a716 100644 --- a/lib.php +++ b/lib.php @@ -411,7 +411,7 @@ function moodleoverflow_get_file_info($browser, $areas, $course, $cm, $context, */ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, array $args, $forcedownload, array $options = array()) { global $DB, $CFG; - + var_dump("if context->contextlever"); if ($context->contextlevel != CONTEXT_MODULE) { return false; } @@ -419,26 +419,28 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, array $arg require_course_login($course, true, $cm); $areas = moodleoverflow_get_file_areas($course, $cm, $context); - + var_dump("if isset areas(filearea)"); // Filearea must contain a real area. if (!isset($areas[$filearea])) { return false; } $postid = (int) array_shift($args); - + var_dump("post = getrecord"); if (!$post = $DB->get_record('moodleoverflow_posts', array('id' => $postid))) { return false; } - + var_dump("discussion = record"); if (!$discussion = $DB->get_record('moodleoverflow_discussions', array('id' => $post->discussion))) { return false; } - + var_dump("moodlevoerflow = record"); if (!$moodleoverflow = $DB->get_record('moodleoverflow', array('id' => $cm->instance))) { return false; } - + var_dump("hier kommen die arsg"); + var_dump($args); + var_dump("jetzt der rest"); $itemid = array_shift($args); $filename = array_pop($args); if (!$args) { @@ -455,16 +457,23 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, array $arg if (!$file = $fs->get_file_by_hash(sha1($fullpath))||$file->is_directory()) { return false; }*/ + var_dump("get file"); $file = $fs->get_file($context->id, 'mod_moodleoverflow', $filearea, $itemid, $filepath, $filename); + var_dump($context->id); + var_dump($filearea); + var_dump($itemid); + var_dump($filepath); + var_dump($filename); // Make sure groups allow this user to see this file. - /*if ($discussion->groupid > 0) { + if ($discussion->groupid > 0) { $groupmode = groups_get_activity_groupmode($cm, $course); if ($groupmode == SEPARATEGROUPS) { + if (!groups_is_member($discussion->groupid) && !has_capability('moodle/site:accessallgroups', $context)) { return false; } } - }*/ + } // Make sure we're allowed to see it... if (!moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $cm)) { diff --git a/version.php b/version.php index b70eabf999..186806e95d 100644 --- a/version.php +++ b/version.php @@ -28,7 +28,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'mod_moodleoverflow'; -$plugin->version = 2023052200; +$plugin->version = 2023053100; $plugin->release = 'v4.2-r1'; $plugin->requires = 2020061500; // Requires Moodle 3.9+. $plugin->maturity = MATURITY_STABLE; From b427894e4f10b52d0f176f7d21afe7ee4e80fd9a Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Fri, 2 Jun 2023 16:35:33 +0200 Subject: [PATCH 03/24] fixed args passing, cleanup needed --- classes/post_form.php | 2 -- lib.php | 35 +++++++++++++---------------------- locallib.php | 1 - 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/classes/post_form.php b/classes/post_form.php index 5d60710001..206a2109a7 100644 --- a/classes/post_form.php +++ b/classes/post_form.php @@ -100,7 +100,6 @@ public function definition() { // Is it a reply? $modform->addElement('hidden', 'reply'); $modform->setType('reply', PARAM_INT); - } /** @@ -142,7 +141,6 @@ public static function attachment_options($moodleoverflow) { 'return_types' => FILE_INTERNAL | FILE_CONTROLLED_LINK ); } - } diff --git a/lib.php b/lib.php index 29e426a716..004622ce7b 100644 --- a/lib.php +++ b/lib.php @@ -409,9 +409,8 @@ function moodleoverflow_get_file_info($browser, $areas, $course, $cm, $context, * @param bool $forcedownload whether or not force download * @param array $options additional options affecting the file serving */ -function moodleoverflow_pluginfile($course, $cm, $context, $filearea, array $args, $forcedownload, array $options = array()) { +function moodleoverflow_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) { global $DB, $CFG; - var_dump("if context->contextlever"); if ($context->contextlevel != CONTEXT_MODULE) { return false; } @@ -425,9 +424,8 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, array $arg return false; } - $postid = (int) array_shift($args); - var_dump("post = getrecord"); - if (!$post = $DB->get_record('moodleoverflow_posts', array('id' => $postid))) { + + /*if (!$post = $DB->get_record('moodleoverflow_posts', array('id' => $postid))) { return false; } var_dump("discussion = record"); @@ -442,6 +440,10 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, array $arg var_dump($args); var_dump("jetzt der rest"); $itemid = array_shift($args); + + */ + + $itemid = array_pop($args); $filename = array_pop($args); if (!$args) { // Empty path, use root. @@ -450,22 +452,11 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, array $arg // Assemble filepath. $filepath = '/' . implode('/', $args) . '/'; } - $fs = get_file_storage(); - $relativepath = implode('/', $args); - /*$fullpath = "/$context->id/mod_moodleoverflow/$filearea/$postid/$relativepath"; - if (!$file = $fs->get_file_by_hash(sha1($fullpath))||$file->is_directory()) { - return false; - }*/ - var_dump("get file"); + $file = $fs->get_file($context->id, 'mod_moodleoverflow', $filearea, $itemid, $filepath, $filename); - var_dump($context->id); - var_dump($filearea); - var_dump($itemid); - var_dump($filepath); - var_dump($filename); // Make sure groups allow this user to see this file. - if ($discussion->groupid > 0) { + /*if ($discussion->groupid > 0) { $groupmode = groups_get_activity_groupmode($cm, $course); if ($groupmode == SEPARATEGROUPS) { @@ -473,15 +464,15 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, array $arg return false; } } - } + }*/ // Make sure we're allowed to see it... - if (!moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $cm)) { + /* if (!moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $cm)) { return false; - } + }*/ // Finally send the file. - send_stored_file($file, 86400, 0, $forcedownload, $options); // Download MUST be forced - security! + send_stored_file($file, 86400, 0, true, $options); // Download MUST be forced - security! } /* Navigation API */ diff --git a/locallib.php b/locallib.php index 0bc6b1b05e..4984b9eeec 100644 --- a/locallib.php +++ b/locallib.php @@ -1585,7 +1585,6 @@ function get_attachments($post, $cm) { // We retrieve all files according to the time that they were created. In the case that several files were uploaded // at the sametime (e.g. in the case of drag/drop upload) we revert to using the filename. $files = $fs->get_area_files($context->id, 'mod_moodleoverflow', 'attachment', $post->id, "filename", false); - var_dump($files); if ($files) { $i = 0; foreach ($files as $file) { From d600cd389683f68c35813dafa58ac1e94c72230f Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Mon, 5 Jun 2023 10:08:34 +0200 Subject: [PATCH 04/24] mino fixup --- classes/privacy/provider.php | 20 ++++++++++---------- lib.php | 6 ++---- locallib.php | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 566ac160ea..b65afab450 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -161,17 +161,17 @@ public static function get_contexts_for_userid(int $userid) : contextlist { )"; $params = [ - 'modname' => 'moodleoverflow', + 'modname' => 'moodleoverflow', 'contextlevel' => CONTEXT_MODULE, - 'duserid' => $userid, - 'dmuserid' => $userid, - 'puserid' => $userid, - 'ruserid' => $userid, - 'suserid' => $userid, - 'dsuserid' => $userid, - 'rauserid' => $userid, - 'tuserid' => $userid, - 'guserid' => $userid + 'duserid' => $userid, + 'dmuserid' => $userid, + 'puserid' => $userid, + 'ruserid' => $userid, + 'suserid' => $userid, + 'dsuserid' => $userid, + 'rauserid' => $userid, + 'tuserid' => $userid, + 'guserid' => $userid ]; $contextlist = new \core_privacy\local\request\contextlist(); diff --git a/lib.php b/lib.php index 004622ce7b..27634f156f 100644 --- a/lib.php +++ b/lib.php @@ -418,13 +418,11 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, $args, $fo require_course_login($course, true, $cm); $areas = moodleoverflow_get_file_areas($course, $cm, $context); - var_dump("if isset areas(filearea)"); // Filearea must contain a real area. if (!isset($areas[$filearea])) { return false; } - /*if (!$post = $DB->get_record('moodleoverflow_posts', array('id' => $postid))) { return false; } @@ -441,7 +439,7 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, $args, $fo var_dump("jetzt der rest"); $itemid = array_shift($args); - */ + */ $itemid = array_pop($args); $filename = array_pop($args); @@ -467,7 +465,7 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, $args, $fo }*/ // Make sure we're allowed to see it... - /* if (!moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $cm)) { + /* if (!moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $cm)) { return false; }*/ diff --git a/locallib.php b/locallib.php index 4984b9eeec..54fa3eeb6c 100644 --- a/locallib.php +++ b/locallib.php @@ -1595,7 +1595,7 @@ function get_attachments($post, $cm) { $iconimage = $OUTPUT->pix_icon(file_file_icon($file), get_mimetype_description($file), 'moodle', array('class' => 'icon')); - $path = moodle_url::make_pluginfile_url($file->get_contextid(), $file->get_component(), $file->get_filearea(), + $path = moodle_url::make_pluginfile_url($file->get_contextid(), $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename()); $attachments[$i]['icon'] = $iconimage; From f23e12c65a74af7ad255df5369620382bfa3bec0 Mon Sep 17 00:00:00 2001 From: TamaroWalter Date: Mon, 5 Jun 2023 11:40:29 +0200 Subject: [PATCH 05/24] delete attachments if post is deleted --- lib.php | 25 ++++++++++--------------- locallib.php | 7 +++++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/lib.php b/lib.php index 27634f156f..5b736a20cf 100644 --- a/lib.php +++ b/lib.php @@ -423,26 +423,20 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, $args, $fo return false; } - /*if (!$post = $DB->get_record('moodleoverflow_posts', array('id' => $postid))) { + $itemid = array_pop($args); + $filename = array_pop($args); + + // Check if post, discussion or moodleoverflow still exists. + if (!$post = $DB->get_record('moodleoverflow_posts', array('id' => $itemid))) { return false; } - var_dump("discussion = record"); if (!$discussion = $DB->get_record('moodleoverflow_discussions', array('id' => $post->discussion))) { return false; } - var_dump("moodlevoerflow = record"); if (!$moodleoverflow = $DB->get_record('moodleoverflow', array('id' => $cm->instance))) { return false; } - var_dump("hier kommen die arsg"); - var_dump($args); - var_dump("jetzt der rest"); - $itemid = array_shift($args); - */ - - $itemid = array_pop($args); - $filename = array_pop($args); if (!$args) { // Empty path, use root. $filepath = '/'; @@ -453,8 +447,9 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, $args, $fo $fs = get_file_storage(); $file = $fs->get_file($context->id, 'mod_moodleoverflow', $filearea, $itemid, $filepath, $filename); + // Make sure groups allow this user to see this file. - /*if ($discussion->groupid > 0) { + if ($discussion->groupid > 0) { $groupmode = groups_get_activity_groupmode($cm, $course); if ($groupmode == SEPARATEGROUPS) { @@ -462,12 +457,12 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, $args, $fo return false; } } - }*/ + } // Make sure we're allowed to see it... - /* if (!moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $cm)) { + if (!moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $cm)) { return false; - }*/ + } // Finally send the file. send_stored_file($file, 86400, 0, true, $options); // Download MUST be forced - security! diff --git a/locallib.php b/locallib.php index 54fa3eeb6c..925aa4ac6c 100644 --- a/locallib.php +++ b/locallib.php @@ -1850,6 +1850,13 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow // Delete the read records. \mod_moodleoverflow\readtracking::moodleoverflow_delete_read_records(-1, $post->id); + // Delete the attachments. + try { + $DB->delete_records('files', array('itemid' => $post->id)); + } catch (Exception $e) { + $e->getMessage(); + } + // Just in case, check for the new last post of the discussion. moodleoverflow_discussion_update_last_post($post->discussion); From 701133f7f3ddeb98aca95670a5246b32140809ae Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Mon, 5 Jun 2023 11:49:31 +0200 Subject: [PATCH 06/24] switched arguments --- lib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib.php b/lib.php index 5b736a20cf..21acd600e9 100644 --- a/lib.php +++ b/lib.php @@ -423,8 +423,8 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, $args, $fo return false; } - $itemid = array_pop($args); $filename = array_pop($args); + $itemid = array_pop($args); // Check if post, discussion or moodleoverflow still exists. if (!$post = $DB->get_record('moodleoverflow_posts', array('id' => $itemid))) { From b1a40d5c39796d080d2a6f372e3cf5b5f3a0e824 Mon Sep 17 00:00:00 2001 From: TamaroWalter Date: Mon, 5 Jun 2023 14:44:04 +0200 Subject: [PATCH 07/24] phpunit test added for deleting posts --- classes/privacy/provider.php | 10 +- locallib.php | 8 +- tests/locallib_test.php | 1 - tests/post_test.php | 172 +++++++++++++++++++++++++++++++++++ 4 files changed, 184 insertions(+), 7 deletions(-) create mode 100644 tests/post_test.php diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index b65afab450..139ec513c4 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -143,9 +143,15 @@ public static function get_contexts_for_userid(int $userid) : contextlist { INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname INNER JOIN {moodleoverflow} mof ON mof.id = cm.instance WHERE EXISTS ( - SELECT 1 FROM {moodleoverflow_discussions} d WHERE d.moodleoverflow = mof.id AND (d.userid = :duserid OR d.usermodified = :dmuserid) + SELECT 1 + FROM {moodleoverflow_discussions} d + WHERE d.moodleoverflow = mof.id AND (d.userid = :duserid OR d.usermodified = :dmuserid) ) OR EXISTS ( - SELECT 1 FROM {moodleoverflow_posts} p WHERE p.discussion IN (SELECT id FROM {moodleoverflow_discussions} WHERE moodleoverflow = mof.id) AND p.userid = :puserid + SELECT 1 + FROM {moodleoverflow_posts} p + WHERE p.discussion IN (SELECT id + FROM {moodleoverflow_discussions} + WHERE moodleoverflow = mof.id) AND p.userid = :puserid ) OR EXISTS ( SELECT 1 FROM {moodleoverflow_read} r WHERE r.moodleoverflowid = mof.id AND r.userid = :ruserid ) OR EXISTS ( diff --git a/locallib.php b/locallib.php index 925aa4ac6c..919b051006 100644 --- a/locallib.php +++ b/locallib.php @@ -1823,10 +1823,10 @@ function moodleoverflow_delete_discussion($discussion, $course, $cm, $moodleover /** * Deletes a single moodleoverflow post. * - * @param object $post The post ID - * @param bool $deletechildren The child posts - * @param object $cm The course module - * @param object $moodleoverflow The moodleoverflow ID + * @param object $post The post + * @param bool $deletechildren The child posts + * @param object $cm The course module + * @param object $moodleoverflow The moodleoverflow * * @return bool Whether the deletion was successful */ diff --git a/tests/locallib_test.php b/tests/locallib_test.php index 33d5ae9c49..6d03e704c2 100644 --- a/tests/locallib_test.php +++ b/tests/locallib_test.php @@ -173,5 +173,4 @@ public function test_moodleoverflow_disallow_subscribe_on_create() { } } - } diff --git a/tests/post_test.php b/tests/post_test.php new file mode 100644 index 0000000000..3b6a17fb30 --- /dev/null +++ b/tests/post_test.php @@ -0,0 +1,172 @@ +. + +/** + * PHP Unit test for post related functions in the locallib. + * + * @package mod_moodleoverflow + * @copyright 2023 Tamaro Walter + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace mod_moodleoverflow; + +defined('MOODLE_INTERNAL') || die(); +require_once(__DIR__ . '/../locallib.php'); + +/** + * PHP Unit test for post related functions in the locallib. + * + * @package mod_moodleoverflow + * @copyright 2023 Tamaro Walter + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class post_test extends \advanced_testcase { + + /** @var \stdClass test course */ + private $course; + + /** @var \stdClass coursemodule */ + private $coursemodule; + + /** @var \stdClass test moodleoverflow */ + private $moodleoverflow; + + /** @var \stdClass test teacher */ + private $teacher; + + /** @var \stdClass a discussion */ + private $discussion; + + /** @var \stdClass a post */ + private $post; + + /** @var \stdClass an attachment */ + private $attachment; + + /** @var \mod_moodleoverflow_generator $generator */ + private $generator; + + + public function setUp(): void { + $this->resetAfterTest(); + $this->helper_course_set_up(); + } + + public function tearDown(): void { + // Clear all caches. + \mod_moodleoverflow\subscriptions::reset_moodleoverflow_cache(); + \mod_moodleoverflow\subscriptions::reset_discussion_cache(); + } + + /** + * Test if a post and its attachment are deleted successfully. + * @covers ::moodleoverflow_delete_post + */ + public function test_moodleoverflow_delete_post() { + global $DB; + + $result = 0; + // The attachment should exist. + if ($DB->get_record('files', array('itemid' => $this->post->id))) { + $result = 1; + } + $this->assertEquals(1, $result); + + // Delete the post from the teacher with its attachment. + moodleoverflow_delete_post($this->post, false, $this->coursemodule, $this->moodleoverflow); + + // Now try to get the attachment. + if (!$DB->get_record('files', array('itemid' => $this->post->id))) { + $result = 2; + } + $this->assertEquals(2, $result); + } + + /** + * Test if a post and its attachment are deleted successfully. + * @covers ::moodleoverflow_delete_discussion + */ + public function test_moodleoverflow_delete_discussion() { + global $DB; + + $result = 0; + // The attachment should exist. + if ($DB->get_record('files', array('itemid' => $this->post->id))) { + $result = 1; + } + $this->assertEquals(1, $result); + + // Delete the post from the teacher with its attachment. + moodleoverflow_delete_discussion($this->discussion[0], $this->course, $this->coursemodule, $this->moodleoverflow); + + // Now try to get the attachment. + if (!$DB->get_record('files', array('itemid' => $this->post->id))) { + $result = 2; + } + $this->assertEquals(2, $result); + } + + /** + * This function creates: + * - a course with a moodleoverflow + * - a new discussion with a post. The post has an attachment. + */ + private function helper_course_set_up() { + global $DB; + // Create a new course with a moodleoverflow forum. + $this->course = $this->getDataGenerator()->create_course(); + $location = array('course' => $this->course->id); + $this->moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $location); + $this->coursemodule = get_coursemodule_from_instance('moodleoverflow', $this->moodleoverflow->id); + + // Create a teacher. + $this->teacher = $this->getDataGenerator()->create_user(array('firstname' => 'Tamaro', 'lastname' => 'Walter')); + $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, 'student'); + + // Create a discussion started from the teacher. + $this->generator = $this->getDataGenerator()->get_plugin_generator('mod_moodleoverflow'); + $this->discussion = $this->generator->post_to_forum($this->moodleoverflow, $this->teacher); + $this->post = $DB->get_record('moodleoverflow_posts', array('id' => $this->discussion[0]->firstpost), '*'); + + // Create an attachment by inserting it directly in the database and update the post record. + $this->attachment = new \stdClass(); + $this->attachment->contenthash = '81a897de6707916841bcafa3fb853377086744ba'; + $this->attachment->pathnamehash = 'bb9fe5ed6ab47359546f7df8858263d9c6814646'; + $modulecontext = \context_module::instance($this->coursemodule->id); + $this->attachment->contextid = $modulecontext->id; + $this->attachment->component = 'mod_moodleoverflow'; + $this->attachment->filearea = 'attachment'; + $this->attachment->itemid = $this->post->id; + $this->attachment->filepath = '/'; + $this->attachment->filename = 'thisfile.png'; + $this->attachment->userid = $this->teacher->id; + $this->attachment->filesize = 129595; + $this->attachment->mimetype = 'image/png'; + $this->attachment->status = 0; + $this->attachment->source = 'thisfile.png'; + $this->attachment->author = $this->teacher->firstname . ' ' . $this->teacher->lastname; + $this->attachment->license = 'unknown'; + $this->attachment->timecreated = $this->post->created; + $this->attachment->timemodified = $this->post->modified; + $this->attachment->sortorder = 0; + $this->attachment->referencefileid = null; + $DB->insert_record('files', $this->attachment); + + $this->post->attachment = 1; + $DB->update_record('moodleoverflow_posts', $this->post); + + } +} From 700beffea48efa26a482d66c4fcc5ec31140ed51 Mon Sep 17 00:00:00 2001 From: TamaroWalter Date: Mon, 19 Jun 2023 14:19:41 +0200 Subject: [PATCH 08/24] deletion of attachments improved --- locallib.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/locallib.php b/locallib.php index 919b051006..9bfd3ac5dc 100644 --- a/locallib.php +++ b/locallib.php @@ -1852,7 +1852,35 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow // Delete the attachments. try { + // First delete the actual files on the disk. + $fs = get_file_storage(); + $context = context_module::instance($cm->id); + $attachments = get_attachments($post, $cm); + + foreach ($attachments as $attachment) { + // Prepare file record object + $fileinfo = array( + 'component' => 'mod_moodleoverflow', // Your component name. + 'filearea' => 'attachment', // Usually = table name + 'itemid' => $post->id, // Usually = ID of row in table + 'contextid' => $context->id, // ID of context + 'filepath' => $attachment['filepath'], // Any path beginning and ending in / + 'filename' => $attachment['filename'] // Any filename + ); + + // Get file + $file = $fs->get_file($fileinfo['contextid'], $fileinfo['component'], $fileinfo['filearea'], + $fileinfo['itemid'], $fileinfo['filepath'], $fileinfo['filename']); + + // Delete it if it exists + if ($file) { + $file->delete(); + } + } + + // Then delete the entry the database. $DB->delete_records('files', array('itemid' => $post->id)); + } catch (Exception $e) { $e->getMessage(); } From 26ef6b11558762a6bf2a92ebe7d18386152c300f Mon Sep 17 00:00:00 2001 From: TamaroWalter Date: Tue, 20 Jun 2023 16:00:00 +0200 Subject: [PATCH 09/24] WIP: deletion of attachments improved --- locallib.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/locallib.php b/locallib.php index 9bfd3ac5dc..bb94b471a3 100644 --- a/locallib.php +++ b/locallib.php @@ -1610,7 +1610,6 @@ function get_attachments($post, $cm) { $i += 1; } } - return $attachments; } @@ -1855,17 +1854,17 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow // First delete the actual files on the disk. $fs = get_file_storage(); $context = context_module::instance($cm->id); - $attachments = get_attachments($post, $cm); + $attachments = $fs->get_area_files($context->id, 'mod_moodleoverflow', 'attachment', $post->id, "filename", false); foreach ($attachments as $attachment) { // Prepare file record object $fileinfo = array( - 'component' => 'mod_moodleoverflow', // Your component name. - 'filearea' => 'attachment', // Usually = table name - 'itemid' => $post->id, // Usually = ID of row in table - 'contextid' => $context->id, // ID of context - 'filepath' => $attachment['filepath'], // Any path beginning and ending in / - 'filename' => $attachment['filename'] // Any filename + 'component' => 'mod_moodleoverflow', // Your component name. + 'filearea' => 'attachment', // Usually = table name + 'itemid' => $post->id, // Usually = ID of row in table + 'contextid' => $context->id, // ID of context + 'filepath' => $attachment->get_filepath(), // Any path beginning and ending in / + 'filename' => $attachment->get_filename() // Any filename ); // Get file From 9b7da9e3cb9c720ca9b6093146c3cdfdba8fcbac Mon Sep 17 00:00:00 2001 From: TamaroWalter Date: Tue, 20 Jun 2023 17:53:28 +0200 Subject: [PATCH 10/24] WIP: deletion of attachments improved --- locallib.php | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/locallib.php b/locallib.php index af7873acd8..3fc9d43c2c 100644 --- a/locallib.php +++ b/locallib.php @@ -1857,20 +1857,9 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow $attachments = $fs->get_area_files($context->id, 'mod_moodleoverflow', 'attachment', $post->id, "filename", false); foreach ($attachments as $attachment) { - // Prepare file record object - $fileinfo = array( - 'component' => 'mod_moodleoverflow', // Your component name. - 'filearea' => 'attachment', // Usually = table name - 'itemid' => $post->id, // Usually = ID of row in table - 'contextid' => $context->id, // ID of context - 'filepath' => $attachment->get_filepath(), // Any path beginning and ending in / - 'filename' => $attachment->get_filename() // Any filename - ); - // Get file - $file = $fs->get_file($fileinfo['contextid'], $fileinfo['component'], $fileinfo['filearea'], - $fileinfo['itemid'], $fileinfo['filepath'], $fileinfo['filename']); - + $file = $fs->get_file($context->id, 'mod_moodleoverflow', 'attachment', $post->id, + $attachment->get_filepath(), $attachment->get_filename()); // Delete it if it exists if ($file) { $file->delete(); From 98378712ffced13a84f5651e9998c947b647f59e Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Thu, 22 Jun 2023 20:02:02 +0200 Subject: [PATCH 11/24] proper exception handling for deleting post --- locallib.php | 85 +++++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/locallib.php b/locallib.php index 3fc9d43c2c..fae7529199 100644 --- a/locallib.php +++ b/locallib.php @@ -1833,24 +1833,28 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow global $DB, $USER; // Iterate through all children and delete them. - $childposts = $DB->get_records('moodleoverflow_posts', array('parent' => $post->id)); - if ($deletechildren && $childposts) { - foreach ($childposts as $childpost) { - moodleoverflow_delete_post($childpost, true, $cm, $moodleoverflow); + // In case something does not work we throw the error as it should be known that something went ... terribly wrong. + // All DB transactions are rolled back. + try { + $transaction = $DB->start_delegated_transaction(); + + $childposts = $DB->get_records('moodleoverflow_posts', array('parent' => $post->id)); + if ($deletechildren && $childposts) { + foreach ($childposts as $childpost) { + moodleoverflow_delete_post($childpost, true, $cm, $moodleoverflow); + } } - } - // Delete the ratings. - if ($DB->delete_records('moodleoverflow_ratings', array('postid' => $post->id))) { + // Delete the ratings. + if ($DB->delete_records('moodleoverflow_ratings', array('postid' => $post->id))) { - // Delete the post. - if ($DB->delete_records('moodleoverflow_posts', array('id' => $post->id))) { + // Delete the post. + if ($DB->delete_records('moodleoverflow_posts', array('id' => $post->id))) { - // Delete the read records. - \mod_moodleoverflow\readtracking::moodleoverflow_delete_read_records(-1, $post->id); + // Delete the read records. + \mod_moodleoverflow\readtracking::moodleoverflow_delete_read_records(-1, $post->id); - // Delete the attachments. - try { + // Delete the attachments. // First delete the actual files on the disk. $fs = get_file_storage(); $context = context_module::instance($cm->id); @@ -1859,44 +1863,43 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow foreach ($attachments as $attachment) { // Get file $file = $fs->get_file($context->id, 'mod_moodleoverflow', 'attachment', $post->id, - $attachment->get_filepath(), $attachment->get_filename()); + $attachment->get_filepath(), $attachment->get_filename()); + // Delete it if it exists if ($file) { $file->delete(); } } - // Then delete the entry the database. - $DB->delete_records('files', array('itemid' => $post->id)); - } catch (Exception $e) { - $e->getMessage(); - } + // Just in case, check for the new last post of the discussion. + moodleoverflow_discussion_update_last_post($post->discussion); - // Just in case, check for the new last post of the discussion. - moodleoverflow_discussion_update_last_post($post->discussion); - - // Get the context module. - $modulecontext = context_module::instance($cm->id); - - // Trigger the post deletion event. - $params = array( - 'context' => $modulecontext, - 'objectid' => $post->id, - 'other' => array( - 'discussionid' => $post->discussion, - 'moodleoverflowid' => $moodleoverflow->id - ) - ); - if ($post->userid !== $USER->id) { - $params['relateduserid'] = $post->userid; - } - $event = \mod_moodleoverflow\event\post_deleted::create($params); - $event->trigger(); + // Get the context module. + $modulecontext = context_module::instance($cm->id); + + // Trigger the post deletion event. + $params = array( + 'context' => $modulecontext, + 'objectid' => $post->id, + 'other' => array( + 'discussionid' => $post->discussion, + 'moodleoverflowid' => $moodleoverflow->id + ) + ); + if ($post->userid !== $USER->id) { + $params['relateduserid'] = $post->userid; + } + $event = \mod_moodleoverflow\event\post_deleted::create($params); + $event->trigger(); - // The post has been deleted. - return true; + // The post has been deleted. + $transaction->allow_commit(); + return true; + } } + } catch (Exception $e) { + $transaction->rollback($e); } // Deleting the post failed. From f1b6007ffb4b1f7b2b8304b56e1d1fee72e4fee8 Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Thu, 22 Jun 2023 20:06:20 +0200 Subject: [PATCH 12/24] codechecker --- locallib.php | 1 - 1 file changed, 1 deletion(-) diff --git a/locallib.php b/locallib.php index fae7529199..256d76ce0e 100644 --- a/locallib.php +++ b/locallib.php @@ -1871,7 +1871,6 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow } } - // Just in case, check for the new last post of the discussion. moodleoverflow_discussion_update_last_post($post->discussion); From 20e9cb526f0213f393c598f4d1f1e504b3655e33 Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Thu, 22 Jun 2023 21:16:14 +0200 Subject: [PATCH 13/24] restucturing ratings_test --- lib.php | 1 - tests/ratings_test.php | 74 +++++++++++++++--------------------------- 2 files changed, 27 insertions(+), 48 deletions(-) diff --git a/lib.php b/lib.php index 21acd600e9..7651957cd6 100644 --- a/lib.php +++ b/lib.php @@ -414,7 +414,6 @@ function moodleoverflow_pluginfile($course, $cm, $context, $filearea, $args, $fo if ($context->contextlevel != CONTEXT_MODULE) { return false; } - require_course_login($course, true, $cm); $areas = moodleoverflow_get_file_areas($course, $cm, $context); diff --git a/tests/ratings_test.php b/tests/ratings_test.php index c7fb701f6e..a74de7e581 100644 --- a/tests/ratings_test.php +++ b/tests/ratings_test.php @@ -194,62 +194,29 @@ public function test_answersorting_twogroups() { // Test case 1: helpful and solved post, only solved posts. $group1 = 'sh'; $group2 = 's'; - $this->create_twogroups($group1, $group2); - $posts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6); - $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts); - $rightorderposts = array($this->post, $this->answer2, $this->answer1, $this->answer3, - $this->answer6, $this->answer5, $this->answer4); - $result = $this->postsorderequal($sortedposts, $rightorderposts); - $this->assertEquals(1, $result); + $this->process_groups($group1, $group2); // Test case 2: helpful and solved post, only helpful posts. - $group1 = 'sh'; $group2 = 'h'; - $this->create_twogroups($group1, $group2); - $posts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6); - $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts); - $rightorderposts = array($this->post, $this->answer2, $this->answer1, $this->answer3, - $this->answer6, $this->answer5, $this->answer4); - $result = $this->postsorderequal($sortedposts, $rightorderposts); - $this->assertEquals(1, $result); + $this->process_groups($group1, $group2); // Test case 3: helpful and solved post, not-marked posts. - $group1 = 'sh'; $group2 = 'o'; - $this->create_twogroups($group1, $group2); - $posts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6); - $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts); - $rightorderposts = array($this->post, $this->answer2, $this->answer1, $this->answer3, - $this->answer6, $this->answer5, $this->answer4); - $result = $this->postsorderequal($sortedposts, $rightorderposts); - $this->assertEquals(1, $result); + $this->process_groups($group1, $group2); // Test case 4: only solved posts and only helpful posts with ratingpreferences = 0. $group1 = 's'; $group2 = 'h'; $this->set_ratingpreferences(0); - $this->create_twogroups($group1, $group2); - $posts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6); - $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts); $rightorderposts = array($this->post, $this->answer6, $this->answer5, $this->answer4, - $this->answer2, $this->answer1, $this->answer3); - $result = $this->postsorderequal($sortedposts, $rightorderposts); - $this->assertEquals(1, $result); + $this->answer2, $this->answer1, $this->answer3); + $this->process_groups($group1, $group2, $rightorderposts); // Test case 5: only solved posts and only helpful posts with ratingpreferences = 1. - $group1 = 's'; - $group2 = 'h'; $this->set_ratingpreferences(1); - $this->create_twogroups($group1, $group2); - $posts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6); - $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts); - $rightorderposts = array($this->post, $this->answer2, $this->answer1, $this->answer3, - $this->answer6, $this->answer5, $this->answer4); - $result = $this->postsorderequal($sortedposts, $rightorderposts); - $this->assertEquals(1, $result); + $this->process_groups($group1, $group2); // Test case 6: only solved posts and not-marked posts. - $group1 = 's'; $group2 = 'o'; $this->create_twogroups($group1, $group2); $posts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6); @@ -261,14 +228,7 @@ public function test_answersorting_twogroups() { // Test case 6: only helpful posts and not-marked posts. $group1 = 'h'; - $group2 = 'o'; - $this->create_twogroups($group1, $group2); - $posts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6); - $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts); - $rightorderposts = array($this->post, $this->answer2, $this->answer1, $this->answer3, - $this->answer6, $this->answer5, $this->answer4); - $result = $this->postsorderequal($sortedposts, $rightorderposts); - $this->assertEquals(1, $result); + $this->process_groups($group1, $group2); } /** @@ -651,4 +611,24 @@ private function create_twogroups($group1, $group2) { // Rightorder (h,o) = answer2, answer1, answer3, answer6, answer5, answer4. } + + /** + * Executing the sort function and comparing the sorted post to the expected order. + * @param String $group1 + * @param string $group2 + * @param array|null $orderposts + * @return void + */ + private function process_groups(String $group1, string $group2, array $orderposts = null) { + $this->create_twogroups($group1, $group2); + $posts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6); + $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts); + $rightorderposts = array($this->post, $this->answer2, $this->answer1, $this->answer3, + $this->answer6, $this->answer5, $this->answer4); + if ($orderposts) { + $rightorderposts = $orderposts; + } + $result = $this->postsorderequal($sortedposts, $rightorderposts); + $this->assertEquals(1, $result); + } } From d9fe9b451074f6d58eeb2eecb36a3497b477be6f Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Thu, 22 Jun 2023 21:35:50 +0200 Subject: [PATCH 14/24] shorten review test, WIP delete post test fails works in practice --- locallib.php | 36 ++++----- tests/review_test.php | 169 +++++++++++++++++------------------------- 2 files changed, 89 insertions(+), 116 deletions(-) diff --git a/locallib.php b/locallib.php index 256d76ce0e..9a250f1799 100644 --- a/locallib.php +++ b/locallib.php @@ -27,6 +27,8 @@ use mod_moodleoverflow\anonymous; use mod_moodleoverflow\capabilities; +use mod_moodleoverflow\event\post_deleted; +use mod_moodleoverflow\readtracking; use mod_moodleoverflow\review; defined('MOODLE_INTERNAL') || die(); @@ -178,8 +180,8 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = - $replies = moodleoverflow_count_discussion_replies($cm); // Check whether the moodleoverflow instance can be tracked and is tracked. - if ($cantrack = \mod_moodleoverflow\readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow)) { - $istracked = \mod_moodleoverflow\readtracking::moodleoverflow_is_tracked($moodleoverflow); + if ($cantrack = readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow)) { + $istracked = readtracking::moodleoverflow_is_tracked($moodleoverflow); } else { $istracked = false; } @@ -849,10 +851,10 @@ function moodleoverflow_add_discussion($discussion, $modulecontext, $userid = nu moodleoverflow_add_attachment($post, $moodleoverflow, $cm); // Mark the created post as read. - $cantrack = \mod_moodleoverflow\readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow); - $istracked = \mod_moodleoverflow\readtracking::moodleoverflow_is_tracked($moodleoverflow); + $cantrack = readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow); + $istracked = readtracking::moodleoverflow_is_tracked($moodleoverflow); if ($cantrack && $istracked) { - \mod_moodleoverflow\readtracking::moodleoverflow_mark_post_read($post->userid, $post); + readtracking::moodleoverflow_mark_post_read($post->userid, $post); } // Trigger event. @@ -932,7 +934,7 @@ function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discuss $modulecontext = context_module::instance($cm->id); // Is the forum tracked? - $istracked = \mod_moodleoverflow\readtracking::moodleoverflow_is_tracked($moodleoverflow); + $istracked = readtracking::moodleoverflow_is_tracked($moodleoverflow); // Retrieve all posts of the discussion. $posts = moodleoverflow_get_all_discussion_posts($discussion->id, $istracked, $modulecontext); @@ -1072,7 +1074,7 @@ function moodleoverflow_get_all_discussion_posts($discussionid, $tracking, $modc // Is it an old post? if ($tracking) { - if (\mod_moodleoverflow\readtracking::moodleoverflow_is_old_post($post)) { + if (readtracking::moodleoverflow_is_old_post($post)) { $posts[$postid]->postread = true; } } @@ -1471,7 +1473,7 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co // Mark the forum post as read. if ($istracked && !$postisread) { - \mod_moodleoverflow\readtracking::moodleoverflow_mark_post_read($USER->id, $post); + readtracking::moodleoverflow_mark_post_read($USER->id, $post); } $mustachedata->iscomment = $level == 2; @@ -1684,10 +1686,10 @@ function moodleoverflow_add_new_post($post) { } // Mark the created post as read if the user is tracking the discussion. - $cantrack = \mod_moodleoverflow\readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow); - $istracked = \mod_moodleoverflow\readtracking::moodleoverflow_is_tracked($moodleoverflow); + $cantrack = readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow); + $istracked = readtracking::moodleoverflow_is_tracked($moodleoverflow); if ($cantrack && $istracked) { - \mod_moodleoverflow\readtracking::moodleoverflow_mark_post_read($post->userid, $post); + readtracking::moodleoverflow_mark_post_read($post->userid, $post); } // Return the id of the created post. @@ -1744,10 +1746,10 @@ function moodleoverflow_update_post($newpost) { moodleoverflow_add_attachment($newpost, $moodleoverflow, $cm); // Mark the edited post as read. - $cantrack = \mod_moodleoverflow\readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow); - $istracked = \mod_moodleoverflow\readtracking::moodleoverflow_is_tracked($moodleoverflow); + $cantrack = readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow); + $istracked = readtracking::moodleoverflow_is_tracked($moodleoverflow); if ($cantrack && $istracked) { - \mod_moodleoverflow\readtracking::moodleoverflow_mark_post_read($USER->id, $post); + readtracking::moodleoverflow_mark_post_read($USER->id, $post); } // The post has been edited successfully. @@ -1807,7 +1809,7 @@ function moodleoverflow_delete_discussion($discussion, $course, $cm, $moodleover } // Delete the read-records for the discussion. - \mod_moodleoverflow\readtracking::moodleoverflow_delete_read_records(-1, -1, $discussion->id); + readtracking::moodleoverflow_delete_read_records(-1, -1, $discussion->id); // Remove the subscriptions for this discussion. $DB->delete_records('moodleoverflow_discuss_subs', array('discussion' => $discussion->id)); @@ -1852,7 +1854,7 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow if ($DB->delete_records('moodleoverflow_posts', array('id' => $post->id))) { // Delete the read records. - \mod_moodleoverflow\readtracking::moodleoverflow_delete_read_records(-1, $post->id); + readtracking::moodleoverflow_delete_read_records(-1, $post->id); // Delete the attachments. // First delete the actual files on the disk. @@ -1889,7 +1891,7 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow if ($post->userid !== $USER->id) { $params['relateduserid'] = $post->userid; } - $event = \mod_moodleoverflow\event\post_deleted::create($params); + $event = post_deleted::create($params); $event->trigger(); // The post has been deleted. diff --git a/tests/review_test.php b/tests/review_test.php index 29aae0090a..e860974dbe 100644 --- a/tests/review_test.php +++ b/tests/review_test.php @@ -114,57 +114,15 @@ public function test_forum_review_everything() { $options = array('course' => $this->course->id, 'needsreview' => review::EVERYTHING, 'forcesubscribe' => MOODLEOVERFLOW_FORCESUBSCRIBE); - $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); - - list(, $teacherpost) = $this->generator->post_to_forum($moodleoverflow, $this->teacher); - list(, $studentpost) = $this->generator->post_to_forum($moodleoverflow, $this->student); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 0, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_REVIEW_SUCCESS, 'reviewed' => 0, - 'timereviewed' => null], $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); - - $this->assertEquals(1, $this->mailsink->count()); // Teacher has to approve student message. - $this->assertEquals(2, $this->messagesink->count()); // Student and teacher get notification for student message. - - $this->mailsink->clear(); - $this->messagesink->clear(); - - $this->assertNull(\mod_moodleoverflow_external::review_approve_post($studentpost->id)); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. - - $post = $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id]); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1], $post); - $this->assertNotNull($post->timereviewed ?? null); - - $this->assertEquals(0, $this->mailsink->count()); - $this->assertEquals(2, $this->messagesink->count()); - - $this->messagesink->clear(); - - $studentanswer1 = $this->generator->reply_to_post($teacherpost, $this->student, false); - $studentanswer2 = $this->generator->reply_to_post($teacherpost, $this->student, false); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. + $studentanswers = $this->start_review_process($options); $this->assertEquals(2, $this->mailsink->count()); $this->assertEquals(0, $this->messagesink->count()); $this->mailsink->clear(); - $this->assertNotNull(\mod_moodleoverflow_external::review_approve_post($studentanswer1->id)); - $this->assertNull(\mod_moodleoverflow_external::review_reject_post($studentanswer2->id, 'This post was not good!')); + $this->assertNotNull(\mod_moodleoverflow_external::review_approve_post($studentanswers[0]->id)); + $this->assertNull(\mod_moodleoverflow_external::review_reject_post($studentanswers[1]->id, 'This post was not good!')); $this->run_send_mails(); $this->run_send_mails(); // Execute twice to ensure no duplicate mails. @@ -179,7 +137,7 @@ public function test_forum_review_everything() { $this->assertEquals($this->student->email, $rejectionmessage->to); // Check post was deleted. - $this->assertEquals(0, $DB->count_records('moodleoverflow_posts', ['id' => $studentanswer2->id])); + $this->assertEquals(0, $DB->count_records('moodleoverflow_posts', ['id' => $studentanswers[1]->id])); } /** @@ -193,63 +151,15 @@ public function test_forum_review_only_questions() { $options = array('course' => $this->course->id, 'needsreview' => review::QUESTIONS, 'forcesubscribe' => MOODLEOVERFLOW_FORCESUBSCRIBE); - $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); - - list(, $teacherpost) = $this->generator->post_to_forum($moodleoverflow, $this->teacher); - list(, $studentpost) = $this->generator->post_to_forum($moodleoverflow, $this->student); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 0, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_REVIEW_SUCCESS, 'reviewed' => 0, - 'timereviewed' => null], $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); - - $this->assertEquals(1, $this->mailsink->count()); // Teacher has to approve student message. - $this->assertEquals(2, $this->messagesink->count()); // Student and teacher get notification for student message. - - $this->mailsink->clear(); - $this->messagesink->clear(); - - $this->assertNull(\mod_moodleoverflow_external::review_approve_post($studentpost->id)); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. - - $post = $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id]); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1], $post); - $this->assertNotNull($post->timereviewed ?? null); - - $this->assertEquals(0, $this->mailsink->count()); - $this->assertEquals(2, $this->messagesink->count()); - - $this->messagesink->clear(); - - $studentanswer1 = $this->generator->reply_to_post($teacherpost, $this->student, false); - $studentanswer2 = $this->generator->reply_to_post($teacherpost, $this->student, false); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer1->id])); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer2->id])); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. + $studentanswers = $this->start_review_process($options); $this->assertEquals(0, $this->mailsink->count()); $this->assertEquals(4, $this->messagesink->count()); $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer1->id])); + $DB->get_record('moodleoverflow_posts', ['id' => $studentanswers[0]->id])); $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer2->id])); + $DB->get_record('moodleoverflow_posts', ['id' => $studentanswers[1]->id])); } /** @@ -259,10 +169,10 @@ public function test_forum_review_disallowed() { global $DB; $options = array('course' => $this->course->id, 'needsreview' => review::EVERYTHING, 'forcesubscribe' => MOODLEOVERFLOW_FORCESUBSCRIBE); - $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); - set_config('allowreview', 0, 'moodleoverflow'); + $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); + list(, $teacherpost) = $this->generator->post_to_forum($moodleoverflow, $this->teacher); list(, $studentpost) = $this->generator->post_to_forum($moodleoverflow, $this->student); @@ -332,4 +242,65 @@ private function assert_matches_properties($expected, $actual) { $this->assertEquals($value, $actual->$key, "Failed asserting that \$obj->$key '" . $actual->$key . "' equals '$value'"); } } + + /** + * Processing the review process and inserting feedback. + * @param array $options + * @return array + * @throws \coding_exception + * @throws \dml_exception + */ + private function start_review_process($options) { + global $DB; + $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); + + list(, $teacherpost) = $this->generator->post_to_forum($moodleoverflow, $this->teacher); + list(, $studentpost) = $this->generator->post_to_forum($moodleoverflow, $this->student); + + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 0, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); + + $this->run_send_mails(); + $this->run_send_mails(); // Execute twice to ensure no duplicate mails. + + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_REVIEW_SUCCESS, 'reviewed' => 0, + 'timereviewed' => null], $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); + + $this->assertEquals(1, $this->mailsink->count()); // Teacher has to approve student message. + $this->assertEquals(2, $this->messagesink->count()); // Student and teacher get notification for student message. + + $this->mailsink->clear(); + $this->messagesink->clear(); + + $this->assertNull(\mod_moodleoverflow_external::review_approve_post($studentpost->id)); + + $this->run_send_mails(); + $this->run_send_mails(); // Execute twice to ensure no duplicate mails. + + $post = $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id]); + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1], $post); + $this->assertNotNull($post->timereviewed ?? null); + + $this->assertEquals(0, $this->mailsink->count()); + $this->assertEquals(2, $this->messagesink->count()); + + $this->messagesink->clear(); + + $studentanswer1 = $this->generator->reply_to_post($teacherpost, $this->student, false); + $studentanswer2 = $this->generator->reply_to_post($teacherpost, $this->student, false); + + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer1->id])); + + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer2->id])); + + $this->run_send_mails(); + $this->run_send_mails(); // Execute twice to ensure no duplicate mails. + return array($studentanswer1, $studentanswer2); + } } From 336dd3c5accb2cc6181eacf0bd004cf8ccc16d9d Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Thu, 22 Jun 2023 21:41:04 +0200 Subject: [PATCH 15/24] do not make deletion of rating a if condition --- locallib.php | 98 ++++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/locallib.php b/locallib.php index 9a250f1799..840e8c787c 100644 --- a/locallib.php +++ b/locallib.php @@ -39,8 +39,8 @@ * Get all discussions in a moodleoverflow instance. * * @param object $cm - * @param int $page - * @param int $perpage + * @param int $page + * @param int $perpage * * @return array */ @@ -290,7 +290,7 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = - // Check if a single post was marked by the question owner and a teacher. $statusboth = false; - if ($markedhelpful && $markedsolution) { + if ($markedhelpful && $markedsolution) { if ($markedhelpful->postid == $markedsolution->postid) { $statusboth = true; } @@ -1472,7 +1472,7 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co $mustachedata->footer = $footer; // Mark the forum post as read. - if ($istracked && !$postisread) { + if ($istracked && !$postisread) { readtracking::moodleoverflow_mark_post_read($USER->id, $post); } @@ -1688,7 +1688,7 @@ function moodleoverflow_add_new_post($post) { // Mark the created post as read if the user is tracking the discussion. $cantrack = readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow); $istracked = readtracking::moodleoverflow_is_tracked($moodleoverflow); - if ($cantrack && $istracked) { + if ($cantrack && $istracked) { readtracking::moodleoverflow_mark_post_read($post->userid, $post); } @@ -1748,7 +1748,7 @@ function moodleoverflow_update_post($newpost) { // Mark the edited post as read. $cantrack = readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow); $istracked = readtracking::moodleoverflow_is_tracked($moodleoverflow); - if ($cantrack && $istracked) { + if ($cantrack && $istracked) { readtracking::moodleoverflow_mark_post_read($USER->id, $post); } @@ -1848,56 +1848,56 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow } // Delete the ratings. - if ($DB->delete_records('moodleoverflow_ratings', array('postid' => $post->id))) { + $DB->delete_records('moodleoverflow_ratings', array('postid' => $post->id)); - // Delete the post. - if ($DB->delete_records('moodleoverflow_posts', array('id' => $post->id))) { + // Delete the post. + if ($DB->delete_records('moodleoverflow_posts', array('id' => $post->id))) { - // Delete the read records. - readtracking::moodleoverflow_delete_read_records(-1, $post->id); + // Delete the read records. + readtracking::moodleoverflow_delete_read_records(-1, $post->id); - // Delete the attachments. - // First delete the actual files on the disk. - $fs = get_file_storage(); - $context = context_module::instance($cm->id); - $attachments = $fs->get_area_files($context->id, 'mod_moodleoverflow', 'attachment', $post->id, "filename", false); + // Delete the attachments. + // First delete the actual files on the disk. + $fs = get_file_storage(); + $context = context_module::instance($cm->id); + $attachments = $fs->get_area_files($context->id, 'mod_moodleoverflow', 'attachment', + $post->id, "filename", false); - foreach ($attachments as $attachment) { - // Get file - $file = $fs->get_file($context->id, 'mod_moodleoverflow', 'attachment', $post->id, - $attachment->get_filepath(), $attachment->get_filename()); + foreach ($attachments as $attachment) { + // Get file + $file = $fs->get_file($context->id, 'mod_moodleoverflow', 'attachment', $post->id, + $attachment->get_filepath(), $attachment->get_filename()); - // Delete it if it exists - if ($file) { - $file->delete(); - } + // Delete it if it exists + if ($file) { + $file->delete(); } + } - // Just in case, check for the new last post of the discussion. - moodleoverflow_discussion_update_last_post($post->discussion); - - // Get the context module. - $modulecontext = context_module::instance($cm->id); - - // Trigger the post deletion event. - $params = array( - 'context' => $modulecontext, - 'objectid' => $post->id, - 'other' => array( - 'discussionid' => $post->discussion, - 'moodleoverflowid' => $moodleoverflow->id - ) - ); - if ($post->userid !== $USER->id) { - $params['relateduserid'] = $post->userid; - } - $event = post_deleted::create($params); - $event->trigger(); - - // The post has been deleted. - $transaction->allow_commit(); - return true; + // Just in case, check for the new last post of the discussion. + moodleoverflow_discussion_update_last_post($post->discussion); + + // Get the context module. + $modulecontext = context_module::instance($cm->id); + + // Trigger the post deletion event. + $params = array( + 'context' => $modulecontext, + 'objectid' => $post->id, + 'other' => array( + 'discussionid' => $post->discussion, + 'moodleoverflowid' => $moodleoverflow->id + ) + ); + if ($post->userid !== $USER->id) { + $params['relateduserid'] = $post->userid; } + $event = post_deleted::create($params); + $event->trigger(); + + // The post has been deleted. + $transaction->allow_commit(); + return true; } } catch (Exception $e) { $transaction->rollback($e); @@ -2070,7 +2070,7 @@ function moodleoverflow_update_user_grade_on_db($moodleoverflow, $postuserrating if ($DB->record_exists('moodleoverflow_grades', array('userid' => $userid, 'moodleoverflowid' => $moodleoverflow->id))) { $DB->set_field('moodleoverflow_grades', 'grade', $grade, array('userid' => $userid, - 'moodleoverflowid' => $moodleoverflow->id )); + 'moodleoverflowid' => $moodleoverflow->id)); } else { From 4286cac14ec97b111b7e7076f6b3fea071d4d8cd Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Fri, 23 Jun 2023 09:26:22 +0200 Subject: [PATCH 16/24] test without try catch --- locallib.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/locallib.php b/locallib.php index 840e8c787c..9fd5792e6e 100644 --- a/locallib.php +++ b/locallib.php @@ -1837,7 +1837,7 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow // Iterate through all children and delete them. // In case something does not work we throw the error as it should be known that something went ... terribly wrong. // All DB transactions are rolled back. - try { + // try { $transaction = $DB->start_delegated_transaction(); $childposts = $DB->get_records('moodleoverflow_posts', array('parent' => $post->id)); @@ -1899,9 +1899,9 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow $transaction->allow_commit(); return true; } - } catch (Exception $e) { - $transaction->rollback($e); - } + // } catch (Exception $e) { + // $transaction->rollback($e); + // } // Deleting the post failed. return false; From 96ec5c96aa0b76eeb66582d037a436ed7bf09cfe Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Fri, 23 Jun 2023 09:31:24 +0200 Subject: [PATCH 17/24] codechecker... --- locallib.php | 104 +++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/locallib.php b/locallib.php index 9fd5792e6e..bb5cff4eba 100644 --- a/locallib.php +++ b/locallib.php @@ -1837,71 +1837,71 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow // Iterate through all children and delete them. // In case something does not work we throw the error as it should be known that something went ... terribly wrong. // All DB transactions are rolled back. - // try { - $transaction = $DB->start_delegated_transaction(); + // try {. + $transaction = $DB->start_delegated_transaction(); - $childposts = $DB->get_records('moodleoverflow_posts', array('parent' => $post->id)); - if ($deletechildren && $childposts) { - foreach ($childposts as $childpost) { - moodleoverflow_delete_post($childpost, true, $cm, $moodleoverflow); - } + $childposts = $DB->get_records('moodleoverflow_posts', array('parent' => $post->id)); + if ($deletechildren && $childposts) { + foreach ($childposts as $childpost) { + moodleoverflow_delete_post($childpost, true, $cm, $moodleoverflow); } + } - // Delete the ratings. - $DB->delete_records('moodleoverflow_ratings', array('postid' => $post->id)); + // Delete the ratings. + $DB->delete_records('moodleoverflow_ratings', array('postid' => $post->id)); - // Delete the post. - if ($DB->delete_records('moodleoverflow_posts', array('id' => $post->id))) { + // Delete the post. + if ($DB->delete_records('moodleoverflow_posts', array('id' => $post->id))) { - // Delete the read records. - readtracking::moodleoverflow_delete_read_records(-1, $post->id); + // Delete the read records. + readtracking::moodleoverflow_delete_read_records(-1, $post->id); - // Delete the attachments. - // First delete the actual files on the disk. - $fs = get_file_storage(); - $context = context_module::instance($cm->id); - $attachments = $fs->get_area_files($context->id, 'mod_moodleoverflow', 'attachment', - $post->id, "filename", false); + // Delete the attachments. + // First delete the actual files on the disk. + $fs = get_file_storage(); + $context = context_module::instance($cm->id); + $attachments = $fs->get_area_files($context->id, 'mod_moodleoverflow', 'attachment', + $post->id, "filename", false); - foreach ($attachments as $attachment) { - // Get file - $file = $fs->get_file($context->id, 'mod_moodleoverflow', 'attachment', $post->id, - $attachment->get_filepath(), $attachment->get_filename()); + foreach ($attachments as $attachment) { + // Get file + $file = $fs->get_file($context->id, 'mod_moodleoverflow', 'attachment', $post->id, + $attachment->get_filepath(), $attachment->get_filename()); - // Delete it if it exists - if ($file) { - $file->delete(); - } + // Delete it if it exists + if ($file) { + $file->delete(); } + } - // Just in case, check for the new last post of the discussion. - moodleoverflow_discussion_update_last_post($post->discussion); - - // Get the context module. - $modulecontext = context_module::instance($cm->id); - - // Trigger the post deletion event. - $params = array( - 'context' => $modulecontext, - 'objectid' => $post->id, - 'other' => array( - 'discussionid' => $post->discussion, - 'moodleoverflowid' => $moodleoverflow->id - ) - ); - if ($post->userid !== $USER->id) { - $params['relateduserid'] = $post->userid; - } - $event = post_deleted::create($params); - $event->trigger(); + // Just in case, check for the new last post of the discussion. + moodleoverflow_discussion_update_last_post($post->discussion); - // The post has been deleted. - $transaction->allow_commit(); - return true; + // Get the context module. + $modulecontext = context_module::instance($cm->id); + + // Trigger the post deletion event. + $params = array( + 'context' => $modulecontext, + 'objectid' => $post->id, + 'other' => array( + 'discussionid' => $post->discussion, + 'moodleoverflowid' => $moodleoverflow->id + ) + ); + if ($post->userid !== $USER->id) { + $params['relateduserid'] = $post->userid; } - // } catch (Exception $e) { + $event = post_deleted::create($params); + $event->trigger(); + + // The post has been deleted. + $transaction->allow_commit(); + return true; + } + // } catch (Exception $e) {. // $transaction->rollback($e); - // } + // }. // Deleting the post failed. return false; From e2752ad3ef9291a6d262309f8e20e1538f92115d Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Fri, 23 Jun 2023 09:33:20 +0200 Subject: [PATCH 18/24] smalelr testing environment --- .github/workflows/moodle-ci.yml | 25 +++---------------------- locallib.php | 2 +- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/.github/workflows/moodle-ci.yml b/.github/workflows/moodle-ci.yml index c020ab22d4..2ce739ff6a 100644 --- a/.github/workflows/moodle-ci.yml +++ b/.github/workflows/moodle-ci.yml @@ -110,28 +110,9 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.0', '8.1'] - moodle-branch: ['MOODLE_401_STABLE', 'MOODLE_402_STABLE'] - database: ['mariadb', 'pgsql'] - include: - - php: '7.4' - moodle-branch: 'MOODLE_39_STABLE' - database: 'mariadb' - - php: '7.4' - moodle-branch: 'MOODLE_39_STABLE' - database: 'pgsql' - - php: '8.0' - moodle-branch: 'MOODLE_311_STABLE' - database: 'mariadb' - - php: '8.0' - moodle-branch: 'MOODLE_311_STABLE' - database: 'pgsql' - - php: '8.0' - moodle-branch: 'MOODLE_400_STABLE' - database: 'mariadb' - - php: '8.0' - moodle-branch: 'MOODLE_400_STABLE' - database: 'pgsql' + php: ['8.0'] + moodle-branch: ['MOODLE_401_STABLE'] + database: ['pgsql'] steps: - name: Start MariaDB diff --git a/locallib.php b/locallib.php index bb5cff4eba..6f910be2a2 100644 --- a/locallib.php +++ b/locallib.php @@ -1900,7 +1900,7 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow return true; } // } catch (Exception $e) {. - // $transaction->rollback($e); + // $transaction->rollback($e); // }. // Deleting the post failed. From 2242a7cdc123439b94f68e85470a02cc95ea10c3 Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Fri, 23 Jun 2023 14:33:48 +0200 Subject: [PATCH 19/24] updated test to delete post, delting also directories --- locallib.php | 111 ++++++++++++++++++++---------------------- tests/fixtures/NH.jpg | Bin 0 -> 31160 bytes tests/post_test.php | 64 +++++++++--------------- 3 files changed, 78 insertions(+), 97 deletions(-) create mode 100644 tests/fixtures/NH.jpg diff --git a/locallib.php b/locallib.php index 6f910be2a2..64b993ff93 100644 --- a/locallib.php +++ b/locallib.php @@ -1837,71 +1837,68 @@ function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow // Iterate through all children and delete them. // In case something does not work we throw the error as it should be known that something went ... terribly wrong. // All DB transactions are rolled back. - // try {. - $transaction = $DB->start_delegated_transaction(); + try { + $transaction = $DB->start_delegated_transaction(); - $childposts = $DB->get_records('moodleoverflow_posts', array('parent' => $post->id)); - if ($deletechildren && $childposts) { - foreach ($childposts as $childpost) { - moodleoverflow_delete_post($childpost, true, $cm, $moodleoverflow); - } - } - - // Delete the ratings. - $DB->delete_records('moodleoverflow_ratings', array('postid' => $post->id)); - - // Delete the post. - if ($DB->delete_records('moodleoverflow_posts', array('id' => $post->id))) { - - // Delete the read records. - readtracking::moodleoverflow_delete_read_records(-1, $post->id); - - // Delete the attachments. - // First delete the actual files on the disk. - $fs = get_file_storage(); - $context = context_module::instance($cm->id); - $attachments = $fs->get_area_files($context->id, 'mod_moodleoverflow', 'attachment', - $post->id, "filename", false); - - foreach ($attachments as $attachment) { - // Get file - $file = $fs->get_file($context->id, 'mod_moodleoverflow', 'attachment', $post->id, - $attachment->get_filepath(), $attachment->get_filename()); - - // Delete it if it exists - if ($file) { - $file->delete(); + $childposts = $DB->get_records('moodleoverflow_posts', array('parent' => $post->id)); + if ($deletechildren && $childposts) { + foreach ($childposts as $childpost) { + moodleoverflow_delete_post($childpost, true, $cm, $moodleoverflow); } } - // Just in case, check for the new last post of the discussion. - moodleoverflow_discussion_update_last_post($post->discussion); + // Delete the ratings. + $DB->delete_records('moodleoverflow_ratings', array('postid' => $post->id)); + + // Delete the post. + if ($DB->delete_records('moodleoverflow_posts', array('id' => $post->id))) { + // Delete the read records. + readtracking::moodleoverflow_delete_read_records(-1, $post->id); + + // Delete the attachments. + // First delete the actual files on the disk. + $fs = get_file_storage(); + $context = context_module::instance($cm->id); + $attachments = $fs->get_area_files($context->id, 'mod_moodleoverflow', 'attachment', + $post->id, "filename", true); + foreach ($attachments as $attachment) { + // Get file + $file = $fs->get_file($context->id, 'mod_moodleoverflow', 'attachment', $post->id, + $attachment->get_filepath(), $attachment->get_filename()); + // Delete it if it exists + if ($file) { + $file->delete(); + } + } - // Get the context module. - $modulecontext = context_module::instance($cm->id); + // Just in case, check for the new last post of the discussion. + moodleoverflow_discussion_update_last_post($post->discussion); + + // Get the context module. + $modulecontext = context_module::instance($cm->id); + + // Trigger the post deletion event. + $params = array( + 'context' => $modulecontext, + 'objectid' => $post->id, + 'other' => array( + 'discussionid' => $post->discussion, + 'moodleoverflowid' => $moodleoverflow->id + ) + ); + if ($post->userid !== $USER->id) { + $params['relateduserid'] = $post->userid; + } + $event = post_deleted::create($params); + $event->trigger(); - // Trigger the post deletion event. - $params = array( - 'context' => $modulecontext, - 'objectid' => $post->id, - 'other' => array( - 'discussionid' => $post->discussion, - 'moodleoverflowid' => $moodleoverflow->id - ) - ); - if ($post->userid !== $USER->id) { - $params['relateduserid'] = $post->userid; + // The post has been deleted. + $transaction->allow_commit(); + return true; } - $event = post_deleted::create($params); - $event->trigger(); - - // The post has been deleted. - $transaction->allow_commit(); - return true; + } catch (Exception $e) { + $transaction->rollback($e); } - // } catch (Exception $e) {. - // $transaction->rollback($e); - // }. // Deleting the post failed. return false; diff --git a/tests/fixtures/NH.jpg b/tests/fixtures/NH.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d4085965ce030d3965227b757a75a239c72c3201 GIT binary patch literal 31160 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'files', array('itemid' => $this->post->id))) { - $result = 1; - } - $this->assertEquals(1, $result); + $numberofattachments = count($DB->get_records('files', array('itemid' => $this->post->id))); + $this->assertEquals(2, $numberofattachments); // Delete the post from the teacher with its attachment. moodleoverflow_delete_post($this->post, false, $this->coursemodule, $this->moodleoverflow); // Now try to get the attachment. - if (!$DB->get_record('files', array('itemid' => $this->post->id))) { - $result = 2; - } - $this->assertEquals(2, $result); + $numberofattachments = count($DB->get_records('files', array('itemid' => $this->post->id))); + + $this->assertEquals(0, $numberofattachments); } /** @@ -102,21 +98,15 @@ public function test_moodleoverflow_delete_post() { public function test_moodleoverflow_delete_discussion() { global $DB; - $result = 0; - // The attachment should exist. - if ($DB->get_record('files', array('itemid' => $this->post->id))) { - $result = 1; - } - $this->assertEquals(1, $result); + $numberofattachments = count($DB->get_records('files', array('itemid' => $this->post->id, 'filearea' => 'attachment'))); + $this->assertEquals(2, $numberofattachments); // Delete the post from the teacher with its attachment. moodleoverflow_delete_discussion($this->discussion[0], $this->course, $this->coursemodule, $this->moodleoverflow); // Now try to get the attachment. - if (!$DB->get_record('files', array('itemid' => $this->post->id))) { - $result = 2; - } - $this->assertEquals(2, $result); + $numberofattachments = count($DB->get_records('files', array('itemid' => $this->post->id))); + $this->assertEquals(0, $numberofattachments); } /** @@ -142,28 +132,22 @@ private function helper_course_set_up() { $this->post = $DB->get_record('moodleoverflow_posts', array('id' => $this->discussion[0]->firstpost), '*'); // Create an attachment by inserting it directly in the database and update the post record. - $this->attachment = new \stdClass(); - $this->attachment->contenthash = '81a897de6707916841bcafa3fb853377086744ba'; - $this->attachment->pathnamehash = 'bb9fe5ed6ab47359546f7df8858263d9c6814646'; + $modulecontext = \context_module::instance($this->coursemodule->id); - $this->attachment->contextid = $modulecontext->id; - $this->attachment->component = 'mod_moodleoverflow'; - $this->attachment->filearea = 'attachment'; - $this->attachment->itemid = $this->post->id; - $this->attachment->filepath = '/'; - $this->attachment->filename = 'thisfile.png'; - $this->attachment->userid = $this->teacher->id; - $this->attachment->filesize = 129595; - $this->attachment->mimetype = 'image/png'; - $this->attachment->status = 0; - $this->attachment->source = 'thisfile.png'; - $this->attachment->author = $this->teacher->firstname . ' ' . $this->teacher->lastname; - $this->attachment->license = 'unknown'; - $this->attachment->timecreated = $this->post->created; - $this->attachment->timemodified = $this->post->modified; - $this->attachment->sortorder = 0; - $this->attachment->referencefileid = null; - $DB->insert_record('files', $this->attachment); + + $fileinfo = [ + 'contextid' => $modulecontext->id, // ID of the context. + 'component' => 'mod_moodleoverflow', // Your component name. + 'filearea' => 'attachment', // Usually = table name. + 'itemid' => $this->post->id, // Usually = ID of row in table. + 'filepath' => '/', // Any path beginning and ending in /. + 'filename' => 'NH.jpg', // Any filename. + ]; + + $fs = get_file_storage(); + + // Create a new file containing the text 'hello world'. + $fs->create_file_from_string($fileinfo, 'hello world'); $this->post->attachment = 1; $DB->update_record('moodleoverflow_posts', $this->post); From e26b6b081c84fefbe64dcfe562b6eb80edf4eed0 Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Fri, 23 Jun 2023 14:58:33 +0200 Subject: [PATCH 20/24] undo reviewtest changes --- tests/review_test.php | 171 ++++++++++++++++++++++++------------------ 1 file changed, 100 insertions(+), 71 deletions(-) diff --git a/tests/review_test.php b/tests/review_test.php index e860974dbe..c2d7e1d41c 100644 --- a/tests/review_test.php +++ b/tests/review_test.php @@ -114,15 +114,57 @@ public function test_forum_review_everything() { $options = array('course' => $this->course->id, 'needsreview' => review::EVERYTHING, 'forcesubscribe' => MOODLEOVERFLOW_FORCESUBSCRIBE); - $studentanswers = $this->start_review_process($options); + $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); + + list(, $teacherpost) = $this->generator->post_to_forum($moodleoverflow, $this->teacher); + list(, $studentpost) = $this->generator->post_to_forum($moodleoverflow, $this->student); + + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 0, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); + + $this->run_send_mails(); + $this->run_send_mails(); // Execute twice to ensure no duplicate mails. + + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_REVIEW_SUCCESS, 'reviewed' => 0, + 'timereviewed' => null], $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); + + $this->assertEquals(1, $this->mailsink->count()); // Teacher has to approve student message. + $this->assertEquals(2, $this->messagesink->count()); // Student and teacher get notification for student message. + + $this->mailsink->clear(); + $this->messagesink->clear(); + + $this->assertNull(\mod_moodleoverflow_external::review_approve_post($studentpost->id)); + + $this->run_send_mails(); + $this->run_send_mails(); // Execute twice to ensure no duplicate mails. + + $post = $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id]); + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1], $post); + $this->assertNotNull($post->timereviewed ?? null); + + $this->assertEquals(0, $this->mailsink->count()); + $this->assertEquals(2, $this->messagesink->count()); + + $this->messagesink->clear(); + + $studentanswer1 = $this->generator->reply_to_post($teacherpost, $this->student, false); + $studentanswer2 = $this->generator->reply_to_post($teacherpost, $this->student, false); + + $this->run_send_mails(); + $this->run_send_mails(); // Execute twice to ensure no duplicate mails. $this->assertEquals(2, $this->mailsink->count()); $this->assertEquals(0, $this->messagesink->count()); $this->mailsink->clear(); - $this->assertNotNull(\mod_moodleoverflow_external::review_approve_post($studentanswers[0]->id)); - $this->assertNull(\mod_moodleoverflow_external::review_reject_post($studentanswers[1]->id, 'This post was not good!')); + $this->assertNotNull(\mod_moodleoverflow_external::review_approve_post($studentanswer1->id)); + $this->assertNull(\mod_moodleoverflow_external::review_reject_post($studentanswer2->id, 'This post was not good!')); $this->run_send_mails(); $this->run_send_mails(); // Execute twice to ensure no duplicate mails. @@ -137,7 +179,7 @@ public function test_forum_review_everything() { $this->assertEquals($this->student->email, $rejectionmessage->to); // Check post was deleted. - $this->assertEquals(0, $DB->count_records('moodleoverflow_posts', ['id' => $studentanswers[1]->id])); + $this->assertEquals(0, $DB->count_records('moodleoverflow_posts', ['id' => $studentanswer2->id])); } /** @@ -151,15 +193,63 @@ public function test_forum_review_only_questions() { $options = array('course' => $this->course->id, 'needsreview' => review::QUESTIONS, 'forcesubscribe' => MOODLEOVERFLOW_FORCESUBSCRIBE); - $studentanswers = $this->start_review_process($options); + $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); + + list(, $teacherpost) = $this->generator->post_to_forum($moodleoverflow, $this->teacher); + list(, $studentpost) = $this->generator->post_to_forum($moodleoverflow, $this->student); + + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 0, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); + + $this->run_send_mails(); + $this->run_send_mails(); // Execute twice to ensure no duplicate mails. + + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_REVIEW_SUCCESS, 'reviewed' => 0, + 'timereviewed' => null], $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); + + $this->assertEquals(1, $this->mailsink->count()); // Teacher has to approve student message. + $this->assertEquals(2, $this->messagesink->count()); // Student and teacher get notification for student message. + + $this->mailsink->clear(); + $this->messagesink->clear(); + + $this->assertNull(\mod_moodleoverflow_external::review_approve_post($studentpost->id)); + + $this->run_send_mails(); + $this->run_send_mails(); // Execute twice to ensure no duplicate mails. + + $post = $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id]); + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1], $post); + $this->assertNotNull($post->timereviewed ?? null); + + $this->assertEquals(0, $this->mailsink->count()); + $this->assertEquals(2, $this->messagesink->count()); + + $this->messagesink->clear(); + + $studentanswer1 = $this->generator->reply_to_post($teacherpost, $this->student, false); + $studentanswer2 = $this->generator->reply_to_post($teacherpost, $this->student, false); + + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer1->id])); + + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer2->id])); + + $this->run_send_mails(); + $this->run_send_mails(); // Execute twice to ensure no duplicate mails. $this->assertEquals(0, $this->mailsink->count()); $this->assertEquals(4, $this->messagesink->count()); $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswers[0]->id])); + $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer1->id])); $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswers[1]->id])); + $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer2->id])); } /** @@ -169,10 +259,10 @@ public function test_forum_review_disallowed() { global $DB; $options = array('course' => $this->course->id, 'needsreview' => review::EVERYTHING, 'forcesubscribe' => MOODLEOVERFLOW_FORCESUBSCRIBE); - set_config('allowreview', 0, 'moodleoverflow'); - $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); + set_config('allowreview', 0, 'moodleoverflow'); + list(, $teacherpost) = $this->generator->post_to_forum($moodleoverflow, $this->teacher); list(, $studentpost) = $this->generator->post_to_forum($moodleoverflow, $this->student); @@ -242,65 +332,4 @@ private function assert_matches_properties($expected, $actual) { $this->assertEquals($value, $actual->$key, "Failed asserting that \$obj->$key '" . $actual->$key . "' equals '$value'"); } } - - /** - * Processing the review process and inserting feedback. - * @param array $options - * @return array - * @throws \coding_exception - * @throws \dml_exception - */ - private function start_review_process($options) { - global $DB; - $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); - - list(, $teacherpost) = $this->generator->post_to_forum($moodleoverflow, $this->teacher); - list(, $studentpost) = $this->generator->post_to_forum($moodleoverflow, $this->student); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 0, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_REVIEW_SUCCESS, 'reviewed' => 0, - 'timereviewed' => null], $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); - - $this->assertEquals(1, $this->mailsink->count()); // Teacher has to approve student message. - $this->assertEquals(2, $this->messagesink->count()); // Student and teacher get notification for student message. - - $this->mailsink->clear(); - $this->messagesink->clear(); - - $this->assertNull(\mod_moodleoverflow_external::review_approve_post($studentpost->id)); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. - - $post = $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id]); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1], $post); - $this->assertNotNull($post->timereviewed ?? null); - - $this->assertEquals(0, $this->mailsink->count()); - $this->assertEquals(2, $this->messagesink->count()); - - $this->messagesink->clear(); - - $studentanswer1 = $this->generator->reply_to_post($teacherpost, $this->student, false); - $studentanswer2 = $this->generator->reply_to_post($teacherpost, $this->student, false); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer1->id])); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer2->id])); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. - return array($studentanswer1, $studentanswer2); - } -} +} \ No newline at end of file From f01800fdcffe61bed936fa91f475f51592773621 Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Fri, 23 Jun 2023 15:33:28 +0200 Subject: [PATCH 21/24] added behat test delete --- tests/behat/delete_file.feature | 57 +++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/behat/delete_file.feature diff --git a/tests/behat/delete_file.feature b/tests/behat/delete_file.feature new file mode 100644 index 0000000000..0d1e7dd4eb --- /dev/null +++ b/tests/behat/delete_file.feature @@ -0,0 +1,57 @@ +@mod @mod_moodleoverflow @javascript @mod_moodleoverflow_delete @javascript @_file_upload +Feature: Add moodleoverflow activities and discussions + In order to delete discussions also files need to be deleted + + Background: Add a moodleoverflow and a discussion + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + | student1 | Student | 1 | student1@example.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + And I log in as "teacher1" + And I am on "Course 1" course homepage + And I turn editing mode on + And I add a "Moodleoverflow" to section "1" and I fill the form with: + | Moodleoverflow name | Test moodleoverflow name | + | Description | Test forum description | + And I add a new discussion to "Test moodleoverflow name" moodleoverflow with: + | Subject | Forum post 1 | + | Message | This is the body | + And I log out + And I log in as "student1" + And I am on "Course 1" course homepage + And I follow "Test moodleoverflow name" + And I follow "Forum post 1" + And I click on "Answer" "link" + And I set the following fields to these values: + | Subject | A reply post | + | Message | This is the message of the answer post | + And I upload "mod/moodleoverflow/tests/fixtures/NH.jpg" file to "Attachment" filemanager + And I press "Post to forum" + + Scenario Outline: + Given I log in as "" + And I am on "Course 1" course homepage + And I follow "Test moodleoverflow name" + And I follow "Forum post 1" + And I should see "This is the message of the answer post" + And "//div[contains(@class, 'moodleoverflowpost')]//div[contains(@class, 'attachments')]//img[contains(@src, 'NH.jpg')]" "xpath_element" should exist + And I click on "Delete" "link" + And I click on "Continue" "button" + Then I should see "Test moodleoverflow name" + And I should see "This is the body" + And I should not see "This is the message of the answer post" + And "//div[contains(@class, 'moodleoverflowpost')]//div[contains(@class, 'attachments')]//img[contains(@src, 'NH.jpg')]" "xpath_element" should not exist + + Examples: + | role | + | student1 | + + + From 9a64c51fb9c3a86c7d59e0ce3d61ade592c1dd12 Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Fri, 23 Jun 2023 15:39:02 +0200 Subject: [PATCH 22/24] newline --- tests/review_test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/review_test.php b/tests/review_test.php index c2d7e1d41c..29aae0090a 100644 --- a/tests/review_test.php +++ b/tests/review_test.php @@ -332,4 +332,4 @@ private function assert_matches_properties($expected, $actual) { $this->assertEquals($value, $actual->$key, "Failed asserting that \$obj->$key '" . $actual->$key . "' equals '$value'"); } } -} \ No newline at end of file +} From b1bd8aed3ddf90f6febb34594b78b7f2042a671c Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Fri, 23 Jun 2023 16:25:18 +0200 Subject: [PATCH 23/24] shorten review test --- tests/review_test.php | 149 +++++++++++++++++------------------------- 1 file changed, 61 insertions(+), 88 deletions(-) diff --git a/tests/review_test.php b/tests/review_test.php index 29aae0090a..82a6823a16 100644 --- a/tests/review_test.php +++ b/tests/review_test.php @@ -114,23 +114,9 @@ public function test_forum_review_everything() { $options = array('course' => $this->course->id, 'needsreview' => review::EVERYTHING, 'forcesubscribe' => MOODLEOVERFLOW_FORCESUBSCRIBE); - $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); - - list(, $teacherpost) = $this->generator->post_to_forum($moodleoverflow, $this->teacher); - list(, $studentpost) = $this->generator->post_to_forum($moodleoverflow, $this->student); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 0, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_REVIEW_SUCCESS, 'reviewed' => 0, - 'timereviewed' => null], $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); + $posts = $this->create_post($options); + $this->check_mail_records($posts['teacherpost'], $posts['studentpost'], 1, 0, MOODLEOVERFLOW_MAILED_REVIEW_SUCCESS); $this->assertEquals(1, $this->mailsink->count()); // Teacher has to approve student message. $this->assertEquals(2, $this->messagesink->count()); // Student and teacher get notification for student message. @@ -138,12 +124,12 @@ public function test_forum_review_everything() { $this->mailsink->clear(); $this->messagesink->clear(); - $this->assertNull(\mod_moodleoverflow_external::review_approve_post($studentpost->id)); + $this->assertNull(\mod_moodleoverflow_external::review_approve_post($posts['studentpost']->id)); $this->run_send_mails(); $this->run_send_mails(); // Execute twice to ensure no duplicate mails. - $post = $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id]); + $post = $DB->get_record('moodleoverflow_posts', ['id' => $posts['studentpost']->id]); $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1], $post); $this->assertNotNull($post->timereviewed ?? null); @@ -152,8 +138,8 @@ public function test_forum_review_everything() { $this->messagesink->clear(); - $studentanswer1 = $this->generator->reply_to_post($teacherpost, $this->student, false); - $studentanswer2 = $this->generator->reply_to_post($teacherpost, $this->student, false); + $studentanswer1 = $this->generator->reply_to_post($posts['teacherpost'], $this->student, false); + $studentanswer2 = $this->generator->reply_to_post($posts['teacherpost'], $this->student, false); $this->run_send_mails(); $this->run_send_mails(); // Execute twice to ensure no duplicate mails. @@ -193,23 +179,8 @@ public function test_forum_review_only_questions() { $options = array('course' => $this->course->id, 'needsreview' => review::QUESTIONS, 'forcesubscribe' => MOODLEOVERFLOW_FORCESUBSCRIBE); - $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); - - list(, $teacherpost) = $this->generator->post_to_forum($moodleoverflow, $this->teacher); - list(, $studentpost) = $this->generator->post_to_forum($moodleoverflow, $this->student); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 0, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_REVIEW_SUCCESS, 'reviewed' => 0, - 'timereviewed' => null], $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); + $posts = $this->create_post($options); + $this->check_mail_records($posts['teacherpost'], $posts['studentpost'], 1, 0, MOODLEOVERFLOW_MAILED_REVIEW_SUCCESS); $this->assertEquals(1, $this->mailsink->count()); // Teacher has to approve student message. $this->assertEquals(2, $this->messagesink->count()); // Student and teacher get notification for student message. @@ -217,12 +188,12 @@ public function test_forum_review_only_questions() { $this->mailsink->clear(); $this->messagesink->clear(); - $this->assertNull(\mod_moodleoverflow_external::review_approve_post($studentpost->id)); + $this->assertNull(\mod_moodleoverflow_external::review_approve_post($posts['studentpost']->id)); $this->run_send_mails(); $this->run_send_mails(); // Execute twice to ensure no duplicate mails. - $post = $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id]); + $post = $DB->get_record('moodleoverflow_posts', ['id' => $posts['studentpost']->id]); $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1], $post); $this->assertNotNull($post->timereviewed ?? null); @@ -231,53 +202,26 @@ public function test_forum_review_only_questions() { $this->messagesink->clear(); - $studentanswer1 = $this->generator->reply_to_post($teacherpost, $this->student, false); - $studentanswer2 = $this->generator->reply_to_post($teacherpost, $this->student, false); + $studentanswer1 = $this->generator->reply_to_post($posts['teacherpost'], $this->student, false); + $studentanswer2 = $this->generator->reply_to_post($posts['teacherpost'], $this->student, false); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer1->id])); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer2->id])); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. + $this->check_mail_records($studentanswer1, $studentanswer2, 1, 1, MOODLEOVERFLOW_MAILED_SUCCESS); $this->assertEquals(0, $this->mailsink->count()); $this->assertEquals(4, $this->messagesink->count()); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer1->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer2->id])); } /** * Test reviews functionality when reviewing is allowed in admin settings. */ public function test_forum_review_disallowed() { - global $DB; $options = array('course' => $this->course->id, 'needsreview' => review::EVERYTHING, 'forcesubscribe' => MOODLEOVERFLOW_FORCESUBSCRIBE); - $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); set_config('allowreview', 0, 'moodleoverflow'); - list(, $teacherpost) = $this->generator->post_to_forum($moodleoverflow, $this->teacher); - list(, $studentpost) = $this->generator->post_to_forum($moodleoverflow, $this->student); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); + $posts = $this->create_post($options); + $this->check_mail_records($posts['teacherpost'], $posts['studentpost'], 1, 1, MOODLEOVERFLOW_MAILED_SUCCESS); $this->assertEquals(0, $this->mailsink->count()); // Teacher has to approve student message. $this->assertEquals(4, $this->messagesink->count()); // Student and teacher get notification for student message. @@ -285,25 +229,13 @@ public function test_forum_review_disallowed() { $this->mailsink->clear(); $this->messagesink->clear(); - $studentanswer1 = $this->generator->reply_to_post($teacherpost, $this->student, false); - $studentanswer2 = $this->generator->reply_to_post($teacherpost, $this->student, false); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer1->id])); + $studentanswer1 = $this->generator->reply_to_post($posts['teacherpost'], $this->student, false); + $studentanswer2 = $this->generator->reply_to_post($posts['teacherpost'], $this->student, false); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer2->id])); - - $this->run_send_mails(); - $this->run_send_mails(); // Execute twice to ensure no duplicate mails. + $this->check_mail_records($studentanswer1, $studentanswer2, 1, 1, MOODLEOVERFLOW_MAILED_SUCCESS); $this->assertEquals(0, $this->mailsink->count()); $this->assertEquals(4, $this->messagesink->count()); - - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer1->id])); - $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => 1, 'timereviewed' => null], - $DB->get_record('moodleoverflow_posts', ['id' => $studentanswer2->id])); } /** @@ -325,11 +257,52 @@ private function run_send_mails() { * @param object|array $actual */ private function assert_matches_properties($expected, $actual) { - $expected = (array) $expected; - $actual = (object) $actual; + $expected = (array)$expected; + $actual = (object)$actual; foreach ($expected as $key => $value) { $this->assertObjectHasAttribute($key, $actual, "Failed asserting that attribute '$key' exists."); $this->assertEquals($value, $actual->$key, "Failed asserting that \$obj->$key '" . $actual->$key . "' equals '$value'"); } } + + /** + * Create two posts. + * @param array $options + * @return array the teacher and the studentpost. + */ + private function create_post($options) { + $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options); + + list(, $teacherpost) = $this->generator->post_to_forum($moodleoverflow, $this->teacher); + list(, $studentpost) = $this->generator->post_to_forum($moodleoverflow, $this->student); + + return array('teacherpost' => $teacherpost, 'studentpost' => $studentpost); + } + + /** + * Check Mail object before and after sending. + * @param \stdClass $teacherpost + * @param \stdClass $studentpost + * @param int $review1 + * @param int $review2 + * @param int $mailed + * @return void + * @throws \dml_exception + */ + private function check_mail_records($teacherpost, $studentpost, $review1, $review2, $mailed) { + global $DB; + + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => $review1, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_PENDING, 'reviewed' => $review2, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); + + $this->run_send_mails(); + $this->run_send_mails(); // Execute twice to ensure no duplicate mails. + + $this->assert_matches_properties(['mailed' => MOODLEOVERFLOW_MAILED_SUCCESS, 'reviewed' => $review1, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $teacherpost->id])); + $this->assert_matches_properties(['mailed' => $mailed, 'reviewed' => $review2, 'timereviewed' => null], + $DB->get_record('moodleoverflow_posts', ['id' => $studentpost->id])); + } } From 5594d6b34633e413268a11e505cf7520c545b426 Mon Sep 17 00:00:00 2001 From: NinaHerrmann Date: Fri, 23 Jun 2023 16:25:53 +0200 Subject: [PATCH 24/24] use full pipeline again --- .github/workflows/moodle-ci.yml | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/.github/workflows/moodle-ci.yml b/.github/workflows/moodle-ci.yml index 2ce739ff6a..c020ab22d4 100644 --- a/.github/workflows/moodle-ci.yml +++ b/.github/workflows/moodle-ci.yml @@ -110,9 +110,28 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.0'] - moodle-branch: ['MOODLE_401_STABLE'] - database: ['pgsql'] + php: ['8.0', '8.1'] + moodle-branch: ['MOODLE_401_STABLE', 'MOODLE_402_STABLE'] + database: ['mariadb', 'pgsql'] + include: + - php: '7.4' + moodle-branch: 'MOODLE_39_STABLE' + database: 'mariadb' + - php: '7.4' + moodle-branch: 'MOODLE_39_STABLE' + database: 'pgsql' + - php: '8.0' + moodle-branch: 'MOODLE_311_STABLE' + database: 'mariadb' + - php: '8.0' + moodle-branch: 'MOODLE_311_STABLE' + database: 'pgsql' + - php: '8.0' + moodle-branch: 'MOODLE_400_STABLE' + database: 'mariadb' + - php: '8.0' + moodle-branch: 'MOODLE_400_STABLE' + database: 'pgsql' steps: - name: Start MariaDB