';
-moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post);
+moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post, $multiplemarks);
echo '
';
diff --git a/externallib.php b/externallib.php
index 66df2d88b8..a7c29fb75f 100644
--- a/externallib.php
+++ b/externallib.php
@@ -46,8 +46,8 @@ class mod_moodleoverflow_external extends external_api {
public static function record_vote_parameters() {
return new external_function_parameters(
array(
- 'postid' => new external_value(PARAM_INT, 'id of post'),
- 'ratingid' => new external_value(PARAM_INT, 'rating')
+ 'postid' => new external_value(PARAM_INT, 'id of post'),
+ 'ratingid' => new external_value(PARAM_INT, 'rating')
)
);
}
@@ -79,8 +79,8 @@ public static function record_vote($postid, $ratingid) {
// Parameter validation.
$params = self::validate_parameters(self::record_vote_parameters(), array(
- 'postid' => $postid,
- 'ratingid' => $ratingid,
+ 'postid' => $postid,
+ 'ratingid' => $ratingid,
));
$transaction = $DB->start_delegated_transaction();
diff --git a/lang/en/moodleoverflow.php b/lang/en/moodleoverflow.php
index 871a4ded87..13d0e76ec1 100644
--- a/lang/en/moodleoverflow.php
+++ b/lang/en/moodleoverflow.php
@@ -188,8 +188,10 @@
$string['ratingfailed'] = 'Rating failed. Try again.';
$string['rateownpost'] = 'You cannot rate your own post.';
$string['marksolved'] = 'Mark as solution';
+$string['alsomarksolved'] = "Also mark as solution";
$string['marknotsolved'] = 'Remove solution mark';
-$string['markhelpful'] = 'Mark as Helpful';
+$string['markhelpful'] = 'Mark as helpful';
+$string['alsomarkhelpful'] = "Also mark as helpful";
$string['marknothelpful'] = 'Not Helpful';
$string['answer'] = '{$a} Answer';
$string['answers'] = '{$a} Answers';
@@ -334,6 +336,8 @@
$string['attachment'] = 'Attachment';
$string['attachments'] = 'Attachments';
$string['attachment_help'] = 'You can optionally attach one or more files to a forum post. If you attach an image, it will be displayed after the message.';
+$string['allowmultiplemarks'] = 'Multiple marks?';
+$string['allowmultiplemarks_help'] = 'A post can be marked as helpful or solved. Within a discussion, only one post can be marked as helpful/solved. Click the checkbox to mark multiple posts as helpful/solved.';
// Templates.
$string['reputation'] = 'Reputation';
diff --git a/lib.php b/lib.php
index 980856a25f..5ee67061d7 100644
--- a/lib.php
+++ b/lib.php
@@ -749,7 +749,12 @@ function moodleoverflow_send_mails() {
$userto->markposts = array();
// Cache the capabilities of the user.
- cron_setup_user($userto);
+ // Check for moodle version. Version 401 supported until 8 December 2025.
+ if ($CFG->branch >= 402) {
+ \core\cron::setup_user($userto);
+ } else {
+ cron_setup_user($userto);
+ }
// Reset the caches.
foreach ($coursemodules as $moodleoverflowid => $unused) {
@@ -838,7 +843,12 @@ function moodleoverflow_send_mails() {
}
// Setup roles and languages.
- cron_setup_user($userto, $course);
+ // Check for moodle version. Version 401 supported until 8 December 2025.
+ if ($CFG->branch >= 402) {
+ \core\cron::setup_user($userto, $course);
+ } else {
+ cron_setup_user($userto, $course);
+ }
// Cache the users capability to view full names.
if (!isset($userto->viewfullnames[$moodleoverflow->id])) {
diff --git a/locallib.php b/locallib.php
index b120deaa22..fc6b5d4537 100644
--- a/locallib.php
+++ b/locallib.php
@@ -269,6 +269,8 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = -
$preparedarray[$i]['starterlink'] = null;
if ($statusstarter) {
$link = '/mod/moodleoverflow/discussion.php?d=';
+ $statusstarter = $statusstarter[array_key_first($statusstarter)];
+
$preparedarray[$i]['starterlink'] = new moodle_url($link .
$statusstarter->discussionid . '#p' . $statusstarter->postid);
}
@@ -278,6 +280,8 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = -
$preparedarray[$i]['teacherlink'] = null;
if ($statusteacher) {
$link = '/mod/moodleoverflow/discussion.php?d=';
+ $statusteacher = $statusteacher[array_key_first($statusteacher)];
+
$preparedarray[$i]['teacherlink'] = new moodle_url($link .
$statusteacher->discussionid . '#p' . $statusteacher->postid);
}
@@ -328,7 +332,8 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = -
}
} else {
// Get his picture, his name and the link to his profile.
- $preparedarray[$i]['picture'] = $OUTPUT->user_picture($startuser, array('courseid' => $moodleoverflow->course, 'link' => false));
+ $preparedarray[$i]['picture'] = $OUTPUT->user_picture($startuser, array('courseid' => $moodleoverflow->course,
+ 'link' => false));
$preparedarray[$i]['username'] = fullname($startuser, has_capability('moodle/site:viewfullnames', $context));
$preparedarray[$i]['userlink'] = $CFG->wwwroot . '/user/view.php?id=' .
$discussion->userid . '&course=' . $moodleoverflow->course;
@@ -443,6 +448,10 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = -
/**
* Prints a popup with a menu of other moodleoverflow in the course.
* Menu to move a topic to another moodleoverflow forum.
+ *
+ * @param object $course
+ * @param object $cm
+ * @param int $movetopopup forum where forum list is being printed.
*/
function moodleoverflow_print_forum_list($course, $cm, $movetopopup) {
global $CFG, $DB, $PAGE;
@@ -480,6 +489,7 @@ function moodleoverflow_print_forum_list($course, $cm, $movetopopup) {
echo $renderer->render_forum_list($mustachedata);
}
+
/**
* Returns an array of counts of replies for each discussion.
*
@@ -911,10 +921,10 @@ function moodleoverflow_user_can_post($modulecontext, $posttoreplyto, $considerr
* @param stdClass $moodleoverflow The moodleoverflow object
* @param stdClass $discussion The discussion object
* @param stdClass $post The post object
+ * @param bool $multiplemarks The setting of allowmultiplemarks
*/
-function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post) {
+function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post, $multiplemarks = false) {
global $USER;
-
// Check if the current is the starter of the discussion.
$ownpost = (isloggedin() && ($USER->id == $post->userid));
@@ -966,7 +976,7 @@ function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discuss
// Print the starting post.
echo moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
- $ownpost, false, '', '', $postread, true, $istracked, 0, $usermapping);
+ $ownpost, false, '', '', $postread, true, $istracked, 0, $usermapping, 0, $multiplemarks);
// Print answer divider.
if ($answercount == 1) {
@@ -980,7 +990,7 @@ function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discuss
// Print the other posts.
echo moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow, $discussion, $post, $istracked, $posts,
- null, $usermapping);
+ null, $usermapping, $multiplemarks);
echo '';
}
@@ -1049,6 +1059,7 @@ function moodleoverflow_get_all_discussion_posts($discussionid, $tracking, $modc
// Assign the ratings to the matching posts.
$posts[$postid]->upvotes = $discussionratings[$post->id]->upvotes;
$posts[$postid]->downvotes = $discussionratings[$post->id]->downvotes;
+ $posts[$postid]->votesdifference = $posts[$postid]->upvotes - $posts[$postid]->downvotes;
$posts[$postid]->statusstarter = $discussionratings[$post->id]->ishelpful;
$posts[$postid]->statusteacher = $discussionratings[$post->id]->issolved;
}
@@ -1108,6 +1119,7 @@ function moodleoverflow_get_all_discussion_posts($discussionid, $tracking, $modc
* @param bool $iscomment
* @param array $usermapping
* @param int $level
+ * @param bool $multiplemarks The Setting of allowmultiplemarks
* @return void|null
* @throws coding_exception
* @throws dml_exception
@@ -1117,7 +1129,7 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
$ownpost = false, $link = false,
$footer = '', $highlight = '', $postisread = null,
$dummyifcantsee = true, $istracked = false,
- $iscomment = false, $usermapping = [], $level = 0) {
+ $iscomment = false, $usermapping = [], $level = 0, $multiplemarks = false) {
global $USER, $CFG, $OUTPUT, $PAGE;
// Require the filelib.
@@ -1174,8 +1186,10 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
$str->markread = get_string('markread', 'moodleoverflow');
$str->markunread = get_string('markunread', 'moodleoverflow');
$str->marksolved = get_string('marksolved', 'moodleoverflow');
+ $str->alsomarksolved = get_string('alsomarksolved', 'moodleoverflow');
$str->marknotsolved = get_string('marknotsolved', 'moodleoverflow');
$str->markhelpful = get_string('markhelpful', 'moodleoverflow');
+ $str->alsomarkhelpful = get_string('alsomarkhelpful', 'moodleoverflow');
$str->marknothelpful = get_string('marknothelpful', 'moodleoverflow');
}
@@ -1214,24 +1228,38 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
$permalink = new moodle_url($discussionlink);
$permalink->set_anchor('p' . $post->id);
+ // Check if multiplemarks are allowed, if so, check if there are already marked posts.
+ $helpfulposts = false;
+ $solvedposts = false;
+ if ($multiplemarks) {
+ $helpfulposts = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussion->id, false);
+ $solvedposts = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussion->id, true);
+ }
+
// If the user has started the discussion, he can mark the answer as helpful.
$canmarkhelpful = (($USER->id == $discussion->userid) && ($USER->id != $post->userid) &&
($iscomment != $post->parent) && !empty($post->parent));
if ($canmarkhelpful) {
-
// When the post is already marked, remove the mark instead.
$link = '/mod/moodleoverflow/discussion.php';
if ($post->statusstarter) {
$commands[] = html_writer::tag('a', $str->marknothelpful,
array('class' => 'markhelpful onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'helpful'));
} else {
- $commands[] = html_writer::tag('a', $str->markhelpful,
+ // If there are already marked posts, change the string of the button.
+ if ($helpfulposts) {
+ $commands[] = html_writer::tag('a', $str->alsomarkhelpful,
+ array('class' => 'markhelpful onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'helpful'));
+ } else {
+ $commands[] = html_writer::tag('a', $str->markhelpful,
array('class' => 'markhelpful onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'helpful'));
+ }
}
}
// A teacher can mark an answer as solved.
- $canmarksolved = (($iscomment != $post->parent) AND !empty($post->parent) AND capabilities::has(capabilities::MARK_SOLVED, $modulecontext));
+ $canmarksolved = (($iscomment != $post->parent) && !empty($post->parent)
+ && capabilities::has(capabilities::MARK_SOLVED, $modulecontext));
if ($canmarksolved) {
// When the post is already marked, remove the mark instead.
@@ -1240,8 +1268,14 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
$commands[] = html_writer::tag('a', $str->marknotsolved,
array('class' => 'marksolved onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'solved'));
} else {
- $commands[] = html_writer::tag('a', $str->marksolved,
+ // If there are already marked posts, change the string of the button.
+ if ($solvedposts) {
+ $commands[] = html_writer::tag('a', $str->alsomarksolved,
array('class' => 'marksolved onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'solved'));
+ } else {
+ $commands[] = html_writer::tag('a', $str->marksolved,
+ array('class' => 'marksolved onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'solved'));
+ }
}
}
@@ -1296,7 +1330,7 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
$mustachedata->isread = false;
$mustachedata->isfirstunread = false;
$mustachedata->isfirstpost = false;
- $mustachedata->iscomment = (!empty($post->parent) AND ($iscomment == $post->parent));
+ $mustachedata->iscomment = (!empty($post->parent) && ($iscomment == $post->parent));
$mustachedata->permalink = $permalink;
// Get the ratings.
@@ -1461,14 +1495,15 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
* @param bool $istracked Whether the user tracks the discussion
* @param array $posts Array of posts within the discussion
* @param bool $iscomment Whether the current post is a comment
- * @param array $usermapping
+ * @param array $usermapping
+ * @param bool $multiplemarks The Setting of allowmultiplemarks
* @return string
* @throws coding_exception
* @throws dml_exception
* @throws moodle_exception
*/
function moodleoverflow_print_posts_nested($course, &$cm, $moodleoverflow, $discussion, $parent,
- $istracked, $posts, $iscomment = null, $usermapping = []) {
+ $istracked, $posts, $iscomment = null, $usermapping = [], $multiplemarks = false) {
global $USER;
// Prepare the output.
@@ -1510,11 +1545,11 @@ function moodleoverflow_print_posts_nested($course, &$cm, $moodleoverflow, $disc
// Print the answer.
$output .= moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
- $ownpost, false, '', '', $postread, true, $istracked, $parentid, $usermapping, $level);
+ $ownpost, false, '', '', $postread, true, $istracked, $parentid, $usermapping, $level, $multiplemarks);
// Print its children.
$output .= moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow,
- $discussion, $post, $istracked, $posts, $parentid, $usermapping);
+ $discussion, $post, $istracked, $posts, $parentid, $usermapping, $multiplemarks);
// End the div.
$output .= "\n";
diff --git a/mod_form.php b/mod_form.php
index 1cb85ae630..4df07d8537 100644
--- a/mod_form.php
+++ b/mod_form.php
@@ -224,6 +224,11 @@ public function definition() {
$mform->addHelpButton('allownegativereputation', 'allownegativereputation', 'moodleoverflow');
$mform->setDefault('allownegativereputation', MOODLEOVERFLOW_REPUTATION_NEGATIVE);
+ // Allow multiple marks of helpful/solved.
+ $mform->addElement('advcheckbox', 'allowmultiplemarks', get_string('allowmultiplemarks', 'moodleoverflow'));
+ $mform->addHelpButton('allowmultiplemarks', 'allowmultiplemarks', 'moodleoverflow');
+ $mform->setDefault('allowmultiplemarks', 0);
+
// Add standard elements, common to all modules.
$this->standard_coursemodule_elements();
diff --git a/renderer.php b/renderer.php
index f6458b547c..905bc3d12d 100644
--- a/renderer.php
+++ b/renderer.php
@@ -49,6 +49,10 @@ public function render_discussion_list($data) {
/**
* Display the forum list in the view.php if a discussion needs to be moved to another forum.
+ *
+ * @param object $data The prepared variables.
+ *
+ * @return string
*/
public function render_forum_list($data) {
return $this->render_from_template('mod_moodleoverflow/forum_list', $data);
diff --git a/tests/behat/behat_mod_moodleoverflow.php b/tests/behat/behat_mod_moodleoverflow.php
index 7d1375c6c1..0f21dd17f0 100644
--- a/tests/behat/behat_mod_moodleoverflow.php
+++ b/tests/behat/behat_mod_moodleoverflow.php
@@ -113,6 +113,10 @@ protected function add_new_discussion($moodleoverflowname, TableNode $table, $bu
$this->execute('behat_general::i_wait_to_be_redirected');
}
+ /**
+ * Gets the container node.
+ * @param string $discussiontitle
+ */
protected function find_moodleoverflow_discussion_card(string $discussiontitle): \Behat\Mink\Element\Element {
return $this->find('xpath',
'//*[contains(concat(" ",normalize-space(@class)," ")," moodleoverflowdiscussion ")][.//*[text()="'.
@@ -124,7 +128,8 @@ protected function find_moodleoverflow_discussion_card(string $discussiontitle):
*
* This step is for advanced users, use it if you don't find anything else suitable for what you need.
*
- * @Then /^"(?P