From 03d29fbfe414e12647ee50215c187fd8208b9f96 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Tue, 4 Apr 2023 15:19:02 +0200
Subject: [PATCH 01/35] setting for multiplemarks added

---
 db/install.xml             |  1 +
 db/upgrade.php             | 17 +++++++++++++++++
 lang/en/moodleoverflow.php |  2 ++
 mod_form.php               |  5 +++++
 version.php                |  2 +-
 5 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/db/install.xml b/db/install.xml
index fb1f1f2b2e..e4651fdc18 100644
--- a/db/install.xml
+++ b/db/install.xml
@@ -27,6 +27,7 @@
         <FIELD NAME="gradecat" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
         <FIELD NAME="anonymous" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="needsreview" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+        <FIELD NAME="allowmultiplemarks" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
diff --git a/db/upgrade.php b/db/upgrade.php
index 8c5b7f2356..03e87bb586 100644
--- a/db/upgrade.php
+++ b/db/upgrade.php
@@ -273,5 +273,22 @@ function xmldb_moodleoverflow_upgrade($oldversion) {
         // Moodleoverflow savepoint reached.
         upgrade_mod_savepoint(true, 2023022400, 'moodleoverflow');
     }
+
+    if ($oldversion < 2023040400) {
+        // Define table moodleoverflow to be edited.
+        $table = new xmldb_table('moodleoverflow');
+
+        // Define field allowmultiplemarks to be added to moodleoverflow
+        $field = new xmldb_field('allowmultiplemarks', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'needsreview');
+
+        //Conditionally launch add field allowmultiplemarks
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Moodleoverflow savepoint reached.
+        upgrade_mod_savepoint(true, 2023040400, 'moodleoverflow');
+    }
+
     return true;
 }
diff --git a/lang/en/moodleoverflow.php b/lang/en/moodleoverflow.php
index d92b8a37c0..bd13b86e7e 100644
--- a/lang/en/moodleoverflow.php
+++ b/lang/en/moodleoverflow.php
@@ -326,6 +326,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/mod_form.php b/mod_form.php
index 1cb85ae630..8e56aa001e 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('checkbox', '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/version.php b/version.php
index d630f8cafc..c600533840 100644
--- a/version.php
+++ b/version.php
@@ -28,7 +28,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 $plugin->component = 'mod_moodleoverflow';
-$plugin->version = 2023022406;
+$plugin->version = 2023040400;
 $plugin->release = 'v4.1-r1';
 $plugin->requires = 2020061500; // Requires Moodle 3.9+.
 $plugin->maturity = MATURITY_STABLE;

From a357fd47851334b0079d02b5b640275cfe6ca99f Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Wed, 5 Apr 2023 16:12:48 +0200
Subject: [PATCH 02/35] 1. feature added, multiple marks are now possible,
 feature not completed. 2. Code checker errors fixed

---
 amd/build/rating.min.js             |  2 +-
 amd/build/rating.min.js.map         |  2 +-
 amd/build/reviewing.min.js.map      |  2 +-
 amd/build/warnmodechange.min.js.map |  2 +-
 amd/src/rating.js                   | 28 +++++++++++++++++++++++-----
 db/access.php                       | 26 +++++++++++++-------------
 db/upgrade.php                      |  4 ++--
 discussion.php                      |  5 ++++-
 externallib.php                     |  8 ++++----
 locallib.php                        |  8 +++++---
 mod_form.php                        |  2 +-
 tests/dailymail_test.php            |  2 +-
 view.php                            |  6 ++++--
 13 files changed, 61 insertions(+), 36 deletions(-)

diff --git a/amd/build/rating.min.js b/amd/build/rating.min.js
index 8683a4e0aa..ce2d7eeadb 100644
--- a/amd/build/rating.min.js
+++ b/amd/build/rating.min.js
@@ -5,6 +5,6 @@ define("mod_moodleoverflow/rating",["exports","core/ajax","core/prefetch","core/
    * @module     mod_moodleoverflow/rating
    * @copyright  2022 Justus Dieckmann WWU
    * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?20:10,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?2:1,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?4:3,rating=shouldRemove?10*baseRating:baseRating;await sendVote(postid,rating,userid);for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}}));
+   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemark){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),0==allowmultiplemark)for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");else shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"));shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}}));
 
 //# sourceMappingURL=rating.min.js.map
\ No newline at end of file
diff --git a/amd/build/rating.min.js.map b/amd/build/rating.min.js.map
index 69367bd7e6..25a7285043 100644
--- a/amd/build/rating.min.js.map
+++ b/amd/build/rating.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n */\nexport function init(userid) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n                for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                    el.classList.remove(htmlclass);\n                    el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                        await getString(`mark${action}`, 'mod_moodleoverflow');\n                }\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","setAttribute","title","otherAction","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAoEqBA,0BACRC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,MAAAA,cACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,MAAAA,mBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SA3Dd,GADE,GA4DsEb,QACjFK,cAAcU,aAAa,4BAA6B,cACxDV,cAAcW,YAAc,mBAAU,UAAYP,OAAQ,0BACvD,OACGQ,YAAcJ,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAlErB,EADE,EAmE+Db,QACnEK,cAAcU,aAAa,4BAA6B,iBAClDG,aAAeP,YAAYQ,qDACGF,mBACpCC,aAAaH,aAAa,4BAA6B,cACvDV,cAAcW,YAAc,mBAAU,iBAAmBP,OAAQ,sBACjES,aAAaF,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKG,UAAuB,YAAXX,OACZY,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAeX,YAAYY,UAAUC,SAASH,WAC9CI,WAAaL,UA7EZ,EADD,EA+EAM,OAASJ,aAA4B,GAAbG,WAAkBA,iBAC1CX,SAASF,OAAQc,OAAQ1B,YAC1B,MAAM2B,MAAMzB,KAAK0B,iBAAiB,uBAAyBP,WAC5DM,GAAGJ,UAAUM,OAAOR,WACpBM,GAAGR,qDAA8CV,cAAYqB,kBACnD,iCAAiBrB,QAAU,sBAEpCa,eACDX,YAAYY,UAAUQ,IAAIV,WAC1BhB,cAAcyB,kBAAoB,oCAAoBrB,QAAU,iHArF9EP,KAAO8B,SAASC,eAAe,sCAStBnB,SAASF,OAAQc,OAAQ1B,cAC9BkC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACF1B,OAAQA,OACR2B,SAAUb,WAEd,UACJxB,KAAK0B,gEAAyD5B,cAAYwC,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,mBAE7BxC,KAAK0B,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,mBAE7B1C,KAAK0B,gEAAyDhB,cAAY4B,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,cAEtBX"}
\ No newline at end of file
+{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {int} allowmultiplemark   // 1 means allowed, 0 means not allowed.\n *\n */\nexport function init(userid, allowmultiplemark) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (allowmultiplemark == 0) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","allowmultiplemark","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAsEqBA,OAAQC,qCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAON,GAArBC,sBAEK,MAAMiC,MAAM/B,KAAKgC,iBAAiB,uBAAyBT,WAC5DQ,GAAGN,UAAUQ,OAAOV,WACpBQ,GAAGV,qDAA8Cd,cAAY2B,kBACnD,iCAAiB3B,QAAU,2BAIrCiB,eACAf,YAAYgB,UAAUQ,OAAOV,WAC7BpB,cAAc+B,kBAAoB,iCAAiB3B,QAAU,uBAIhEiB,eACDf,YAAYgB,UAAUU,IAAIZ,WAC1BpB,cAAc+B,kBAAoB,oCAAoB3B,QAAU,iHA9G9EY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOoC,SAASC,eAAe,sCAStBzB,SAASF,OAAQoB,OAAQjC,cAC9ByC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,UACJ9B,KAAKgC,gEAAyDnC,cAAY+C,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,eAAzB,IAEJ9C,KAAKgC,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,eAAzB,IAEJhD,KAAKgC,gEAAyDtB,cAAYkC,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,UAAzB,IAEGX"}
\ No newline at end of file
diff --git a/amd/build/reviewing.min.js.map b/amd/build/reviewing.min.js.map
index d5002e736d..13a68e7925 100644
--- a/amd/build/reviewing.min.js.map
+++ b/amd/build/reviewing.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"reviewing.min.js","sources":["../src/reviewing.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements reviewing functionality\n *\n * @module     mod_moodleoverflow/reviewing\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Init function.\n */\nexport function init() {\n    Prefetch.prefetchTemplates(['mod_moodleoverflow/reject_post_form', 'mod_moodleoverflow/review_buttons']);\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['post_was_approved', 'jump_to_next_post_needing_review', 'there_are_no_posts_needing_review', 'post_was_rejected']);\n\n    const root = document.getElementById('moodleoverflow-posts');\n    root.onclick = async(e) => {\n        const action = e.target.getAttribute('data-moodleoverflow-action');\n\n        if (!action) {\n            return;\n        }\n\n        const post = e.target.closest('*[data-moodleoverflow-postid]');\n        const reviewRow = e.target.closest('.reviewrow');\n        const postID = post.getAttribute('data-moodleoverflow-postid');\n\n        if (action === 'approve') {\n            reviewRow.innerHTML = '.';\n            const nextPostURL = await Ajax.call([{\n                methodname: 'mod_moodleoverflow_review_approve_post',\n                args: {\n                    postid: postID,\n                }\n            }])[0];\n\n            let message = await getString('post_was_approved', 'mod_moodleoverflow') + ' ';\n            if (nextPostURL) {\n                message += `<a href=\"${nextPostURL}\">`\n                    + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n                    + \"</a>\";\n            } else {\n                message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n            }\n            reviewRow.innerHTML = message;\n            post.classList.remove(\"pendingreview\");\n        } else if (action === 'reject') {\n            reviewRow.innerHTML = '.';\n            reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/reject_post_form', {});\n        } else if (action === 'reject-submit') {\n            const rejectMessage = post.querySelector('textarea.reject-reason').value.toString().trim();\n            reviewRow.innerHTML = '.';\n            const args = {\n                postid: postID,\n                reason: rejectMessage ? rejectMessage : null\n            };\n            const nextPostURL = await Ajax.call([{\n                methodname: 'mod_moodleoverflow_review_reject_post',\n                args: args\n            }])[0];\n\n            let message = await getString('post_was_rejected', 'mod_moodleoverflow') + ' ';\n            if (nextPostURL) {\n                message += `<a href=\"${nextPostURL}\">`\n                    + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n                    + \"</a>\";\n            } else {\n                message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n            }\n            reviewRow.innerHTML = message;\n        } else if (action === 'reject-cancel') {\n            reviewRow.innerHTML = '.';\n            reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/review_buttons', {});\n        }\n    };\n}"],"names":["prefetchTemplates","prefetchStrings","document","getElementById","onclick","async","action","e","target","getAttribute","post","closest","reviewRow","postID","innerHTML","nextPostURL","Ajax","call","methodname","args","postid","message","classList","remove","Templates","render","rejectMessage","querySelector","value","toString","trim","reason"],"mappings":";;;;;;;wGA+BaA,kBAAkB,CAAC,sCAAuC,wDAC1DC,gBAAgB,qBACrB,CAAC,oBAAqB,mCAAoC,oCAAqC,sBAEtFC,SAASC,eAAe,wBAChCC,QAAUC,MAAAA,UACLC,OAASC,EAAEC,OAAOC,aAAa,kCAEhCH,oBAICI,KAAOH,EAAEC,OAAOG,QAAQ,iCACxBC,UAAYL,EAAEC,OAAOG,QAAQ,cAC7BE,OAASH,KAAKD,aAAa,iCAElB,YAAXH,OAAsB,CACtBM,UAAUE,UAAY,UAChBC,kBAAoBC,cAAKC,KAAK,CAAC,CACjCC,WAAY,yCACZC,KAAM,CACFC,OAAQP,WAEZ,OAEAQ,cAAgB,mBAAU,oBAAqB,sBAAwB,IAEvEA,SADAN,YACW,mBAAYA,wBACX,mBAAU,mCAAoC,sBACpD,aAEW,mBAAU,oCAAqC,sBAEpEH,UAAUE,UAAYO,QACtBX,KAAKY,UAAUC,OAAO,sBACnB,GAAe,WAAXjB,OACPM,UAAUE,UAAY,IACtBF,UAAUE,gBAAkBU,mBAAUC,OAAO,sCAAuC,SACjF,GAAe,kBAAXnB,OAA4B,OAC7BoB,cAAgBhB,KAAKiB,cAAc,0BAA0BC,MAAMC,WAAWC,OACpFlB,UAAUE,UAAY,UAChBK,KAAO,CACTC,OAAQP,OACRkB,OAAQL,eAAgC,MAEtCX,kBAAoBC,cAAKC,KAAK,CAAC,CACjCC,WAAY,wCACZC,KAAMA,QACN,OAEAE,cAAgB,mBAAU,oBAAqB,sBAAwB,IAEvEA,SADAN,YACW,mBAAYA,wBACX,mBAAU,mCAAoC,sBACpD,aAEW,mBAAU,oCAAqC,sBAEpEH,UAAUE,UAAYO,YACJ,kBAAXf,SACPM,UAAUE,UAAY,IACtBF,UAAUE,gBAAkBU,mBAAUC,OAAO,oCAAqC"}
\ No newline at end of file
+{"version":3,"file":"reviewing.min.js","sources":["../src/reviewing.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements reviewing functionality\n *\n * @module     mod_moodleoverflow/reviewing\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Init function.\n */\nexport function init() {\n    Prefetch.prefetchTemplates(['mod_moodleoverflow/reject_post_form', 'mod_moodleoverflow/review_buttons']);\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['post_was_approved', 'jump_to_next_post_needing_review', 'there_are_no_posts_needing_review', 'post_was_rejected']);\n\n    const root = document.getElementById('moodleoverflow-posts');\n    root.onclick = async(e) => {\n        const action = e.target.getAttribute('data-moodleoverflow-action');\n\n        if (!action) {\n            return;\n        }\n\n        const post = e.target.closest('*[data-moodleoverflow-postid]');\n        const reviewRow = e.target.closest('.reviewrow');\n        const postID = post.getAttribute('data-moodleoverflow-postid');\n\n        if (action === 'approve') {\n            reviewRow.innerHTML = '.';\n            const nextPostURL = await Ajax.call([{\n                methodname: 'mod_moodleoverflow_review_approve_post',\n                args: {\n                    postid: postID,\n                }\n            }])[0];\n\n            let message = await getString('post_was_approved', 'mod_moodleoverflow') + ' ';\n            if (nextPostURL) {\n                message += `<a href=\"${nextPostURL}\">`\n                    + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n                    + \"</a>\";\n            } else {\n                message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n            }\n            reviewRow.innerHTML = message;\n            post.classList.remove(\"pendingreview\");\n        } else if (action === 'reject') {\n            reviewRow.innerHTML = '.';\n            reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/reject_post_form', {});\n        } else if (action === 'reject-submit') {\n            const rejectMessage = post.querySelector('textarea.reject-reason').value.toString().trim();\n            reviewRow.innerHTML = '.';\n            const args = {\n                postid: postID,\n                reason: rejectMessage ? rejectMessage : null\n            };\n            const nextPostURL = await Ajax.call([{\n                methodname: 'mod_moodleoverflow_review_reject_post',\n                args: args\n            }])[0];\n\n            let message = await getString('post_was_rejected', 'mod_moodleoverflow') + ' ';\n            if (nextPostURL) {\n                message += `<a href=\"${nextPostURL}\">`\n                    + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n                    + \"</a>\";\n            } else {\n                message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n            }\n            reviewRow.innerHTML = message;\n        } else if (action === 'reject-cancel') {\n            reviewRow.innerHTML = '.';\n            reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/review_buttons', {});\n        }\n    };\n}"],"names":["prefetchTemplates","prefetchStrings","document","getElementById","onclick","async","action","e","target","getAttribute","post","closest","reviewRow","postID","innerHTML","nextPostURL","Ajax","call","methodname","args","postid","message","classList","remove","Templates","render","rejectMessage","querySelector","value","toString","trim","reason"],"mappings":";;;;;;;wGA+BaA,kBAAkB,CAAC,sCAAuC,wDAC1DC,gBAAgB,qBACrB,CAAC,oBAAqB,mCAAoC,oCAAqC,sBAEtFC,SAASC,eAAe,wBAChCC,QAAUC,gBACLC,OAASC,EAAEC,OAAOC,aAAa,kCAEhCH,oBAICI,KAAOH,EAAEC,OAAOG,QAAQ,iCACxBC,UAAYL,EAAEC,OAAOG,QAAQ,cAC7BE,OAASH,KAAKD,aAAa,iCAElB,YAAXH,OAAsB,CACtBM,UAAUE,UAAY,UAChBC,kBAAoBC,cAAKC,KAAK,CAAC,CACjCC,WAAY,yCACZC,KAAM,CACFC,OAAQP,WAEZ,OAEAQ,cAAgB,mBAAU,oBAAqB,sBAAwB,IAEvEA,SADAN,YACW,mBAAYA,wBACX,mBAAU,mCAAoC,sBACpD,aAEW,mBAAU,oCAAqC,sBAEpEH,UAAUE,UAAYO,QACtBX,KAAKY,UAAUC,OAAO,sBACnB,GAAe,WAAXjB,OACPM,UAAUE,UAAY,IACtBF,UAAUE,gBAAkBU,mBAAUC,OAAO,sCAAuC,SACjF,GAAe,kBAAXnB,OAA4B,OAC7BoB,cAAgBhB,KAAKiB,cAAc,0BAA0BC,MAAMC,WAAWC,OACpFlB,UAAUE,UAAY,UAChBK,KAAO,CACTC,OAAQP,OACRkB,OAAQL,eAAgC,MAEtCX,kBAAoBC,cAAKC,KAAK,CAAC,CACjCC,WAAY,wCACZC,KAAMA,QACN,OAEAE,cAAgB,mBAAU,oBAAqB,sBAAwB,IAEvEA,SADAN,YACW,mBAAYA,wBACX,mBAAU,mCAAoC,sBACpD,aAEW,mBAAU,oCAAqC,sBAEpEH,UAAUE,UAAYO,YACJ,kBAAXf,SACPM,UAAUE,UAAY,IACtBF,UAAUE,gBAAkBU,mBAAUC,OAAO,oCAAqC"}
\ No newline at end of file
diff --git a/amd/build/warnmodechange.min.js.map b/amd/build/warnmodechange.min.js.map
index 70683e5540..b4120395cd 100644
--- a/amd/build/warnmodechange.min.js.map
+++ b/amd/build/warnmodechange.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"warnmodechange.min.js","sources":["../src/warnmodechange.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Warns on changing the subscription mode.\n *\n * @module     mod_moodleoverflow/warnmodechange\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {get_string as getString} from 'core/str';\nimport Notification from 'core/notification';\nimport Prefetch from 'core/prefetch';\n\n/**\n * Init function.\n * @param {string} previousSetting\n */\nexport function init(previousSetting) {\n    Prefetch.prefetchStrings('mod_moodleoverflow', ['switchtooptional', 'switchtoauto']);\n    Prefetch.prefetchStrings('moodle', ['confirm', 'cancel']);\n    const form = document.querySelector('form.mform');\n    const select = document.getElementById('id_forcesubscribe');\n    form.onsubmit = async(e) => {\n        const value = select.selectedOptions[0].value;\n        if (value == previousSetting || value == 1 || value == 3) {\n            return;\n        }\n        e.preventDefault();\n        await Notification.confirm(\n            await getString('confirm'),\n            await getString(value == 0 ? 'switchtooptional' : 'switchtoauto', 'mod_moodleoverflow'),\n            await getString('confirm'),\n            await getString('cancel'),\n            () => {\n                // Prevent this listener from preventing the event again.\n                form.onsubmit = undefined;\n                form.requestSubmit(e.submitter);\n            }, undefined);\n    };\n}"],"names":["previousSetting","prefetchStrings","form","document","querySelector","select","getElementById","onsubmit","async","value","selectedOptions","e","preventDefault","Notification","confirm","undefined","requestSubmit","submitter"],"mappings":";;;;;;;oFA8BqBA,mCACRC,gBAAgB,qBAAsB,CAAC,mBAAoB,mCAC3DA,gBAAgB,SAAU,CAAC,UAAW,iBACzCC,KAAOC,SAASC,cAAc,cAC9BC,OAASF,SAASG,eAAe,qBACvCJ,KAAKK,SAAWC,MAAAA,UACNC,MAAQJ,OAAOK,gBAAgB,GAAGD,MACpCA,OAAST,iBAA4B,GAATS,OAAuB,GAATA,QAG9CE,EAAEC,uBACIC,sBAAaC,cACT,mBAAU,iBACV,mBAAmB,GAATL,MAAa,mBAAqB,eAAgB,4BAC5D,mBAAU,iBACV,mBAAU,WAChB,KAEIP,KAAKK,cAAWQ,EAChBb,KAAKc,cAAcL,EAAEM,kBACtBF"}
\ No newline at end of file
+{"version":3,"file":"warnmodechange.min.js","sources":["../src/warnmodechange.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Warns on changing the subscription mode.\n *\n * @module     mod_moodleoverflow/warnmodechange\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {get_string as getString} from 'core/str';\nimport Notification from 'core/notification';\nimport Prefetch from 'core/prefetch';\n\n/**\n * Init function.\n * @param {string} previousSetting\n */\nexport function init(previousSetting) {\n    Prefetch.prefetchStrings('mod_moodleoverflow', ['switchtooptional', 'switchtoauto']);\n    Prefetch.prefetchStrings('moodle', ['confirm', 'cancel']);\n    const form = document.querySelector('form.mform');\n    const select = document.getElementById('id_forcesubscribe');\n    form.onsubmit = async(e) => {\n        const value = select.selectedOptions[0].value;\n        if (value == previousSetting || value == 1 || value == 3) {\n            return;\n        }\n        e.preventDefault();\n        await Notification.confirm(\n            await getString('confirm'),\n            await getString(value == 0 ? 'switchtooptional' : 'switchtoauto', 'mod_moodleoverflow'),\n            await getString('confirm'),\n            await getString('cancel'),\n            () => {\n                // Prevent this listener from preventing the event again.\n                form.onsubmit = undefined;\n                form.requestSubmit(e.submitter);\n            }, undefined);\n    };\n}"],"names":["previousSetting","prefetchStrings","form","document","querySelector","select","getElementById","onsubmit","async","value","selectedOptions","e","preventDefault","Notification","confirm","undefined","requestSubmit","submitter"],"mappings":";;;;;;;oFA8BqBA,mCACRC,gBAAgB,qBAAsB,CAAC,mBAAoB,mCAC3DA,gBAAgB,SAAU,CAAC,UAAW,iBACzCC,KAAOC,SAASC,cAAc,cAC9BC,OAASF,SAASG,eAAe,qBACvCJ,KAAKK,SAAWC,gBACNC,MAAQJ,OAAOK,gBAAgB,GAAGD,MACpCA,OAAST,iBAA4B,GAATS,OAAuB,GAATA,QAG9CE,EAAEC,uBACIC,sBAAaC,cACT,mBAAU,iBACV,mBAAmB,GAATL,MAAa,mBAAqB,eAAgB,4BAC5D,mBAAU,iBACV,mBAAU,WAChB,KAEIP,KAAKK,cAAWQ,EAChBb,KAAKc,cAAcL,EAAEM,kBACtBF,GATP"}
\ No newline at end of file
diff --git a/amd/src/rating.js b/amd/src/rating.js
index 7d504d76c7..7730b52596 100644
--- a/amd/src/rating.js
+++ b/amd/src/rating.js
@@ -65,8 +65,10 @@ async function sendVote(postid, rating, userid) {
  * Init function.
  *
  * @param {int} userid
+ * @param {int} allowmultiplemark   // 1 means allowed, 0 means not allowed.
+ *
  */
-export function init(userid) {
+export function init(userid, allowmultiplemark) {
     Prefetch.prefetchStrings('mod_moodleoverflow',
         ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',
             'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);
@@ -109,11 +111,27 @@ export function init(userid) {
                 const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;
                 const rating = shouldRemove ? baseRating * 10 : baseRating;
                 await sendVote(postid, rating, userid);
-                for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {
-                    el.classList.remove(htmlclass);
-                    el.querySelector(`[data-moodleoverflow-action="${action}"]`).textContent =
-                        await getString(`mark${action}`, 'mod_moodleoverflow');
+
+                /* If multiplemarks are not allowed (that is the default mode): delete all marks.
+                   else: only delete the mark if the post is being unmarked.
+
+                   then add a mark, if the post is being marked.
+                */
+                if (allowmultiplemark == 0) {
+                    // Delete all marks in the discussion
+                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {
+                        el.classList.remove(htmlclass);
+                        el.querySelector(`[data-moodleoverflow-action="${action}"]`).textContent =
+                            await getString(`mark${action}`, 'mod_moodleoverflow');
+                    }
+                } else {
+                    // Remove only the mark of the unmarked post.
+                    if (shouldRemove) {
+                        postElement.classList.remove(htmlclass);
+                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');
+                    }
                 }
+                // If the post is being marked, mark it.
                 if (!shouldRemove) {
                     postElement.classList.add(htmlclass);
                     actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');
diff --git a/db/access.php b/db/access.php
index 6e5fee1b3e..1c5e4b80ff 100644
--- a/db/access.php
+++ b/db/access.php
@@ -111,11 +111,11 @@
     ),
 
     'mod/moodleoverflow:deleteownpost' => array(
-        'captype'              => 'write',
-        'contextlevel'         => CONTEXT_MODULE,
-        'archetypes'           => array(
-            'student'        => CAP_ALLOW,
-            'teacher'        => CAP_ALLOW,
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_MODULE,
+        'archetypes' => array(
+            'student' => CAP_ALLOW,
+            'teacher' => CAP_ALLOW,
             'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
         ),
@@ -123,10 +123,10 @@
     ),
 
     'mod/moodleoverflow:deleteanypost' => array(
-        'captype'              => 'write',
-        'contextlevel'         => CONTEXT_MODULE,
-        'archetypes'           => array(
-            'teacher'        => CAP_ALLOW,
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_MODULE,
+        'archetypes' => array(
+            'teacher' => CAP_ALLOW,
             'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
         ),
@@ -158,10 +158,10 @@
 
     'mod/moodleoverflow:managesubscriptions' => array(
         'riskbitmask' => RISK_SPAM,
-        'captype'              => 'write',
-        'contextlevel'         => CONTEXT_MODULE,
-        'archetypes'           => array(
-            'teacher'        => CAP_ALLOW,
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_MODULE,
+        'archetypes' => array(
+            'teacher' => CAP_ALLOW,
             'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
         ),
diff --git a/db/upgrade.php b/db/upgrade.php
index 03e87bb586..1abb5af03e 100644
--- a/db/upgrade.php
+++ b/db/upgrade.php
@@ -278,10 +278,10 @@ function xmldb_moodleoverflow_upgrade($oldversion) {
         // Define table moodleoverflow to be edited.
         $table = new xmldb_table('moodleoverflow');
 
-        // Define field allowmultiplemarks to be added to moodleoverflow
+        // Define field allowmultiplemarks to be added to moodleoverflow.
         $field = new xmldb_field('allowmultiplemarks', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'needsreview');
 
-        //Conditionally launch add field allowmultiplemarks
+        // Conditionally launch add field allowmultiplemarks.
         if (!$dbman->field_exists($table, $field)) {
             $dbman->add_field($table, $field);
         }
diff --git a/discussion.php b/discussion.php
index e0af3e24c4..cbc7fee593 100644
--- a/discussion.php
+++ b/discussion.php
@@ -50,6 +50,9 @@
     throw new moodle_exception('invalidcourseid');
 }
 
+// Save the allowmultiplemarks setting.
+$marksetting = $DB->get_record('moodleoverflow', array('id' => $moodleoverflow->id), 'allowmultiplemarks');
+
 // Get the related coursemodule and its context.
 if (!$cm = get_coursemodule_from_instance('moodleoverflow', $moodleoverflow->id, $course->id)) {
     throw new moodle_exception('invalidcoursemodule');
@@ -128,7 +131,7 @@
 
 $PAGE->requires->js_call_amd('mod_moodleoverflow/reviewing', 'init');
 
-$PAGE->requires->js_call_amd('mod_moodleoverflow/rating', 'init', [$USER->id]);
+$PAGE->requires->js_call_amd('mod_moodleoverflow/rating', 'init', [$USER->id, $marksetting->allowmultiplemarks]);
 
 // Initiate the page.
 $PAGE->set_title($course->shortname . ': ' . format_string($discussion->name));
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/locallib.php b/locallib.php
index 64b8589ab0..f7f9294d9f 100644
--- a/locallib.php
+++ b/locallib.php
@@ -300,7 +300,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;
@@ -1203,7 +1204,8 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
     }
 
     // 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.
@@ -1268,7 +1270,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.
diff --git a/mod_form.php b/mod_form.php
index 8e56aa001e..d171d23e83 100644
--- a/mod_form.php
+++ b/mod_form.php
@@ -224,7 +224,7 @@ public function definition() {
         $mform->addHelpButton('allownegativereputation', 'allownegativereputation', 'moodleoverflow');
         $mform->setDefault('allownegativereputation', MOODLEOVERFLOW_REPUTATION_NEGATIVE);
 
-        // Allow multiple marks of helpful/solved
+        // Allow multiple marks of helpful/solved.
         $mform->addElement('checkbox', 'allowmultiplemarks', get_string('allowmultiplemarks', 'moodleoverflow'));
         $mform->addHelpButton('allowmultiplemarks', 'allowmultiplemarks', 'moodleoverflow');
         $mform->setDefault('allowmultiplemarks', 0);
diff --git a/tests/dailymail_test.php b/tests/dailymail_test.php
index b6afe4d54e..3064dd674e 100644
--- a/tests/dailymail_test.php
+++ b/tests/dailymail_test.php
@@ -197,7 +197,7 @@ public function test_mail_not_send() {
      */
     public function test_records_removed() {
         global $DB;
-        // create user with maildigest = on.
+        // Create user with maildigest = on.
         $this->helper_create_user_and_discussion('1');
 
         // Now send the mails.
diff --git a/view.php b/view.php
index ad62eca45b..ae87c80bc0 100644
--- a/view.php
+++ b/view.php
@@ -36,6 +36,7 @@
 $page = optional_param('page', 0, PARAM_INT);     // Which page to show.
 $movetopopup = optional_param('movetopopup', 0, PARAM_INT);     // Which Topic to move.
 $linktoforum = optional_param('movetoforum', 0, PARAM_INT);     // Forum to which it is moved.
+
 // Set the parameters.
 $params = array();
 if ($id) {
@@ -61,7 +62,8 @@
     throw new moodle_exception('missingparameter');
 }
 
-
+// Save the allowmultiplemarks setting.
+$marksetting = $DB->get_record('moodleoverflow', array('id' => $moodleoverflow->id), 'allowmultiplemarks');
 
 // Require a login.
 require_login($course, true, $cm);
@@ -87,7 +89,7 @@
 $PAGE->set_title(format_string($moodleoverflow->name));
 $PAGE->set_heading(format_string($course->fullname));
 
-$PAGE->requires->js_call_amd('mod_moodleoverflow/rating', 'init', [$USER->id]);
+$PAGE->requires->js_call_amd('mod_moodleoverflow/rating', 'init', [$USER->id, $marksetting->allowmultiplemarks]);
 
 // Output starts here.
 echo $OUTPUT->header();

From b98a9de382d2a15fee5d50ca98f30280e083392d Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Thu, 6 Apr 2023 12:35:36 +0200
Subject: [PATCH 03/35] comment added for marks setting in discussion.php

---
 discussion.php             |  4 ++++
 lang/en/moodleoverflow.php |  3 +++
 locallib.php               | 31 ++++++++++++++++++++++++++++++-
 3 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/discussion.php b/discussion.php
index cbc7fee593..d5c79bfee5 100644
--- a/discussion.php
+++ b/discussion.php
@@ -142,6 +142,10 @@
 
 // Start the side-output.
 echo $OUTPUT->header();
+
+// Print a text, if multiple marks are allowed.
+moodleoverflow_print_multiplemarks_comment($modulecontext, $post);
+
 echo $OUTPUT->heading(format_string($discussion->name), 1, 'discussionname');
 
 // Guests and users can not subscribe to a discussion.
diff --git a/lang/en/moodleoverflow.php b/lang/en/moodleoverflow.php
index bd13b86e7e..a128ed4a37 100644
--- a/lang/en/moodleoverflow.php
+++ b/lang/en/moodleoverflow.php
@@ -328,6 +328,9 @@
 $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.';
+$string['multiplemarkscommentteacherownpost'] = 'Multiple marks are enabled, several posts can be marked as helpful or solved';
+$string['multiplemarkscommentstudentownpost'] = 'Multiple marks are enabled, you can mark several posts as helpful';
+$string['multiplemarkscommentteacher'] = 'Multiple marks are enabled, several posts can be marked as solved';
 
 // Templates.
 $string['reputation'] = 'Reputation';
diff --git a/locallib.php b/locallib.php
index f7f9294d9f..082931d93e 100644
--- a/locallib.php
+++ b/locallib.php
@@ -876,6 +876,35 @@ function moodleoverflow_user_can_post($modulecontext, $posttoreplyto, $considerr
     return !$considerreviewstatus || $posttoreplyto->reviewed == 1;
 }
 
+/**
+ * Prints a text, if multiple marks are allowed.
+ *
+ * @param stdClass $context        The moodleoverflow context.
+ * @param stdClass $post           The post object.
+ */
+function moodleoverflow_print_multiplemarks_comment($context, $post) {
+    global $USER;
+
+    // Check if the current user is the starter of the discussion.
+    $ownpost = (isloggedin() && ($USER->id == $post->userid));
+
+    // Check if the current user can mark posts as solved.
+    $canmarksolved = (has_capability('mod/moodleoverflow:marksolved', $context));
+    $comment = '';
+
+    if ($ownpost) {
+        if ($canmarksolved) {
+            $comment = get_string('multiplemarkscommentteacherownpost', 'moodleoverflow');
+        } else {
+            $comment = get_string('multiplemarkscommentstudentownpost', 'moodleoverflow');
+        }
+    }
+    else if ($canmarksolved) {
+        $comment = get_string('multiplemarkscommentteacher', 'moodleoverflow');
+    }
+    $comment = $comment . '<br>' . '<br>';
+    echo $comment;
+}
 /**
  * Prints a moodleoverflow discussion.
  *
@@ -888,7 +917,7 @@ function moodleoverflow_user_can_post($modulecontext, $posttoreplyto, $considerr
 function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post) {
     global $USER;
 
-    // Check if the current is the starter of the discussion.
+    // Check if the current user is the starter of the discussion.
     $ownpost = (isloggedin() && ($USER->id == $post->userid));
 
     // Fetch the modulecontext.

From d2d9d2c7b676d275e3a62aa2f938d04a33011171 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Thu, 6 Apr 2023 11:32:15 +0200
Subject: [PATCH 04/35] multiplemarks-checkbox bug fixed

---
 mod_form.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mod_form.php b/mod_form.php
index d171d23e83..4df07d8537 100644
--- a/mod_form.php
+++ b/mod_form.php
@@ -225,7 +225,7 @@ public function definition() {
         $mform->setDefault('allownegativereputation', MOODLEOVERFLOW_REPUTATION_NEGATIVE);
 
         // Allow multiple marks of helpful/solved.
-        $mform->addElement('checkbox', 'allowmultiplemarks', get_string('allowmultiplemarks', 'moodleoverflow'));
+        $mform->addElement('advcheckbox', 'allowmultiplemarks', get_string('allowmultiplemarks', 'moodleoverflow'));
         $mform->addHelpButton('allowmultiplemarks', 'allowmultiplemarks', 'moodleoverflow');
         $mform->setDefault('allowmultiplemarks', 0);
 

From 5b04f0e2c1d83daeb51fd166b8f1c290ae125e1e Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 17 Apr 2023 16:04:07 +0200
Subject: [PATCH 05/35] WIP: change button string if posts are marked

---
 amd/build/rating.min.js.map |   2 +-
 amd/src/rating.js           |  18 +++++
 discussion.php              |  12 ++-
 lang/en/moodleoverflow.php  |   9 +--
 lib.php                     |   6 ++
 locallib.php                | 142 +++++++++++++++++++++++++-----------
 6 files changed, 135 insertions(+), 54 deletions(-)

diff --git a/amd/build/rating.min.js.map b/amd/build/rating.min.js.map
index 25a7285043..0df23bfbb9 100644
--- a/amd/build/rating.min.js.map
+++ b/amd/build/rating.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {int} allowmultiplemark   // 1 means allowed, 0 means not allowed.\n *\n */\nexport function init(userid, allowmultiplemark) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (allowmultiplemark == 0) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","allowmultiplemark","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAsEqBA,OAAQC,qCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAON,GAArBC,sBAEK,MAAMiC,MAAM/B,KAAKgC,iBAAiB,uBAAyBT,WAC5DQ,GAAGN,UAAUQ,OAAOV,WACpBQ,GAAGV,qDAA8Cd,cAAY2B,kBACnD,iCAAiB3B,QAAU,2BAIrCiB,eACAf,YAAYgB,UAAUQ,OAAOV,WAC7BpB,cAAc+B,kBAAoB,iCAAiB3B,QAAU,uBAIhEiB,eACDf,YAAYgB,UAAUU,IAAIZ,WAC1BpB,cAAc+B,kBAAoB,oCAAoB3B,QAAU,iHA9G9EY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOoC,SAASC,eAAe,sCAStBzB,SAASF,OAAQoB,OAAQjC,cAC9ByC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,UACJ9B,KAAKgC,gEAAyDnC,cAAY+C,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,eAAzB,IAEJ9C,KAAKgC,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,eAAzB,IAEJhD,KAAKgC,gEAAyDtB,cAAYkC,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,UAAzB,IAEGX"}
\ No newline at end of file
+{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {int} allowmultiplemark   // 1 means allowed, 0 means not allowed.\n *\n */\nexport function init(userid, allowmultiplemark) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (allowmultiplemark == 0) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                    /*\n                    // Iterate trough all posts in the discussion and check if there are other marked posts.\n                    var othermarkedposts = 0;\n                    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n                        if (el.classList.contains(htmlclass)) {\n                            othermarkedposts = 1;\n                            break;\n                        }\n                    }\n                    // If there are other marked posts, iterate trough each post and update the string to 'also mark as ...' .\n                    // Else: do nothing, because if there is no marked post, the string doesn't change.\n                    if (othermarkedposts == 1) {\n                        for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n                            if (!el.classList.contains(htmlclass)) {\n                                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n                            }\n                        }\n                    }*/\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","allowmultiplemark","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAsEqBA,OAAQC,qCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAON,GAArBC,sBAEK,MAAMiC,MAAM/B,KAAKgC,iBAAiB,uBAAyBT,WAC5DQ,GAAGN,UAAUQ,OAAOV,WACpBQ,GAAGV,qDAA8Cd,cAAY2B,kBACnD,iCAAiB3B,QAAU,2BAIrCiB,eACAf,YAAYgB,UAAUQ,OAAOV,WAC7BpB,cAAc+B,kBAAoB,iCAAiB3B,QAAU,uBAuBhEiB,eACDf,YAAYgB,UAAUU,IAAIZ,WAC1BpB,cAAc+B,kBAAoB,oCAAoB3B,QAAU,iHAjI9EY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOoC,SAASC,eAAe,sCAStBzB,SAASF,OAAQoB,OAAQjC,cAC9ByC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,UACJ9B,KAAKgC,gEAAyDnC,cAAY+C,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,eAAzB,IAEJ9C,KAAKgC,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,eAAzB,IAEJhD,KAAKgC,gEAAyDtB,cAAYkC,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,UAAzB,IAEGX"}
\ No newline at end of file
diff --git a/amd/src/rating.js b/amd/src/rating.js
index 7730b52596..1039a133ce 100644
--- a/amd/src/rating.js
+++ b/amd/src/rating.js
@@ -130,6 +130,24 @@ export function init(userid, allowmultiplemark) {
                         postElement.classList.remove(htmlclass);
                         actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');
                     }
+                    // Iterate trough all posts in the discussion and check if there are other marked posts.
+                    var othermarkedposts = 0;
+                    for (const el of root.querySelectorAll('.moodleoverflowpost')) {
+                        if (el.classList.contains(htmlclass)) {
+                            othermarkedposts = 1;
+                            break;
+                        }
+                    }
+                    // If there are other marked posts, iterate trough each post and update the string to 'also mark as ...' .
+                    // Else: do nothing, because if there is no marked post, the string doesn't change.
+                    if (othermarkedposts == 1) {
+                        for (const el of root.querySelectorAll('.moodleoverflowpost')) {
+                            if (!el.classList.contains(htmlclass)) {
+                                el.querySelector(`[data-moodleoverflow-action="${action}"]`).textContent =
+                                    await getString(`alsomark${action}`, 'mod_moodleoverflow');
+                            }
+                        }
+                    }
                 }
                 // If the post is being marked, mark it.
                 if (!shouldRemove) {
diff --git a/discussion.php b/discussion.php
index d5c79bfee5..d751744169 100644
--- a/discussion.php
+++ b/discussion.php
@@ -143,9 +143,6 @@
 // Start the side-output.
 echo $OUTPUT->header();
 
-// Print a text, if multiple marks are allowed.
-moodleoverflow_print_multiplemarks_comment($modulecontext, $post);
-
 echo $OUTPUT->heading(format_string($discussion->name), 1, 'discussionname');
 
 // Guests and users can not subscribe to a discussion.
@@ -157,7 +154,14 @@
 
 echo '<div id="moodleoverflow-posts"><div id="moodleoverflow-root">';
 
-moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post);
+// if multiplemarks are on, then print the discussion considering the markdata, else print the discussion.
+if ($marksetting) {
+    $multiplemarksdata = moodleoverflow_get_discussion_markdata($d);
+    moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post, $multiplemarksdata);
+} else {
+    moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post);
+}
+
 
 echo '</div></div>';
 
diff --git a/lang/en/moodleoverflow.php b/lang/en/moodleoverflow.php
index a128ed4a37..cd61080e6c 100644
--- a/lang/en/moodleoverflow.php
+++ b/lang/en/moodleoverflow.php
@@ -187,9 +187,11 @@
 $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['marknothelpful'] = 'Not Helpful';
+$string['markhelpful'] = 'Mark as helpful';
+$string['alsomarkhelpful'] = 'Also mark as helpful';
+$string['marknothelpful'] = 'Not helpful';
 $string['answer'] = '{$a} Answer';
 $string['answers'] = '{$a} Answers';
 
@@ -328,9 +330,6 @@
 $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.';
-$string['multiplemarkscommentteacherownpost'] = 'Multiple marks are enabled, several posts can be marked as helpful or solved';
-$string['multiplemarkscommentstudentownpost'] = 'Multiple marks are enabled, you can mark several posts as helpful';
-$string['multiplemarkscommentteacher'] = 'Multiple marks are enabled, several posts can be marked as solved';
 
 // Templates.
 $string['reputation'] = 'Reputation';
diff --git a/lib.php b/lib.php
index 980856a25f..01ced85c7c 100644
--- a/lib.php
+++ b/lib.php
@@ -80,6 +80,12 @@
 define('RATING_HELPFUL', 4);
 define('RATING_REMOVE_HELPFUL', 40);
 
+// Mark constants.
+define('MOODLEOVERFLOW_NEITHERHELPFULORSOLVED', 0);
+define('MOODLEOVERFLOW_HELPFUL', 1);
+define('MOODLEOVERFLOW_SOLVED', 2);
+define('MOODLEOVERFLOW_HELPFULANDSOLVED', 3);
+
 /* Moodle core API */
 
 /**
diff --git a/locallib.php b/locallib.php
index 082931d93e..98e42e3588 100644
--- a/locallib.php
+++ b/locallib.php
@@ -453,6 +453,42 @@ function moodleoverflow_print_forum_list($course, $cm, $movetopopup) {
     echo $renderer->render_forum_list($mustachedata);
 }
 
+
+/**
+ * Get the data about a the marks of a discussion:
+ * - if a helpful or solved post exists
+ * - which posts are helpful/solved
+ *
+ * Return an object with the data
+ *
+ * @param int $discussionid
+ * @return object
+ */
+function moodleoverflow_get_discussion_markdata($discussionid) {
+    $helpful = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussionid, false);
+    $solved = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussionid, true);
+    $markdata = new \stdClass();
+    $markdata->markstatus = 0;
+    $markdata->helpfulpostid = false;
+    $markdata->solvedpostid = false;
+    if ($helpful) {
+        if ($solved) {
+            $markdata->markstatus = 3;
+            $markdata->helpfulpostid = $helpful->postid;
+            $markdata->solvedpostid = $solved->postid;
+        } else {
+            $markdata->markstatus = 2;
+            $markdata->helpfulpostid = $helpful->postid;
+        }
+    } else {
+        if ($solved) {
+            $markdata->markstatus = 1;
+            $markdata->solvedpostid = $solved->postid;
+        }
+    }
+
+    return $markdata;
+}
 /**
  * Returns an array of counts of replies for each discussion.
  *
@@ -876,35 +912,7 @@ function moodleoverflow_user_can_post($modulecontext, $posttoreplyto, $considerr
     return !$considerreviewstatus || $posttoreplyto->reviewed == 1;
 }
 
-/**
- * Prints a text, if multiple marks are allowed.
- *
- * @param stdClass $context        The moodleoverflow context.
- * @param stdClass $post           The post object.
- */
-function moodleoverflow_print_multiplemarks_comment($context, $post) {
-    global $USER;
-
-    // Check if the current user is the starter of the discussion.
-    $ownpost = (isloggedin() && ($USER->id == $post->userid));
-
-    // Check if the current user can mark posts as solved.
-    $canmarksolved = (has_capability('mod/moodleoverflow:marksolved', $context));
-    $comment = '';
 
-    if ($ownpost) {
-        if ($canmarksolved) {
-            $comment = get_string('multiplemarkscommentteacherownpost', 'moodleoverflow');
-        } else {
-            $comment = get_string('multiplemarkscommentstudentownpost', 'moodleoverflow');
-        }
-    }
-    else if ($canmarksolved) {
-        $comment = get_string('multiplemarkscommentteacher', 'moodleoverflow');
-    }
-    $comment = $comment . '<br>' . '<br>';
-    echo $comment;
-}
 /**
  * Prints a moodleoverflow discussion.
  *
@@ -913,8 +921,9 @@ function moodleoverflow_print_multiplemarks_comment($context, $post) {
  * @param stdClass $moodleoverflow The moodleoverflow object
  * @param stdClass $discussion     The discussion object
  * @param stdClass $post           The post object
+ * @param boolean  $markdata       Object with information about marked posts. Only needed when multiplemarks are allowed
  */
-function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post) {
+function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post, $markdata = false) {
     global $USER;
 
     // Check if the current user is the starter of the discussion.
@@ -966,9 +975,16 @@ function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discuss
     // Check if the post was read.
     $postread = !empty($post->postread);
 
-    // Print the starting post.
-    echo moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
-        $ownpost, false, '', '', $postread, true, $istracked, 0, $usermapping);
+    // Check if there is markdata, if yes, print the starting post considering the markdata.
+    if ($markdata == false) {
+        // Print the starting post.
+        echo moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
+                                       $ownpost, false, '', '', $postread, true, $istracked, 0, $usermapping);
+    } else {
+        // Print the starting post.
+        echo moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
+                                       $ownpost, false, '', '', $postread, true, $istracked, 0, $usermapping, 0, $markdata);
+    }
 
     // Print answer divider.
     if ($answercount == 1) {
@@ -980,9 +996,16 @@ function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discuss
 
     echo '<div id="moodleoverflow-posts">';
 
-    // Print the other posts.
-    echo moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow, $discussion, $post, $istracked, $posts,
-        null, $usermapping);
+    // Check if there is markdata, if yes, print the other posts considering the markdata.
+    if ($markdata == false) {
+        // Print the other posts.
+        echo moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow, $discussion, $post, $istracked, $posts,
+                                               null, $usermapping);
+    } else {
+        // Print the other posts.
+        echo moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow, $discussion, $post, $istracked, $posts,
+                                               null, $usermapping, $markdata);
+    }
 
     echo '</div>';
 }
@@ -1110,6 +1133,7 @@ function moodleoverflow_get_all_discussion_posts($discussionid, $tracking, $modc
  * @param bool $iscomment
  * @param array $usermapping
  * @param int $level
+ * @param object $markdata      Information about marked posts in a discussion, only needed if multiplemarks are allowed
  * @return void|null
  * @throws coding_exception
  * @throws dml_exception
@@ -1119,8 +1143,8 @@ 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) {
-    global $USER, $CFG, $OUTPUT, $PAGE;
+                                   $iscomment = false, $usermapping = [], $level = 0, $markdata = false) {
+    global $USER, $CFG, $OUTPUT, $PAGE, $DB;
 
     // Require the filelib.
     require_once($CFG->libdir . '/filelib.php');
@@ -1176,8 +1200,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');
     }
 
@@ -1227,8 +1253,16 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
             $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 is markdata: consider it.
+            // Markdata saves the ID of a marked post in the discussion.
+            // If multiplemarks are allowed, other posts than the marked post will have another string.
+            if ($markdata != false && $markdata->helpfulpostid != false && $post->id != $markdata->helpfulpostid) {
+                $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'));
+            }
         }
     }
 
@@ -1243,8 +1277,16 @@ 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 is markdata: consider it.
+            // Markdata saves the ID of a marked post in the discussion.
+            // If multiplemarks are allowed, other posts than the marked post will have another string.
+            if ($markdata != false && $markdata->solvedpostid != false && $post->id != $markdata->solvedpostid) {
+                $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'));
+            }
         }
     }
 
@@ -1465,13 +1507,14 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
  * @param array  $posts          Array of posts within the discussion
  * @param bool   $iscomment      Whether the current post is a comment
  * @param array $usermapping
+ * @param object $markdata       Information about marked posts in a discussion, only needed if multiplemarks are allowed
  * @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 = [], $markdata = false) {
     global $USER;
 
     // Prepare the output.
@@ -1511,13 +1554,24 @@ function moodleoverflow_print_posts_nested($course, &$cm, $moodleoverflow, $disc
             // Determine whether the post has been read by the current user.
             $postread = !empty($post->postread);
 
-            // Print the answer.
-            $output .= moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
+            // Check if there is markdata, if yes print the answer and children considering the markdata.
+            if ($markdata == false) {
+                // Print the answer.
+                $output .= moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
                 $ownpost, false, '', '', $postread, true, $istracked, $parentid, $usermapping, $level);
 
-            // Print its children.
-            $output .= moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow,
+                // Print its children.
+                $output .= moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow,
                 $discussion, $post, $istracked, $posts, $parentid, $usermapping);
+            } else {
+                // Print the answer.
+                $output .= moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
+                           $ownpost, false, '', '', $postread, true, $istracked, $parentid, $usermapping, $level, $markdata);
+
+                // Print its children.
+                $output .= moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow,
+                           $discussion, $post, $istracked, $posts, $parentid, $usermapping, $markdata);
+            }
 
             // End the div.
             $output .= "</div>\n";

From f08df49d4dd2c3e9bcf76e98ff4cc78e1a69fb82 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Tue, 18 Apr 2023 17:58:12 +0200
Subject: [PATCH 06/35] WIP: change button strings is not working

---
 amd/build/rating.min.js     |  2 +-
 amd/build/rating.min.js.map |  2 +-
 amd/src/rating.js           | 16 ++++++++++------
 classes/ratings.php         | 27 +++++++++++++++------------
 discussion.php              |  5 +++--
 locallib.php                |  9 +++++++--
 6 files changed, 37 insertions(+), 24 deletions(-)

diff --git a/amd/build/rating.min.js b/amd/build/rating.min.js
index ce2d7eeadb..b88babf908 100644
--- a/amd/build/rating.min.js
+++ b/amd/build/rating.min.js
@@ -5,6 +5,6 @@ define("mod_moodleoverflow/rating",["exports","core/ajax","core/prefetch","core/
    * @module     mod_moodleoverflow/rating
    * @copyright  2022 Justus Dieckmann WWU
    * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemark){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),0==allowmultiplemark)for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");else shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"));shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}}));
+   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemark){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","alsomarksolved","marknotsolved","markhelpful","alsomarkhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),0==allowmultiplemark)for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");else shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"));shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}}));
 
 //# sourceMappingURL=rating.min.js.map
\ No newline at end of file
diff --git a/amd/build/rating.min.js.map b/amd/build/rating.min.js.map
index 0df23bfbb9..b848a3e4d6 100644
--- a/amd/build/rating.min.js.map
+++ b/amd/build/rating.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {int} allowmultiplemark   // 1 means allowed, 0 means not allowed.\n *\n */\nexport function init(userid, allowmultiplemark) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (allowmultiplemark == 0) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                    /*\n                    // Iterate trough all posts in the discussion and check if there are other marked posts.\n                    var othermarkedposts = 0;\n                    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n                        if (el.classList.contains(htmlclass)) {\n                            othermarkedposts = 1;\n                            break;\n                        }\n                    }\n                    // If there are other marked posts, iterate trough each post and update the string to 'also mark as ...' .\n                    // Else: do nothing, because if there is no marked post, the string doesn't change.\n                    if (othermarkedposts == 1) {\n                        for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n                            if (!el.classList.contains(htmlclass)) {\n                                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n                            }\n                        }\n                    }*/\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","allowmultiplemark","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAsEqBA,OAAQC,qCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAON,GAArBC,sBAEK,MAAMiC,MAAM/B,KAAKgC,iBAAiB,uBAAyBT,WAC5DQ,GAAGN,UAAUQ,OAAOV,WACpBQ,GAAGV,qDAA8Cd,cAAY2B,kBACnD,iCAAiB3B,QAAU,2BAIrCiB,eACAf,YAAYgB,UAAUQ,OAAOV,WAC7BpB,cAAc+B,kBAAoB,iCAAiB3B,QAAU,uBAuBhEiB,eACDf,YAAYgB,UAAUU,IAAIZ,WAC1BpB,cAAc+B,kBAAoB,oCAAoB3B,QAAU,iHAjI9EY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOoC,SAASC,eAAe,sCAStBzB,SAASF,OAAQoB,OAAQjC,cAC9ByC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,UACJ9B,KAAKgC,gEAAyDnC,cAAY+C,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,eAAzB,IAEJ9C,KAAKgC,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,eAAzB,IAEJhD,KAAKgC,gEAAyDtB,cAAYkC,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,UAAzB,IAEGX"}
\ No newline at end of file
+{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {int} allowmultiplemark   // 1 means allowed, 0 means not allowed.\n *\n */\nexport function init(userid, allowmultiplemark) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'alsomarksolved', 'marknotsolved', 'markhelpful', 'alsomarkhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (allowmultiplemark == 0) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                    /*// Iterate trough all posts in the discussion and check if there are other marked posts.\n                    var othermarkedposts = 0;\n                    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n                        if (el.classList.contains(htmlclass)) {\n                            othermarkedposts = 1;\n                            break;\n                        }\n                    }\n                    // If there are other marked posts, iterate trough each post and update the string to 'also mark as ...' .\n                    // Else: do nothing, because if there is no marked post, the string doesn't change.\n                    if (othermarkedposts == 1) {\n                        for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n                            if (el.classList.contains(htmlclass)) {\n\n                                //el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                                    //await getString(`alsomark${action}`, 'mod_moodleoverflow');\n                            }\n                            else {\n\n                            }\n                        }\n                    }*/\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","allowmultiplemark","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAsEqBA,OAAQC,qCAChBC,gBAAgB,qBACrB,CAAC,aAAc,iBAAkB,gBAAiB,cAAe,kBAAmB,iBAChF,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAON,GAArBC,sBAEK,MAAMiC,MAAM/B,KAAKgC,iBAAiB,uBAAyBT,WAC5DQ,GAAGN,UAAUQ,OAAOV,WACpBQ,GAAGV,qDAA8Cd,cAAY2B,kBACnD,iCAAiB3B,QAAU,2BAIrCiB,eACAf,YAAYgB,UAAUQ,OAAOV,WAC7BpB,cAAc+B,kBAAoB,iCAAiB3B,QAAU,uBA0BhEiB,eACDf,YAAYgB,UAAUU,IAAIZ,WAC1BpB,cAAc+B,kBAAoB,oCAAoB3B,QAAU,iHApI9EY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOoC,SAASC,eAAe,sCAStBzB,SAASF,OAAQoB,OAAQjC,cAC9ByC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,UACJ9B,KAAKgC,gEAAyDnC,cAAY+C,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,eAAzB,IAEJ9C,KAAKgC,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,eAAzB,IAEJhD,KAAKgC,gEAAyDtB,cAAYkC,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,UAAzB,IAEGX"}
\ No newline at end of file
diff --git a/amd/src/rating.js b/amd/src/rating.js
index 1039a133ce..0fb87925b1 100644
--- a/amd/src/rating.js
+++ b/amd/src/rating.js
@@ -70,7 +70,7 @@ async function sendVote(postid, rating, userid) {
  */
 export function init(userid, allowmultiplemark) {
     Prefetch.prefetchStrings('mod_moodleoverflow',
-        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',
+        ['marksolved', 'alsomarksolved', 'marknotsolved', 'markhelpful', 'alsomarkhelpful', 'marknothelpful',
             'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);
 
     root.onclick = async(event) => {
@@ -130,7 +130,7 @@ export function init(userid, allowmultiplemark) {
                         postElement.classList.remove(htmlclass);
                         actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');
                     }
-                    // Iterate trough all posts in the discussion and check if there are other marked posts.
+                    /*// Iterate trough all posts in the discussion and check if there are other marked posts.
                     var othermarkedposts = 0;
                     for (const el of root.querySelectorAll('.moodleoverflowpost')) {
                         if (el.classList.contains(htmlclass)) {
@@ -142,12 +142,16 @@ export function init(userid, allowmultiplemark) {
                     // Else: do nothing, because if there is no marked post, the string doesn't change.
                     if (othermarkedposts == 1) {
                         for (const el of root.querySelectorAll('.moodleoverflowpost')) {
-                            if (!el.classList.contains(htmlclass)) {
-                                el.querySelector(`[data-moodleoverflow-action="${action}"]`).textContent =
-                                    await getString(`alsomark${action}`, 'mod_moodleoverflow');
+                            if (el.classList.contains(htmlclass)) {
+
+                                //el.querySelector(`[data-moodleoverflow-action="${action}"]`).textContent =
+                                    //await getString(`alsomark${action}`, 'mod_moodleoverflow');
+                            }
+                            else {
+
                             }
                         }
-                    }
+                    }*/
                 }
                 // If the post is being marked, mark it.
                 if (!shouldRemove) {
diff --git a/classes/ratings.php b/classes/ratings.php
index 899755d6d1..94beee681a 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -46,7 +46,7 @@ class ratings {
      *
      * @return bool|int
      */
-    public static function moodleoverflow_add_rating($moodleoverflow, $postid, $rating, $cm, $userid = null) {
+    public static function moodleoverflow_add_rating($moodleoverflow, $postid, $rating, $cm, $userid = null, $allowmultiplemarks = 0) {
         global $DB, $USER, $SESSION;
 
         // Has a user been submitted?
@@ -138,20 +138,23 @@ public static function moodleoverflow_add_rating($moodleoverflow, $postid, $rati
                 throw new moodle_exception('notteacher', 'moodleoverflow');
             }
 
-            // Get other ratings in the discussion.
-            $sql = "SELECT *
-                    FROM {moodleoverflow_ratings}
-                    WHERE discussionid = ? AND rating = ?";
-            $otherrating = $DB->get_record_sql($sql, [ $discussion->id, $rating ]);
+            if ($allowmultiplemarks == 0) {
+                // Get other ratings in the discussion.
+                $sql = "SELECT *
+                        FROM {moodleoverflow_ratings}
+                        WHERE discussionid = ? AND rating = ?";
+                $otherrating = $DB->get_record_sql($sql, [ $discussion->id, $rating ]);
 
-            // If there is an old rating, update it. Else create a new rating record.
-            if ($otherrating) {
-                return self::moodleoverflow_update_rating_record($post->id, $rating, $userid, $otherrating->id, $modulecontext);
-            } else {
-                $mid = $moodleoverflow->id;
+                // If there is an old rating, update it. Else create a new rating record.
+                if ($otherrating) {
+                    return self::moodleoverflow_update_rating_record($post->id, $rating, $userid, $otherrating->id, $modulecontext);
+                } else {
+                    $mid = $moodleoverflow->id;
 
-                return self::moodleoverflow_add_rating_record($mid, $discussion->id, $post->id, $rating, $userid, $modulecontext);
+                    return self::moodleoverflow_add_rating_record($mid, $discussion->id, $post->id, $rating, $userid, $modulecontext);
+                }
             }
+
         }
 
         // Update an rating record.
diff --git a/discussion.php b/discussion.php
index d751744169..7e81d33195 100644
--- a/discussion.php
+++ b/discussion.php
@@ -76,7 +76,8 @@
 
     if (in_array($ratingid, array(RATING_SOLVED, RATING_REMOVE_SOLVED, RATING_HELPFUL, RATING_REMOVE_HELPFUL))) {
         // Rate the post.
-        if (!\mod_moodleoverflow\ratings::moodleoverflow_add_rating($moodleoverflow, $ratedpost, $ratingid, $cm)) {
+        if (!\mod_moodleoverflow\ratings::moodleoverflow_add_rating($moodleoverflow, $ratedpost, $ratingid, $cm, null,
+                                                                    $marksetting->allowmultiplemarks)) {
             throw new moodle_exception('ratingfailed', 'moodleoverflow');
         }
 
@@ -155,7 +156,7 @@
 echo '<div id="moodleoverflow-posts"><div id="moodleoverflow-root">';
 
 // if multiplemarks are on, then print the discussion considering the markdata, else print the discussion.
-if ($marksetting) {
+if ($marksetting->allowmultiplemarks == 1) {
     $multiplemarksdata = moodleoverflow_get_discussion_markdata($d);
     moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post, $multiplemarksdata);
 } else {
diff --git a/locallib.php b/locallib.php
index 98e42e3588..22e1d96211 100644
--- a/locallib.php
+++ b/locallib.php
@@ -1242,6 +1242,9 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
     $permalink = new moodle_url($discussionlink);
     $permalink->set_anchor('p' . $post->id);
 
+    // Save the setting of multiplemarks.
+    $multiplemarkssetting = $DB->get_record('moodleoverflow', array('id' => $moodleoverflow->id), 'allowmultiplemarks');
+
     // 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));
@@ -1256,7 +1259,8 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
             // If there is markdata: consider it.
             // Markdata saves the ID of a marked post in the discussion.
             // If multiplemarks are allowed, other posts than the marked post will have another string.
-            if ($markdata != false && $markdata->helpfulpostid != false && $post->id != $markdata->helpfulpostid) {
+            if ($multiplemarkssetting->allowmultiplemarks == 1 && $markdata != false && $markdata->helpfulpostid != false
+                                           && $post->id != $markdata->helpfulpostid) {
                 $commands[] = html_writer::tag('a', $str->alsomarkhelpful,
                     array('class' => 'markhelpful onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'helpful'));
             } else {
@@ -1280,7 +1284,8 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
             // If there is markdata: consider it.
             // Markdata saves the ID of a marked post in the discussion.
             // If multiplemarks are allowed, other posts than the marked post will have another string.
-            if ($markdata != false && $markdata->solvedpostid != false && $post->id != $markdata->solvedpostid) {
+            if ($multiplemarkssetting->allowmultiplemarks == 1 && $markdata != false && $markdata->solvedpostid != false
+                                           && $post->id != $markdata->solvedpostid) {
                 $commands[] = html_writer::tag('a', $str->alsomarksolved,
                     array('class' => 'marksolved onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'solved'));
             } else {

From 0c6855dff3c509b1148a78672d4728764d08a26e Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 24 Apr 2023 14:36:14 +0200
Subject: [PATCH 07/35] feature problems solved, multiple marks are now really
 possible

---
 classes/ratings.php | 162 ++++++++++++++++++++++++++++++++++----------
 locallib.php        |   1 +
 2 files changed, 126 insertions(+), 37 deletions(-)

diff --git a/classes/ratings.php b/classes/ratings.php
index 899755d6d1..7d26e04aad 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -77,6 +77,12 @@ public static function moodleoverflow_add_rating($moodleoverflow, $postid, $rati
             throw new moodle_exception('invalidcourseid');
         }
 
+        // Are multiple marks allowed?
+        $markssetting = $DB->get_record('moodleoverflow', array('id' => $moodleoverflow->id), 'allowmultiplemarks');
+        $multiplemarks = false;
+        if ($markssetting->allowmultiplemarks == 1) {
+            $multiplemarks = true;
+        }
         // Retrieve the contexts.
         $modulecontext = \context_module::instance($cm->id);
         $coursecontext = \context_course::instance($course->id);
@@ -138,20 +144,28 @@ public static function moodleoverflow_add_rating($moodleoverflow, $postid, $rati
                 throw new moodle_exception('notteacher', 'moodleoverflow');
             }
 
-            // Get other ratings in the discussion.
-            $sql = "SELECT *
-                    FROM {moodleoverflow_ratings}
-                    WHERE discussionid = ? AND rating = ?";
-            $otherrating = $DB->get_record_sql($sql, [ $discussion->id, $rating ]);
-
-            // If there is an old rating, update it. Else create a new rating record.
-            if ($otherrating) {
-                return self::moodleoverflow_update_rating_record($post->id, $rating, $userid, $otherrating->id, $modulecontext);
+            // Check if multiple marks are not enabled.
+            if (!$multiplemarks) {
+                // Get other ratings in the discussion.
+                $sql = "SELECT *
+                        FROM {moodleoverflow_ratings}
+                        WHERE discussionid = ? AND rating = ?";
+                $otherrating = $DB->get_record_sql($sql, [ $discussion->id, $rating ]);
+
+                // If there is an old rating, update it. Else create a new rating record.
+                if ($otherrating) {
+                    return self::moodleoverflow_update_rating_record($post->id, $rating, $userid, $otherrating->id, $modulecontext);
+                } else {
+                    $mid = $moodleoverflow->id;
+
+                    return self::moodleoverflow_add_rating_record($mid, $discussion->id, $post->id, $rating, $userid, $modulecontext);
+                }
             } else {
+                // If multiplemarks are allowed, only create a new rating.
                 $mid = $moodleoverflow->id;
-
                 return self::moodleoverflow_add_rating_record($mid, $discussion->id, $post->id, $rating, $userid, $modulecontext);
             }
+            
         }
 
         // Update an rating record.
@@ -247,56 +261,96 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
         $statusstarter = self::moodleoverflow_discussion_is_solved($discussionid, false);
         $statusteacher = self::moodleoverflow_discussion_is_solved($discussionid, true);
 
-        // The answer that is marked as correct by both is displayed first.
+        // The answers that are marked as correct by both are displayed first.
         if ($statusteacher && $statusstarter) {
+            $markedposts = array();
+            foreach ($statusteacher as $solvedposts ) {
+                foreach ($statusstarter as $helpfulposts) {
+                    // Is the same answer correct for both? 
+                    if ($solvedposts->postid == $helpfulposts->postid) {
+                        // Save the post that is marked as solved and helpful and go to the next post.
+                        $markedposts[] = $postscopy[$solvedposts->postid];
+                        break;
+                    }
+                }
+            }
+            // Now sort the posts by their votes.
+            self::moodleoverflow_sort_post_by_votes($markedposts);
 
-            // Is the same answer correct for both?
-            if ($statusstarter->postid == $statusteacher->postid) {
-
+            // Iterate trough the marked posts and add it to the new Order.
+            foreach ($markedposts as $post) {
                 // Add the post to the new order and delete it from the posts array.
-                $neworder[] = (int) $statusstarter->postid;
-                unset($postscopy[$statusstarter->postid]);
-
-                // Unset the stati to skip the following if-statements.
-                $statusstarter = false;
-                $statusteacher = false;
+                $neworder[] = (int) $post->id;
+                unset($postscopy[$post->id]);
             }
         }
 
         // If the answers the teacher marks are preferred, and only
         // the teacher marked an answer as solved, display it first.
         if ($preferteacher && $statusteacher) {
+            // Save the marked posts
+            $markedposts = array();
+            foreach ($statusteacher as $solvedpost) {
+                if(array_key_exists($solvedpost->postid, $postscopy)) {
+                    $markedposts[] = $postscopy[$solvedpost->postid];
+                }
+            }
+            // Sort the solved answers.
+            self::moodleoverflow_sort_post_by_votes($markedposts);
 
-            // Add the post to the new order and delete it from the posts array.
-            $neworder[] = (int) $statusteacher->postid;
-            unset($postscopy[$statusteacher->postid]);
-
-            // Unset the status to skip the following if-statements.
-            $statusteacher = false;
+            // Iterate trough the marked posts and add it to the new Order.
+            foreach ($markedposts as $post) {
+                // Add the post to the new order and delete it from the posts array.
+                $neworder[] = (int) $post->id;
+                unset($postscopy[$post->id]);
+            }
         }
 
         // If the user who started the discussion has marked
         // an answer as helpful, display this answer first.
         if ($statusstarter) {
+            // Save the marked posts
+            $markedposts = array();
+            foreach ($statusstarter as $helpfulpost) {
+                if(array_key_exists($helpfulpost->postid, $postscopy)) {
+                    $markedposts[] = $postscopy[$helpfulpost->postid];
+                }
+            }
+            // Sort the helpful answers.
+            self::moodleoverflow_sort_post_by_votes($markedposts);
 
-            // Add the post to the new order and delete it from the posts array.
-            $neworder[] = (int) $statusstarter->postid;
-            unset($postscopy[$statusstarter->postid]);
+            // Iterate trough the marked posts and add it to the new Order.
+            foreach ($markedposts as $post) {
+                // Add the post to the new order and delete it from the posts array.
+                $neworder[] = (int) $post->id;
+                unset($postscopy[$post->id]);
+            }
         }
 
         // If a teacher has marked an answer as solved, display it next.
         if ($statusteacher) {
+            // Save the marked posts
+            $markedposts = array();
+            foreach ($statusteacher as $solvedpost) {
+                if(array_key_exists($solvedpost->postid, $postscopy)) {
+                    $markedposts[] = $postscopy[$solvedpost->postid];
+                }
+            }
+            // Sort the solved answers.
+            self::moodleoverflow_sort_post_by_votes($markedposts);
 
-            // Add the post to the new order and delete it from the posts array.
-            $neworder[] = (int) $statusteacher->postid;
-            unset($postscopy[$statusteacher->postid]);
+            // Iterate trough the marked posts and add it to the new Order.
+            foreach ($markedposts as $post) {
+                // Add the post to the new order and delete it from the posts array.
+                $neworder[] = (int) $post->id;
+                unset($postscopy[$post->id]);
+            }
         }
 
         // All answers that are not marked by someone should now be left.
 
         // Search for all comments.
         foreach ($postscopy as $postid => $post) {
-
             // Add all comments to the order.
             // They are independant from the votes.
             if ($post->parent != $parent->id) {
@@ -426,8 +480,8 @@ public static function moodleoverflow_discussion_is_solved($discussionid, $teach
             // Check if a teacher marked a solution as solved.
             if ($DB->record_exists('moodleoverflow_ratings', array('discussionid' => $discussionid, 'rating' => 3))) {
 
-                // Return the rating record.
-                return $DB->get_record('moodleoverflow_ratings', array('discussionid' => $discussionid, 'rating' => 3));
+                // Return the rating records.
+                return $DB->get_records('moodleoverflow_ratings', array('discussionid' => $discussionid, 'rating' => 3));
             }
 
             // The teacher has not marked the discussion as solved.
@@ -437,8 +491,8 @@ public static function moodleoverflow_discussion_is_solved($discussionid, $teach
         // Check if the topic starter marked a solution as helpful.
         if ($DB->record_exists('moodleoverflow_ratings', array('discussionid' => $discussionid, 'rating' => 4))) {
 
-            // Return the rating record.
-            return $DB->get_record('moodleoverflow_ratings', array('discussionid' => $discussionid, 'rating' => 4));
+            // Return the rating records.
+            return $DB->get_records('moodleoverflow_ratings', array('discussionid' => $discussionid, 'rating' => 4));
         }
 
         // The topic starter has not marked a solution as helpful.
@@ -787,4 +841,38 @@ public static function moodleoverflow_user_can_rate($post, $modulecontext, $user
             && $post->reviewed == 1;
     }
 
+    private static function moodleoverflow_sort_post_by_votes(array $posts) {
+        // Function uses quicksort to sort the posts in descending order.
+        self::moodleoverflow_quicksort_post_by_votes($posts, 0, sizeof($posts) - 1);
+        
+    }
+
+    private static function moodleoverflow_quicksort_post_by_votes(array $posts, $low, $high) {
+        if ($low >= $high) return;
+        $left = $low;
+        $right = $high;
+        $pivot = $posts[ ($low + $high) / 2]->votesdifference;
+        do {
+            while ($posts[$left]->votesdifference > $pivot) {
+                $left++;
+            }
+            while ($posts[$right]->votesdifference < $pivot) {
+                $right--;
+            }
+            if ($left <= $right) {
+                $temp = $posts[$right];
+                $post[$right] = $posts[$left];
+                $posts[$left] = $temp;
+                $right--;
+                $left++;
+            }
+        } while ($left <= $right);
+        if ($low < $right) {
+            self::moodleoverflow_quicksort_post_by_votes($posts, $low, $right);
+        }
+        if ($high > $left ) {
+            self::moodleoverflow_quicksort_post_by_votes($posts, $left, $high);
+        }
+    }
+
 }
diff --git a/locallib.php b/locallib.php
index f7f9294d9f..a9af33f0d6 100644
--- a/locallib.php
+++ b/locallib.php
@@ -1022,6 +1022,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;
     }

From 865c60f40fa3452c35f59dcda96d226c5f5802e2 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 24 Apr 2023 14:52:13 +0200
Subject: [PATCH 08/35] revert problems fixed

---
 amd/build/rating.min.js     |  2 +-
 amd/build/rating.min.js.map |  2 +-
 amd/src/rating.js           | 16 ++++++----------
 classes/ratings.php         |  1 +
 discussion.php              |  5 ++---
 locallib.php                |  9 ++-------
 6 files changed, 13 insertions(+), 22 deletions(-)

diff --git a/amd/build/rating.min.js b/amd/build/rating.min.js
index b88babf908..ce2d7eeadb 100644
--- a/amd/build/rating.min.js
+++ b/amd/build/rating.min.js
@@ -5,6 +5,6 @@ define("mod_moodleoverflow/rating",["exports","core/ajax","core/prefetch","core/
    * @module     mod_moodleoverflow/rating
    * @copyright  2022 Justus Dieckmann WWU
    * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemark){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","alsomarksolved","marknotsolved","markhelpful","alsomarkhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),0==allowmultiplemark)for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");else shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"));shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}}));
+   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemark){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),0==allowmultiplemark)for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");else shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"));shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}}));
 
 //# sourceMappingURL=rating.min.js.map
\ No newline at end of file
diff --git a/amd/build/rating.min.js.map b/amd/build/rating.min.js.map
index b848a3e4d6..0df23bfbb9 100644
--- a/amd/build/rating.min.js.map
+++ b/amd/build/rating.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {int} allowmultiplemark   // 1 means allowed, 0 means not allowed.\n *\n */\nexport function init(userid, allowmultiplemark) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'alsomarksolved', 'marknotsolved', 'markhelpful', 'alsomarkhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (allowmultiplemark == 0) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                    /*// Iterate trough all posts in the discussion and check if there are other marked posts.\n                    var othermarkedposts = 0;\n                    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n                        if (el.classList.contains(htmlclass)) {\n                            othermarkedposts = 1;\n                            break;\n                        }\n                    }\n                    // If there are other marked posts, iterate trough each post and update the string to 'also mark as ...' .\n                    // Else: do nothing, because if there is no marked post, the string doesn't change.\n                    if (othermarkedposts == 1) {\n                        for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n                            if (el.classList.contains(htmlclass)) {\n\n                                //el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                                    //await getString(`alsomark${action}`, 'mod_moodleoverflow');\n                            }\n                            else {\n\n                            }\n                        }\n                    }*/\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","allowmultiplemark","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAsEqBA,OAAQC,qCAChBC,gBAAgB,qBACrB,CAAC,aAAc,iBAAkB,gBAAiB,cAAe,kBAAmB,iBAChF,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAON,GAArBC,sBAEK,MAAMiC,MAAM/B,KAAKgC,iBAAiB,uBAAyBT,WAC5DQ,GAAGN,UAAUQ,OAAOV,WACpBQ,GAAGV,qDAA8Cd,cAAY2B,kBACnD,iCAAiB3B,QAAU,2BAIrCiB,eACAf,YAAYgB,UAAUQ,OAAOV,WAC7BpB,cAAc+B,kBAAoB,iCAAiB3B,QAAU,uBA0BhEiB,eACDf,YAAYgB,UAAUU,IAAIZ,WAC1BpB,cAAc+B,kBAAoB,oCAAoB3B,QAAU,iHApI9EY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOoC,SAASC,eAAe,sCAStBzB,SAASF,OAAQoB,OAAQjC,cAC9ByC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,UACJ9B,KAAKgC,gEAAyDnC,cAAY+C,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,eAAzB,IAEJ9C,KAAKgC,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,eAAzB,IAEJhD,KAAKgC,gEAAyDtB,cAAYkC,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,UAAzB,IAEGX"}
\ No newline at end of file
+{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {int} allowmultiplemark   // 1 means allowed, 0 means not allowed.\n *\n */\nexport function init(userid, allowmultiplemark) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (allowmultiplemark == 0) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                    /*\n                    // Iterate trough all posts in the discussion and check if there are other marked posts.\n                    var othermarkedposts = 0;\n                    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n                        if (el.classList.contains(htmlclass)) {\n                            othermarkedposts = 1;\n                            break;\n                        }\n                    }\n                    // If there are other marked posts, iterate trough each post and update the string to 'also mark as ...' .\n                    // Else: do nothing, because if there is no marked post, the string doesn't change.\n                    if (othermarkedposts == 1) {\n                        for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n                            if (!el.classList.contains(htmlclass)) {\n                                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n                            }\n                        }\n                    }*/\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","allowmultiplemark","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAsEqBA,OAAQC,qCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAON,GAArBC,sBAEK,MAAMiC,MAAM/B,KAAKgC,iBAAiB,uBAAyBT,WAC5DQ,GAAGN,UAAUQ,OAAOV,WACpBQ,GAAGV,qDAA8Cd,cAAY2B,kBACnD,iCAAiB3B,QAAU,2BAIrCiB,eACAf,YAAYgB,UAAUQ,OAAOV,WAC7BpB,cAAc+B,kBAAoB,iCAAiB3B,QAAU,uBAuBhEiB,eACDf,YAAYgB,UAAUU,IAAIZ,WAC1BpB,cAAc+B,kBAAoB,oCAAoB3B,QAAU,iHAjI9EY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOoC,SAASC,eAAe,sCAStBzB,SAASF,OAAQoB,OAAQjC,cAC9ByC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,UACJ9B,KAAKgC,gEAAyDnC,cAAY+C,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,eAAzB,IAEJ9C,KAAKgC,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,eAAzB,IAEJhD,KAAKgC,gEAAyDtB,cAAYkC,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,UAAzB,IAEGX"}
\ No newline at end of file
diff --git a/amd/src/rating.js b/amd/src/rating.js
index 0fb87925b1..1039a133ce 100644
--- a/amd/src/rating.js
+++ b/amd/src/rating.js
@@ -70,7 +70,7 @@ async function sendVote(postid, rating, userid) {
  */
 export function init(userid, allowmultiplemark) {
     Prefetch.prefetchStrings('mod_moodleoverflow',
-        ['marksolved', 'alsomarksolved', 'marknotsolved', 'markhelpful', 'alsomarkhelpful', 'marknothelpful',
+        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',
             'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);
 
     root.onclick = async(event) => {
@@ -130,7 +130,7 @@ export function init(userid, allowmultiplemark) {
                         postElement.classList.remove(htmlclass);
                         actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');
                     }
-                    /*// Iterate trough all posts in the discussion and check if there are other marked posts.
+                    // Iterate trough all posts in the discussion and check if there are other marked posts.
                     var othermarkedposts = 0;
                     for (const el of root.querySelectorAll('.moodleoverflowpost')) {
                         if (el.classList.contains(htmlclass)) {
@@ -142,16 +142,12 @@ export function init(userid, allowmultiplemark) {
                     // Else: do nothing, because if there is no marked post, the string doesn't change.
                     if (othermarkedposts == 1) {
                         for (const el of root.querySelectorAll('.moodleoverflowpost')) {
-                            if (el.classList.contains(htmlclass)) {
-
-                                //el.querySelector(`[data-moodleoverflow-action="${action}"]`).textContent =
-                                    //await getString(`alsomark${action}`, 'mod_moodleoverflow');
-                            }
-                            else {
-
+                            if (!el.classList.contains(htmlclass)) {
+                                el.querySelector(`[data-moodleoverflow-action="${action}"]`).textContent =
+                                    await getString(`alsomark${action}`, 'mod_moodleoverflow');
                             }
                         }
-                    }*/
+                    }
                 }
                 // If the post is being marked, mark it.
                 if (!shouldRemove) {
diff --git a/classes/ratings.php b/classes/ratings.php
index 7c97de4df5..be010daf41 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -157,6 +157,7 @@ public static function moodleoverflow_add_rating($moodleoverflow, $postid, $rati
                 // If there is an old rating, update it. Else create a new rating record.
                 if ($otherrating) {
                     return self::moodleoverflow_update_rating_record($post->id, $rating, $userid, $otherrating->id, $modulecontext);
+    
                 } else {
                     $mid = $moodleoverflow->id;
 
diff --git a/discussion.php b/discussion.php
index 7e81d33195..d751744169 100644
--- a/discussion.php
+++ b/discussion.php
@@ -76,8 +76,7 @@
 
     if (in_array($ratingid, array(RATING_SOLVED, RATING_REMOVE_SOLVED, RATING_HELPFUL, RATING_REMOVE_HELPFUL))) {
         // Rate the post.
-        if (!\mod_moodleoverflow\ratings::moodleoverflow_add_rating($moodleoverflow, $ratedpost, $ratingid, $cm, null,
-                                                                    $marksetting->allowmultiplemarks)) {
+        if (!\mod_moodleoverflow\ratings::moodleoverflow_add_rating($moodleoverflow, $ratedpost, $ratingid, $cm)) {
             throw new moodle_exception('ratingfailed', 'moodleoverflow');
         }
 
@@ -156,7 +155,7 @@
 echo '<div id="moodleoverflow-posts"><div id="moodleoverflow-root">';
 
 // if multiplemarks are on, then print the discussion considering the markdata, else print the discussion.
-if ($marksetting->allowmultiplemarks == 1) {
+if ($marksetting) {
     $multiplemarksdata = moodleoverflow_get_discussion_markdata($d);
     moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post, $multiplemarksdata);
 } else {
diff --git a/locallib.php b/locallib.php
index b2e5123b61..2c322b2383 100644
--- a/locallib.php
+++ b/locallib.php
@@ -1243,9 +1243,6 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
     $permalink = new moodle_url($discussionlink);
     $permalink->set_anchor('p' . $post->id);
 
-    // Save the setting of multiplemarks.
-    $multiplemarkssetting = $DB->get_record('moodleoverflow', array('id' => $moodleoverflow->id), 'allowmultiplemarks');
-
     // 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));
@@ -1260,8 +1257,7 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
             // If there is markdata: consider it.
             // Markdata saves the ID of a marked post in the discussion.
             // If multiplemarks are allowed, other posts than the marked post will have another string.
-            if ($multiplemarkssetting->allowmultiplemarks == 1 && $markdata != false && $markdata->helpfulpostid != false
-                                           && $post->id != $markdata->helpfulpostid) {
+            if ($markdata != false && $markdata->helpfulpostid != false && $post->id != $markdata->helpfulpostid) {
                 $commands[] = html_writer::tag('a', $str->alsomarkhelpful,
                     array('class' => 'markhelpful onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'helpful'));
             } else {
@@ -1285,8 +1281,7 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
             // If there is markdata: consider it.
             // Markdata saves the ID of a marked post in the discussion.
             // If multiplemarks are allowed, other posts than the marked post will have another string.
-            if ($multiplemarkssetting->allowmultiplemarks == 1 && $markdata != false && $markdata->solvedpostid != false
-                                           && $post->id != $markdata->solvedpostid) {
+            if ($markdata != false && $markdata->solvedpostid != false && $post->id != $markdata->solvedpostid) {
                 $commands[] = html_writer::tag('a', $str->alsomarksolved,
                     array('class' => 'marksolved onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'solved'));
             } else {

From fe52f57403a6c4dfaa71d2f7c93378460f0f0d6f Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 24 Apr 2023 14:52:50 +0200
Subject: [PATCH 09/35] Revert "WIP: change button string if posts are marked"

This reverts commit 5b04f0e2c1d83daeb51fd166b8f1c290ae125e1e.
---
 amd/build/rating.min.js.map |   2 +-
 amd/src/rating.js           |  18 -----
 discussion.php              |  12 +--
 lang/en/moodleoverflow.php  |   9 ++-
 lib.php                     |   6 --
 locallib.php                | 142 +++++++++++-------------------------
 6 files changed, 54 insertions(+), 135 deletions(-)

diff --git a/amd/build/rating.min.js.map b/amd/build/rating.min.js.map
index 0df23bfbb9..25a7285043 100644
--- a/amd/build/rating.min.js.map
+++ b/amd/build/rating.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {int} allowmultiplemark   // 1 means allowed, 0 means not allowed.\n *\n */\nexport function init(userid, allowmultiplemark) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (allowmultiplemark == 0) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                    /*\n                    // Iterate trough all posts in the discussion and check if there are other marked posts.\n                    var othermarkedposts = 0;\n                    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n                        if (el.classList.contains(htmlclass)) {\n                            othermarkedposts = 1;\n                            break;\n                        }\n                    }\n                    // If there are other marked posts, iterate trough each post and update the string to 'also mark as ...' .\n                    // Else: do nothing, because if there is no marked post, the string doesn't change.\n                    if (othermarkedposts == 1) {\n                        for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n                            if (!el.classList.contains(htmlclass)) {\n                                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n                            }\n                        }\n                    }*/\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","allowmultiplemark","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAsEqBA,OAAQC,qCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAON,GAArBC,sBAEK,MAAMiC,MAAM/B,KAAKgC,iBAAiB,uBAAyBT,WAC5DQ,GAAGN,UAAUQ,OAAOV,WACpBQ,GAAGV,qDAA8Cd,cAAY2B,kBACnD,iCAAiB3B,QAAU,2BAIrCiB,eACAf,YAAYgB,UAAUQ,OAAOV,WAC7BpB,cAAc+B,kBAAoB,iCAAiB3B,QAAU,uBAuBhEiB,eACDf,YAAYgB,UAAUU,IAAIZ,WAC1BpB,cAAc+B,kBAAoB,oCAAoB3B,QAAU,iHAjI9EY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOoC,SAASC,eAAe,sCAStBzB,SAASF,OAAQoB,OAAQjC,cAC9ByC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,UACJ9B,KAAKgC,gEAAyDnC,cAAY+C,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,eAAzB,IAEJ9C,KAAKgC,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,eAAzB,IAEJhD,KAAKgC,gEAAyDtB,cAAYkC,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,UAAzB,IAEGX"}
\ No newline at end of file
+{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {int} allowmultiplemark   // 1 means allowed, 0 means not allowed.\n *\n */\nexport function init(userid, allowmultiplemark) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (allowmultiplemark == 0) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","allowmultiplemark","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAsEqBA,OAAQC,qCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAON,GAArBC,sBAEK,MAAMiC,MAAM/B,KAAKgC,iBAAiB,uBAAyBT,WAC5DQ,GAAGN,UAAUQ,OAAOV,WACpBQ,GAAGV,qDAA8Cd,cAAY2B,kBACnD,iCAAiB3B,QAAU,2BAIrCiB,eACAf,YAAYgB,UAAUQ,OAAOV,WAC7BpB,cAAc+B,kBAAoB,iCAAiB3B,QAAU,uBAIhEiB,eACDf,YAAYgB,UAAUU,IAAIZ,WAC1BpB,cAAc+B,kBAAoB,oCAAoB3B,QAAU,iHA9G9EY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOoC,SAASC,eAAe,sCAStBzB,SAASF,OAAQoB,OAAQjC,cAC9ByC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,UACJ9B,KAAKgC,gEAAyDnC,cAAY+C,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,eAAzB,IAEJ9C,KAAKgC,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,eAAzB,IAEJhD,KAAKgC,gEAAyDtB,cAAYkC,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,UAAzB,IAEGX"}
\ No newline at end of file
diff --git a/amd/src/rating.js b/amd/src/rating.js
index 1039a133ce..7730b52596 100644
--- a/amd/src/rating.js
+++ b/amd/src/rating.js
@@ -130,24 +130,6 @@ export function init(userid, allowmultiplemark) {
                         postElement.classList.remove(htmlclass);
                         actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');
                     }
-                    // Iterate trough all posts in the discussion and check if there are other marked posts.
-                    var othermarkedposts = 0;
-                    for (const el of root.querySelectorAll('.moodleoverflowpost')) {
-                        if (el.classList.contains(htmlclass)) {
-                            othermarkedposts = 1;
-                            break;
-                        }
-                    }
-                    // If there are other marked posts, iterate trough each post and update the string to 'also mark as ...' .
-                    // Else: do nothing, because if there is no marked post, the string doesn't change.
-                    if (othermarkedposts == 1) {
-                        for (const el of root.querySelectorAll('.moodleoverflowpost')) {
-                            if (!el.classList.contains(htmlclass)) {
-                                el.querySelector(`[data-moodleoverflow-action="${action}"]`).textContent =
-                                    await getString(`alsomark${action}`, 'mod_moodleoverflow');
-                            }
-                        }
-                    }
                 }
                 // If the post is being marked, mark it.
                 if (!shouldRemove) {
diff --git a/discussion.php b/discussion.php
index d751744169..d5c79bfee5 100644
--- a/discussion.php
+++ b/discussion.php
@@ -143,6 +143,9 @@
 // Start the side-output.
 echo $OUTPUT->header();
 
+// Print a text, if multiple marks are allowed.
+moodleoverflow_print_multiplemarks_comment($modulecontext, $post);
+
 echo $OUTPUT->heading(format_string($discussion->name), 1, 'discussionname');
 
 // Guests and users can not subscribe to a discussion.
@@ -154,14 +157,7 @@
 
 echo '<div id="moodleoverflow-posts"><div id="moodleoverflow-root">';
 
-// if multiplemarks are on, then print the discussion considering the markdata, else print the discussion.
-if ($marksetting) {
-    $multiplemarksdata = moodleoverflow_get_discussion_markdata($d);
-    moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post, $multiplemarksdata);
-} else {
-    moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post);
-}
-
+moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post);
 
 echo '</div></div>';
 
diff --git a/lang/en/moodleoverflow.php b/lang/en/moodleoverflow.php
index cd61080e6c..a128ed4a37 100644
--- a/lang/en/moodleoverflow.php
+++ b/lang/en/moodleoverflow.php
@@ -187,11 +187,9 @@
 $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['alsomarkhelpful'] = 'Also mark as helpful';
-$string['marknothelpful'] = 'Not helpful';
+$string['markhelpful'] = 'Mark as Helpful';
+$string['marknothelpful'] = 'Not Helpful';
 $string['answer'] = '{$a} Answer';
 $string['answers'] = '{$a} Answers';
 
@@ -330,6 +328,9 @@
 $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.';
+$string['multiplemarkscommentteacherownpost'] = 'Multiple marks are enabled, several posts can be marked as helpful or solved';
+$string['multiplemarkscommentstudentownpost'] = 'Multiple marks are enabled, you can mark several posts as helpful';
+$string['multiplemarkscommentteacher'] = 'Multiple marks are enabled, several posts can be marked as solved';
 
 // Templates.
 $string['reputation'] = 'Reputation';
diff --git a/lib.php b/lib.php
index 01ced85c7c..980856a25f 100644
--- a/lib.php
+++ b/lib.php
@@ -80,12 +80,6 @@
 define('RATING_HELPFUL', 4);
 define('RATING_REMOVE_HELPFUL', 40);
 
-// Mark constants.
-define('MOODLEOVERFLOW_NEITHERHELPFULORSOLVED', 0);
-define('MOODLEOVERFLOW_HELPFUL', 1);
-define('MOODLEOVERFLOW_SOLVED', 2);
-define('MOODLEOVERFLOW_HELPFULANDSOLVED', 3);
-
 /* Moodle core API */
 
 /**
diff --git a/locallib.php b/locallib.php
index 2c322b2383..b25af997ff 100644
--- a/locallib.php
+++ b/locallib.php
@@ -453,42 +453,6 @@ function moodleoverflow_print_forum_list($course, $cm, $movetopopup) {
     echo $renderer->render_forum_list($mustachedata);
 }
 
-
-/**
- * Get the data about a the marks of a discussion:
- * - if a helpful or solved post exists
- * - which posts are helpful/solved
- *
- * Return an object with the data
- *
- * @param int $discussionid
- * @return object
- */
-function moodleoverflow_get_discussion_markdata($discussionid) {
-    $helpful = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussionid, false);
-    $solved = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussionid, true);
-    $markdata = new \stdClass();
-    $markdata->markstatus = 0;
-    $markdata->helpfulpostid = false;
-    $markdata->solvedpostid = false;
-    if ($helpful) {
-        if ($solved) {
-            $markdata->markstatus = 3;
-            $markdata->helpfulpostid = $helpful->postid;
-            $markdata->solvedpostid = $solved->postid;
-        } else {
-            $markdata->markstatus = 2;
-            $markdata->helpfulpostid = $helpful->postid;
-        }
-    } else {
-        if ($solved) {
-            $markdata->markstatus = 1;
-            $markdata->solvedpostid = $solved->postid;
-        }
-    }
-
-    return $markdata;
-}
 /**
  * Returns an array of counts of replies for each discussion.
  *
@@ -912,7 +876,35 @@ function moodleoverflow_user_can_post($modulecontext, $posttoreplyto, $considerr
     return !$considerreviewstatus || $posttoreplyto->reviewed == 1;
 }
 
+/**
+ * Prints a text, if multiple marks are allowed.
+ *
+ * @param stdClass $context        The moodleoverflow context.
+ * @param stdClass $post           The post object.
+ */
+function moodleoverflow_print_multiplemarks_comment($context, $post) {
+    global $USER;
+
+    // Check if the current user is the starter of the discussion.
+    $ownpost = (isloggedin() && ($USER->id == $post->userid));
+
+    // Check if the current user can mark posts as solved.
+    $canmarksolved = (has_capability('mod/moodleoverflow:marksolved', $context));
+    $comment = '';
 
+    if ($ownpost) {
+        if ($canmarksolved) {
+            $comment = get_string('multiplemarkscommentteacherownpost', 'moodleoverflow');
+        } else {
+            $comment = get_string('multiplemarkscommentstudentownpost', 'moodleoverflow');
+        }
+    }
+    else if ($canmarksolved) {
+        $comment = get_string('multiplemarkscommentteacher', 'moodleoverflow');
+    }
+    $comment = $comment . '<br>' . '<br>';
+    echo $comment;
+}
 /**
  * Prints a moodleoverflow discussion.
  *
@@ -921,9 +913,8 @@ 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 boolean  $markdata       Object with information about marked posts. Only needed when multiplemarks are allowed
  */
-function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post, $markdata = false) {
+function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post) {
     global $USER;
 
     // Check if the current user is the starter of the discussion.
@@ -975,16 +966,9 @@ function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discuss
     // Check if the post was read.
     $postread = !empty($post->postread);
 
-    // Check if there is markdata, if yes, print the starting post considering the markdata.
-    if ($markdata == false) {
-        // Print the starting post.
-        echo moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
-                                       $ownpost, false, '', '', $postread, true, $istracked, 0, $usermapping);
-    } else {
-        // Print the starting post.
-        echo moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
-                                       $ownpost, false, '', '', $postread, true, $istracked, 0, $usermapping, 0, $markdata);
-    }
+    // Print the starting post.
+    echo moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
+        $ownpost, false, '', '', $postread, true, $istracked, 0, $usermapping);
 
     // Print answer divider.
     if ($answercount == 1) {
@@ -996,16 +980,9 @@ function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discuss
 
     echo '<div id="moodleoverflow-posts">';
 
-    // Check if there is markdata, if yes, print the other posts considering the markdata.
-    if ($markdata == false) {
-        // Print the other posts.
-        echo moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow, $discussion, $post, $istracked, $posts,
-                                               null, $usermapping);
-    } else {
-        // Print the other posts.
-        echo moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow, $discussion, $post, $istracked, $posts,
-                                               null, $usermapping, $markdata);
-    }
+    // Print the other posts.
+    echo moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow, $discussion, $post, $istracked, $posts,
+        null, $usermapping);
 
     echo '</div>';
 }
@@ -1134,7 +1111,6 @@ function moodleoverflow_get_all_discussion_posts($discussionid, $tracking, $modc
  * @param bool $iscomment
  * @param array $usermapping
  * @param int $level
- * @param object $markdata      Information about marked posts in a discussion, only needed if multiplemarks are allowed
  * @return void|null
  * @throws coding_exception
  * @throws dml_exception
@@ -1144,8 +1120,8 @@ 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, $markdata = false) {
-    global $USER, $CFG, $OUTPUT, $PAGE, $DB;
+                                   $iscomment = false, $usermapping = [], $level = 0) {
+    global $USER, $CFG, $OUTPUT, $PAGE;
 
     // Require the filelib.
     require_once($CFG->libdir . '/filelib.php');
@@ -1201,10 +1177,8 @@ 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');
     }
 
@@ -1254,16 +1228,8 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
             $commands[] = html_writer::tag('a', $str->marknothelpful,
                     array('class' => 'markhelpful onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'helpful'));
         } else {
-            // If there is markdata: consider it.
-            // Markdata saves the ID of a marked post in the discussion.
-            // If multiplemarks are allowed, other posts than the marked post will have another string.
-            if ($markdata != false && $markdata->helpfulpostid != false && $post->id != $markdata->helpfulpostid) {
-                $commands[] = html_writer::tag('a', $str->alsomarkhelpful,
+            $commands[] = html_writer::tag('a', $str->markhelpful,
                     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'));
-            }
         }
     }
 
@@ -1278,16 +1244,8 @@ 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 {
-            // If there is markdata: consider it.
-            // Markdata saves the ID of a marked post in the discussion.
-            // If multiplemarks are allowed, other posts than the marked post will have another string.
-            if ($markdata != false && $markdata->solvedpostid != false && $post->id != $markdata->solvedpostid) {
-                $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,
+            $commands[] = html_writer::tag('a', $str->marksolved,
                     array('class' => 'marksolved onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'solved'));
-            }
         }
     }
 
@@ -1508,14 +1466,13 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
  * @param array  $posts          Array of posts within the discussion
  * @param bool   $iscomment      Whether the current post is a comment
  * @param array $usermapping
- * @param object $markdata       Information about marked posts in a discussion, only needed if multiplemarks are allowed
  * @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 = [], $markdata = false) {
+                                           $istracked, $posts, $iscomment = null, $usermapping = []) {
     global $USER;
 
     // Prepare the output.
@@ -1555,24 +1512,13 @@ function moodleoverflow_print_posts_nested($course, &$cm, $moodleoverflow, $disc
             // Determine whether the post has been read by the current user.
             $postread = !empty($post->postread);
 
-            // Check if there is markdata, if yes print the answer and children considering the markdata.
-            if ($markdata == false) {
-                // Print the answer.
-                $output .= moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
+            // Print the answer.
+            $output .= moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
                 $ownpost, false, '', '', $postread, true, $istracked, $parentid, $usermapping, $level);
 
-                // Print its children.
-                $output .= moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow,
+            // Print its children.
+            $output .= moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow,
                 $discussion, $post, $istracked, $posts, $parentid, $usermapping);
-            } else {
-                // Print the answer.
-                $output .= moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $course,
-                           $ownpost, false, '', '', $postread, true, $istracked, $parentid, $usermapping, $level, $markdata);
-
-                // Print its children.
-                $output .= moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow,
-                           $discussion, $post, $istracked, $posts, $parentid, $usermapping, $markdata);
-            }
 
             // End the div.
             $output .= "</div>\n";

From 68609d67278af39d3ddfa84ee817e5827a058fe3 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 24 Apr 2023 15:02:02 +0200
Subject: [PATCH 10/35] Revert "comment added for marks setting in
 discussion.php"

This reverts commit b98a9de382d2a15fee5d50ca98f30280e083392d.
---
 discussion.php             |  4 ----
 lang/en/moodleoverflow.php |  3 ---
 locallib.php               | 31 +------------------------------
 3 files changed, 1 insertion(+), 37 deletions(-)

diff --git a/discussion.php b/discussion.php
index d5c79bfee5..cbc7fee593 100644
--- a/discussion.php
+++ b/discussion.php
@@ -142,10 +142,6 @@
 
 // Start the side-output.
 echo $OUTPUT->header();
-
-// Print a text, if multiple marks are allowed.
-moodleoverflow_print_multiplemarks_comment($modulecontext, $post);
-
 echo $OUTPUT->heading(format_string($discussion->name), 1, 'discussionname');
 
 // Guests and users can not subscribe to a discussion.
diff --git a/lang/en/moodleoverflow.php b/lang/en/moodleoverflow.php
index a128ed4a37..bd13b86e7e 100644
--- a/lang/en/moodleoverflow.php
+++ b/lang/en/moodleoverflow.php
@@ -328,9 +328,6 @@
 $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.';
-$string['multiplemarkscommentteacherownpost'] = 'Multiple marks are enabled, several posts can be marked as helpful or solved';
-$string['multiplemarkscommentstudentownpost'] = 'Multiple marks are enabled, you can mark several posts as helpful';
-$string['multiplemarkscommentteacher'] = 'Multiple marks are enabled, several posts can be marked as solved';
 
 // Templates.
 $string['reputation'] = 'Reputation';
diff --git a/locallib.php b/locallib.php
index b25af997ff..a9af33f0d6 100644
--- a/locallib.php
+++ b/locallib.php
@@ -876,35 +876,6 @@ function moodleoverflow_user_can_post($modulecontext, $posttoreplyto, $considerr
     return !$considerreviewstatus || $posttoreplyto->reviewed == 1;
 }
 
-/**
- * Prints a text, if multiple marks are allowed.
- *
- * @param stdClass $context        The moodleoverflow context.
- * @param stdClass $post           The post object.
- */
-function moodleoverflow_print_multiplemarks_comment($context, $post) {
-    global $USER;
-
-    // Check if the current user is the starter of the discussion.
-    $ownpost = (isloggedin() && ($USER->id == $post->userid));
-
-    // Check if the current user can mark posts as solved.
-    $canmarksolved = (has_capability('mod/moodleoverflow:marksolved', $context));
-    $comment = '';
-
-    if ($ownpost) {
-        if ($canmarksolved) {
-            $comment = get_string('multiplemarkscommentteacherownpost', 'moodleoverflow');
-        } else {
-            $comment = get_string('multiplemarkscommentstudentownpost', 'moodleoverflow');
-        }
-    }
-    else if ($canmarksolved) {
-        $comment = get_string('multiplemarkscommentteacher', 'moodleoverflow');
-    }
-    $comment = $comment . '<br>' . '<br>';
-    echo $comment;
-}
 /**
  * Prints a moodleoverflow discussion.
  *
@@ -917,7 +888,7 @@ function moodleoverflow_print_multiplemarks_comment($context, $post) {
 function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post) {
     global $USER;
 
-    // Check if the current user is the starter of the discussion.
+    // Check if the current is the starter of the discussion.
     $ownpost = (isloggedin() && ($USER->id == $post->userid));
 
     // Fetch the modulecontext.

From a781f29ab6be083c343c17b65c008b4e87239542 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 24 Apr 2023 16:22:40 +0200
Subject: [PATCH 11/35] mark button strings update on reload, only js still
 needed

---
 amd/build/rating.min.js     |  2 +-
 amd/build/rating.min.js.map |  2 +-
 amd/src/rating.js           |  6 ++---
 discussion.php              |  8 +++++--
 lang/en/moodleoverflow.php  |  4 +++-
 locallib.php                | 47 ++++++++++++++++++++++++++++---------
 6 files changed, 50 insertions(+), 19 deletions(-)

diff --git a/amd/build/rating.min.js b/amd/build/rating.min.js
index ce2d7eeadb..fc1ba94c81 100644
--- a/amd/build/rating.min.js
+++ b/amd/build/rating.min.js
@@ -5,6 +5,6 @@ define("mod_moodleoverflow/rating",["exports","core/ajax","core/prefetch","core/
    * @module     mod_moodleoverflow/rating
    * @copyright  2022 Justus Dieckmann WWU
    * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemark){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),0==allowmultiplemark)for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");else shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"));shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}}));
+   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemarks){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),0==allowmultiplemarks)for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");else shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"));shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}}));
 
 //# sourceMappingURL=rating.min.js.map
\ No newline at end of file
diff --git a/amd/build/rating.min.js.map b/amd/build/rating.min.js.map
index 25a7285043..64007e289e 100644
--- a/amd/build/rating.min.js.map
+++ b/amd/build/rating.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {int} allowmultiplemark   // 1 means allowed, 0 means not allowed.\n *\n */\nexport function init(userid, allowmultiplemark) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (allowmultiplemark == 0) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","allowmultiplemark","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAsEqBA,OAAQC,qCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAON,GAArBC,sBAEK,MAAMiC,MAAM/B,KAAKgC,iBAAiB,uBAAyBT,WAC5DQ,GAAGN,UAAUQ,OAAOV,WACpBQ,GAAGV,qDAA8Cd,cAAY2B,kBACnD,iCAAiB3B,QAAU,2BAIrCiB,eACAf,YAAYgB,UAAUQ,OAAOV,WAC7BpB,cAAc+B,kBAAoB,iCAAiB3B,QAAU,uBAIhEiB,eACDf,YAAYgB,UAAUU,IAAIZ,WAC1BpB,cAAc+B,kBAAoB,oCAAoB3B,QAAU,iHA9G9EY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOoC,SAASC,eAAe,sCAStBzB,SAASF,OAAQoB,OAAQjC,cAC9ByC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,UACJ9B,KAAKgC,gEAAyDnC,cAAY+C,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,eAAzB,IAEJ9C,KAAKgC,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,eAAzB,IAEJhD,KAAKgC,gEAAyDtB,cAAYkC,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,UAAzB,IAEGX"}
\ No newline at end of file
+{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {int} allowmultiplemarks   // 1 means allowed, 0 means not allowed.\n *\n */\nexport function init(userid, allowmultiplemarks) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (allowmultiplemarks == false) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","allowmultiplemarks","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAsEqBA,OAAQC,sCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAOL,GAAtBC,uBAEK,MAAMiC,MAAM/B,KAAKgC,iBAAiB,uBAAyBT,WAC5DQ,GAAGN,UAAUQ,OAAOV,WACpBQ,GAAGV,qDAA8Cd,cAAY2B,kBACnD,iCAAiB3B,QAAU,2BAIrCiB,eACAf,YAAYgB,UAAUQ,OAAOV,WAC7BpB,cAAc+B,kBAAoB,iCAAiB3B,QAAU,uBAIhEiB,eACDf,YAAYgB,UAAUU,IAAIZ,WAC1BpB,cAAc+B,kBAAoB,oCAAoB3B,QAAU,iHA9G9EY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOoC,SAASC,eAAe,sCAStBzB,SAASF,OAAQoB,OAAQjC,cAC9ByC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,UACJ9B,KAAKgC,gEAAyDnC,cAAY+C,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,eAAzB,IAEJ9C,KAAKgC,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,eAAzB,IAEJhD,KAAKgC,gEAAyDtB,cAAYkC,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,UAAzB,IAEGX"}
\ No newline at end of file
diff --git a/amd/src/rating.js b/amd/src/rating.js
index 7730b52596..f866b749ff 100644
--- a/amd/src/rating.js
+++ b/amd/src/rating.js
@@ -65,10 +65,10 @@ async function sendVote(postid, rating, userid) {
  * Init function.
  *
  * @param {int} userid
- * @param {int} allowmultiplemark   // 1 means allowed, 0 means not allowed.
+ * @param {int} allowmultiplemarks   // 1 means allowed, 0 means not allowed.
  *
  */
-export function init(userid, allowmultiplemark) {
+export function init(userid, allowmultiplemarks) {
     Prefetch.prefetchStrings('mod_moodleoverflow',
         ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',
             'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);
@@ -117,7 +117,7 @@ export function init(userid, allowmultiplemark) {
 
                    then add a mark, if the post is being marked.
                 */
-                if (allowmultiplemark == 0) {
+                if (allowmultiplemarks == false) {
                     // Delete all marks in the discussion
                     for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {
                         el.classList.remove(htmlclass);
diff --git a/discussion.php b/discussion.php
index cbc7fee593..54014664fa 100644
--- a/discussion.php
+++ b/discussion.php
@@ -52,6 +52,10 @@
 
 // Save the allowmultiplemarks setting.
 $marksetting = $DB->get_record('moodleoverflow', array('id' => $moodleoverflow->id), 'allowmultiplemarks');
+$multiplemarks = false;
+if ($marksetting->allowmultiplemarks == 1) {
+    $multiplemarks = true;
+}
 
 // Get the related coursemodule and its context.
 if (!$cm = get_coursemodule_from_instance('moodleoverflow', $moodleoverflow->id, $course->id)) {
@@ -131,7 +135,7 @@
 
 $PAGE->requires->js_call_amd('mod_moodleoverflow/reviewing', 'init');
 
-$PAGE->requires->js_call_amd('mod_moodleoverflow/rating', 'init', [$USER->id, $marksetting->allowmultiplemarks]);
+$PAGE->requires->js_call_amd('mod_moodleoverflow/rating', 'init', [$USER->id, $multiplemarks]);
 
 // Initiate the page.
 $PAGE->set_title($course->shortname . ': ' . format_string($discussion->name));
@@ -153,7 +157,7 @@
 
 echo '<div id="moodleoverflow-posts"><div id="moodleoverflow-root">';
 
-moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post);
+moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post, $multiplemarks);
 
 echo '</div></div>';
 
diff --git a/lang/en/moodleoverflow.php b/lang/en/moodleoverflow.php
index bd13b86e7e..2f87de27d2 100644
--- a/lang/en/moodleoverflow.php
+++ b/lang/en/moodleoverflow.php
@@ -187,8 +187,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';
diff --git a/locallib.php b/locallib.php
index a9af33f0d6..c9309ac845 100644
--- a/locallib.php
+++ b/locallib.php
@@ -241,6 +241,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);
         }
@@ -250,6 +252,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);
         }
@@ -453,6 +457,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.
  *
@@ -885,9 +890,8 @@ function moodleoverflow_user_can_post($modulecontext, $posttoreplyto, $considerr
  * @param stdClass $discussion     The discussion object
  * @param stdClass $post           The post object
  */
-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));
 
@@ -939,7 +943,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) {
@@ -953,7 +957,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 '</div>';
 }
@@ -1091,7 +1095,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.
@@ -1148,8 +1152,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');
     }
 
@@ -1188,19 +1194,32 @@ 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'));
+            }
         }
     }
 
@@ -1215,8 +1234,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'));
+            }
         }
     }
 
@@ -1443,7 +1468,7 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
  * @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.
@@ -1485,11 +1510,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 .= "</div>\n";

From 6671a637b9e899346730e495513f83d0519522fc Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Tue, 25 Apr 2023 15:50:11 +0200
Subject: [PATCH 12/35] js added to change action button strings. Feature ready
 for pull request

---
 amd/build/rating.min.js     |  2 +-
 amd/build/rating.min.js.map |  2 +-
 amd/src/rating.js           | 41 +++++++++++++++++++++++++++++++++++--
 classes/ratings.php         | 31 ++++++++++++++--------------
 locallib.php                |  2 +-
 5 files changed, 58 insertions(+), 20 deletions(-)

diff --git a/amd/build/rating.min.js b/amd/build/rating.min.js
index fc1ba94c81..f60ce3513a 100644
--- a/amd/build/rating.min.js
+++ b/amd/build/rating.min.js
@@ -5,6 +5,6 @@ define("mod_moodleoverflow/rating",["exports","core/ajax","core/prefetch","core/
    * @module     mod_moodleoverflow/rating
    * @copyright  2022 Justus Dieckmann WWU
    * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemarks){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),0==allowmultiplemarks)for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");else shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"));shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}}));
+   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemarks){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),allowmultiplemarks)shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"),changeStrings(htmlclass,action));else for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"),allowmultiplemarks&&changeStrings(htmlclass,action))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}async function changeStrings(htmlclass,action){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","alsomarksolved","markhelpful","alsomarkhelpful"]);var othermarkedposts=!1;for(const el of root.querySelectorAll(".moodleoverflowpost"))if(el.classList.contains(htmlclass)){othermarkedposts=!0;break}for(const el of root.querySelectorAll(".moodleoverflowpost"))!el.classList.contains(htmlclass)&&el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]'))&&(el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=othermarkedposts?await(0,_str.get_string)("alsomark".concat(action),"mod_moodleoverflow"):await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"))}}));
 
 //# sourceMappingURL=rating.min.js.map
\ No newline at end of file
diff --git a/amd/build/rating.min.js.map b/amd/build/rating.min.js.map
index 64007e289e..11f1b470a8 100644
--- a/amd/build/rating.min.js.map
+++ b/amd/build/rating.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {int} allowmultiplemarks   // 1 means allowed, 0 means not allowed.\n *\n */\nexport function init(userid, allowmultiplemarks) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (allowmultiplemarks == false) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                }\n            }\n        }\n    };\n\n}"],"names":["userid","allowmultiplemarks","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","el","querySelectorAll","remove","textContent","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating"],"mappings":";;;;;;;oFAsEqBA,OAAQC,sCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAOL,GAAtBC,uBAEK,MAAMiC,MAAM/B,KAAKgC,iBAAiB,uBAAyBT,WAC5DQ,GAAGN,UAAUQ,OAAOV,WACpBQ,GAAGV,qDAA8Cd,cAAY2B,kBACnD,iCAAiB3B,QAAU,2BAIrCiB,eACAf,YAAYgB,UAAUQ,OAAOV,WAC7BpB,cAAc+B,kBAAoB,iCAAiB3B,QAAU,uBAIhEiB,eACDf,YAAYgB,UAAUU,IAAIZ,WAC1BpB,cAAc+B,kBAAoB,oCAAoB3B,QAAU,iHA9G9EY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOoC,SAASC,eAAe,sCAStBzB,SAASF,OAAQoB,OAAQjC,cAC9ByC,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,UACJ9B,KAAKgC,gEAAyDnC,cAAY+C,SAASC,IAC/EA,EAAEX,YAAcI,SAASQ,eAAzB,IAEJ9C,KAAKgC,gEAAyDM,SAASS,eAAaH,SAASC,IACzFA,EAAEX,YAAcI,SAASU,eAAzB,IAEJhD,KAAKgC,gEAAyDtB,cAAYkC,SAASC,IAC/EA,EAAEX,YAAcI,SAASW,UAAzB,IAEGX"}
\ No newline at end of file
+{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {boolean} allowmultiplemarks   // true means allowed, false means not allowed.\n *\n */\nexport function init(userid, allowmultiplemarks) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (!allowmultiplemarks) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                        changeStrings(htmlclass, action);\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                    if (allowmultiplemarks) {\n                        changeStrings(htmlclass, action);\n                    }\n                }\n\n            }\n        }\n    };\n\n}\n\n/**\n * Function to change the String of the post data-action button.\n * Only usable if mulitplemarks are allowed.\n * @param {string} htmlclass the class where the String is being updated\n * @param {string} action    helpful or solved mark\n */\nasync function changeStrings(htmlclass, action) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'alsomarksolved', 'markhelpful', 'alsomarkhelpful',]);\n\n    // 1. Step: Are there other posts in the Discussion, that are solved/helpful?\n    var othermarkedposts = false;\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (el.classList.contains(htmlclass)) {\n            othermarkedposts = true;\n            break;\n        }\n    }\n    // 2. Step: Change the strings of the action Button of the unmarked posts.\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (!el.classList.contains(htmlclass) && el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`)) {\n            if (othermarkedposts) {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n            } else {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`mark${action}`, 'mod_moodleoverflow');\n            }\n        }\n    }\n}"],"names":["userid","allowmultiplemarks","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","remove","textContent","changeStrings","el","querySelectorAll","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating","othermarkedposts"],"mappings":";;;;;;;oFAsEqBA,OAAQC,sCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAO1BC,mBASG0B,eACAf,YAAYgB,UAAUM,OAAOR,WAC7BpB,cAAc6B,kBAAoB,iCAAiBzB,QAAU,sBAC7D0B,cAAcV,UAAWhB,kBAVxB,MAAM2B,MAAMlC,KAAKmC,iBAAiB,uBAAyBZ,WAC5DW,GAAGT,UAAUM,OAAOR,WACpBW,GAAGb,qDAA8Cd,cAAYyB,kBACnD,iCAAiBzB,QAAU,sBAWxCiB,eACDf,YAAYgB,UAAUW,IAAIb,WAC1BpB,cAAc6B,kBAAoB,oCAAoBzB,QAAU,sBAC5DT,oBACAmC,cAAcV,UAAWhB,mGAjH3CY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOqC,SAASC,eAAe,sCAStB1B,SAASF,OAAQoB,OAAQjC,cAC9B0C,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFjC,OAAQA,OACRkC,SAAUd,WAEd,UACJ9B,KAAKmC,gEAAyDtC,cAAYgD,SAASC,IAC/EA,EAAEd,YAAcO,SAASQ,eAAzB,IAEJ/C,KAAKmC,gEAAyDI,SAASS,eAAaH,SAASC,IACzFA,EAAEd,YAAcO,SAASU,eAAzB,IAEJjD,KAAKmC,gEAAyDzB,cAAYmC,SAASC,IAC/EA,EAAEd,YAAcO,SAASW,UAAzB,IAEGX,wBAgGIN,cAAcV,UAAWhB,0BAC3BR,gBAAgB,qBACrB,CAAC,aAAc,iBAAkB,cAAe,wBAGhDoD,kBAAmB,MAClB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,0BAC/BD,GAAGT,UAAUC,SAASH,WAAY,CAClC4B,kBAAmB,YAKtB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,wBAC9BD,GAAGT,UAAUC,SAASH,YAAcW,GAAGb,qDAA8Cd,gBAElF2B,GAAGb,qDAA8Cd,cAAYyB,YAD7DmB,uBAEU,qCAAqB5C,QAAU,4BAG/B,iCAAiBA,QAAU"}
\ No newline at end of file
diff --git a/amd/src/rating.js b/amd/src/rating.js
index f866b749ff..41ac768c30 100644
--- a/amd/src/rating.js
+++ b/amd/src/rating.js
@@ -65,7 +65,7 @@ async function sendVote(postid, rating, userid) {
  * Init function.
  *
  * @param {int} userid
- * @param {int} allowmultiplemarks   // 1 means allowed, 0 means not allowed.
+ * @param {boolean} allowmultiplemarks   // true means allowed, false means not allowed.
  *
  */
 export function init(userid, allowmultiplemarks) {
@@ -117,7 +117,7 @@ export function init(userid, allowmultiplemarks) {
 
                    then add a mark, if the post is being marked.
                 */
-                if (allowmultiplemarks == false) {
+                if (!allowmultiplemarks) {
                     // Delete all marks in the discussion
                     for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {
                         el.classList.remove(htmlclass);
@@ -129,15 +129,52 @@ export function init(userid, allowmultiplemarks) {
                     if (shouldRemove) {
                         postElement.classList.remove(htmlclass);
                         actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');
+                        changeStrings(htmlclass, action);
                     }
                 }
                 // If the post is being marked, mark it.
                 if (!shouldRemove) {
                     postElement.classList.add(htmlclass);
                     actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');
+                    if (allowmultiplemarks) {
+                        changeStrings(htmlclass, action);
+                    }
                 }
+
             }
         }
     };
 
+}
+
+/**
+ * Function to change the String of the post data-action button.
+ * Only usable if mulitplemarks are allowed.
+ * @param {string} htmlclass the class where the String is being updated
+ * @param {string} action    helpful or solved mark
+ */
+async function changeStrings(htmlclass, action) {
+    Prefetch.prefetchStrings('mod_moodleoverflow',
+        ['marksolved', 'alsomarksolved', 'markhelpful', 'alsomarkhelpful',]);
+
+    // 1. Step: Are there other posts in the Discussion, that are solved/helpful?
+    var othermarkedposts = false;
+    for (const el of root.querySelectorAll('.moodleoverflowpost')) {
+        if (el.classList.contains(htmlclass)) {
+            othermarkedposts = true;
+            break;
+        }
+    }
+    // 2. Step: Change the strings of the action Button of the unmarked posts.
+    for (const el of root.querySelectorAll('.moodleoverflowpost')) {
+        if (!el.classList.contains(htmlclass) && el.querySelector(`[data-moodleoverflow-action="${action}"]`)) {
+            if (othermarkedposts) {
+                el.querySelector(`[data-moodleoverflow-action="${action}"]`).textContent =
+                    await getString(`alsomark${action}`, 'mod_moodleoverflow');
+            } else {
+                el.querySelector(`[data-moodleoverflow-action="${action}"]`).textContent =
+                    await getString(`mark${action}`, 'mod_moodleoverflow');
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/classes/ratings.php b/classes/ratings.php
index be010daf41..76ea3246fe 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -157,11 +157,12 @@ public static function moodleoverflow_add_rating($moodleoverflow, $postid, $rati
                 // If there is an old rating, update it. Else create a new rating record.
                 if ($otherrating) {
                     return self::moodleoverflow_update_rating_record($post->id, $rating, $userid, $otherrating->id, $modulecontext);
-    
+
                 } else {
                     $mid = $moodleoverflow->id;
 
-                    return self::moodleoverflow_add_rating_record($mid, $discussion->id, $post->id, $rating, $userid, $modulecontext);
+                    return self::moodleoverflow_add_rating_record($mid, $discussion->id, $post->id,
+                                                                  $rating, $userid, $modulecontext);
                 }
 
             } else {
@@ -169,7 +170,6 @@ public static function moodleoverflow_add_rating($moodleoverflow, $postid, $rati
                 $mid = $moodleoverflow->id;
                 return self::moodleoverflow_add_rating_record($mid, $discussion->id, $post->id, $rating, $userid, $modulecontext);
             }
-            
         }
 
         // Update an rating record.
@@ -268,9 +268,9 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
         // The answers that are marked as correct by both are displayed first.
         if ($statusteacher && $statusstarter) {
             $markedposts = array();
-            foreach ($statusteacher as $solvedposts ) {
+            foreach ($statusteacher as $solvedposts) {
                 foreach ($statusstarter as $helpfulposts) {
-                    // Is the same answer correct for both? 
+                    // Is the same answer correct for both?
                     if ($solvedposts->postid == $helpfulposts->postid) {
                         // Save the post that is marked as solved and helpful and go to the next post.
                         $markedposts[] = $postscopy[$solvedposts->postid];
@@ -292,10 +292,10 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
         // If the answers the teacher marks are preferred, and only
         // the teacher marked an answer as solved, display it first.
         if ($preferteacher && $statusteacher) {
-            // Save the marked posts
+            // Save the marked posts.
             $markedposts = array();
             foreach ($statusteacher as $solvedpost) {
-                if(array_key_exists($solvedpost->postid, $postscopy)) {
+                if (array_key_exists($solvedpost->postid, $postscopy)) {
                     $markedposts[] = $postscopy[$solvedpost->postid];
                 }
             }
@@ -313,10 +313,10 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
         // If the user who started the discussion has marked
         // an answer as helpful, display this answer first.
         if ($statusstarter) {
-            // Save the marked posts
+            // Save the marked posts.
             $markedposts = array();
             foreach ($statusstarter as $helpfulpost) {
-                if(array_key_exists($helpfulpost->postid, $postscopy)) {
+                if (array_key_exists($helpfulpost->postid, $postscopy)) {
                     $markedposts[] = $postscopy[$helpfulpost->postid];
                 }
             }
@@ -333,10 +333,10 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
 
         // If a teacher has marked an answer as solved, display it next.
         if ($statusteacher) {
-            // Save the marked posts
+            // Save the marked posts.
             $markedposts = array();
             foreach ($statusteacher as $solvedpost) {
-                if(array_key_exists($solvedpost->postid, $postscopy)) {
+                if (array_key_exists($solvedpost->postid, $postscopy)) {
                     $markedposts[] = $postscopy[$solvedpost->postid];
                 }
             }
@@ -847,15 +847,16 @@ public static function moodleoverflow_user_can_rate($post, $modulecontext, $user
 
     private static function moodleoverflow_sort_post_by_votes(array $posts) {
         // Function uses quicksort to sort the posts in descending order.
-        self::moodleoverflow_quicksort_post_by_votes($posts, 0, sizeof($posts) - 1);
-        
+        self::moodleoverflow_quicksort_post_by_votes($posts, 0, count($posts) - 1);
     }
 
     private static function moodleoverflow_quicksort_post_by_votes(array $posts, $low, $high) {
-        if ($low >= $high) return;
+        if ($low >= $high) {
+            return;
+        }
         $left = $low;
         $right = $high;
-        $pivot = $posts[ ($low + $high) / 2]->votesdifference;
+        $pivot = $posts[($low + $high) / 2]->votesdifference;
         do {
             while ($posts[$left]->votesdifference > $pivot) {
                 $left++;
diff --git a/locallib.php b/locallib.php
index c9309ac845..7a42655ec3 100644
--- a/locallib.php
+++ b/locallib.php
@@ -1213,7 +1213,7 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
                     array('class' => 'markhelpful onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'helpful'));
         } else {
             // If there are already marked posts, change the string of the button.
-            if($helpfulposts) {
+            if ($helpfulposts) {
                 $commands[] = html_writer::tag('a', $str->alsomarkhelpful,
                     array('class' => 'markhelpful onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'helpful'));
             } else {

From 5e11d7df92e12ac19b01d535ad68b33988abf980 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 8 May 2023 11:54:16 +0200
Subject: [PATCH 13/35] update 4.2

---
 .github/workflows/moodle-ci.yml      | 20 ++++++++++++++++----
 .github/workflows/moodle-release.yml |  2 +-
 version.php                          |  4 ++--
 3 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/moodle-ci.yml b/.github/workflows/moodle-ci.yml
index f1fb3f254f..c020ab22d4 100644
--- a/.github/workflows/moodle-ci.yml
+++ b/.github/workflows/moodle-ci.yml
@@ -8,8 +8,8 @@ jobs:
 
     strategy:
       matrix:
-        php: ['8.0']
-        moodle-branch: ['MOODLE_401_STABLE']
+        php: ['8.1']
+        moodle-branch: ['MOODLE_402_STABLE']
         database: ['pgsql']
 
     steps:
@@ -110,8 +110,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        php: ['8.0']
-        moodle-branch: ['MOODLE_311_STABLE', 'MOODLE_400_STABLE', 'MOODLE_401_STABLE']
+        php: ['8.0', '8.1']
+        moodle-branch: ['MOODLE_401_STABLE', 'MOODLE_402_STABLE']
         database: ['mariadb', 'pgsql']
         include:
           - php: '7.4'
@@ -120,6 +120,18 @@ jobs:
           - 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
diff --git a/.github/workflows/moodle-release.yml b/.github/workflows/moodle-release.yml
index 97c70a6554..317a7916fa 100644
--- a/.github/workflows/moodle-release.yml
+++ b/.github/workflows/moodle-release.yml
@@ -44,7 +44,7 @@ jobs:
                                          --data-urlencode "altdownloadurl=${ZIPURL}" \
                                          --data-urlencode "releasenotes=${BODY}" \
                                          --data-urlencode "releasenotesformat=4")
-          echo "::set-output name=response::${RESPONSE}"
+          echo "response=${RESPONSE}" >> $GITHUB_OUTPUT
       - name: Evaluate the response
         id: evaluate-response
         env:
diff --git a/version.php b/version.php
index 45384c4fd9..7215a84622 100644
--- a/version.php
+++ b/version.php
@@ -28,8 +28,8 @@
 defined('MOODLE_INTERNAL') || die();
 
 $plugin->component = 'mod_moodleoverflow';
-$plugin->version = 2023050801;
-$plugin->release = 'v4.1-r1';
+$plugin->version = 2023050802;
+$plugin->release = 'v4.2-r1';
 $plugin->requires = 2020061500; // Requires Moodle 3.9+.
 $plugin->maturity = MATURITY_STABLE;
 $plugin->dependencies = array();

From 5d3ccadf86ce1ab6b5d0f546cb16380244a1cb99 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 8 May 2023 13:45:34 +0200
Subject: [PATCH 14/35] phpUnit issues fixed for 402 update

---
 classes/task/send_mails.php |  2 +-
 lib.php                     |  4 ++--
 tests/review_test.php       | 12 +++++++++---
 3 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/classes/task/send_mails.php b/classes/task/send_mails.php
index 7417ee6a38..0c1d445334 100644
--- a/classes/task/send_mails.php
+++ b/classes/task/send_mails.php
@@ -128,7 +128,7 @@ public function send_review_notifications() {
 
             foreach ($usersto as $userto) {
                 try {
-                    cron_setup_user($userto, $course);
+                    \core\cron::setup_user($userto, $course);
 
                     $maildata = new moodleoverflow_email(
                         $course,
diff --git a/lib.php b/lib.php
index 980856a25f..fe78089abb 100644
--- a/lib.php
+++ b/lib.php
@@ -749,7 +749,7 @@ function moodleoverflow_send_mails() {
             $userto->markposts = array();
 
             // Cache the capabilities of the user.
-            cron_setup_user($userto);
+            \core\cron::setup_user($userto);
 
             // Reset the caches.
             foreach ($coursemodules as $moodleoverflowid => $unused) {
@@ -838,7 +838,7 @@ function moodleoverflow_send_mails() {
                 }
 
                 // Setup roles and languages.
-                cron_setup_user($userto, $course);
+                \core\cron::setup_user($userto, $course);
 
                 // Cache the users capability to view full names.
                 if (!isset($userto->viewfullnames[$moodleoverflow->id])) {
diff --git a/tests/review_test.php b/tests/review_test.php
index b6e2cb0562..1dfc8cd2d2 100644
--- a/tests/review_test.php
+++ b/tests/review_test.php
@@ -30,7 +30,6 @@
 
 global $CFG;
 require_once($CFG->dirroot . '/mod/moodleoverflow/lib.php');
-require_once($CFG->dirroot . '/mod/moodleoverflow/externallib.php');
 
 /**
  * PHPUnit Tests for testing readtracking.
@@ -38,6 +37,7 @@
  * @package   mod_moodleoverflow
  * @copyright 2017 Kennet Winter <k_wint10@uni-muenster.de>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ *
  * @group mod_moodleoverflow
  */
 class review_test extends \advanced_testcase {
@@ -104,10 +104,13 @@ protected function tearDown(): void {
     }
 
     /**
+     * @runInSeparateProcess
      * Test reviews functionality in forums where teachers should review everything.
      */
     public function test_forum_review_everything() {
-        global $DB;
+        global $DB, $CFG;
+        require_once($CFG->dirroot . '/mod/moodleoverflow/externallib.php');
+
         $options = array('course' => $this->course->id, 'needsreview' => review::EVERYTHING,
             'forcesubscribe' => MOODLEOVERFLOW_FORCESUBSCRIBE);
         $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options);
@@ -179,10 +182,13 @@ public function test_forum_review_everything() {
     }
 
     /**
+     * @runInSeparateProcess
      * Test reviews functionality in forums where teachers should review questions.
      */
     public function test_forum_review_only_questions() {
-        global $DB;
+        global $DB, $CFG;
+        require_once($CFG->dirroot . '/mod/moodleoverflow/externallib.php');
+
         $options = array('course' => $this->course->id, 'needsreview' => review::QUESTIONS,
             'forcesubscribe' => MOODLEOVERFLOW_FORCESUBSCRIBE);
         $moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $options);

From 32e14fbac5bf2bf669950b492809969036026b9b Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 8 May 2023 15:48:41 +0200
Subject: [PATCH 15/35] phpdoc issues fixed

---
 classes/capabilities.php                 | 35 ++++++++++++
 classes/tables/userstats_table.php       | 63 +++++++++++++++++++--
 classes/task/send_mails.php              |  9 ++-
 lib.php                                  | 14 ++++-
 locallib.php                             | 12 +++-
 renderer.php                             |  4 ++
 tests/behat/behat_mod_moodleoverflow.php |  4 ++
 tests/dailymail_test.php                 | 13 ++++-
 tests/review_test.php                    |  6 +-
 tests/userstats_test.php                 | 72 +++++++++++++++++++++---
 10 files changed, 209 insertions(+), 23 deletions(-)

diff --git a/classes/capabilities.php b/classes/capabilities.php
index be17015033..44dda716d2 100644
--- a/classes/capabilities.php
+++ b/classes/capabilities.php
@@ -35,22 +35,57 @@
  */
 class capabilities {
 
+    /** capability */
     const ADD_INSTANCE = 'mod/moodleoverflow:addinstance';
+
+    /** capability */
     const VIEW_DISCUSSION = 'mod/moodleoverflow:viewdiscussion';
+
+    /** capability */
     const REPLY_POST = 'mod/moodleoverflow:replypost';
+
+    /** capability */
     const START_DISCUSSION = 'mod/moodleoverflow:startdiscussion';
+
+    /** capability */
     const EDIT_ANY_POST = 'mod/moodleoverflow:editanypost';
+
+    /** capability */
     const DELETE_OWN_POST = 'mod/moodleoverflow:deleteownpost';
+
+    /** capability */
     const DELETE_ANY_POST = 'mod/moodleoverflow:deleteanypost';
+
+    /** capability */
     const RATE_POST = 'mod/moodleoverflow:ratepost';
+
+    /** capability */
     const MARK_SOLVED = 'mod/moodleoverflow:marksolved';
+
+    /** capability */
     const MANAGE_SUBSCRIPTIONS = 'mod/moodleoverflow:managesubscriptions';
+
+    /** capability */
     const ALLOW_FORCE_SUBSCRIBE = 'mod/moodleoverflow:allowforcesubscribe';
+
+    /** capability */
     const CREATE_ATTACHMENT = 'mod/moodleoverflow:createattachment';
+
+    /** capability */
     const REVIEW_POST = 'mod/moodleoverflow:reviewpost';
 
+    /** @var array */
     private static $cache = [];
 
+    /**
+     * Saves the cache from has_capability.
+     *
+     * @param string            $capability The capability that is being checked.
+     * @param context           $context    The context.
+     * @param int|null          $userid     The user ID.
+     *
+     * @return bool true or false
+     */
     public static function has(string $capability, context $context, $userid = null): bool {
         global $USER;
         if (!$userid) {
diff --git a/classes/tables/userstats_table.php b/classes/tables/userstats_table.php
index 4883aeb58b..5d040b1990 100644
--- a/classes/tables/userstats_table.php
+++ b/classes/tables/userstats_table.php
@@ -39,14 +39,26 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class userstats_table extends \flexible_table {
-    private $courseid;                // Course ID.
-    private $moodleoverflowid;        // Moodleoverflow that started the printing of statistics.
-    private $userstatsdata = array(); // Userstatsdata  is a table that will have objects with every user and his statistics.
-    private $helpactivity;            // Help icon for amountofactivity-column.
+
+    /** @var int the Course ID*/
+    private $courseid;
+
+    /** @var int Moodleoverflow that started the printing of statistics*/
+    private $moodleoverflowid;
+
+    /** @var array table that will have objects with every user and his statistics. */
+    private $userstatsdata = array();
+
+    /** @var \stdClass Help icon for amountofactivity-column.*/
+    private $helpactivity;
 
     /**
      * Constructor for workflow_table.
+     *
      * @param int $uniqueid Unique id of this table.
+     * @param int $courseid
+     * @param int $moodleoverflow ID if the moodleoverflow
+     * @param string $url The url of the table
      */
     public function __construct($uniqueid, $courseid, $moodleoverflow, $url) {
         global $PAGE;
@@ -87,6 +99,10 @@ public function out() {
 
     /**
      * Method to sort the userstatsdata-table.
+     *
+     * @param array $sortorder The sort order array.
+     *
+     * @return void
      */
     private function sort_table_data($sortorder) {
         $key = $sortorder['sortby'];
@@ -103,6 +119,13 @@ private function sort_table_data($sortorder) {
 
     /**
      * Sorts userstatsdata with quicksort algorithm.
+     *
+     * @param int    $low       index for quicksort.
+     * @param int    $high      index for quicksort.
+     * @param int    $key       the column that is being sorted (upvotes, downvotes etc.).
+     * @param string $order     sort in ascending or descending order.
+     *
+     * @return void
      */
     private function quick_usertable_sort($low, $high, $key, $order) {
         if ($low >= $high) {
@@ -256,10 +279,21 @@ public function set_helpactivity() {
     }
 
     // Functions that show the data.
+
+    /**
+     * username column
+     * @param object $row
+     * @return string
+     */
     public function col_username($row) {
         return $row->link;
     }
 
+    /**
+     * upvotes column
+     * @param object $row
+     * @return string
+     */
     public function col_receivedupvotes($row) {
         if ($row->receivedupvotes > 0) {
             return \html_writer::tag('h5', \html_writer::start_span('badge badge-success') .
@@ -270,6 +304,11 @@ public function col_receivedupvotes($row) {
         }
     }
 
+    /**
+     * downvotes column
+     * @param object $row
+     * @return string
+     */
     public function col_receiveddownvotes($row) {
         if ($row->receiveddownvotes > 0) {
             return \html_writer::tag('h5', \html_writer::start_span('badge badge-success') .
@@ -280,6 +319,11 @@ public function col_receiveddownvotes($row) {
         }
     }
 
+    /**
+     * activity column
+     * @param object $row
+     * @return string
+     */
     public function col_activity($row) {
         if ($row->activity > 0) {
             return \html_writer::tag('h5', \html_writer::start_span('badge badge-success') .
@@ -290,6 +334,11 @@ public function col_activity($row) {
         }
     }
 
+    /**
+     * reputation column
+     * @param object $row
+     * @return string
+     */
     public function col_reputation($row) {
         if ($row->reputation > 0) {
             return \html_writer::tag('h5', \html_writer::start_span('badge badge-success') .
@@ -300,6 +349,12 @@ public function col_reputation($row) {
         }
     }
 
+    /**
+     * error handling
+     * @param object $colname
+     * @param int    $attempt
+     * @return null
+     */
     public function other_cols($colname, $attempt) {
         return null;
     }
diff --git a/classes/task/send_mails.php b/classes/task/send_mails.php
index 0c1d445334..268774bc29 100644
--- a/classes/task/send_mails.php
+++ b/classes/task/send_mails.php
@@ -67,7 +67,7 @@ public function execute() {
      * Sends initial notifications for needed reviews to all users with review capability.
      */
     public function send_review_notifications() {
-        global $DB, $OUTPUT, $PAGE;
+        global $DB, $OUTPUT, $PAGE, $CFG;
 
         $rendererhtml = $PAGE->get_renderer('mod_moodleoverflow', 'email', 'htmlemail');
         $renderertext = $PAGE->get_renderer('mod_moodleoverflow', 'email', 'textemail');
@@ -128,7 +128,12 @@ public function send_review_notifications() {
 
             foreach ($usersto as $userto) {
                 try {
-                    \core\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);
+                    }
 
                     $maildata = new moodleoverflow_email(
                         $course,
diff --git a/lib.php b/lib.php
index fe78089abb..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.
-            \core\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.
-                \core\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..c138fdfd50 100644
--- a/locallib.php
+++ b/locallib.php
@@ -328,7 +328,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 +444,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;
@@ -1231,7 +1236,8 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
     }
 
     // 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.
@@ -1296,7 +1302,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.
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..709dae2ef6 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()="'.
diff --git a/tests/dailymail_test.php b/tests/dailymail_test.php
index b6df80f5d1..c8675b675c 100644
--- a/tests/dailymail_test.php
+++ b/tests/dailymail_test.php
@@ -41,11 +41,22 @@
  */
 class dailymail_test extends \advanced_testcase {
 
+    /** @var \stdClass */
     private $sink;
+
+    /** @var \stdClass */
     private $course;
+
+    /** @var \stdClass */
     private $user;
+
+    /** @var \stdClass */
     private $moodleoverflow;
+
+    /** @var \stdClass */
     private $coursemodule;
+
+    /** @var \stdClass */
     private $discussion;
 
     /**
@@ -81,7 +92,7 @@ public function tearDown(): void {
 
     /**
      * Function that creates a new user, which adds a new discussion an post to the moodleoverflow.
-     * @param $maildigest The maildigest setting: 0 = off , 1 = on
+     * @param int $maildigest The maildigest setting: 0 = off , 1 = on
      */
     public function helper_create_user_and_discussion($maildigest) {
         // Create a user enrolled in the course as student.
diff --git a/tests/review_test.php b/tests/review_test.php
index 1dfc8cd2d2..29aae0090a 100644
--- a/tests/review_test.php
+++ b/tests/review_test.php
@@ -104,8 +104,9 @@ protected function tearDown(): void {
     }
 
     /**
-     * @runInSeparateProcess
      * Test reviews functionality in forums where teachers should review everything.
+     *
+     * @runInSeparateProcess
      */
     public function test_forum_review_everything() {
         global $DB, $CFG;
@@ -182,8 +183,9 @@ public function test_forum_review_everything() {
     }
 
     /**
-     * @runInSeparateProcess
      * Test reviews functionality in forums where teachers should review questions.
+     *
+     * @runInSeparateProcess
      */
     public function test_forum_review_only_questions() {
         global $DB, $CFG;
diff --git a/tests/userstats_test.php b/tests/userstats_test.php
index ca657f7d3c..8c0039bbfa 100644
--- a/tests/userstats_test.php
+++ b/tests/userstats_test.php
@@ -29,22 +29,53 @@
 
 global $CFG;
 require_once($CFG->dirroot . '/mod/moodleoverflow/lib.php');
+
+/**
+ * PHPUnit Tests for testing userstats.
+ *
+ * @package mod_moodleoverflow
+ * @copyright 2023 Tamaro Walter
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
 class userstats_test extends \advanced_testcase {
 
+    /** @var \stdClass */
     private $course;
+
+    /** @var \stdClass */
     private $coursemodule;
-    private $context;
+
+    /** @var \stdClass */
     private $moodleoverflow;
+
+    /** @var \stdClass */
     private $teacher;
+
+    /** @var \stdClass */
     private $user1;
+
+    /** @var \stdClass */
     private $user2;
-    private $discussion1;       // Discussion from user1.
-    private $discussion2;       // Discussion from user2.
-    private $post1;             // First post from discussion1.
-    private $post2;             // First post from discussion2.
-    private $answer1;           // Answerpost to discussion1 from user2.
-    private $answer2;           // Answerpost to discussion2 from user1.
-    private $generator;         // Generator for moodleoverflow.
+
+    /** @var \stdClass */
+    private $discussion1;
+
+    /** @var \stdClass */
+    private $discussion2;
+
+    /** @var \stdClass */
+    private $post1;
+
+    /** @var \stdClass */
+    private $post2;
+
+    /** @var \stdClass */
+    private $answer1;
+
+    /** @var \stdClass */
+    private $answer2;
+    /** @var \mod_moodleoverflow_generator $generator */
+    private $generator;
 
     /**
      * Test setUp.
@@ -156,7 +187,6 @@ private function helper_course_set_up() {
         $location = array('course' => $this->course->id);
         $this->moodleoverflow = $this->getDataGenerator()->create_module('moodleoverflow', $location);
         $this->coursemodule = get_coursemodule_from_instance('moodleoverflow', $this->moodleoverflow->id);
-        $this->context = \context_course::instance($this->course->id);
 
         // Create a teacher.
         $this->teacher = $this->getDataGenerator()->create_user(array('firstname' => 'Tamaro', 'lastname' => 'Walter'));
@@ -192,6 +222,12 @@ private function create_statstable() {
 
     /**
      * Create a upvote to a post in an existing discussion.
+     *
+     * @param object $author       // The creator of the rating.
+     * @param object $discussion   // Discussion object.
+     * @param object $post         // Post that is being rated.
+     *
+     * @return $rating
      */
     private function create_upvote($author, $discussion, $post) {
         $record = (object) [
@@ -208,6 +244,12 @@ private function create_upvote($author, $discussion, $post) {
 
     /**
      * Create a downvote to a post in an existing discussion.
+     *
+     * @param object $author       // The creator of the rating.
+     * @param object $discussion   // Discussion object.
+     * @param object $post         // Post that is being rated.
+     *
+     * @return $rating
      */
     private function create_downvote($author, $discussion, $post) {
         $record = (object) [
@@ -224,6 +266,12 @@ private function create_downvote($author, $discussion, $post) {
 
     /**
      * Create a helpful rating to a post in an existing discussion.
+     *
+     * @param object $author       // The creator of the rating.
+     * @param object $discussion   // Discussion object.
+     * @param object $post         // Post that is being rated.
+     *
+     * @return $rating
      */
     private function create_helpful($author, $discussion, $post) {
         $record = (object) [
@@ -240,6 +288,12 @@ private function create_helpful($author, $discussion, $post) {
 
     /**
      * Create a solution rating to a post in an existing discussion.
+     *
+     * @param object $author       // The creator of the rating.
+     * @param object $discussion   // Discussion object.
+     * @param object $post         // Post that is being rated.
+     *
+     * @return $rating
      */
     private function create_solution($author, $discussion, $post) {
         $record = (object) [

From 58fcc2f6267e9f50884a8120b9d8677432208197 Mon Sep 17 00:00:00 2001
From: NinaHerrmann <nina.herrmann@uni-muenster.de>
Date: Tue, 9 May 2023 16:49:23 +0200
Subject: [PATCH 16/35] new build

---
 amd/build/activityhelp.min.js       | 2 +-
 amd/build/activityhelp.min.js.map   | 2 +-
 amd/build/rating.min.js             | 9 ++++++++-
 amd/build/rating.min.js.map         | 3 +--
 amd/build/reviewing.min.js          | 9 ++++++++-
 amd/build/reviewing.min.js.map      | 3 +--
 amd/build/warnmodechange.min.js     | 9 ++++++++-
 amd/build/warnmodechange.min.js.map | 3 +--
 8 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/amd/build/activityhelp.min.js b/amd/build/activityhelp.min.js
index a3dc794e34..a875b70b82 100644
--- a/amd/build/activityhelp.min.js
+++ b/amd/build/activityhelp.min.js
@@ -6,6 +6,6 @@ define("mod_moodleoverflow/activityhelp",["exports"],(function(_exports){Object.
    * @copyright  2023 Tamaro Walter
    * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
    */
-var Selectors_actions={showHelpIcon:'[data-action="showhelpicon"]'};_exports.init=function(){document.addEventListener("click",(function(event){event.target.closest(Selectors_actions.showHelpIcon)&&event.preventDefault()}))}}));
+const Selectors_actions={showHelpIcon:'[data-action="showhelpicon"]'};_exports.init=()=>{document.addEventListener("click",(event=>{event.target.closest(Selectors_actions.showHelpIcon)&&event.preventDefault()}))}}));
 
 //# sourceMappingURL=activityhelp.min.js.map
\ No newline at end of file
diff --git a/amd/build/activityhelp.min.js.map b/amd/build/activityhelp.min.js.map
index 7bae1c7cab..64d79cd463 100644
--- a/amd/build/activityhelp.min.js.map
+++ b/amd/build/activityhelp.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"activityhelp.min.js","sources":["../src/activityhelp.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Show a help string for the amount of activity column in userstats_table.php\n *\n * @module     mod_moodleoverflow/activityhelp\n * @copyright  2023 Tamaro Walter\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\n\nconst Selectors = {\n    actions: {\n        showHelpIcon: '[data-action=\"showhelpicon\"]',\n    },\n};\n\n/**\n * Function that shows the help string.\n */\nexport const init = () => {\n    document.addEventListener('click', event => {\n        if (event.target.closest(Selectors.actions.showHelpIcon)) {\n            event.preventDefault();\n        }\n    });\n};"],"names":["Selectors","showHelpIcon","document","addEventListener","event","target","closest","preventDefault"],"mappings":";;;;;;;;IAyBMA,kBACO,CACLC,aAAc,8CAOF,WAChBC,SAASC,iBAAiB,SAAS,SAAAC,OAC3BA,MAAMC,OAAOC,QAAQN,kBAAkBC,eACvCG,MAAMG"}
\ No newline at end of file
+{"version":3,"file":"activityhelp.min.js","sources":["../src/activityhelp.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Show a help string for the amount of activity column in userstats_table.php\n *\n * @module     mod_moodleoverflow/activityhelp\n * @copyright  2023 Tamaro Walter\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\n\nconst Selectors = {\n    actions: {\n        showHelpIcon: '[data-action=\"showhelpicon\"]',\n    },\n};\n\n/**\n * Function that shows the help string.\n */\nexport const init = () => {\n    document.addEventListener('click', event => {\n        if (event.target.closest(Selectors.actions.showHelpIcon)) {\n            event.preventDefault();\n        }\n    });\n};"],"names":["Selectors","showHelpIcon","document","addEventListener","event","target","closest","preventDefault"],"mappings":";;;;;;;;MAyBMA,kBACO,CACLC,aAAc,8CAOF,KAChBC,SAASC,iBAAiB,SAASC,QAC3BA,MAAMC,OAAOC,QAAQN,kBAAkBC,eACvCG,MAAMG"}
\ No newline at end of file
diff --git a/amd/build/rating.min.js b/amd/build/rating.min.js
index 4dc7b4ea4e..f60ce3513a 100644
--- a/amd/build/rating.min.js
+++ b/amd/build/rating.min.js
@@ -1,3 +1,10 @@
-define("mod_moodleoverflow/rating",["exports","core/ajax","core/prefetch","core/str"],(function(_exports,_ajax,_prefetch,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _createForOfIteratorHelper(o,allowArrayLike){var it="undefined"!=typeof Symbol&&o[Symbol.iterator]||o["@@iterator"];if(!it){if(Array.isArray(o)||(it=function(o,minLen){if(!o)return;if("string"==typeof o)return _arrayLikeToArray(o,minLen);var n=Object.prototype.toString.call(o).slice(8,-1);"Object"===n&&o.constructor&&(n=o.constructor.name);if("Map"===n||"Set"===n)return Array.from(o);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return _arrayLikeToArray(o,minLen)}(o))||allowArrayLike&&o&&"number"==typeof o.length){it&&(o=it);var i=0,F=function(){};return{s:F,n:function(){return i>=o.length?{done:!0}:{done:!1,value:o[i++]}},e:function(_e){throw _e},f:F}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var err,normalCompletion=!0,didErr=!1;return{s:function(){it=it.call(o)},n:function(){var step=it.next();return normalCompletion=step.done,step},e:function(_e2){didErr=!0,err=_e2},f:function(){try{normalCompletion||null==it.return||it.return()}finally{if(didErr)throw err}}}}function _arrayLikeToArray(arr,len){(null==len||len>arr.length)&&(len=arr.length);for(var i=0,arr2=new Array(len);i<len;i++)arr2[i]=arr[i];return arr2}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=(_ref=_asyncToGenerator(regeneratorRuntime.mark((function _callee(event){var actionElement,action,postElement,postid,isupvote,otherAction,otherElement,isHelpful,htmlclass,shouldRemove,baseRating,rating,_iterator,_step,el;return regeneratorRuntime.wrap((function(_context){for(;;)switch(_context.prev=_context.next){case 0:if(actionElement=event.target.closest("[data-moodleoverflow-action]")){_context.next=3;break}return _context.abrupt("return");case 3:action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid"),_context.t0=action,_context.next="upvote"===_context.t0||"downvote"===_context.t0?9:"helpful"===_context.t0||"solved"===_context.t0?32:63;break;case 9:if(isupvote="upvote"===action,"clicked"!==actionElement.getAttribute("data-moodleoverflow-state")){_context.next=19;break}return _context.next=13,sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid);case 13:return actionElement.setAttribute("data-moodleoverflow-state","notclicked"),_context.next=16,(0,_str.get_string)("action_"+action,"mod_moodleoverflow");case 16:actionElement.title=_context.sent,_context.next=31;break;case 19:return otherAction=isupvote?"downvote":"upvote",_context.next=22,sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid);case 22:return actionElement.setAttribute("data-moodleoverflow-state","clicked"),(otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'))).setAttribute("data-moodleoverflow-state","notclicked"),_context.next=27,(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow");case 27:return actionElement.title=_context.sent,_context.next=30,(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow");case 30:otherElement.title=_context.sent;case 31:return _context.abrupt("break",63);case 32:return htmlclass=(isHelpful="helpful"===action)?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating,_context.next=39,sendVote(postid,rating,userid);case 39:_iterator=_createForOfIteratorHelper(root.querySelectorAll(".moodleoverflowpost."+htmlclass)),_context.prev=40,_iterator.s();case 42:if((_step=_iterator.n()).done){_context.next=50;break}return(el=_step.value).classList.remove(htmlclass),_context.next=47,(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");case 47:el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=_context.sent;case 48:_context.next=42;break;case 50:_context.next=55;break;case 52:_context.prev=52,_context.t1=_context.catch(40),_iterator.e(_context.t1);case 55:return _context.prev=55,_iterator.f(),_context.finish(55);case 58:if(shouldRemove){_context.next=63;break}return postElement.classList.add(htmlclass),_context.next=62,(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow");case 62:actionElement.textContent=_context.sent;case 63:case"end":return _context.stop()}}),_callee,null,[[40,52,55,58]])}))),function(_x4){return _ref.apply(this,arguments)});var _ref},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);var RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");function sendVote(_x,_x2,_x3){return _sendVote.apply(this,arguments)}function _sendVote(){return(_sendVote=_asyncToGenerator(regeneratorRuntime.mark((function _callee2(postid,rating,userid){var response;return regeneratorRuntime.wrap((function(_context2){for(;;)switch(_context2.prev=_context2.next){case 0:return _context2.next=2,_ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];case 2:return response=_context2.sent,root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((function(i){i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((function(i){i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((function(i){i.textContent=response.postrating})),_context2.abrupt("return",response);case 7:case"end":return _context2.stop()}}),_callee2)})))).apply(this,arguments)}}));
+define("mod_moodleoverflow/rating",["exports","core/ajax","core/prefetch","core/str"],(function(_exports,_ajax,_prefetch,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
+/**
+   * Implements rating functionality
+   *
+   * @module     mod_moodleoverflow/rating
+   * @copyright  2022 Justus Dieckmann WWU
+   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemarks){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),allowmultiplemarks)shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"),changeStrings(htmlclass,action));else for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"),allowmultiplemarks&&changeStrings(htmlclass,action))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}async function changeStrings(htmlclass,action){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","alsomarksolved","markhelpful","alsomarkhelpful"]);var othermarkedposts=!1;for(const el of root.querySelectorAll(".moodleoverflowpost"))if(el.classList.contains(htmlclass)){othermarkedposts=!0;break}for(const el of root.querySelectorAll(".moodleoverflowpost"))!el.classList.contains(htmlclass)&&el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]'))&&(el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=othermarkedposts?await(0,_str.get_string)("alsomark".concat(action),"mod_moodleoverflow"):await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"))}}));
 
 //# sourceMappingURL=rating.min.js.map
\ No newline at end of file
diff --git a/amd/build/rating.min.js.map b/amd/build/rating.min.js.map
index 8243d74e75..11f1b470a8 100644
--- a/amd/build/rating.min.js.map
+++ b/amd/build/rating.min.js.map
@@ -1,2 +1 @@
-{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {boolean} allowmultiplemarks   // true means allowed, false means not allowed.\n *\n */\nexport function init(userid, allowmultiplemarks) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (!allowmultiplemarks) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                        changeStrings(htmlclass, action);\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                    if (allowmultiplemarks) {\n                        changeStrings(htmlclass, action);\n                    }\n                }\n\n            }\n        }\n    };\n\n}\n\n/**\n * Function to change the String of the post data-action button.\n * Only usable if mulitplemarks are allowed.\n * @param {string} htmlclass the class where the String is being updated\n * @param {string} action    helpful or solved mark\n */\nasync function changeStrings(htmlclass, action) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'alsomarksolved', 'markhelpful', 'alsomarkhelpful',]);\n\n    // 1. Step: Are there other posts in the Discussion, that are solved/helpful?\n    var othermarkedposts = false;\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (el.classList.contains(htmlclass)) {\n            othermarkedposts = true;\n            break;\n        }\n    }\n    // 2. Step: Change the strings of the action Button of the unmarked posts.\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (!el.classList.contains(htmlclass) && el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`)) {\n            if (othermarkedposts) {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n            } else {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`mark${action}`, 'mod_moodleoverflow');\n            }\n        }\n    }\n}"],"names":["userid","allowmultiplemarks","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","remove","textContent","changeStrings","el","querySelectorAll","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating","othermarkedposts"],"mappings":";;;;;;;oFAsEqBA,OAAQC,sCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAO1BC,mBASG0B,eACAf,YAAYgB,UAAUM,OAAOR,WAC7BpB,cAAc6B,kBAAoB,iCAAiBzB,QAAU,sBAC7D0B,cAAcV,UAAWhB,kBAVxB,MAAM2B,MAAMlC,KAAKmC,iBAAiB,uBAAyBZ,WAC5DW,GAAGT,UAAUM,OAAOR,WACpBW,GAAGb,qDAA8Cd,cAAYyB,kBACnD,iCAAiBzB,QAAU,sBAWxCiB,eACDf,YAAYgB,UAAUW,IAAIb,WAC1BpB,cAAc6B,kBAAoB,oCAAoBzB,QAAU,sBAC5DT,oBACAmC,cAAcV,UAAWhB,mGAjH3CY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOqC,SAASC,eAAe,sCAStB1B,SAASF,OAAQoB,OAAQjC,cAC9B0C,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFjC,OAAQA,OACRkC,SAAUd,WAEd,UACJ9B,KAAKmC,gEAAyDtC,cAAYgD,SAASC,IAC/EA,EAAEd,YAAcO,SAASQ,eAAzB,IAEJ/C,KAAKmC,gEAAyDI,SAASS,eAAaH,SAASC,IACzFA,EAAEd,YAAcO,SAASU,eAAzB,IAEJjD,KAAKmC,gEAAyDzB,cAAYmC,SAASC,IAC/EA,EAAEd,YAAcO,SAASW,UAAzB,IAEGX,wBAgGIN,cAAcV,UAAWhB,0BAC3BR,gBAAgB,qBACrB,CAAC,aAAc,iBAAkB,cAAe,wBAGhDoD,kBAAmB,MAClB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,0BAC/BD,GAAGT,UAAUC,SAASH,WAAY,CAClC4B,kBAAmB,YAKtB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,wBAC9BD,GAAGT,UAAUC,SAASH,YAAcW,GAAGb,qDAA8Cd,gBAElF2B,GAAGb,qDAA8Cd,cAAYyB,YAD7DmB,uBAEU,qCAAqB5C,QAAU,4BAG/B,iCAAiBA,QAAU"}
-
+{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {boolean} allowmultiplemarks   // true means allowed, false means not allowed.\n *\n */\nexport function init(userid, allowmultiplemarks) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (!allowmultiplemarks) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                        changeStrings(htmlclass, action);\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                    if (allowmultiplemarks) {\n                        changeStrings(htmlclass, action);\n                    }\n                }\n\n            }\n        }\n    };\n\n}\n\n/**\n * Function to change the String of the post data-action button.\n * Only usable if mulitplemarks are allowed.\n * @param {string} htmlclass the class where the String is being updated\n * @param {string} action    helpful or solved mark\n */\nasync function changeStrings(htmlclass, action) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'alsomarksolved', 'markhelpful', 'alsomarkhelpful',]);\n\n    // 1. Step: Are there other posts in the Discussion, that are solved/helpful?\n    var othermarkedposts = false;\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (el.classList.contains(htmlclass)) {\n            othermarkedposts = true;\n            break;\n        }\n    }\n    // 2. Step: Change the strings of the action Button of the unmarked posts.\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (!el.classList.contains(htmlclass) && el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`)) {\n            if (othermarkedposts) {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n            } else {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`mark${action}`, 'mod_moodleoverflow');\n            }\n        }\n    }\n}"],"names":["userid","allowmultiplemarks","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","remove","textContent","changeStrings","el","querySelectorAll","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating","othermarkedposts"],"mappings":";;;;;;;oFAsEqBA,OAAQC,sCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAO1BC,mBASG0B,eACAf,YAAYgB,UAAUM,OAAOR,WAC7BpB,cAAc6B,kBAAoB,iCAAiBzB,QAAU,sBAC7D0B,cAAcV,UAAWhB,kBAVxB,MAAM2B,MAAMlC,KAAKmC,iBAAiB,uBAAyBZ,WAC5DW,GAAGT,UAAUM,OAAOR,WACpBW,GAAGb,qDAA8Cd,cAAYyB,kBACnD,iCAAiBzB,QAAU,sBAWxCiB,eACDf,YAAYgB,UAAUW,IAAIb,WAC1BpB,cAAc6B,kBAAoB,oCAAoBzB,QAAU,sBAC5DT,oBACAmC,cAAcV,UAAWhB,mGAjH3CY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOqC,SAASC,eAAe,sCAStB1B,SAASF,OAAQoB,OAAQjC,cAC9B0C,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFjC,OAAQA,OACRkC,SAAUd,WAEd,UACJ9B,KAAKmC,gEAAyDtC,cAAYgD,SAASC,IAC/EA,EAAEd,YAAcO,SAASQ,eAAzB,IAEJ/C,KAAKmC,gEAAyDI,SAASS,eAAaH,SAASC,IACzFA,EAAEd,YAAcO,SAASU,eAAzB,IAEJjD,KAAKmC,gEAAyDzB,cAAYmC,SAASC,IAC/EA,EAAEd,YAAcO,SAASW,UAAzB,IAEGX,wBAgGIN,cAAcV,UAAWhB,0BAC3BR,gBAAgB,qBACrB,CAAC,aAAc,iBAAkB,cAAe,wBAGhDoD,kBAAmB,MAClB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,0BAC/BD,GAAGT,UAAUC,SAASH,WAAY,CAClC4B,kBAAmB,YAKtB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,wBAC9BD,GAAGT,UAAUC,SAASH,YAAcW,GAAGb,qDAA8Cd,gBAElF2B,GAAGb,qDAA8Cd,cAAYyB,YAD7DmB,uBAEU,qCAAqB5C,QAAU,4BAG/B,iCAAiBA,QAAU"}
\ No newline at end of file
diff --git a/amd/build/reviewing.min.js b/amd/build/reviewing.min.js
index c811cbbdfd..7dc1ac1e4f 100644
--- a/amd/build/reviewing.min.js
+++ b/amd/build/reviewing.min.js
@@ -1,3 +1,10 @@
-define("mod_moodleoverflow/reviewing",["exports","core/ajax","core/prefetch","core/templates","core/str"],(function(_exports,_ajax,_prefetch,_templates,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(){_prefetch.default.prefetchTemplates(["mod_moodleoverflow/reject_post_form","mod_moodleoverflow/review_buttons"]),_prefetch.default.prefetchStrings("mod_moodleoverflow",["post_was_approved","jump_to_next_post_needing_review","there_are_no_posts_needing_review","post_was_rejected"]),document.getElementById("moodleoverflow-posts").onclick=(fn=regeneratorRuntime.mark((function _callee(e){var action,post,reviewRow,postID,nextPostURL,message,rejectMessage,args,_nextPostURL,_message;return regeneratorRuntime.wrap((function(_context){for(;;)switch(_context.prev=_context.next){case 0:if(action=e.target.getAttribute("data-moodleoverflow-action")){_context.next=3;break}return _context.abrupt("return");case 3:if(post=e.target.closest("*[data-moodleoverflow-postid]"),reviewRow=e.target.closest(".reviewrow"),postID=post.getAttribute("data-moodleoverflow-postid"),"approve"!==action){_context.next=33;break}return reviewRow.innerHTML=".",_context.next=10,_ajax.default.call([{methodname:"mod_moodleoverflow_review_approve_post",args:{postid:postID}}])[0];case 10:return nextPostURL=_context.sent,_context.next=13,(0,_str.get_string)("post_was_approved","mod_moodleoverflow");case 13:if(_context.t0=_context.sent,message=_context.t0+" ",!nextPostURL){_context.next=25;break}return _context.t1=message,_context.t2='<a href="'.concat(nextPostURL,'">'),_context.next=20,(0,_str.get_string)("jump_to_next_post_needing_review","mod_moodleoverflow");case 20:_context.t3=_context.sent,_context.t4=_context.t2+_context.t3,message=_context.t1+=_context.t4+"</a>",_context.next=29;break;case 25:return _context.t5=message,_context.next=28,(0,_str.get_string)("there_are_no_posts_needing_review","mod_moodleoverflow");case 28:message=_context.t5+=_context.sent;case 29:reviewRow.innerHTML=message,post.classList.remove("pendingreview"),_context.next=73;break;case 33:if("reject"!==action){_context.next=40;break}return reviewRow.innerHTML=".",_context.next=37,_templates.default.render("mod_moodleoverflow/reject_post_form",{});case 37:reviewRow.innerHTML=_context.sent,_context.next=73;break;case 40:if("reject-submit"!==action){_context.next=68;break}return rejectMessage=post.querySelector("textarea.reject-reason").value.toString().trim(),reviewRow.innerHTML=".",args={postid:postID,reason:rejectMessage||null},_context.next=46,_ajax.default.call([{methodname:"mod_moodleoverflow_review_reject_post",args:args}])[0];case 46:return _nextPostURL=_context.sent,_context.next=49,(0,_str.get_string)("post_was_rejected","mod_moodleoverflow");case 49:if(_context.t6=_context.sent,_message=_context.t6+" ",!_nextPostURL){_context.next=61;break}return _context.t7=_message,_context.t8='<a href="'.concat(_nextPostURL,'">'),_context.next=56,(0,_str.get_string)("jump_to_next_post_needing_review","mod_moodleoverflow");case 56:_context.t9=_context.sent,_context.t10=_context.t8+_context.t9,_message=_context.t7+=_context.t10+"</a>",_context.next=65;break;case 61:return _context.t11=_message,_context.next=64,(0,_str.get_string)("there_are_no_posts_needing_review","mod_moodleoverflow");case 64:_message=_context.t11+=_context.sent;case 65:reviewRow.innerHTML=_message,_context.next=73;break;case 68:if("reject-cancel"!==action){_context.next=73;break}return reviewRow.innerHTML=".",_context.next=72,_templates.default.render("mod_moodleoverflow/review_buttons",{});case 72:reviewRow.innerHTML=_context.sent;case 73:case"end":return _context.stop()}}),_callee)})),_ref=function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))},function(_x){return _ref.apply(this,arguments)});var fn,_ref},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch),_templates=_interopRequireDefault(_templates)}));
+define("mod_moodleoverflow/reviewing",["exports","core/ajax","core/prefetch","core/templates","core/str"],(function(_exports,_ajax,_prefetch,_templates,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
+/**
+   * Implements reviewing functionality
+   *
+   * @module     mod_moodleoverflow/reviewing
+   * @copyright  2022 Justus Dieckmann WWU
+   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(){_prefetch.default.prefetchTemplates(["mod_moodleoverflow/reject_post_form","mod_moodleoverflow/review_buttons"]),_prefetch.default.prefetchStrings("mod_moodleoverflow",["post_was_approved","jump_to_next_post_needing_review","there_are_no_posts_needing_review","post_was_rejected"]);document.getElementById("moodleoverflow-posts").onclick=async e=>{const action=e.target.getAttribute("data-moodleoverflow-action");if(!action)return;const post=e.target.closest("*[data-moodleoverflow-postid]"),reviewRow=e.target.closest(".reviewrow"),postID=post.getAttribute("data-moodleoverflow-postid");if("approve"===action){reviewRow.innerHTML=".";const nextPostURL=await _ajax.default.call([{methodname:"mod_moodleoverflow_review_approve_post",args:{postid:postID}}])[0];let message=await(0,_str.get_string)("post_was_approved","mod_moodleoverflow")+" ";message+=nextPostURL?'<a href="'.concat(nextPostURL,'">')+await(0,_str.get_string)("jump_to_next_post_needing_review","mod_moodleoverflow")+"</a>":await(0,_str.get_string)("there_are_no_posts_needing_review","mod_moodleoverflow"),reviewRow.innerHTML=message,post.classList.remove("pendingreview")}else if("reject"===action)reviewRow.innerHTML=".",reviewRow.innerHTML=await _templates.default.render("mod_moodleoverflow/reject_post_form",{});else if("reject-submit"===action){const rejectMessage=post.querySelector("textarea.reject-reason").value.toString().trim();reviewRow.innerHTML=".";const args={postid:postID,reason:rejectMessage||null},nextPostURL=await _ajax.default.call([{methodname:"mod_moodleoverflow_review_reject_post",args:args}])[0];let message=await(0,_str.get_string)("post_was_rejected","mod_moodleoverflow")+" ";message+=nextPostURL?'<a href="'.concat(nextPostURL,'">')+await(0,_str.get_string)("jump_to_next_post_needing_review","mod_moodleoverflow")+"</a>":await(0,_str.get_string)("there_are_no_posts_needing_review","mod_moodleoverflow"),reviewRow.innerHTML=message}else"reject-cancel"===action&&(reviewRow.innerHTML=".",reviewRow.innerHTML=await _templates.default.render("mod_moodleoverflow/review_buttons",{}))}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch),_templates=_interopRequireDefault(_templates)}));
 
 //# sourceMappingURL=reviewing.min.js.map
\ No newline at end of file
diff --git a/amd/build/reviewing.min.js.map b/amd/build/reviewing.min.js.map
index 0f8f0c1d6b..13a68e7925 100644
--- a/amd/build/reviewing.min.js.map
+++ b/amd/build/reviewing.min.js.map
@@ -1,2 +1 @@
-{"version":3,"file":"reviewing.min.js","sources":["../src/reviewing.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements reviewing functionality\n *\n * @module     mod_moodleoverflow/reviewing\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Init function.\n */\nexport function init() {\n    Prefetch.prefetchTemplates(['mod_moodleoverflow/reject_post_form', 'mod_moodleoverflow/review_buttons']);\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['post_was_approved', 'jump_to_next_post_needing_review', 'there_are_no_posts_needing_review', 'post_was_rejected']);\n\n    const root = document.getElementById('moodleoverflow-posts');\n    root.onclick = async(e) => {\n        const action = e.target.getAttribute('data-moodleoverflow-action');\n\n        if (!action) {\n            return;\n        }\n\n        const post = e.target.closest('*[data-moodleoverflow-postid]');\n        const reviewRow = e.target.closest('.reviewrow');\n        const postID = post.getAttribute('data-moodleoverflow-postid');\n\n        if (action === 'approve') {\n            reviewRow.innerHTML = '.';\n            const nextPostURL = await Ajax.call([{\n                methodname: 'mod_moodleoverflow_review_approve_post',\n                args: {\n                    postid: postID,\n                }\n            }])[0];\n\n            let message = await getString('post_was_approved', 'mod_moodleoverflow') + ' ';\n            if (nextPostURL) {\n                message += `<a href=\"${nextPostURL}\">`\n                    + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n                    + \"</a>\";\n            } else {\n                message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n            }\n            reviewRow.innerHTML = message;\n            post.classList.remove(\"pendingreview\");\n        } else if (action === 'reject') {\n            reviewRow.innerHTML = '.';\n            reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/reject_post_form', {});\n        } else if (action === 'reject-submit') {\n            const rejectMessage = post.querySelector('textarea.reject-reason').value.toString().trim();\n            reviewRow.innerHTML = '.';\n            const args = {\n                postid: postID,\n                reason: rejectMessage ? rejectMessage : null\n            };\n            const nextPostURL = await Ajax.call([{\n                methodname: 'mod_moodleoverflow_review_reject_post',\n                args: args\n            }])[0];\n\n            let message = await getString('post_was_rejected', 'mod_moodleoverflow') + ' ';\n            if (nextPostURL) {\n                message += `<a href=\"${nextPostURL}\">`\n                    + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n                    + \"</a>\";\n            } else {\n                message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n            }\n            reviewRow.innerHTML = message;\n        } else if (action === 'reject-cancel') {\n            reviewRow.innerHTML = '.';\n            reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/review_buttons', {});\n        }\n    };\n}"],"names":["prefetchTemplates","prefetchStrings","document","getElementById","onclick","async","action","e","target","getAttribute","post","closest","reviewRow","postID","innerHTML","nextPostURL","Ajax","call","methodname","args","postid","message","classList","remove","Templates","render","rejectMessage","querySelector","value","toString","trim","reason"],"mappings":";;;;;;;wGA+BaA,kBAAkB,CAAC,sCAAuC,wDAC1DC,gBAAgB,qBACrB,CAAC,oBAAqB,mCAAoC,oCAAqC,sBAEtFC,SAASC,eAAe,wBAChCC,QAAUC,gBACLC,OAASC,EAAEC,OAAOC,aAAa,kCAEhCH,oBAICI,KAAOH,EAAEC,OAAOG,QAAQ,iCACxBC,UAAYL,EAAEC,OAAOG,QAAQ,cAC7BE,OAASH,KAAKD,aAAa,iCAElB,YAAXH,OAAsB,CACtBM,UAAUE,UAAY,UAChBC,kBAAoBC,cAAKC,KAAK,CAAC,CACjCC,WAAY,yCACZC,KAAM,CACFC,OAAQP,WAEZ,OAEAQ,cAAgB,mBAAU,oBAAqB,sBAAwB,IAEvEA,SADAN,YACW,mBAAYA,wBACX,mBAAU,mCAAoC,sBACpD,aAEW,mBAAU,oCAAqC,sBAEpEH,UAAUE,UAAYO,QACtBX,KAAKY,UAAUC,OAAO,sBACnB,GAAe,WAAXjB,OACPM,UAAUE,UAAY,IACtBF,UAAUE,gBAAkBU,mBAAUC,OAAO,sCAAuC,SACjF,GAAe,kBAAXnB,OAA4B,OAC7BoB,cAAgBhB,KAAKiB,cAAc,0BAA0BC,MAAMC,WAAWC,OACpFlB,UAAUE,UAAY,UAChBK,KAAO,CACTC,OAAQP,OACRkB,OAAQL,eAAgC,MAEtCX,kBAAoBC,cAAKC,KAAK,CAAC,CACjCC,WAAY,wCACZC,KAAMA,QACN,OAEAE,cAAgB,mBAAU,oBAAqB,sBAAwB,IAEvEA,SADAN,YACW,mBAAYA,wBACX,mBAAU,mCAAoC,sBACpD,aAEW,mBAAU,oCAAqC,sBAEpEH,UAAUE,UAAYO,YACJ,kBAAXf,SACPM,UAAUE,UAAY,IACtBF,UAAUE,gBAAkBU,mBAAUC,OAAO,oCAAqC"}
-
+{"version":3,"file":"reviewing.min.js","sources":["../src/reviewing.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements reviewing functionality\n *\n * @module     mod_moodleoverflow/reviewing\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Init function.\n */\nexport function init() {\n    Prefetch.prefetchTemplates(['mod_moodleoverflow/reject_post_form', 'mod_moodleoverflow/review_buttons']);\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['post_was_approved', 'jump_to_next_post_needing_review', 'there_are_no_posts_needing_review', 'post_was_rejected']);\n\n    const root = document.getElementById('moodleoverflow-posts');\n    root.onclick = async(e) => {\n        const action = e.target.getAttribute('data-moodleoverflow-action');\n\n        if (!action) {\n            return;\n        }\n\n        const post = e.target.closest('*[data-moodleoverflow-postid]');\n        const reviewRow = e.target.closest('.reviewrow');\n        const postID = post.getAttribute('data-moodleoverflow-postid');\n\n        if (action === 'approve') {\n            reviewRow.innerHTML = '.';\n            const nextPostURL = await Ajax.call([{\n                methodname: 'mod_moodleoverflow_review_approve_post',\n                args: {\n                    postid: postID,\n                }\n            }])[0];\n\n            let message = await getString('post_was_approved', 'mod_moodleoverflow') + ' ';\n            if (nextPostURL) {\n                message += `<a href=\"${nextPostURL}\">`\n                    + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n                    + \"</a>\";\n            } else {\n                message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n            }\n            reviewRow.innerHTML = message;\n            post.classList.remove(\"pendingreview\");\n        } else if (action === 'reject') {\n            reviewRow.innerHTML = '.';\n            reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/reject_post_form', {});\n        } else if (action === 'reject-submit') {\n            const rejectMessage = post.querySelector('textarea.reject-reason').value.toString().trim();\n            reviewRow.innerHTML = '.';\n            const args = {\n                postid: postID,\n                reason: rejectMessage ? rejectMessage : null\n            };\n            const nextPostURL = await Ajax.call([{\n                methodname: 'mod_moodleoverflow_review_reject_post',\n                args: args\n            }])[0];\n\n            let message = await getString('post_was_rejected', 'mod_moodleoverflow') + ' ';\n            if (nextPostURL) {\n                message += `<a href=\"${nextPostURL}\">`\n                    + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n                    + \"</a>\";\n            } else {\n                message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n            }\n            reviewRow.innerHTML = message;\n        } else if (action === 'reject-cancel') {\n            reviewRow.innerHTML = '.';\n            reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/review_buttons', {});\n        }\n    };\n}"],"names":["prefetchTemplates","prefetchStrings","document","getElementById","onclick","async","action","e","target","getAttribute","post","closest","reviewRow","postID","innerHTML","nextPostURL","Ajax","call","methodname","args","postid","message","classList","remove","Templates","render","rejectMessage","querySelector","value","toString","trim","reason"],"mappings":";;;;;;;wGA+BaA,kBAAkB,CAAC,sCAAuC,wDAC1DC,gBAAgB,qBACrB,CAAC,oBAAqB,mCAAoC,oCAAqC,sBAEtFC,SAASC,eAAe,wBAChCC,QAAUC,gBACLC,OAASC,EAAEC,OAAOC,aAAa,kCAEhCH,oBAICI,KAAOH,EAAEC,OAAOG,QAAQ,iCACxBC,UAAYL,EAAEC,OAAOG,QAAQ,cAC7BE,OAASH,KAAKD,aAAa,iCAElB,YAAXH,OAAsB,CACtBM,UAAUE,UAAY,UAChBC,kBAAoBC,cAAKC,KAAK,CAAC,CACjCC,WAAY,yCACZC,KAAM,CACFC,OAAQP,WAEZ,OAEAQ,cAAgB,mBAAU,oBAAqB,sBAAwB,IAEvEA,SADAN,YACW,mBAAYA,wBACX,mBAAU,mCAAoC,sBACpD,aAEW,mBAAU,oCAAqC,sBAEpEH,UAAUE,UAAYO,QACtBX,KAAKY,UAAUC,OAAO,sBACnB,GAAe,WAAXjB,OACPM,UAAUE,UAAY,IACtBF,UAAUE,gBAAkBU,mBAAUC,OAAO,sCAAuC,SACjF,GAAe,kBAAXnB,OAA4B,OAC7BoB,cAAgBhB,KAAKiB,cAAc,0BAA0BC,MAAMC,WAAWC,OACpFlB,UAAUE,UAAY,UAChBK,KAAO,CACTC,OAAQP,OACRkB,OAAQL,eAAgC,MAEtCX,kBAAoBC,cAAKC,KAAK,CAAC,CACjCC,WAAY,wCACZC,KAAMA,QACN,OAEAE,cAAgB,mBAAU,oBAAqB,sBAAwB,IAEvEA,SADAN,YACW,mBAAYA,wBACX,mBAAU,mCAAoC,sBACpD,aAEW,mBAAU,oCAAqC,sBAEpEH,UAAUE,UAAYO,YACJ,kBAAXf,SACPM,UAAUE,UAAY,IACtBF,UAAUE,gBAAkBU,mBAAUC,OAAO,oCAAqC"}
\ No newline at end of file
diff --git a/amd/build/warnmodechange.min.js b/amd/build/warnmodechange.min.js
index 370da13a32..5042ba704b 100644
--- a/amd/build/warnmodechange.min.js
+++ b/amd/build/warnmodechange.min.js
@@ -1,3 +1,10 @@
-define("mod_moodleoverflow/warnmodechange",["exports","core/str","core/notification","core/prefetch"],(function(_exports,_str,_notification,_prefetch){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(previousSetting){_prefetch.default.prefetchStrings("mod_moodleoverflow",["switchtooptional","switchtoauto"]),_prefetch.default.prefetchStrings("moodle",["confirm","cancel"]);var form=document.querySelector("form.mform"),select=document.getElementById("id_forcesubscribe");form.onsubmit=(fn=regeneratorRuntime.mark((function _callee(e){var value;return regeneratorRuntime.wrap((function(_context){for(;;)switch(_context.prev=_context.next){case 0:if((value=select.selectedOptions[0].value)!=previousSetting&&1!=value&&3!=value){_context.next=3;break}return _context.abrupt("return");case 3:return e.preventDefault(),_context.t0=_notification.default,_context.next=7,(0,_str.get_string)("confirm");case 7:return _context.t1=_context.sent,_context.next=10,(0,_str.get_string)(0==value?"switchtooptional":"switchtoauto","mod_moodleoverflow");case 10:return _context.t2=_context.sent,_context.next=13,(0,_str.get_string)("confirm");case 13:return _context.t3=_context.sent,_context.next=16,(0,_str.get_string)("cancel");case 16:return _context.t4=_context.sent,_context.t5=function(){form.onsubmit=void 0,form.requestSubmit(e.submitter)},_context.t6=void 0,_context.next=21,_context.t0.confirm.call(_context.t0,_context.t1,_context.t2,_context.t3,_context.t4,_context.t5,_context.t6);case 21:case"end":return _context.stop()}}),_callee)})),_ref=function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))},function(_x){return _ref.apply(this,arguments)});var fn,_ref},_notification=_interopRequireDefault(_notification),_prefetch=_interopRequireDefault(_prefetch)}));
+define("mod_moodleoverflow/warnmodechange",["exports","core/str","core/notification","core/prefetch"],(function(_exports,_str,_notification,_prefetch){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
+/**
+   * Warns on changing the subscription mode.
+   *
+   * @module     mod_moodleoverflow/warnmodechange
+   * @copyright  2022 Justus Dieckmann WWU
+   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(previousSetting){_prefetch.default.prefetchStrings("mod_moodleoverflow",["switchtooptional","switchtoauto"]),_prefetch.default.prefetchStrings("moodle",["confirm","cancel"]);const form=document.querySelector("form.mform"),select=document.getElementById("id_forcesubscribe");form.onsubmit=async e=>{const value=select.selectedOptions[0].value;value!=previousSetting&&1!=value&&3!=value&&(e.preventDefault(),await _notification.default.confirm(await(0,_str.get_string)("confirm"),await(0,_str.get_string)(0==value?"switchtooptional":"switchtoauto","mod_moodleoverflow"),await(0,_str.get_string)("confirm"),await(0,_str.get_string)("cancel"),(()=>{form.onsubmit=void 0,form.requestSubmit(e.submitter)}),void 0))}},_notification=_interopRequireDefault(_notification),_prefetch=_interopRequireDefault(_prefetch)}));
 
 //# sourceMappingURL=warnmodechange.min.js.map
\ No newline at end of file
diff --git a/amd/build/warnmodechange.min.js.map b/amd/build/warnmodechange.min.js.map
index 80beade8a2..b4120395cd 100644
--- a/amd/build/warnmodechange.min.js.map
+++ b/amd/build/warnmodechange.min.js.map
@@ -1,2 +1 @@
-{"version":3,"file":"warnmodechange.min.js","sources":["../src/warnmodechange.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Warns on changing the subscription mode.\n *\n * @module     mod_moodleoverflow/warnmodechange\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {get_string as getString} from 'core/str';\nimport Notification from 'core/notification';\nimport Prefetch from 'core/prefetch';\n\n/**\n * Init function.\n * @param {string} previousSetting\n */\nexport function init(previousSetting) {\n    Prefetch.prefetchStrings('mod_moodleoverflow', ['switchtooptional', 'switchtoauto']);\n    Prefetch.prefetchStrings('moodle', ['confirm', 'cancel']);\n    const form = document.querySelector('form.mform');\n    const select = document.getElementById('id_forcesubscribe');\n    form.onsubmit = async(e) => {\n        const value = select.selectedOptions[0].value;\n        if (value == previousSetting || value == 1 || value == 3) {\n            return;\n        }\n        e.preventDefault();\n        await Notification.confirm(\n            await getString('confirm'),\n            await getString(value == 0 ? 'switchtooptional' : 'switchtoauto', 'mod_moodleoverflow'),\n            await getString('confirm'),\n            await getString('cancel'),\n            () => {\n                // Prevent this listener from preventing the event again.\n                form.onsubmit = undefined;\n                form.requestSubmit(e.submitter);\n            }, undefined);\n    };\n}"],"names":["previousSetting","prefetchStrings","form","document","querySelector","select","getElementById","onsubmit","async","value","selectedOptions","e","preventDefault","Notification","confirm","undefined","requestSubmit","submitter"],"mappings":";;;;;;;oFA8BqBA,mCACRC,gBAAgB,qBAAsB,CAAC,mBAAoB,mCAC3DA,gBAAgB,SAAU,CAAC,UAAW,iBACzCC,KAAOC,SAASC,cAAc,cAC9BC,OAASF,SAASG,eAAe,qBACvCJ,KAAKK,SAAWC,gBACNC,MAAQJ,OAAOK,gBAAgB,GAAGD,MACpCA,OAAST,iBAA4B,GAATS,OAAuB,GAATA,QAG9CE,EAAEC,uBACIC,sBAAaC,cACT,mBAAU,iBACV,mBAAmB,GAATL,MAAa,mBAAqB,eAAgB,4BAC5D,mBAAU,iBACV,mBAAU,WAChB,KAEIP,KAAKK,cAAWQ,EAChBb,KAAKc,cAAcL,EAAEM,kBACtBF,GATP"}
-
+{"version":3,"file":"warnmodechange.min.js","sources":["../src/warnmodechange.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Warns on changing the subscription mode.\n *\n * @module     mod_moodleoverflow/warnmodechange\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {get_string as getString} from 'core/str';\nimport Notification from 'core/notification';\nimport Prefetch from 'core/prefetch';\n\n/**\n * Init function.\n * @param {string} previousSetting\n */\nexport function init(previousSetting) {\n    Prefetch.prefetchStrings('mod_moodleoverflow', ['switchtooptional', 'switchtoauto']);\n    Prefetch.prefetchStrings('moodle', ['confirm', 'cancel']);\n    const form = document.querySelector('form.mform');\n    const select = document.getElementById('id_forcesubscribe');\n    form.onsubmit = async(e) => {\n        const value = select.selectedOptions[0].value;\n        if (value == previousSetting || value == 1 || value == 3) {\n            return;\n        }\n        e.preventDefault();\n        await Notification.confirm(\n            await getString('confirm'),\n            await getString(value == 0 ? 'switchtooptional' : 'switchtoauto', 'mod_moodleoverflow'),\n            await getString('confirm'),\n            await getString('cancel'),\n            () => {\n                // Prevent this listener from preventing the event again.\n                form.onsubmit = undefined;\n                form.requestSubmit(e.submitter);\n            }, undefined);\n    };\n}"],"names":["previousSetting","prefetchStrings","form","document","querySelector","select","getElementById","onsubmit","async","value","selectedOptions","e","preventDefault","Notification","confirm","undefined","requestSubmit","submitter"],"mappings":";;;;;;;oFA8BqBA,mCACRC,gBAAgB,qBAAsB,CAAC,mBAAoB,mCAC3DA,gBAAgB,SAAU,CAAC,UAAW,iBACzCC,KAAOC,SAASC,cAAc,cAC9BC,OAASF,SAASG,eAAe,qBACvCJ,KAAKK,SAAWC,gBACNC,MAAQJ,OAAOK,gBAAgB,GAAGD,MACpCA,OAAST,iBAA4B,GAATS,OAAuB,GAATA,QAG9CE,EAAEC,uBACIC,sBAAaC,cACT,mBAAU,iBACV,mBAAmB,GAATL,MAAa,mBAAqB,eAAgB,4BAC5D,mBAAU,iBACV,mBAAU,WAChB,KAEIP,KAAKK,cAAWQ,EAChBb,KAAKc,cAAcL,EAAEM,kBACtBF,GATP"}
\ No newline at end of file

From 3c79050b785715399b9980c14fc592aa85e4c186 Mon Sep 17 00:00:00 2001
From: Nina Herrmann <nina.herrmann@uni-muenster.de>
Date: Fri, 12 May 2023 11:58:06 +0200
Subject: [PATCH 17/35] Update capabilities.php

---
 classes/capabilities.php | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/classes/capabilities.php b/classes/capabilities.php
index 44dda716d2..7a6f72eb3d 100644
--- a/classes/capabilities.php
+++ b/classes/capabilities.php
@@ -35,46 +35,46 @@
  */
 class capabilities {
 
-    /** capability */
+    /** capability add instance */
     const ADD_INSTANCE = 'mod/moodleoverflow:addinstance';
 
-    /** capability */
+    /** capability view discussions*/
     const VIEW_DISCUSSION = 'mod/moodleoverflow:viewdiscussion';
 
-    /** capability */
+    /** capability reply in discussions*/
     const REPLY_POST = 'mod/moodleoverflow:replypost';
 
-    /** capability */
+    /** capability start discussions*/
     const START_DISCUSSION = 'mod/moodleoverflow:startdiscussion';
 
-    /** capability */
+    /** capability edit post from other course participants*/
     const EDIT_ANY_POST = 'mod/moodleoverflow:editanypost';
 
-    /** capability */
+    /** capability delete your post*/
     const DELETE_OWN_POST = 'mod/moodleoverflow:deleteownpost';
 
-    /** capability */
+    /** capability delete post from any course participant*/
     const DELETE_ANY_POST = 'mod/moodleoverflow:deleteanypost';
 
-    /** capability */
+    /** capability rate a post*/
     const RATE_POST = 'mod/moodleoverflow:ratepost';
 
-    /** capability */
+    /** capability mark a post as a solution for a questions*/
     const MARK_SOLVED = 'mod/moodleoverflow:marksolved';
 
-    /** capability */
+    /** capability manage the subscription of a moodleoverflow instance */
     const MANAGE_SUBSCRIPTIONS = 'mod/moodleoverflow:managesubscriptions';
 
-    /** capability */
+    /** capability force the subscription of participants */
     const ALLOW_FORCE_SUBSCRIBE = 'mod/moodleoverflow:allowforcesubscribe';
 
-    /** capability */
+    /** capability attach files to posts */
     const CREATE_ATTACHMENT = 'mod/moodleoverflow:createattachment';
 
-    /** capability */
+    /** capability review post to be published*/
     const REVIEW_POST = 'mod/moodleoverflow:reviewpost';
 
-    /** @var array */
+    /** @var array cache capabilities*/
     private static $cache = [];
 
     /**

From 723cdcecce00e74b8319fe853f5396e4d777a828 Mon Sep 17 00:00:00 2001
From: Nina Herrmann <nina.herrmann@uni-muenster.de>
Date: Fri, 12 May 2023 13:15:05 +0200
Subject: [PATCH 18/35] Update ratings.php

short check
---
 classes/ratings.php | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/classes/ratings.php b/classes/ratings.php
index 76ea3246fe..e210e6fe65 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -79,10 +79,7 @@ public static function moodleoverflow_add_rating($moodleoverflow, $postid, $rati
 
         // Are multiple marks allowed?
         $markssetting = $DB->get_record('moodleoverflow', array('id' => $moodleoverflow->id), 'allowmultiplemarks');
-        $multiplemarks = false;
-        if ($markssetting->allowmultiplemarks == 1) {
-            $multiplemarks = true;
-        }
+        $multiplemarks = (bool) $markssetting->allowmultiplemarks;
 
         // Retrieve the contexts.
         $modulecontext = \context_module::instance($cm->id);

From 656420bb870f26fff33082bffbb5764758abb69a Mon Sep 17 00:00:00 2001
From: Nina Herrmann <nina.herrmann@uni-muenster.de>
Date: Fri, 12 May 2023 14:29:39 +0200
Subject: [PATCH 19/35] Update dailymail_test.php

---
 tests/dailymail_test.php | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/tests/dailymail_test.php b/tests/dailymail_test.php
index c8675b675c..3c615730ab 100644
--- a/tests/dailymail_test.php
+++ b/tests/dailymail_test.php
@@ -41,22 +41,22 @@
  */
 class dailymail_test extends \advanced_testcase {
 
-    /** @var \stdClass */
+    /** @var \stdClass collection of messages */
     private $sink;
 
-    /** @var \stdClass */
+    /** @var \stdClass test course */
     private $course;
 
-    /** @var \stdClass */
+    /** @var \stdClass test user*/
     private $user;
 
-    /** @var \stdClass */
+    /** @var \stdClass moodleoverflow instance */
     private $moodleoverflow;
 
-    /** @var \stdClass */
+    /** @var \stdClass coursemodule instance */
     private $coursemodule;
 
-    /** @var \stdClass */
+    /** @var \stdClass discussion instance */
     private $discussion;
 
     /**

From cdf2085590313ade818eef199b9de883d9fe8ab5 Mon Sep 17 00:00:00 2001
From: Nina Herrmann <nina.herrmann@uni-muenster.de>
Date: Fri, 12 May 2023 14:37:04 +0200
Subject: [PATCH 20/35] Update userstats_test.php

---
 tests/userstats_test.php | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/tests/userstats_test.php b/tests/userstats_test.php
index 8c0039bbfa..24ac3ed817 100644
--- a/tests/userstats_test.php
+++ b/tests/userstats_test.php
@@ -39,40 +39,40 @@
  */
 class userstats_test extends \advanced_testcase {
 
-    /** @var \stdClass */
+    /** @var \stdClass test course */
     private $course;
 
-    /** @var \stdClass */
+    /** @var \stdClass coursemodule */
     private $coursemodule;
 
-    /** @var \stdClass */
+    /** @var \stdClass test moodleoverflow */
     private $moodleoverflow;
 
-    /** @var \stdClass */
+    /** @var \stdClass test teacher */
     private $teacher;
 
-    /** @var \stdClass */
+    /** @var \stdClass test user */
     private $user1;
 
-    /** @var \stdClass */
+    /** @var \stdClass another test user */
     private $user2;
 
-    /** @var \stdClass */
+    /** @var \stdClass fake discussion */
     private $discussion1;
 
-    /** @var \stdClass */
+    /** @var \stdClass another faked discussion */
     private $discussion2;
 
-    /** @var \stdClass */
+    /** @var \stdClass a post */
     private $post1;
 
-    /** @var \stdClass */
+    /** @var \stdClass another post */
     private $post2;
 
-    /** @var \stdClass */
+    /** @var \stdClass answer to a post */
     private $answer1;
 
-    /** @var \stdClass */
+    /** @var \stdClass another answer to a post */
     private $answer2;
     /** @var \mod_moodleoverflow_generator $generator */
     private $generator;

From 565c6dc147e0c9bcf78c3f57e1ab1bfe669a6ed5 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 15 May 2023 14:25:21 +0200
Subject: [PATCH 21/35] post sorting updated1

---
 classes/ratings.php | 224 ++++++++++++++++++--------------------------
 1 file changed, 89 insertions(+), 135 deletions(-)

diff --git a/classes/ratings.php b/classes/ratings.php
index 76ea3246fe..ada282c927 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -230,160 +230,119 @@ public static function moodleoverflow_get_reputation($moodleoverflowid, $userid
         return self::moodleoverflow_get_reputation_instance($moodleoverflow->id, $userid);
     }
 
-    /**
-     * Sort a discussion by the ratings of their posts.
-     *
-     * @param array $posts
-     *
-     * @return array
-     */
     public static function moodleoverflow_sort_answers_by_ratings($posts) {
-        // Create copies to manipulate.
-        $parentcopy = $posts;
-        $postscopy = $posts;
-        $anothercopy = $posts;
-
-        // Check if teacher ratings are prefered.
-        $preferteacher = (array_shift($anothercopy)->ratingpreference == 1);
-
-        // Create an array with all the keys of the older array.
-        $oldorder = array();
-        foreach ($postscopy as $postid => $post) {
-            $oldorder[] = $postid;
+        // Create a copy that only has the answer posts and save the parent post.
+        $answerposts = $posts;
+        $parentpost = array_shift($answerposts);
+        
+        // Create an empty array for the sorted posts and add the parent post.
+        $sortedposts = array();
+        $sortedposts[0] = $parentpost;
+        // Check if solved posts are preferred over helpful posts.
+        $solutionspreferred = $posts[array_key_first($posts)]->ratingpreference == 1;
+
+        // Sort the answer posts by ratings.
+
+        // Build groups of different types of answers (Solved and helpful, only solved/helpful, other).
+        // If a group is complete, sort the group.
+        $index = 1;
+        $startsolvedandhelpful = 1;
+        $startsolved = 1;
+        $starthelpful = 1;
+        $startother = 1;
+        // Solved and helpful posts are first.
+        foreach ($answerposts as $post) {
+            if ($post->statusteacher == 1 && $post->statusstarter == 1) {
+                $sortedposts[$index] = $post;
+                $index++;
+            }
+        }
+        //Update the indices and sort the group by votes.
+        if ($index > $startsolvedandhelpful) {
+            $startsolved = $index;
+            $starthelpful = $index;
+            $startother = $index;
+            self::moodleoverflow_quicksort_post_by_votes($sortedposts, $startsolvedandhelpful, $index - 1);
         }
 
-        // Create an array for the new order.
-        $neworder = array();
+        // Check if solutions are preferred.
+        if ($solutionspreferred) {
 
-        // The parent post stays the parent post.
-        $parent = array_shift($parentcopy);
-        unset($postscopy[$parent->id]);
-        $discussionid = $parent->discussion;
-        $neworder[] = (int) $parent->id;
-
-        // Check if answers has been marked.
-        $statusstarter = self::moodleoverflow_discussion_is_solved($discussionid, false);
-        $statusteacher = self::moodleoverflow_discussion_is_solved($discussionid, true);
-
-        // The answers that are marked as correct by both are displayed first.
-        if ($statusteacher && $statusstarter) {
-            $markedposts = array();
-            foreach ($statusteacher as $solvedposts) {
-                foreach ($statusstarter as $helpfulposts) {
-                    // Is the same answer correct for both?
-                    if ($solvedposts->postid == $helpfulposts->postid) {
-                        // Save the post that is marked as solved and helpful and go to the next post.
-                        $markedposts[] = $postscopy[$solvedposts->postid];
-                        break;
-                    }
+            // Build the group of only solved posts.
+            foreach ($answerposts as $post) {
+                if ($post->statusteacher == 1 && $post->statusstarter == 0) {
+                    $sortedposts[$index] = $post;
+                    $index++;
                 }
             }
-            // Now sort the posts by their votes.
-            self::moodleoverflow_sort_post_by_votes($markedposts);
-
-            // Iterate trough the marked posts and add it to the new Order.
-            foreach ($markedposts as $post) {
-                // Add the post to the new order and delete it from the posts array.
-                $neworder[] = (int) $post->id;
-                unset($postscopy[$post->id]);
+            // Update the indices and sort the group by votes.
+            if ($index > $startsolved) {
+                $starthelpful = $index;
+                $startother = $index;
+                self::moodleoverflow_quicksort_post_by_votes($sortedposts, $startsolved, $index - 1);
             }
-        }
 
-        // If the answers the teacher marks are preferred, and only
-        // the teacher marked an answer as solved, display it first.
-        if ($preferteacher && $statusteacher) {
-            // Save the marked posts.
-            $markedposts = array();
-            foreach ($statusteacher as $solvedpost) {
-                if (array_key_exists($solvedpost->postid, $postscopy)) {
-                    $markedposts[] = $postscopy[$solvedpost->postid];
+            // Build the group of only helpful posts.
+            foreach ($answerposts as $post) {
+                if ($post->statusteacher == 0 && $post->statusstarter == 1) {
+                    $sortedposts[$index] = $post;
+                    $index++;
                 }
             }
-            // Sort the solved answers.
-            self::moodleoverflow_sort_post_by_votes($markedposts);
-
-            // Iterate trough the marked posts and add it to the new Order.
-            foreach ($markedposts as $post) {
-                // Add the post to the new order and delete it from the posts array.
-                $neworder[] = (int) $post->id;
-                unset($postscopy[$post->id]);
+            // Update the indices and sort the group by votes.
+            if ($index > $starthelpful) {
+                $startother = $index;
+                self::moodleoverflow_quicksort_post_by_votes($sortedposts, $starthelpful, $index - 1);
             }
-        }
+        } else {
 
-        // If the user who started the discussion has marked
-        // an answer as helpful, display this answer first.
-        if ($statusstarter) {
-            // Save the marked posts.
-            $markedposts = array();
-            foreach ($statusstarter as $helpfulpost) {
-                if (array_key_exists($helpfulpost->postid, $postscopy)) {
-                    $markedposts[] = $postscopy[$helpfulpost->postid];
+            // Build the group of only helpful posts.
+            foreach ($answerposts as $post) {
+                if ($post->statusteacher == 0 && $post->statusstarter == 1) {
+                    $sortedposts[$index] = $post;
+                    $index++;
                 }
             }
-            // Sort the helpful answers.
-            self::moodleoverflow_sort_post_by_votes($markedposts);
-
-            // Iterate trough the marked posts and add it to the new Order.
-            foreach ($markedposts as $post) {
-                // Add the post to the new order and delete it from the posts array.
-                $neworder[] = (int) $post->id;
-                unset($postscopy[$post->id]);
+            // Update the indices and sort the group by votes.
+            if ($index > $starthelpful) {
+                $startsolved = $index;
+                $startother = $index;
+                self::moodleoverflow_quicksort_post_by_votes($sortedposts, $starthelpful, $index - 1);
             }
-        }
 
-        // If a teacher has marked an answer as solved, display it next.
-        if ($statusteacher) {
-            // Save the marked posts.
-            $markedposts = array();
-            foreach ($statusteacher as $solvedpost) {
-                if (array_key_exists($solvedpost->postid, $postscopy)) {
-                    $markedposts[] = $postscopy[$solvedpost->postid];
+            // Build the group of only solved posts.
+            foreach ($answerposts as $post) {
+                if ($post->statusteacher == 1 && $post->statusstarter == 0) {
+                    $sortedposts[$index] = $post;
+                    $index++;
                 }
             }
-            // Sort the solved answers.
-            self::moodleoverflow_sort_post_by_votes($markedposts);
-
-            // Iterate trough the marked posts and add it to the new Order.
-            foreach ($markedposts as $post) {
-                // Add the post to the new order and delete it from the posts array.
-                $neworder[] = (int) $post->id;
-                unset($postscopy[$post->id]);
+            // Update the indices and sort the group by votes.
+            if ($index > $startsolved) {
+                $startother = $index;
+                self::moodleoverflow_quicksort_post_by_votes($sortedposts, $startsolved, $index - 1);
             }
         }
 
-        // All answers that are not marked by someone should now be left.
-
-        // Search for all comments.
-        foreach ($postscopy as $postid => $post) {
-            // Add all comments to the order.
-            // They are independant from the votes.
-            if ($post->parent != $parent->id) {
-                $neworder[] = $postid;
-                unset($postscopy[$postid]);
+        // Now build the group of posts without ratings like helpful/solved.
+        foreach ($answerposts as $post) {
+            if ($post->statusteacher == 0 && $post->statusstarter == 0) {
+                $sortedposts[$index] = $post;
+                $index++;
             }
         }
-
-        // Sort the remaining answers by their total votes.
-        $votesarray = array();
-        foreach ($postscopy as $postid => $post) {
-            $votesarray[$post->id] = $post->upvotes - $post->downvotes;
-        }
-        arsort($votesarray);
-
-        // Add the remaining messages to the new order.
-        foreach ($votesarray as $postid => $votes) {
-            $neworder[] = $postid;
+        // Update the indices and sort the group by votes.
+        if ($index > $startother) {
+            self::moodleoverflow_quicksort_post_by_votes($sortedposts, $startother, $index - 1);
         }
 
-        // The new order is determined.
-        // It has to be applied now.
-        $sortedposts = array();
-        foreach ($neworder as $k) {
-            $sortedposts[$k] = $posts[$k];
+        // Rearrange the indices and return the sorted posts.
+        $neworder = array();
+        foreach ($sortedposts as $post) {
+            $neworder[$post->id] = $post;
         }
-
-        // Return the sorted posts.
-        return $sortedposts;
+        // return now the sorted posts.
+        return $neworder;
     }
 
     /**
@@ -845,18 +804,13 @@ public static function moodleoverflow_user_can_rate($post, $modulecontext, $user
             && $post->reviewed == 1;
     }
 
-    private static function moodleoverflow_sort_post_by_votes(array $posts) {
-        // Function uses quicksort to sort the posts in descending order.
-        self::moodleoverflow_quicksort_post_by_votes($posts, 0, count($posts) - 1);
-    }
-
-    private static function moodleoverflow_quicksort_post_by_votes(array $posts, $low, $high) {
+    private static function moodleoverflow_quicksort_post_by_votes(array &$posts, $low, $high) {
         if ($low >= $high) {
             return;
         }
         $left = $low;
         $right = $high;
-        $pivot = $posts[($low + $high) / 2]->votesdifference;
+        $pivot = $posts[intval(($low + $high) / 2)]->votesdifference;
         do {
             while ($posts[$left]->votesdifference > $pivot) {
                 $left++;

From 0951e8d9e8cde68769aba300ec2783a12d81caad Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 15 May 2023 15:58:47 +0200
Subject: [PATCH 22/35] WIP: sort of posts by rating

---
 classes/ratings.php      |   8 ++
 tests/ratings_test.php   | 224 +++++++++++++++++++++++++++++++++++++++
 tests/userstats_test.php |   3 +-
 3 files changed, 234 insertions(+), 1 deletion(-)
 create mode 100644 tests/ratings_test.php

diff --git a/classes/ratings.php b/classes/ratings.php
index 4aee9c48f5..0c6bb5288c 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -227,6 +227,11 @@ public static function moodleoverflow_get_reputation($moodleoverflowid, $userid
         return self::moodleoverflow_get_reputation_instance($moodleoverflow->id, $userid);
     }
 
+    /**
+     * Sort the answers of a discussion by their marks and votes.
+     * 
+     * @param object $posts all the posts from a discussion.
+     */
     public static function moodleoverflow_sort_answers_by_ratings($posts) {
         // Create a copy that only has the answer posts and save the parent post.
         $answerposts = $posts;
@@ -235,12 +240,15 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
         // Create an empty array for the sorted posts and add the parent post.
         $sortedposts = array();
         $sortedposts[0] = $parentpost;
+
         // Check if solved posts are preferred over helpful posts.
         $solutionspreferred = $posts[array_key_first($posts)]->ratingpreference == 1;
 
         // Sort the answer posts by ratings.
 
         // Build groups of different types of answers (Solved and helpful, only solved/helpful, other).
+        // statusteacher == 1 means the post is marked as solved.
+        // statusstarter == 1 means the post is marked as helpful.
         // If a group is complete, sort the group.
         $index = 1;
         $startsolvedandhelpful = 1;
diff --git a/tests/ratings_test.php b/tests/ratings_test.php
new file mode 100644
index 0000000000..232e34afc3
--- /dev/null
+++ b/tests/ratings_test.php
@@ -0,0 +1,224 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * The module moodleoverflow tests.
+ *
+ * @package   mod_moodleoverflow
+ * @copyright 2023 Tamaro Walter
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace mod_moodleoverflow;
+use mod_moodleoverflow\ratings;
+
+defined('MOODLE_INTERNAL') || die();
+
+
+global $CFG;
+require_once($CFG->dirroot . '/mod/moodleoverflow/locallib.php');
+
+/**
+ * PHPUnit Tests for testing the ratings.php.
+ *
+ * @package mod_moodleoverflow
+ * @copyright 2023 Tamaro Walter
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class ratings_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 test user */
+    private $user1;
+
+    /** @var \stdClass another test user */
+    private $user2;
+
+    /** @var \stdClass a discussion */
+    private $discussion;
+
+    /** @var \stdClass a post from the teacher*/
+    private $post;
+
+    /** @var \stdClass answer from user 1 */
+    private $answer1;
+
+    /** @var \stdClass answer from user 1 */
+    private $answer2;
+
+    /** @var \stdClass answer from user 1 */
+    private $answer3;
+
+    /** @var \stdClass answer from user 2 */
+    private $answer4;
+
+    /** @var \stdClass answer from user 2 */
+    private $answer5;
+
+    /** @var \stdClass answer from user 2 */
+    private $answer6;
+
+    /** @var \mod_moodleoverflow_generator $generator */
+    private $generator;
+
+    /**
+     * Test setUp.
+     */
+    public function setUp(): void {
+        $this->resetAfterTest();
+        $this->helper_course_set_up();
+    }
+
+    /**
+     * Test tearDown.
+     */
+    public function tearDown(): void {
+        // Clear all caches.
+        \mod_moodleoverflow\subscriptions::reset_moodleoverflow_cache();
+        \mod_moodleoverflow\subscriptions::reset_discussion_cache();
+    }
+
+    // Begin of test functions
+    
+    /**
+     * Test, if rating can sort after ever group with ratingpreferences on helpful first.
+     */
+    public function test_everygrouphelpfulon() {
+        // Create helpful and solved and up and downvotes ratings.
+        $this->create_everygroup();
+        $this->set_ratingpreferences(1);
+        // Create a array of the posts and sort them.
+        $posts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
+        $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
+        var_dump($sortedposts);
+        $this->assertEquals(1,1);
+    }
+
+    // Helper functions
+    
+    /**
+     * This function creates:
+     * - a course with a moodleoverflow
+     * - a teacher, who creates a discussion with a post
+     * - 2 users, which answer to the post from the teacher
+     */
+    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 2 users.
+        $this->user1 = $this->getDataGenerator()->create_user(array('firstname' => 'Ava', 'lastname' => 'Davis'));
+        $this->getDataGenerator()->enrol_user($this->user1->id, $this->course->id, 'student');
+        $this->user2 = $this->getDataGenerator()->create_user(array('firstname' => 'Ethan', 'lastname' => 'Brown'));
+        $this->getDataGenerator()->enrol_user($this->user2->id, $this->course->id, 'student');
+
+        // Create a discussion, a parent post and six answers
+        $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), '*');
+        $this->answer1 = $this->generator->reply_to_post($this->discussion[1], $this->user1, true);
+        $this->answer2 = $this->generator->reply_to_post($this->discussion[1], $this->user1, true);
+        $this->answer3 = $this->generator->reply_to_post($this->discussion[1], $this->user1, true);
+        $this->answer4 = $this->generator->reply_to_post($this->discussion[1], $this->user2, true);
+        $this->answer5 = $this->generator->reply_to_post($this->discussion[1], $this->user2, true);
+        $this->answer6 = $this->generator->reply_to_post($this->discussion[1], $this->user2, true);
+    }
+
+    /**
+     * creates a rating of every type by adding attributes to the post:
+     * - post that is solved and helpful
+     * . post that is only helpful
+     * - post that is only solved
+     * - post that is not marked
+     */
+    private function create_everygroup() {
+        // Answer1.
+        $this->answer1->upvotes = 0;
+        $this->answer1->downvotes = 0;
+        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes;
+        $this->answer1->statusstarter = 1;
+        $this->answer1->statusteacher = 1;
+        
+        // Answer2.
+        $this->answer2->upvotes = 0;
+        $this->answer2->downvotes = 0;
+        $this->answer2->votesdifference = $this->answer2->upvotes - $this->answer2->downvotes;
+        $this->answer2->statusstarter = 0;
+        $this->answer2->statusteacher = 1;
+        
+        // Answer3.
+        $this->answer3->upvotes = 0;
+        $this->answer3->downvotes = 0;
+        $this->answer3->votesdifference = $this->answer3->upvotes - $this->answer3->downvotes;
+        $this->answer3->statusstarter = 1;
+        $this->answer3->statusteacher = 0;
+
+        // Answer4.
+        $this->answer4->upvotes = 1;
+        $this->answer4->downvotes = 0;
+        $this->answer4->votesdifference = $this->answer4->upvotes - $this->answer4->downvotes;
+        $this->answer4->statusstarter = 0;
+        $this->answer4->statusteacher = 0;
+
+        // Answer5.
+        $this->answer5->upvotes = 0;
+        $this->answer5->downvotes = 1;
+        $this->answer5->votesdifference = $this->answer5->upvotes - $this->answer5->downvotes;
+        $this->answer5->statusstarter = 0;
+        $this->answer5->statusteacher = 0;
+
+        // Answer6.
+        $this->answer6->upvotes = 0;
+        $this->answer6->downvotes = 0;
+        $this->answer6->votesdifference = $this->answer6->upvotes - $this->answer6->downvotes;
+        $this->answer6->statusstarter = 0;
+        $this->answer6->statusteacher = 0;
+    }
+
+    /**
+     * sets the ratingpreferences to 1 or 0:
+     * 1 = solved posts will be shown above helpful posts.
+     * 0 = helpful posts will be shown above solved posts.
+     */
+    private function set_ratingpreferences($preference) {
+        if ($preference == 0 || $preference == 1) {
+            $this->post->ratingpreference = $preference;
+            $this->answer1->ratingpreference = $preference;
+            $this->answer2->ratingpreference = $preference;
+            $this->answer3->ratingpreference = $preference;
+            $this->answer4->ratingpreference = $preference;
+            $this->answer5->ratingpreference = $preference;
+            $this->answer6->ratingpreference = $preference;
+        }
+    }
+}
diff --git a/tests/userstats_test.php b/tests/userstats_test.php
index 24ac3ed817..01bb9862fb 100644
--- a/tests/userstats_test.php
+++ b/tests/userstats_test.php
@@ -57,7 +57,7 @@ class userstats_test extends \advanced_testcase {
     /** @var \stdClass another test user */
     private $user2;
 
-    /** @var \stdClass fake discussion */
+    /** @var \stdClass a discussion */
     private $discussion1;
 
     /** @var \stdClass another faked discussion */
@@ -74,6 +74,7 @@ class userstats_test extends \advanced_testcase {
 
     /** @var \stdClass another answer to a post */
     private $answer2;
+
     /** @var \mod_moodleoverflow_generator $generator */
     private $generator;
 

From 82e7ff7409e189ab5f62cc39295edad1318ca7cb Mon Sep 17 00:00:00 2001
From: NinaHerrmann <nina.herrmann@uni-muenster.de>
Date: Mon, 15 May 2023 14:41:34 +0200
Subject: [PATCH 23/35] minor changes

---
 amd/src/rating.js        | 8 ++++----
 tests/dailymail_test.php | 2 --
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/amd/src/rating.js b/amd/src/rating.js
index 41ac768c30..133eeb71db 100644
--- a/amd/src/rating.js
+++ b/amd/src/rating.js
@@ -112,10 +112,10 @@ export function init(userid, allowmultiplemarks) {
                 const rating = shouldRemove ? baseRating * 10 : baseRating;
                 await sendVote(postid, rating, userid);
 
-                /* If multiplemarks are not allowed (that is the default mode): delete all marks.
-                   else: only delete the mark if the post is being unmarked.
+                /* If       multiplemarks are not allowed (that is the default mode): delete all marks.
+                   else:    only delete the mark if the post is being unmarked.
 
-                   then add a mark, if the post is being marked.
+                   Add a mark, if the post is being marked.
                 */
                 if (!allowmultiplemarks) {
                     // Delete all marks in the discussion
@@ -149,7 +149,7 @@ export function init(userid, allowmultiplemarks) {
 
 /**
  * Function to change the String of the post data-action button.
- * Only usable if mulitplemarks are allowed.
+ * Only used if mulitplemarks are allowed.
  * @param {string} htmlclass the class where the String is being updated
  * @param {string} action    helpful or solved mark
  */
diff --git a/tests/dailymail_test.php b/tests/dailymail_test.php
index 3c615730ab..29fc74bdce 100644
--- a/tests/dailymail_test.php
+++ b/tests/dailymail_test.php
@@ -86,8 +86,6 @@ public function tearDown(): void {
         \mod_moodleoverflow\subscriptions::reset_discussion_cache();
     }
 
-
-
     // Helper functions.
 
     /**

From a161e7ad19b90c890eec204b5a414c9d24d7717a Mon Sep 17 00:00:00 2001
From: NinaHerrmann <nina.herrmann@uni-muenster.de>
Date: Mon, 15 May 2023 14:53:50 +0200
Subject: [PATCH 24/35] rename statusteacher and statusstarter in markedhelpful
 and markedsolution

---
 amd/build/rating.min.js            |  2 +-
 amd/build/rating.min.js.map        |  2 +-
 amd/src/rating.js                  |  2 +-
 classes/ratings.php                | 12 ++++----
 locallib.php                       | 44 +++++++++++++++---------------
 styles.css                         | 10 +++----
 templates/discussion_list.mustache | 16 +++++------
 templates/discussions.mustache     |  8 +++---
 8 files changed, 48 insertions(+), 48 deletions(-)

diff --git a/amd/build/rating.min.js b/amd/build/rating.min.js
index f60ce3513a..276685f62e 100644
--- a/amd/build/rating.min.js
+++ b/amd/build/rating.min.js
@@ -5,6 +5,6 @@ define("mod_moodleoverflow/rating",["exports","core/ajax","core/prefetch","core/
    * @module     mod_moodleoverflow/rating
    * @copyright  2022 Justus Dieckmann WWU
    * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemarks){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"statusstarter":"statusteacher",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),allowmultiplemarks)shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"),changeStrings(htmlclass,action));else for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"),allowmultiplemarks&&changeStrings(htmlclass,action))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}async function changeStrings(htmlclass,action){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","alsomarksolved","markhelpful","alsomarkhelpful"]);var othermarkedposts=!1;for(const el of root.querySelectorAll(".moodleoverflowpost"))if(el.classList.contains(htmlclass)){othermarkedposts=!0;break}for(const el of root.querySelectorAll(".moodleoverflowpost"))!el.classList.contains(htmlclass)&&el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]'))&&(el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=othermarkedposts?await(0,_str.get_string)("alsomark".concat(action),"mod_moodleoverflow"):await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"))}}));
+   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemarks){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"markedhelpful":"markedsolution",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),allowmultiplemarks)shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"),changeStrings(htmlclass,action));else for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"),allowmultiplemarks&&changeStrings(htmlclass,action))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}async function changeStrings(htmlclass,action){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","alsomarksolved","markhelpful","alsomarkhelpful"]);var othermarkedposts=!1;for(const el of root.querySelectorAll(".moodleoverflowpost"))if(el.classList.contains(htmlclass)){othermarkedposts=!0;break}for(const el of root.querySelectorAll(".moodleoverflowpost"))!el.classList.contains(htmlclass)&&el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]'))&&(el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=othermarkedposts?await(0,_str.get_string)("alsomark".concat(action),"mod_moodleoverflow"):await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"))}}));
 
 //# sourceMappingURL=rating.min.js.map
\ No newline at end of file
diff --git a/amd/build/rating.min.js.map b/amd/build/rating.min.js.map
index 11f1b470a8..a1fb0b6643 100644
--- a/amd/build/rating.min.js.map
+++ b/amd/build/rating.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {boolean} allowmultiplemarks   // true means allowed, false means not allowed.\n *\n */\nexport function init(userid, allowmultiplemarks) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (!allowmultiplemarks) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                        changeStrings(htmlclass, action);\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                    if (allowmultiplemarks) {\n                        changeStrings(htmlclass, action);\n                    }\n                }\n\n            }\n        }\n    };\n\n}\n\n/**\n * Function to change the String of the post data-action button.\n * Only usable if mulitplemarks are allowed.\n * @param {string} htmlclass the class where the String is being updated\n * @param {string} action    helpful or solved mark\n */\nasync function changeStrings(htmlclass, action) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'alsomarksolved', 'markhelpful', 'alsomarkhelpful',]);\n\n    // 1. Step: Are there other posts in the Discussion, that are solved/helpful?\n    var othermarkedposts = false;\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (el.classList.contains(htmlclass)) {\n            othermarkedposts = true;\n            break;\n        }\n    }\n    // 2. Step: Change the strings of the action Button of the unmarked posts.\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (!el.classList.contains(htmlclass) && el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`)) {\n            if (othermarkedposts) {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n            } else {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`mark${action}`, 'mod_moodleoverflow');\n            }\n        }\n    }\n}"],"names":["userid","allowmultiplemarks","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","remove","textContent","changeStrings","el","querySelectorAll","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating","othermarkedposts"],"mappings":";;;;;;;oFAsEqBA,OAAQC,sCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAO1BC,mBASG0B,eACAf,YAAYgB,UAAUM,OAAOR,WAC7BpB,cAAc6B,kBAAoB,iCAAiBzB,QAAU,sBAC7D0B,cAAcV,UAAWhB,kBAVxB,MAAM2B,MAAMlC,KAAKmC,iBAAiB,uBAAyBZ,WAC5DW,GAAGT,UAAUM,OAAOR,WACpBW,GAAGb,qDAA8Cd,cAAYyB,kBACnD,iCAAiBzB,QAAU,sBAWxCiB,eACDf,YAAYgB,UAAUW,IAAIb,WAC1BpB,cAAc6B,kBAAoB,oCAAoBzB,QAAU,sBAC5DT,oBACAmC,cAAcV,UAAWhB,mGAjH3CY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOqC,SAASC,eAAe,sCAStB1B,SAASF,OAAQoB,OAAQjC,cAC9B0C,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFjC,OAAQA,OACRkC,SAAUd,WAEd,UACJ9B,KAAKmC,gEAAyDtC,cAAYgD,SAASC,IAC/EA,EAAEd,YAAcO,SAASQ,eAAzB,IAEJ/C,KAAKmC,gEAAyDI,SAASS,eAAaH,SAASC,IACzFA,EAAEd,YAAcO,SAASU,eAAzB,IAEJjD,KAAKmC,gEAAyDzB,cAAYmC,SAASC,IAC/EA,EAAEd,YAAcO,SAASW,UAAzB,IAEGX,wBAgGIN,cAAcV,UAAWhB,0BAC3BR,gBAAgB,qBACrB,CAAC,aAAc,iBAAkB,cAAe,wBAGhDoD,kBAAmB,MAClB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,0BAC/BD,GAAGT,UAAUC,SAASH,WAAY,CAClC4B,kBAAmB,YAKtB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,wBAC9BD,GAAGT,UAAUC,SAASH,YAAcW,GAAGb,qDAA8Cd,gBAElF2B,GAAGb,qDAA8Cd,cAAYyB,YAD7DmB,uBAEU,qCAAqB5C,QAAU,4BAG/B,iCAAiBA,QAAU"}
\ No newline at end of file
+{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {boolean} allowmultiplemarks   // true means allowed, false means not allowed.\n *\n */\nexport function init(userid, allowmultiplemarks) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'markedhelpful' : 'markedsolution';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (!allowmultiplemarks) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                        changeStrings(htmlclass, action);\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                    if (allowmultiplemarks) {\n                        changeStrings(htmlclass, action);\n                    }\n                }\n\n            }\n        }\n    };\n\n}\n\n/**\n * Function to change the String of the post data-action button.\n * Only usable if mulitplemarks are allowed.\n * @param {string} htmlclass the class where the String is being updated\n * @param {string} action    helpful or solved mark\n */\nasync function changeStrings(htmlclass, action) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'alsomarksolved', 'markhelpful', 'alsomarkhelpful',]);\n\n    // 1. Step: Are there other posts in the Discussion, that are solved/helpful?\n    var othermarkedposts = false;\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (el.classList.contains(htmlclass)) {\n            othermarkedposts = true;\n            break;\n        }\n    }\n    // 2. Step: Change the strings of the action Button of the unmarked posts.\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (!el.classList.contains(htmlclass) && el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`)) {\n            if (othermarkedposts) {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n            } else {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`mark${action}`, 'mod_moodleoverflow');\n            }\n        }\n    }\n}"],"names":["userid","allowmultiplemarks","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","remove","textContent","changeStrings","el","querySelectorAll","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating","othermarkedposts"],"mappings":";;;;;;;oFAsEqBA,OAAQC,sCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAO1BC,mBASG0B,eACAf,YAAYgB,UAAUM,OAAOR,WAC7BpB,cAAc6B,kBAAoB,iCAAiBzB,QAAU,sBAC7D0B,cAAcV,UAAWhB,kBAVxB,MAAM2B,MAAMlC,KAAKmC,iBAAiB,uBAAyBZ,WAC5DW,GAAGT,UAAUM,OAAOR,WACpBW,GAAGb,qDAA8Cd,cAAYyB,kBACnD,iCAAiBzB,QAAU,sBAWxCiB,eACDf,YAAYgB,UAAUW,IAAIb,WAC1BpB,cAAc6B,kBAAoB,oCAAoBzB,QAAU,sBAC5DT,oBACAmC,cAAcV,UAAWhB,mGAjH3CY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOqC,SAASC,eAAe,sCAStB1B,SAASF,OAAQoB,OAAQjC,cAC9B0C,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFjC,OAAQA,OACRkC,SAAUd,WAEd,UACJ9B,KAAKmC,gEAAyDtC,cAAYgD,SAASC,IAC/EA,EAAEd,YAAcO,SAASQ,eAAzB,IAEJ/C,KAAKmC,gEAAyDI,SAASS,eAAaH,SAASC,IACzFA,EAAEd,YAAcO,SAASU,eAAzB,IAEJjD,KAAKmC,gEAAyDzB,cAAYmC,SAASC,IAC/EA,EAAEd,YAAcO,SAASW,UAAzB,IAEGX,wBAgGIN,cAAcV,UAAWhB,0BAC3BR,gBAAgB,qBACrB,CAAC,aAAc,iBAAkB,cAAe,wBAGhDoD,kBAAmB,MAClB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,0BAC/BD,GAAGT,UAAUC,SAASH,WAAY,CAClC4B,kBAAmB,YAKtB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,wBAC9BD,GAAGT,UAAUC,SAASH,YAAcW,GAAGb,qDAA8Cd,gBAElF2B,GAAGb,qDAA8Cd,cAAYyB,YAD7DmB,uBAEU,qCAAqB5C,QAAU,4BAG/B,iCAAiBA,QAAU"}
\ No newline at end of file
diff --git a/amd/src/rating.js b/amd/src/rating.js
index 133eeb71db..0f44f3ba77 100644
--- a/amd/src/rating.js
+++ b/amd/src/rating.js
@@ -106,7 +106,7 @@ export function init(userid, allowmultiplemarks) {
             case 'helpful':
             case 'solved': {
                 const isHelpful = action === 'helpful';
-                const htmlclass = isHelpful ? 'statusstarter' : 'statusteacher';
+                const htmlclass = isHelpful ? 'markedhelpful' : 'markedsolution';
                 const shouldRemove = postElement.classList.contains(htmlclass);
                 const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;
                 const rating = shouldRemove ? baseRating * 10 : baseRating;
diff --git a/classes/ratings.php b/classes/ratings.php
index 0c6bb5288c..ac05e83a77 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -257,7 +257,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
         $startother = 1;
         // Solved and helpful posts are first.
         foreach ($answerposts as $post) {
-            if ($post->statusteacher == 1 && $post->statusstarter == 1) {
+            if ($post->markedsolution == 1 && $post->markedhelpful == 1) {
                 $sortedposts[$index] = $post;
                 $index++;
             }
@@ -275,7 +275,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
 
             // Build the group of only solved posts.
             foreach ($answerposts as $post) {
-                if ($post->statusteacher == 1 && $post->statusstarter == 0) {
+                if ($post->markedsolution == 1 && $post->markedhelpful == 0) {
                     $sortedposts[$index] = $post;
                     $index++;
                 }
@@ -289,7 +289,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
 
             // Build the group of only helpful posts.
             foreach ($answerposts as $post) {
-                if ($post->statusteacher == 0 && $post->statusstarter == 1) {
+                if ($post->markedsolution == 0 && $post->markedhelpful == 1) {
                     $sortedposts[$index] = $post;
                     $index++;
                 }
@@ -303,7 +303,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
 
             // Build the group of only helpful posts.
             foreach ($answerposts as $post) {
-                if ($post->statusteacher == 0 && $post->statusstarter == 1) {
+                if ($post->markedsolution == 0 && $post->markedhelpful == 1) {
                     $sortedposts[$index] = $post;
                     $index++;
                 }
@@ -317,7 +317,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
 
             // Build the group of only solved posts.
             foreach ($answerposts as $post) {
-                if ($post->statusteacher == 1 && $post->statusstarter == 0) {
+                if ($post->markedsolution == 1 && $post->markedhelpful == 0) {
                     $sortedposts[$index] = $post;
                     $index++;
                 }
@@ -331,7 +331,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
 
         // Now build the group of posts without ratings like helpful/solved.
         foreach ($answerposts as $post) {
-            if ($post->statusteacher == 0 && $post->statusstarter == 0) {
+            if ($post->markedsolution == 0 && $post->markedhelpful == 0) {
                 $sortedposts[$index] = $post;
                 $index++;
             }
diff --git a/locallib.php b/locallib.php
index de9c882893..71ff6052eb 100644
--- a/locallib.php
+++ b/locallib.php
@@ -265,31 +265,31 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = -
         }
 
         // Check if the question owner marked the question as helpful.
-        $statusstarter = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussion->discussion, false);
+        $markedhelpful = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussion->discussion, false);
         $preparedarray[$i]['starterlink'] = null;
-        if ($statusstarter) {
+        if ($markedhelpful) {
             $link = '/mod/moodleoverflow/discussion.php?d=';
-            $statusstarter = $statusstarter[array_key_first($statusstarter)];
+            $markedhelpful = $markedhelpful[array_key_first($markedhelpful)];
 
             $preparedarray[$i]['starterlink'] = new moodle_url($link .
-                $statusstarter->discussionid . '#p' . $statusstarter->postid);
+                $markedhelpful->discussionid . '#p' . $markedhelpful->postid);
         }
 
         // Check if a teacher marked a post as solved.
-        $statusteacher = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussion->discussion, true);
+        $markedsolution = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussion->discussion, true);
         $preparedarray[$i]['teacherlink'] = null;
-        if ($statusteacher) {
+        if ($markedsolution) {
             $link = '/mod/moodleoverflow/discussion.php?d=';
-            $statusteacher = $statusteacher[array_key_first($statusteacher)];
+            $markedsolution = $markedsolution[array_key_first($markedsolution)];
 
             $preparedarray[$i]['teacherlink'] = new moodle_url($link .
-                $statusteacher->discussionid . '#p' . $statusteacher->postid);
+                $markedsolution->discussionid . '#p' . $markedsolution->postid);
         }
 
         // Check if a single post was marked by the question owner and a teacher.
         $statusboth = false;
-        if ($statusstarter  && $statusteacher) {
-            if ($statusstarter->postid == $statusteacher->postid) {
+        if ($markedhelpful  && $markedsolution) {
+            if ($markedhelpful->postid == $markedsolution->postid) {
                 $statusboth = true;
             }
         }
@@ -401,8 +401,8 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = -
         $preparedarray[$i]['linktopopup'] = $linktopopup;
 
         // Add all created data to an array.
-        $preparedarray[$i]['statusstarter'] = $statusstarter;
-        $preparedarray[$i]['statusteacher'] = $statusteacher;
+        $preparedarray[$i]['markedhelpful'] = $markedhelpful;
+        $preparedarray[$i]['markedsolution'] = $markedsolution;
         $preparedarray[$i]['statusboth'] = $statusboth;
         $preparedarray[$i]['votes'] = $votes;
 
@@ -1059,8 +1059,8 @@ function moodleoverflow_get_all_discussion_posts($discussionid, $tracking, $modc
         $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;
+        $posts[$postid]->markedhelpful = $discussionratings[$post->id]->ishelpful;
+        $posts[$postid]->markedsolution = $discussionratings[$post->id]->issolved;
     }
 
     // Order the answers by their ratings.
@@ -1240,7 +1240,7 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
     if ($canmarkhelpful) {
         // When the post is already marked, remove the mark instead.
         $link = '/mod/moodleoverflow/discussion.php';
-        if ($post->statusstarter) {
+        if ($post->markedhelpful) {
             $commands[] = html_writer::tag('a', $str->marknothelpful,
                     array('class' => 'markhelpful onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'helpful'));
         } else {
@@ -1262,7 +1262,7 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
 
         // When the post is already marked, remove the mark instead.
         $link = '/mod/moodleoverflow/discussion.php';
-        if ($post->statusteacher) {
+        if ($post->markedsolution) {
             $commands[] = html_writer::tag('a', $str->marknotsolved,
                     array('class' => 'marksolved onlyifreviewed', 'role' => 'button', 'data-moodleoverflow-action' => 'solved'));
         } else {
@@ -1335,8 +1335,8 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
     $mustachedata->votes = $post->upvotes - $post->downvotes;
 
     // Check if the post is marked.
-    $mustachedata->statusstarter = $post->statusstarter;
-    $mustachedata->statusteacher = $post->statusteacher;
+    $mustachedata->markedhelpful = $post->markedhelpful;
+    $mustachedata->markedsolution = $post->markedsolution;
 
     // Did the user rated this post?
     $rating = \mod_moodleoverflow\ratings::moodleoverflow_user_rated($post->id);
@@ -1377,11 +1377,11 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
             }
         }
     }
-    if ($post->statusstarter) {
-        $postclass .= ' statusstarter';
+    if ($post->markedhelpful) {
+        $postclass .= ' markedhelpful';
     }
-    if ($post->statusteacher) {
-        $postclass .= ' statusteacher';
+    if ($post->markedsolution) {
+        $postclass .= ' markedsolution';
     }
     $mustachedata->postclass = $postclass;
 
diff --git a/styles.css b/styles.css
index c6c3c8fbf7..f0c92ff5bf 100644
--- a/styles.css
+++ b/styles.css
@@ -191,7 +191,7 @@
     width: 4px;
 }
 
-.moodleoverflowpost.statusteacher.statusstarter:before {
+.moodleoverflowpost.markedsolution.markedhelpful:before {
     background: linear-gradient(80deg, rgba(234, 133, 22, 1) 50%, rgba(112, 160, 52, 1) 50%);
     bottom: -1px;
     content: "";
@@ -202,7 +202,7 @@
     width: 4px;
 }
 
-.moodleoverflowpost.statusteacher:before {
+.moodleoverflowpost.markedsolution:before {
     background: rgba(112, 160, 52, 1);
     bottom: -1px;
     content: "";
@@ -213,7 +213,7 @@
     width: 4px;
 }
 
-.moodleoverflowpost.statusstarter:before {
+.moodleoverflowpost.markedhelpful:before {
     background: rgba(234, 133, 22, 1);
     bottom: -1px;
     content: "";
@@ -229,11 +229,11 @@
     display: none;
 }
 
-.moodleoverflowpost.statusstarter .onlyifhelpful {
+.moodleoverflowpost.markedhelpful .onlyifhelpful {
     display: initial;
 }
 
-.moodleoverflowpost.statusteacher .onlyifsolved {
+.moodleoverflowpost.markedsolution .onlyifsolved {
     display: initial;
 }
 
diff --git a/templates/discussion_list.mustache b/templates/discussion_list.mustache
index 19902f0a78..1cb4a05c76 100644
--- a/templates/discussion_list.mustache
+++ b/templates/discussion_list.mustache
@@ -119,23 +119,23 @@
                         {{#pix}}i/duration, moodle, {{#str}}pending_review, mod_moodleoverflow{{/str}}{{/pix}}
                     {{/ questionunderreview }}
                     {{^ questionunderreview }}
-                        {{#statusteacher}}
+                        {{#markedsolution}}
                             <a href="{{teacherlink}}">
                                 {{# pix}} status/c_outline, moodleoverflow, {{#str}}teacherrating, moodleoverflow{{/str}} {{/ pix}}
                             </a>
-                        {{/statusteacher}}
-                        {{^statusteacher}}
+                        {{/markedsolution}}
+                        {{^markedsolution}}
                             {{# pix}} status/c_blank, moodleoverflow, {{#str}}marknotsolved, moodleoverflow{{/str}}{{/ pix}}
-                        {{/statusteacher}}
+                        {{/markedsolution}}
 
-                        {{#statusstarter}}
+                        {{#markedhelpful}}
                             <a href="{{starterlink}}">
                                 {{# pix}} status/b_outline, moodleoverflow, {{#str}}starterrating, moodleoverflow{{/str}} {{/ pix}}
                             </a>
-                        {{/statusstarter}}
-                        {{^statusstarter}}
+                        {{/markedhelpful}}
+                        {{^markedhelpful}}
                             {{# pix}} status/b_blank, moodleoverflow, {{#str}}marknothelpful, moodleoverflow{{/str}} {{/ pix}}
-                        {{/statusstarter}}
+                        {{/markedhelpful}}
                     {{/ questionunderreview }}
                 </td>
 
diff --git a/templates/discussions.mustache b/templates/discussions.mustache
index e8c30202ea..48340589d3 100644
--- a/templates/discussions.mustache
+++ b/templates/discussions.mustache
@@ -46,16 +46,16 @@
                         {{#pix}}i/duration, moodle, {{#str}}pending_review, mod_moodleoverflow{{/str}}{{/pix}}
                     {{/ questionunderreview }}
                     {{^ questionunderreview }}
-                        {{#statusteacher}}
+                        {{#markedsolution}}
                             <a href="{{teacherlink}}">{{! avoid whitespace
                                 !}}{{# pix}} i/status-solved, moodleoverflow, {{#str}}containsteacherrating, moodleoverflow{{/str}} {{/ pix}}{{!
                             !}}</a>
-                        {{/statusteacher}}
-                        {{#statusstarter}}
+                        {{/markedsolution}}
+                        {{#markedhelpful}}
                             <a href="{{starterlink}}">{{! avoid whitespace
                                 !}}{{# pix}} i/status-helpful, moodleoverflow, {{#str}}containsstarterrating, moodleoverflow{{/str}} {{/ pix}}{{!
                             !}}</a>
-                        {{/ statusstarter }}
+                        {{/ markedhelpful }}
                     {{/ questionunderreview }}
                 </div>
                 <div class="d-flex flex-colum flex-wrap justify-content-around">

From f2781289ae65b82f2551b3e1157380813d5ad65f Mon Sep 17 00:00:00 2001
From: NinaHerrmann <nina.herrmann@uni-muenster.de>
Date: Mon, 15 May 2023 15:03:44 +0200
Subject: [PATCH 25/35] grunt build

---
 amd/build/rating.min.js.map | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/amd/build/rating.min.js.map b/amd/build/rating.min.js.map
index a1fb0b6643..e0ec5b562e 100644
--- a/amd/build/rating.min.js.map
+++ b/amd/build/rating.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {boolean} allowmultiplemarks   // true means allowed, false means not allowed.\n *\n */\nexport function init(userid, allowmultiplemarks) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'markedhelpful' : 'markedsolution';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else: only delete the mark if the post is being unmarked.\n\n                   then add a mark, if the post is being marked.\n                */\n                if (!allowmultiplemarks) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                        changeStrings(htmlclass, action);\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                    if (allowmultiplemarks) {\n                        changeStrings(htmlclass, action);\n                    }\n                }\n\n            }\n        }\n    };\n\n}\n\n/**\n * Function to change the String of the post data-action button.\n * Only usable if mulitplemarks are allowed.\n * @param {string} htmlclass the class where the String is being updated\n * @param {string} action    helpful or solved mark\n */\nasync function changeStrings(htmlclass, action) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'alsomarksolved', 'markhelpful', 'alsomarkhelpful',]);\n\n    // 1. Step: Are there other posts in the Discussion, that are solved/helpful?\n    var othermarkedposts = false;\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (el.classList.contains(htmlclass)) {\n            othermarkedposts = true;\n            break;\n        }\n    }\n    // 2. Step: Change the strings of the action Button of the unmarked posts.\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (!el.classList.contains(htmlclass) && el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`)) {\n            if (othermarkedposts) {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n            } else {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`mark${action}`, 'mod_moodleoverflow');\n            }\n        }\n    }\n}"],"names":["userid","allowmultiplemarks","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","remove","textContent","changeStrings","el","querySelectorAll","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating","othermarkedposts"],"mappings":";;;;;;;oFAsEqBA,OAAQC,sCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,gBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAO1BC,mBASG0B,eACAf,YAAYgB,UAAUM,OAAOR,WAC7BpB,cAAc6B,kBAAoB,iCAAiBzB,QAAU,sBAC7D0B,cAAcV,UAAWhB,kBAVxB,MAAM2B,MAAMlC,KAAKmC,iBAAiB,uBAAyBZ,WAC5DW,GAAGT,UAAUM,OAAOR,WACpBW,GAAGb,qDAA8Cd,cAAYyB,kBACnD,iCAAiBzB,QAAU,sBAWxCiB,eACDf,YAAYgB,UAAUW,IAAIb,WAC1BpB,cAAc6B,kBAAoB,oCAAoBzB,QAAU,sBAC5DT,oBACAmC,cAAcV,UAAWhB,mGAjH3CY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOqC,SAASC,eAAe,sCAStB1B,SAASF,OAAQoB,OAAQjC,cAC9B0C,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFjC,OAAQA,OACRkC,SAAUd,WAEd,UACJ9B,KAAKmC,gEAAyDtC,cAAYgD,SAASC,IAC/EA,EAAEd,YAAcO,SAASQ,eAAzB,IAEJ/C,KAAKmC,gEAAyDI,SAASS,eAAaH,SAASC,IACzFA,EAAEd,YAAcO,SAASU,eAAzB,IAEJjD,KAAKmC,gEAAyDzB,cAAYmC,SAASC,IAC/EA,EAAEd,YAAcO,SAASW,UAAzB,IAEGX,wBAgGIN,cAAcV,UAAWhB,0BAC3BR,gBAAgB,qBACrB,CAAC,aAAc,iBAAkB,cAAe,wBAGhDoD,kBAAmB,MAClB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,0BAC/BD,GAAGT,UAAUC,SAASH,WAAY,CAClC4B,kBAAmB,YAKtB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,wBAC9BD,GAAGT,UAAUC,SAASH,YAAcW,GAAGb,qDAA8Cd,gBAElF2B,GAAGb,qDAA8Cd,cAAYyB,YAD7DmB,uBAEU,qCAAqB5C,QAAU,4BAG/B,iCAAiBA,QAAU"}
\ No newline at end of file
+{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {boolean} allowmultiplemarks   // true means allowed, false means not allowed.\n *\n */\nexport function init(userid, allowmultiplemarks) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'markedhelpful' : 'markedsolution';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If       multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else:    only delete the mark if the post is being unmarked.\n\n                   Add a mark, if the post is being marked.\n                */\n                if (!allowmultiplemarks) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                        changeStrings(htmlclass, action);\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                    if (allowmultiplemarks) {\n                        changeStrings(htmlclass, action);\n                    }\n                }\n\n            }\n        }\n    };\n\n}\n\n/**\n * Function to change the String of the post data-action button.\n * Only used if mulitplemarks are allowed.\n * @param {string} htmlclass the class where the String is being updated\n * @param {string} action    helpful or solved mark\n */\nasync function changeStrings(htmlclass, action) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'alsomarksolved', 'markhelpful', 'alsomarkhelpful',]);\n\n    // 1. Step: Are there other posts in the Discussion, that are solved/helpful?\n    var othermarkedposts = false;\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (el.classList.contains(htmlclass)) {\n            othermarkedposts = true;\n            break;\n        }\n    }\n    // 2. Step: Change the strings of the action Button of the unmarked posts.\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (!el.classList.contains(htmlclass) && el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`)) {\n            if (othermarkedposts) {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n            } else {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`mark${action}`, 'mod_moodleoverflow');\n            }\n        }\n    }\n}"],"names":["userid","allowmultiplemarks","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","remove","textContent","changeStrings","el","querySelectorAll","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating","othermarkedposts"],"mappings":";;;;;;;oFAsEqBA,OAAQC,sCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,iBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAO1BC,mBASG0B,eACAf,YAAYgB,UAAUM,OAAOR,WAC7BpB,cAAc6B,kBAAoB,iCAAiBzB,QAAU,sBAC7D0B,cAAcV,UAAWhB,kBAVxB,MAAM2B,MAAMlC,KAAKmC,iBAAiB,uBAAyBZ,WAC5DW,GAAGT,UAAUM,OAAOR,WACpBW,GAAGb,qDAA8Cd,cAAYyB,kBACnD,iCAAiBzB,QAAU,sBAWxCiB,eACDf,YAAYgB,UAAUW,IAAIb,WAC1BpB,cAAc6B,kBAAoB,oCAAoBzB,QAAU,sBAC5DT,oBACAmC,cAAcV,UAAWhB,mGAjH3CY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOqC,SAASC,eAAe,sCAStB1B,SAASF,OAAQoB,OAAQjC,cAC9B0C,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFjC,OAAQA,OACRkC,SAAUd,WAEd,UACJ9B,KAAKmC,gEAAyDtC,cAAYgD,SAASC,IAC/EA,EAAEd,YAAcO,SAASQ,eAAzB,IAEJ/C,KAAKmC,gEAAyDI,SAASS,eAAaH,SAASC,IACzFA,EAAEd,YAAcO,SAASU,eAAzB,IAEJjD,KAAKmC,gEAAyDzB,cAAYmC,SAASC,IAC/EA,EAAEd,YAAcO,SAASW,UAAzB,IAEGX,wBAgGIN,cAAcV,UAAWhB,0BAC3BR,gBAAgB,qBACrB,CAAC,aAAc,iBAAkB,cAAe,wBAGhDoD,kBAAmB,MAClB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,0BAC/BD,GAAGT,UAAUC,SAASH,WAAY,CAClC4B,kBAAmB,YAKtB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,wBAC9BD,GAAGT,UAAUC,SAASH,YAAcW,GAAGb,qDAA8Cd,gBAElF2B,GAAGb,qDAA8Cd,cAAYyB,YAD7DmB,uBAEU,qCAAqB5C,QAAU,4BAG/B,iCAAiBA,QAAU"}
\ No newline at end of file

From 5294e8004aa5e133f0f634d71c8f9215ebe01a29 Mon Sep 17 00:00:00 2001
From: NinaHerrmann <nina.herrmann@uni-muenster.de>
Date: Tue, 16 May 2023 14:13:04 +0200
Subject: [PATCH 26/35] WIP - var_dumps to check missing post in ratings.php
 sorting

---
 classes/ratings.php | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/classes/ratings.php b/classes/ratings.php
index ac05e83a77..e9c35ac91f 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -243,7 +243,8 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
 
         // Check if solved posts are preferred over helpful posts.
         $solutionspreferred = $posts[array_key_first($posts)]->ratingpreference == 1;
-
+        var_dump('start');
+        var_dump($posts);
         // Sort the answer posts by ratings.
 
         // Build groups of different types of answers (Solved and helpful, only solved/helpful, other).
@@ -342,10 +343,15 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
         }
 
         // Rearrange the indices and return the sorted posts.
+        var_dump('sortedpost');
+        var_dump($sortedposts);
+
         $neworder = array();
         foreach ($sortedposts as $post) {
             $neworder[$post->id] = $post;
         }
+        var_dump('neworder');
+        var_dump($neworder);
         // return now the sorted posts.
         return $neworder;
     }

From 4145d3c45344e34b2450a6879144bd1782054015 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Tue, 16 May 2023 15:22:59 +0200
Subject: [PATCH 27/35] sorting error fixed in ratings.php

---
 classes/ratings.php | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/classes/ratings.php b/classes/ratings.php
index e9c35ac91f..fbf7be5052 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -242,11 +242,12 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
         $sortedposts[0] = $parentpost;
 
         // Check if solved posts are preferred over helpful posts.
-        $solutionspreferred = $posts[array_key_first($posts)]->ratingpreference == 1;
-        var_dump('start');
-        var_dump($posts);
-        // Sort the answer posts by ratings.
+        $solutionspreferred = false;
+        if ($posts[array_key_first($posts)]->ratingpreference == 1) {
+            $solutionspreferred = true;
+        }
 
+        // Sort the answer posts by ratings.
         // Build groups of different types of answers (Solved and helpful, only solved/helpful, other).
         // statusteacher == 1 means the post is marked as solved.
         // statusstarter == 1 means the post is marked as helpful.
@@ -343,15 +344,12 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
         }
 
         // Rearrange the indices and return the sorted posts.
-        var_dump('sortedpost');
-        var_dump($sortedposts);
 
         $neworder = array();
         foreach ($sortedposts as $post) {
             $neworder[$post->id] = $post;
         }
-        var_dump('neworder');
-        var_dump($neworder);
+ 
         // return now the sorted posts.
         return $neworder;
     }
@@ -831,7 +829,7 @@ private static function moodleoverflow_quicksort_post_by_votes(array &$posts, $l
             }
             if ($left <= $right) {
                 $temp = $posts[$right];
-                $post[$right] = $posts[$left];
+                $posts[$right] = $posts[$left];
                 $posts[$left] = $temp;
                 $right--;
                 $left++;

From c76720f3caa50e1d67a9408445d087f6209f1792 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Tue, 16 May 2023 17:40:45 +0200
Subject: [PATCH 28/35] WIP: write test cases for rating.php

---
 amd/build/activityhelp.min.js       |   2 +-
 amd/build/activityhelp.min.js.map   |   2 +-
 amd/build/rating.min.js             |   9 +-
 amd/build/rating.min.js.map         |   2 +-
 amd/build/reviewing.min.js          |   9 +-
 amd/build/reviewing.min.js.map      |   2 +-
 amd/build/warnmodechange.min.js     |   9 +-
 amd/build/warnmodechange.min.js.map |   2 +-
 classes/ratings.php                 |  10 +-
 tests/ratings_test.php              | 154 ++++++++++++++++++++++------
 10 files changed, 133 insertions(+), 68 deletions(-)

diff --git a/amd/build/activityhelp.min.js b/amd/build/activityhelp.min.js
index a875b70b82..a3dc794e34 100644
--- a/amd/build/activityhelp.min.js
+++ b/amd/build/activityhelp.min.js
@@ -6,6 +6,6 @@ define("mod_moodleoverflow/activityhelp",["exports"],(function(_exports){Object.
    * @copyright  2023 Tamaro Walter
    * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
    */
-const Selectors_actions={showHelpIcon:'[data-action="showhelpicon"]'};_exports.init=()=>{document.addEventListener("click",(event=>{event.target.closest(Selectors_actions.showHelpIcon)&&event.preventDefault()}))}}));
+var Selectors_actions={showHelpIcon:'[data-action="showhelpicon"]'};_exports.init=function(){document.addEventListener("click",(function(event){event.target.closest(Selectors_actions.showHelpIcon)&&event.preventDefault()}))}}));
 
 //# sourceMappingURL=activityhelp.min.js.map
\ No newline at end of file
diff --git a/amd/build/activityhelp.min.js.map b/amd/build/activityhelp.min.js.map
index 64d79cd463..7bae1c7cab 100644
--- a/amd/build/activityhelp.min.js.map
+++ b/amd/build/activityhelp.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"activityhelp.min.js","sources":["../src/activityhelp.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Show a help string for the amount of activity column in userstats_table.php\n *\n * @module     mod_moodleoverflow/activityhelp\n * @copyright  2023 Tamaro Walter\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\n\nconst Selectors = {\n    actions: {\n        showHelpIcon: '[data-action=\"showhelpicon\"]',\n    },\n};\n\n/**\n * Function that shows the help string.\n */\nexport const init = () => {\n    document.addEventListener('click', event => {\n        if (event.target.closest(Selectors.actions.showHelpIcon)) {\n            event.preventDefault();\n        }\n    });\n};"],"names":["Selectors","showHelpIcon","document","addEventListener","event","target","closest","preventDefault"],"mappings":";;;;;;;;MAyBMA,kBACO,CACLC,aAAc,8CAOF,KAChBC,SAASC,iBAAiB,SAASC,QAC3BA,MAAMC,OAAOC,QAAQN,kBAAkBC,eACvCG,MAAMG"}
\ No newline at end of file
+{"version":3,"file":"activityhelp.min.js","sources":["../src/activityhelp.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Show a help string for the amount of activity column in userstats_table.php\n *\n * @module     mod_moodleoverflow/activityhelp\n * @copyright  2023 Tamaro Walter\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\n\nconst Selectors = {\n    actions: {\n        showHelpIcon: '[data-action=\"showhelpicon\"]',\n    },\n};\n\n/**\n * Function that shows the help string.\n */\nexport const init = () => {\n    document.addEventListener('click', event => {\n        if (event.target.closest(Selectors.actions.showHelpIcon)) {\n            event.preventDefault();\n        }\n    });\n};"],"names":["Selectors","showHelpIcon","document","addEventListener","event","target","closest","preventDefault"],"mappings":";;;;;;;;IAyBMA,kBACO,CACLC,aAAc,8CAOF,WAChBC,SAASC,iBAAiB,SAAS,SAAAC,OAC3BA,MAAMC,OAAOC,QAAQN,kBAAkBC,eACvCG,MAAMG"}
\ No newline at end of file
diff --git a/amd/build/rating.min.js b/amd/build/rating.min.js
index 276685f62e..7a148eb2b0 100644
--- a/amd/build/rating.min.js
+++ b/amd/build/rating.min.js
@@ -1,10 +1,3 @@
-define("mod_moodleoverflow/rating",["exports","core/ajax","core/prefetch","core/str"],(function(_exports,_ajax,_prefetch,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
-/**
-   * Implements rating functionality
-   *
-   * @module     mod_moodleoverflow/rating
-   * @copyright  2022 Justus Dieckmann WWU
-   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemarks){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=async event=>{const actionElement=event.target.closest("[data-moodleoverflow-action]");if(!actionElement)return;const action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid");switch(action){case"upvote":case"downvote":{const isupvote="upvote"===action;if("clicked"===actionElement.getAttribute("data-moodleoverflow-state"))await sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_"+action,"mod_moodleoverflow");else{const otherAction=isupvote?"downvote":"upvote";await sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid),actionElement.setAttribute("data-moodleoverflow-state","clicked");const otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'));otherElement.setAttribute("data-moodleoverflow-state","notclicked"),actionElement.title=await(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow"),otherElement.title=await(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow")}}break;case"helpful":case"solved":{const isHelpful="helpful"===action,htmlclass=isHelpful?"markedhelpful":"markedsolution",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating;if(await sendVote(postid,rating,userid),allowmultiplemarks)shouldRemove&&(postElement.classList.remove(htmlclass),actionElement.textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"),changeStrings(htmlclass,action));else for(const el of root.querySelectorAll(".moodleoverflowpost."+htmlclass))el.classList.remove(htmlclass),el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");shouldRemove||(postElement.classList.add(htmlclass),actionElement.textContent=await(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow"),allowmultiplemarks&&changeStrings(htmlclass,action))}}}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);const RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");async function sendVote(postid,rating,userid){const response=await _ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];return root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((i=>{i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((i=>{i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((i=>{i.textContent=response.postrating})),response}async function changeStrings(htmlclass,action){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","alsomarksolved","markhelpful","alsomarkhelpful"]);var othermarkedposts=!1;for(const el of root.querySelectorAll(".moodleoverflowpost"))if(el.classList.contains(htmlclass)){othermarkedposts=!0;break}for(const el of root.querySelectorAll(".moodleoverflowpost"))!el.classList.contains(htmlclass)&&el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]'))&&(el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=othermarkedposts?await(0,_str.get_string)("alsomark".concat(action),"mod_moodleoverflow"):await(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow"))}}));
+define("mod_moodleoverflow/rating",["exports","core/ajax","core/prefetch","core/str"],(function(_exports,_ajax,_prefetch,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _createForOfIteratorHelper(o,allowArrayLike){var it="undefined"!=typeof Symbol&&o[Symbol.iterator]||o["@@iterator"];if(!it){if(Array.isArray(o)||(it=function(o,minLen){if(!o)return;if("string"==typeof o)return _arrayLikeToArray(o,minLen);var n=Object.prototype.toString.call(o).slice(8,-1);"Object"===n&&o.constructor&&(n=o.constructor.name);if("Map"===n||"Set"===n)return Array.from(o);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return _arrayLikeToArray(o,minLen)}(o))||allowArrayLike&&o&&"number"==typeof o.length){it&&(o=it);var i=0,F=function(){};return{s:F,n:function(){return i>=o.length?{done:!0}:{done:!1,value:o[i++]}},e:function(_e){throw _e},f:F}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var err,normalCompletion=!0,didErr=!1;return{s:function(){it=it.call(o)},n:function(){var step=it.next();return normalCompletion=step.done,step},e:function(_e2){didErr=!0,err=_e2},f:function(){try{normalCompletion||null==it.return||it.return()}finally{if(didErr)throw err}}}}function _arrayLikeToArray(arr,len){(null==len||len>arr.length)&&(len=arr.length);for(var i=0,arr2=new Array(len);i<len;i++)arr2[i]=arr[i];return arr2}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(userid,allowmultiplemarks){_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","marknotsolved","markhelpful","marknothelpful","action_remove_upvote","action_upvote","action_remove_downvote","action_downvote"]),root.onclick=(_ref=_asyncToGenerator(regeneratorRuntime.mark((function _callee(event){var actionElement,action,postElement,postid,isupvote,otherAction,otherElement,isHelpful,htmlclass,shouldRemove,baseRating,rating,_iterator,_step,el;return regeneratorRuntime.wrap((function(_context){for(;;)switch(_context.prev=_context.next){case 0:if(actionElement=event.target.closest("[data-moodleoverflow-action]")){_context.next=3;break}return _context.abrupt("return");case 3:action=actionElement.getAttribute("data-moodleoverflow-action"),postElement=actionElement.closest("[data-moodleoverflow-postid]"),postid=null==postElement?void 0:postElement.getAttribute("data-moodleoverflow-postid"),_context.t0=action,_context.next="upvote"===_context.t0||"downvote"===_context.t0?9:"helpful"===_context.t0||"solved"===_context.t0?32:73;break;case 9:if(isupvote="upvote"===action,"clicked"!==actionElement.getAttribute("data-moodleoverflow-state")){_context.next=19;break}return _context.next=13,sendVote(postid,isupvote?RATING_REMOVE_UPVOTE:RATING_REMOVE_DOWNVOTE,userid);case 13:return actionElement.setAttribute("data-moodleoverflow-state","notclicked"),_context.next=16,(0,_str.get_string)("action_"+action,"mod_moodleoverflow");case 16:actionElement.title=_context.sent,_context.next=31;break;case 19:return otherAction=isupvote?"downvote":"upvote",_context.next=22,sendVote(postid,isupvote?RATING_UPVOTE:RATING_DOWNVOTE,userid);case 22:return actionElement.setAttribute("data-moodleoverflow-state","clicked"),(otherElement=postElement.querySelector('[data-moodleoverflow-action="'.concat(otherAction,'"]'))).setAttribute("data-moodleoverflow-state","notclicked"),_context.next=27,(0,_str.get_string)("action_remove_"+action,"mod_moodleoverflow");case 27:return actionElement.title=_context.sent,_context.next=30,(0,_str.get_string)("action_"+otherAction,"mod_moodleoverflow");case 30:otherElement.title=_context.sent;case 31:return _context.abrupt("break",73);case 32:return htmlclass=(isHelpful="helpful"===action)?"markedhelpful":"markedsolution",shouldRemove=postElement.classList.contains(htmlclass),baseRating=isHelpful?RATING_HELPFUL:RATING_SOLVED,rating=shouldRemove?10*baseRating:baseRating,_context.next=39,sendVote(postid,rating,userid);case 39:if(allowmultiplemarks){_context.next=61;break}_iterator=_createForOfIteratorHelper(root.querySelectorAll(".moodleoverflowpost."+htmlclass)),_context.prev=41,_iterator.s();case 43:if((_step=_iterator.n()).done){_context.next=51;break}return(el=_step.value).classList.remove(htmlclass),_context.next=48,(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");case 48:el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=_context.sent;case 49:_context.next=43;break;case 51:_context.next=56;break;case 53:_context.prev=53,_context.t1=_context.catch(41),_iterator.e(_context.t1);case 56:return _context.prev=56,_iterator.f(),_context.finish(56);case 59:_context.next=67;break;case 61:if(!shouldRemove){_context.next=67;break}return postElement.classList.remove(htmlclass),_context.next=65,(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");case 65:actionElement.textContent=_context.sent,changeStrings(htmlclass,action);case 67:if(shouldRemove){_context.next=73;break}return postElement.classList.add(htmlclass),_context.next=71,(0,_str.get_string)("marknot".concat(action),"mod_moodleoverflow");case 71:actionElement.textContent=_context.sent,allowmultiplemarks&&changeStrings(htmlclass,action);case 73:case"end":return _context.stop()}}),_callee,null,[[41,53,56,59]])}))),function(_x4){return _ref.apply(this,arguments)});var _ref},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch);var RATING_DOWNVOTE=1,RATING_UPVOTE=2,RATING_REMOVE_DOWNVOTE=10,RATING_REMOVE_UPVOTE=20,RATING_SOLVED=3,RATING_HELPFUL=4,root=document.getElementById("moodleoverflow-root");function sendVote(_x,_x2,_x3){return _sendVote.apply(this,arguments)}function _sendVote(){return(_sendVote=_asyncToGenerator(regeneratorRuntime.mark((function _callee2(postid,rating,userid){var response;return regeneratorRuntime.wrap((function(_context2){for(;;)switch(_context2.prev=_context2.next){case 0:return _context2.next=2,_ajax.default.call([{methodname:"mod_moodleoverflow_record_vote",args:{postid:postid,ratingid:rating}}])[0];case 2:return response=_context2.sent,root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(userid,'"]')).forEach((function(i){i.textContent=response.raterreputation})),root.querySelectorAll('[data-moodleoverflow-userreputation="'.concat(response.ownerid,'"]')).forEach((function(i){i.textContent=response.ownerreputation})),root.querySelectorAll('[data-moodleoverflow-postreputation="'.concat(postid,'"]')).forEach((function(i){i.textContent=response.postrating})),_context2.abrupt("return",response);case 7:case"end":return _context2.stop()}}),_callee2)})))).apply(this,arguments)}function changeStrings(_x5,_x6){return _changeStrings.apply(this,arguments)}function _changeStrings(){return(_changeStrings=_asyncToGenerator(regeneratorRuntime.mark((function _callee3(htmlclass,action){var othermarkedposts,_iterator2,_step2,_iterator3,_step3,_el;return regeneratorRuntime.wrap((function(_context3){for(;;)switch(_context3.prev=_context3.next){case 0:_prefetch.default.prefetchStrings("mod_moodleoverflow",["marksolved","alsomarksolved","markhelpful","alsomarkhelpful"]),othermarkedposts=!1,_iterator2=_createForOfIteratorHelper(root.querySelectorAll(".moodleoverflowpost")),_context3.prev=3,_iterator2.s();case 5:if((_step2=_iterator2.n()).done){_context3.next=12;break}if(!_step2.value.classList.contains(htmlclass)){_context3.next=10;break}return othermarkedposts=!0,_context3.abrupt("break",12);case 10:_context3.next=5;break;case 12:_context3.next=17;break;case 14:_context3.prev=14,_context3.t0=_context3.catch(3),_iterator2.e(_context3.t0);case 17:return _context3.prev=17,_iterator2.f(),_context3.finish(17);case 20:_iterator3=_createForOfIteratorHelper(root.querySelectorAll(".moodleoverflowpost")),_context3.prev=21,_iterator3.s();case 23:if((_step3=_iterator3.n()).done){_context3.next=37;break}if((_el=_step3.value).classList.contains(htmlclass)||!_el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]'))){_context3.next=35;break}if(!othermarkedposts){_context3.next=32;break}return _context3.next=29,(0,_str.get_string)("alsomark".concat(action),"mod_moodleoverflow");case 29:_el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=_context3.sent,_context3.next=35;break;case 32:return _context3.next=34,(0,_str.get_string)("mark".concat(action),"mod_moodleoverflow");case 34:_el.querySelector('[data-moodleoverflow-action="'.concat(action,'"]')).textContent=_context3.sent;case 35:_context3.next=23;break;case 37:_context3.next=42;break;case 39:_context3.prev=39,_context3.t1=_context3.catch(21),_iterator3.e(_context3.t1);case 42:return _context3.prev=42,_iterator3.f(),_context3.finish(42);case 45:case"end":return _context3.stop()}}),_callee3,null,[[3,14,17,20],[21,39,42,45]])})))).apply(this,arguments)}}));
 
 //# sourceMappingURL=rating.min.js.map
\ No newline at end of file
diff --git a/amd/build/rating.min.js.map b/amd/build/rating.min.js.map
index e0ec5b562e..6ca7b5f0bc 100644
--- a/amd/build/rating.min.js.map
+++ b/amd/build/rating.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {boolean} allowmultiplemarks   // true means allowed, false means not allowed.\n *\n */\nexport function init(userid, allowmultiplemarks) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'markedhelpful' : 'markedsolution';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If       multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else:    only delete the mark if the post is being unmarked.\n\n                   Add a mark, if the post is being marked.\n                */\n                if (!allowmultiplemarks) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                        changeStrings(htmlclass, action);\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                    if (allowmultiplemarks) {\n                        changeStrings(htmlclass, action);\n                    }\n                }\n\n            }\n        }\n    };\n\n}\n\n/**\n * Function to change the String of the post data-action button.\n * Only used if mulitplemarks are allowed.\n * @param {string} htmlclass the class where the String is being updated\n * @param {string} action    helpful or solved mark\n */\nasync function changeStrings(htmlclass, action) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'alsomarksolved', 'markhelpful', 'alsomarkhelpful',]);\n\n    // 1. Step: Are there other posts in the Discussion, that are solved/helpful?\n    var othermarkedposts = false;\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (el.classList.contains(htmlclass)) {\n            othermarkedposts = true;\n            break;\n        }\n    }\n    // 2. Step: Change the strings of the action Button of the unmarked posts.\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (!el.classList.contains(htmlclass) && el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`)) {\n            if (othermarkedposts) {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n            } else {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`mark${action}`, 'mod_moodleoverflow');\n            }\n        }\n    }\n}"],"names":["userid","allowmultiplemarks","prefetchStrings","root","onclick","async","actionElement","event","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","isHelpful","htmlclass","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","remove","textContent","changeStrings","el","querySelectorAll","add","document","getElementById","response","Ajax","call","methodname","args","ratingid","forEach","i","raterreputation","ownerid","ownerreputation","postrating","othermarkedposts"],"mappings":";;;;;;;oFAsEqBA,OAAQC,sCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,QAAUC,oBACLC,cAAgBC,MAAMC,OAAOC,QAAQ,oCACtCH,2BAICI,OAASJ,cAAcK,aAAa,8BACpCC,YAAcN,cAAcG,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,qCAEjCD,YACC,aACA,kBACKI,SAAsB,WAAXJ,UAC+C,YAA5DJ,cAAcK,aAAa,mCACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBjB,QACjFM,cAAcY,aAAa,4BAA6B,cACxDZ,cAAca,YAAc,mBAAU,UAAYT,OAAQ,0BACvD,OACGU,YAAcN,SAAW,WAAa,eACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBtB,QACnEM,cAAcY,aAAa,4BAA6B,iBAClDK,aAAeX,YAAYY,qDACGJ,mBACpCG,aAAaL,aAAa,4BAA6B,cACvDZ,cAAca,YAAc,mBAAU,iBAAmBT,OAAQ,sBACjEa,aAAaJ,YAAc,mBAAU,UAAYC,YAAa,iCAIjE,cACA,gBACKK,UAAuB,YAAXf,OACZgB,UAAYD,UAAY,gBAAkB,iBAC1CE,aAAef,YAAYgB,UAAUC,SAASH,WAC9CI,WAAaL,UAAYM,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,oBAC1Cf,SAASF,OAAQoB,OAAQjC,QAO1BC,mBASG0B,eACAf,YAAYgB,UAAUM,OAAOR,WAC7BpB,cAAc6B,kBAAoB,iCAAiBzB,QAAU,sBAC7D0B,cAAcV,UAAWhB,kBAVxB,MAAM2B,MAAMlC,KAAKmC,iBAAiB,uBAAyBZ,WAC5DW,GAAGT,UAAUM,OAAOR,WACpBW,GAAGb,qDAA8Cd,cAAYyB,kBACnD,iCAAiBzB,QAAU,sBAWxCiB,eACDf,YAAYgB,UAAUW,IAAIb,WAC1BpB,cAAc6B,kBAAoB,oCAAoBzB,QAAU,sBAC5DT,oBACAmC,cAAcV,UAAWhB,mGAjH3CY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB5B,KAAOqC,SAASC,eAAe,sCAStB1B,SAASF,OAAQoB,OAAQjC,cAC9B0C,eAAiBC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFjC,OAAQA,OACRkC,SAAUd,WAEd,UACJ9B,KAAKmC,gEAAyDtC,cAAYgD,SAASC,IAC/EA,EAAEd,YAAcO,SAASQ,eAAzB,IAEJ/C,KAAKmC,gEAAyDI,SAASS,eAAaH,SAASC,IACzFA,EAAEd,YAAcO,SAASU,eAAzB,IAEJjD,KAAKmC,gEAAyDzB,cAAYmC,SAASC,IAC/EA,EAAEd,YAAcO,SAASW,UAAzB,IAEGX,wBAgGIN,cAAcV,UAAWhB,0BAC3BR,gBAAgB,qBACrB,CAAC,aAAc,iBAAkB,cAAe,wBAGhDoD,kBAAmB,MAClB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,0BAC/BD,GAAGT,UAAUC,SAASH,WAAY,CAClC4B,kBAAmB,YAKtB,MAAMjB,MAAMlC,KAAKmC,iBAAiB,wBAC9BD,GAAGT,UAAUC,SAASH,YAAcW,GAAGb,qDAA8Cd,gBAElF2B,GAAGb,qDAA8Cd,cAAYyB,YAD7DmB,uBAEU,qCAAqB5C,QAAU,4BAG/B,iCAAiBA,QAAU"}
\ No newline at end of file
+{"version":3,"file":"rating.min.js","sources":["../src/rating.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements rating functionality\n *\n * @module     mod_moodleoverflow/rating\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst RATING_DOWNVOTE = 1;\nconst RATING_UPVOTE = 2;\nconst RATING_REMOVE_DOWNVOTE = 10;\nconst RATING_REMOVE_UPVOTE = 20;\nconst RATING_SOLVED = 3;\nconst RATING_HELPFUL = 4;\n\nconst root = document.getElementById('moodleoverflow-root');\n\n/**\n * Send a vote via AJAX, then updates post and user ratings.\n * @param {int} postid\n * @param {int} rating\n * @param {int} userid\n * @returns {Promise<*>}\n */\nasync function sendVote(postid, rating, userid) {\n    const response = await Ajax.call([{\n        methodname: 'mod_moodleoverflow_record_vote',\n        args: {\n            postid: postid,\n            ratingid: rating\n        }\n    }])[0];\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${userid}\"]`).forEach((i) => {\n        i.textContent = response.raterreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-userreputation=\"${response.ownerid}\"]`).forEach((i) => {\n        i.textContent = response.ownerreputation;\n    });\n    root.querySelectorAll(`[data-moodleoverflow-postreputation=\"${postid}\"]`).forEach((i) => {\n        i.textContent = response.postrating;\n    });\n    return response;\n}\n\n\n/**\n * Init function.\n *\n * @param {int} userid\n * @param {boolean} allowmultiplemarks   // true means allowed, false means not allowed.\n *\n */\nexport function init(userid, allowmultiplemarks) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'marknotsolved', 'markhelpful', 'marknothelpful',\n            'action_remove_upvote', 'action_upvote', 'action_remove_downvote', 'action_downvote']);\n\n    root.onclick = async(event) => {\n        const actionElement = event.target.closest('[data-moodleoverflow-action]');\n        if (!actionElement) {\n            return;\n        }\n\n        const action = actionElement.getAttribute('data-moodleoverflow-action');\n        const postElement = actionElement.closest('[data-moodleoverflow-postid]');\n        const postid = postElement?.getAttribute('data-moodleoverflow-postid');\n\n        switch (action) {\n            case 'upvote':\n            case 'downvote': {\n                const isupvote = action === 'upvote';\n                if (actionElement.getAttribute('data-moodleoverflow-state') === 'clicked') {\n                    await sendVote(postid, isupvote ? RATING_REMOVE_UPVOTE : RATING_REMOVE_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_' + action, 'mod_moodleoverflow');\n                } else {\n                    const otherAction = isupvote ? 'downvote' : 'upvote';\n                    await sendVote(postid, isupvote ? RATING_UPVOTE : RATING_DOWNVOTE, userid);\n                    actionElement.setAttribute('data-moodleoverflow-state', 'clicked');\n                    const otherElement = postElement.querySelector(\n                        `[data-moodleoverflow-action=\"${otherAction}\"]`);\n                    otherElement.setAttribute('data-moodleoverflow-state', 'notclicked');\n                    actionElement.title = await getString('action_remove_' + action, 'mod_moodleoverflow');\n                    otherElement.title = await getString('action_' + otherAction, 'mod_moodleoverflow');\n                }\n            }\n            break;\n            case 'helpful':\n            case 'solved': {\n                const isHelpful = action === 'helpful';\n                const htmlclass = isHelpful ? 'markedhelpful' : 'markedsolution';\n                const shouldRemove = postElement.classList.contains(htmlclass);\n                const baseRating = isHelpful ? RATING_HELPFUL : RATING_SOLVED;\n                const rating = shouldRemove ? baseRating * 10 : baseRating;\n                await sendVote(postid, rating, userid);\n\n                /* If       multiplemarks are not allowed (that is the default mode): delete all marks.\n                   else:    only delete the mark if the post is being unmarked.\n\n                   Add a mark, if the post is being marked.\n                */\n                if (!allowmultiplemarks) {\n                    // Delete all marks in the discussion\n                    for (const el of root.querySelectorAll('.moodleoverflowpost.' + htmlclass)) {\n                        el.classList.remove(htmlclass);\n                        el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                            await getString(`mark${action}`, 'mod_moodleoverflow');\n                    }\n                } else {\n                    // Remove only the mark of the unmarked post.\n                    if (shouldRemove) {\n                        postElement.classList.remove(htmlclass);\n                        actionElement.textContent = await getString(`mark${action}`, 'mod_moodleoverflow');\n                        changeStrings(htmlclass, action);\n                    }\n                }\n                // If the post is being marked, mark it.\n                if (!shouldRemove) {\n                    postElement.classList.add(htmlclass);\n                    actionElement.textContent = await getString(`marknot${action}`, 'mod_moodleoverflow');\n                    if (allowmultiplemarks) {\n                        changeStrings(htmlclass, action);\n                    }\n                }\n\n            }\n        }\n    };\n\n}\n\n/**\n * Function to change the String of the post data-action button.\n * Only used if mulitplemarks are allowed.\n * @param {string} htmlclass the class where the String is being updated\n * @param {string} action    helpful or solved mark\n */\nasync function changeStrings(htmlclass, action) {\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['marksolved', 'alsomarksolved', 'markhelpful', 'alsomarkhelpful',]);\n\n    // 1. Step: Are there other posts in the Discussion, that are solved/helpful?\n    var othermarkedposts = false;\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (el.classList.contains(htmlclass)) {\n            othermarkedposts = true;\n            break;\n        }\n    }\n    // 2. Step: Change the strings of the action Button of the unmarked posts.\n    for (const el of root.querySelectorAll('.moodleoverflowpost')) {\n        if (!el.classList.contains(htmlclass) && el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`)) {\n            if (othermarkedposts) {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`alsomark${action}`, 'mod_moodleoverflow');\n            } else {\n                el.querySelector(`[data-moodleoverflow-action=\"${action}\"]`).textContent =\n                    await getString(`mark${action}`, 'mod_moodleoverflow');\n            }\n        }\n    }\n}"],"names":["userid","allowmultiplemarks","prefetchStrings","root","onclick","event","actionElement","target","closest","action","getAttribute","postElement","postid","isupvote","sendVote","RATING_REMOVE_UPVOTE","RATING_REMOVE_DOWNVOTE","setAttribute","title","otherAction","RATING_UPVOTE","RATING_DOWNVOTE","otherElement","querySelector","htmlclass","isHelpful","shouldRemove","classList","contains","baseRating","RATING_HELPFUL","RATING_SOLVED","rating","querySelectorAll","el","remove","textContent","changeStrings","add","document","getElementById","Ajax","call","methodname","args","ratingid","response","forEach","i","raterreputation","ownerid","ownerreputation","postrating","othermarkedposts"],"mappings":"8kEAsEqBA,OAAQC,sCAChBC,gBAAgB,qBACrB,CAAC,aAAc,gBAAiB,cAAe,iBAC3C,uBAAwB,gBAAiB,yBAA0B,oBAE3EC,KAAKC,yDAAU,iBAAMC,mQACXC,cAAgBD,MAAME,OAAOC,QAAQ,+FAKrCC,OAASH,cAAcI,aAAa,8BACpCC,YAAcL,cAAcE,QAAQ,gCACpCI,OAASD,yBAAAA,YAAaD,aAAa,0CAEjCD,qBACC,wBACA,2BAkBA,yBACA,6CAlBKI,SAAsB,WAAXJ,OAC+C,YAA5DH,cAAcI,aAAa,6EACrBI,SAASF,OAAQC,SAAWE,qBAAuBC,uBAAwBhB,uBACjFM,cAAcW,aAAa,4BAA6B,gCAC5B,mBAAU,UAAYR,OAAQ,8BAA1DH,cAAcY,0DAERC,YAAcN,SAAW,WAAa,0BACtCC,SAASF,OAAQC,SAAWO,cAAgBC,gBAAiBrB,uBACnEM,cAAcW,aAAa,4BAA6B,YAClDK,aAAeX,YAAYY,qDACGJ,oBACvBF,aAAa,4BAA6B,gCAC3B,mBAAU,iBAAmBR,OAAQ,qCAAjEH,cAAcY,sCACa,mBAAU,UAAYC,YAAa,8BAA9DG,aAAaJ,8EAOXM,WADAC,UAAuB,YAAXhB,QACY,gBAAkB,iBAC1CiB,aAAef,YAAYgB,UAAUC,SAASJ,WAC9CK,WAAaJ,UAAYK,eAAiBC,cAC1CC,OAASN,aAA4B,GAAbG,WAAkBA,4BAC1Cf,SAASF,OAAQoB,OAAQhC,mBAO1BC,gFAEgBE,KAAK8B,iBAAiB,uBAAyBT,gHAArDU,gBACJP,UAAUQ,OAAOX,6BAEV,iCAAiBf,QAAU,8BADrCyB,GAAGX,qDAA8Cd,cAAY2B,sRAK7DV,4CACAf,YAAYgB,UAAUQ,OAAOX,6BACK,iCAAiBf,QAAU,8BAA7DH,cAAc8B,0BACdC,cAAcb,UAAWf,mBAI5BiB,4CACDf,YAAYgB,UAAUW,IAAId,6BACQ,oCAAoBf,QAAU,8BAAhEH,cAAc8B,0BACVnC,oBACAoC,cAAcb,UAAWf,sOAjH3CY,gBAAkB,EAClBD,cAAgB,EAChBJ,uBAAyB,GACzBD,qBAAuB,GACvBgB,cAAgB,EAChBD,eAAiB,EAEjB3B,KAAOoC,SAASC,eAAe,gCAStB1B,6IAAf,kBAAwBF,OAAQoB,OAAQhC,qJACbyC,cAAKC,KAAK,CAAC,CAC9BC,WAAY,iCACZC,KAAM,CACFhC,OAAQA,OACRiC,SAAUb,WAEd,iBANEc,wBAON3C,KAAK8B,gEAAyDjC,cAAY+C,SAAQ,SAACC,GAC/EA,EAAEZ,YAAcU,SAASG,mBAE7B9C,KAAK8B,gEAAyDa,SAASI,eAAaH,SAAQ,SAACC,GACzFA,EAAEZ,YAAcU,SAASK,mBAE7BhD,KAAK8B,gEAAyDrB,cAAYmC,SAAQ,SAACC,GAC/EA,EAAEZ,YAAcU,SAASM,wCAEtBN,oGAgGIT,8JAAf,kBAA6Bb,UAAWf,+LAC3BP,gBAAgB,qBACrB,CAAC,aAAc,iBAAkB,cAAe,oBAGhDmD,kBAAmB,wCACNlD,KAAK8B,iBAAiB,yIAC5BN,UAAUC,SAASJ,2CACtB6B,kBAAmB,sSAKVlD,KAAK8B,iBAAiB,8HAA5BC,kBACCP,UAAUC,SAASJ,aAAcU,IAAGX,qDAA8Cd,2CAClF4C,oEAEU,qCAAqB5C,QAAU,8BADzCyB,IAAGX,qDAA8Cd,cAAY2B,qFAInD,iCAAiB3B,QAAU,8BADrCyB,IAAGX,qDAA8Cd,cAAY2B"}
\ No newline at end of file
diff --git a/amd/build/reviewing.min.js b/amd/build/reviewing.min.js
index 7dc1ac1e4f..c811cbbdfd 100644
--- a/amd/build/reviewing.min.js
+++ b/amd/build/reviewing.min.js
@@ -1,10 +1,3 @@
-define("mod_moodleoverflow/reviewing",["exports","core/ajax","core/prefetch","core/templates","core/str"],(function(_exports,_ajax,_prefetch,_templates,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
-/**
-   * Implements reviewing functionality
-   *
-   * @module     mod_moodleoverflow/reviewing
-   * @copyright  2022 Justus Dieckmann WWU
-   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(){_prefetch.default.prefetchTemplates(["mod_moodleoverflow/reject_post_form","mod_moodleoverflow/review_buttons"]),_prefetch.default.prefetchStrings("mod_moodleoverflow",["post_was_approved","jump_to_next_post_needing_review","there_are_no_posts_needing_review","post_was_rejected"]);document.getElementById("moodleoverflow-posts").onclick=async e=>{const action=e.target.getAttribute("data-moodleoverflow-action");if(!action)return;const post=e.target.closest("*[data-moodleoverflow-postid]"),reviewRow=e.target.closest(".reviewrow"),postID=post.getAttribute("data-moodleoverflow-postid");if("approve"===action){reviewRow.innerHTML=".";const nextPostURL=await _ajax.default.call([{methodname:"mod_moodleoverflow_review_approve_post",args:{postid:postID}}])[0];let message=await(0,_str.get_string)("post_was_approved","mod_moodleoverflow")+" ";message+=nextPostURL?'<a href="'.concat(nextPostURL,'">')+await(0,_str.get_string)("jump_to_next_post_needing_review","mod_moodleoverflow")+"</a>":await(0,_str.get_string)("there_are_no_posts_needing_review","mod_moodleoverflow"),reviewRow.innerHTML=message,post.classList.remove("pendingreview")}else if("reject"===action)reviewRow.innerHTML=".",reviewRow.innerHTML=await _templates.default.render("mod_moodleoverflow/reject_post_form",{});else if("reject-submit"===action){const rejectMessage=post.querySelector("textarea.reject-reason").value.toString().trim();reviewRow.innerHTML=".";const args={postid:postID,reason:rejectMessage||null},nextPostURL=await _ajax.default.call([{methodname:"mod_moodleoverflow_review_reject_post",args:args}])[0];let message=await(0,_str.get_string)("post_was_rejected","mod_moodleoverflow")+" ";message+=nextPostURL?'<a href="'.concat(nextPostURL,'">')+await(0,_str.get_string)("jump_to_next_post_needing_review","mod_moodleoverflow")+"</a>":await(0,_str.get_string)("there_are_no_posts_needing_review","mod_moodleoverflow"),reviewRow.innerHTML=message}else"reject-cancel"===action&&(reviewRow.innerHTML=".",reviewRow.innerHTML=await _templates.default.render("mod_moodleoverflow/review_buttons",{}))}},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch),_templates=_interopRequireDefault(_templates)}));
+define("mod_moodleoverflow/reviewing",["exports","core/ajax","core/prefetch","core/templates","core/str"],(function(_exports,_ajax,_prefetch,_templates,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(){_prefetch.default.prefetchTemplates(["mod_moodleoverflow/reject_post_form","mod_moodleoverflow/review_buttons"]),_prefetch.default.prefetchStrings("mod_moodleoverflow",["post_was_approved","jump_to_next_post_needing_review","there_are_no_posts_needing_review","post_was_rejected"]),document.getElementById("moodleoverflow-posts").onclick=(fn=regeneratorRuntime.mark((function _callee(e){var action,post,reviewRow,postID,nextPostURL,message,rejectMessage,args,_nextPostURL,_message;return regeneratorRuntime.wrap((function(_context){for(;;)switch(_context.prev=_context.next){case 0:if(action=e.target.getAttribute("data-moodleoverflow-action")){_context.next=3;break}return _context.abrupt("return");case 3:if(post=e.target.closest("*[data-moodleoverflow-postid]"),reviewRow=e.target.closest(".reviewrow"),postID=post.getAttribute("data-moodleoverflow-postid"),"approve"!==action){_context.next=33;break}return reviewRow.innerHTML=".",_context.next=10,_ajax.default.call([{methodname:"mod_moodleoverflow_review_approve_post",args:{postid:postID}}])[0];case 10:return nextPostURL=_context.sent,_context.next=13,(0,_str.get_string)("post_was_approved","mod_moodleoverflow");case 13:if(_context.t0=_context.sent,message=_context.t0+" ",!nextPostURL){_context.next=25;break}return _context.t1=message,_context.t2='<a href="'.concat(nextPostURL,'">'),_context.next=20,(0,_str.get_string)("jump_to_next_post_needing_review","mod_moodleoverflow");case 20:_context.t3=_context.sent,_context.t4=_context.t2+_context.t3,message=_context.t1+=_context.t4+"</a>",_context.next=29;break;case 25:return _context.t5=message,_context.next=28,(0,_str.get_string)("there_are_no_posts_needing_review","mod_moodleoverflow");case 28:message=_context.t5+=_context.sent;case 29:reviewRow.innerHTML=message,post.classList.remove("pendingreview"),_context.next=73;break;case 33:if("reject"!==action){_context.next=40;break}return reviewRow.innerHTML=".",_context.next=37,_templates.default.render("mod_moodleoverflow/reject_post_form",{});case 37:reviewRow.innerHTML=_context.sent,_context.next=73;break;case 40:if("reject-submit"!==action){_context.next=68;break}return rejectMessage=post.querySelector("textarea.reject-reason").value.toString().trim(),reviewRow.innerHTML=".",args={postid:postID,reason:rejectMessage||null},_context.next=46,_ajax.default.call([{methodname:"mod_moodleoverflow_review_reject_post",args:args}])[0];case 46:return _nextPostURL=_context.sent,_context.next=49,(0,_str.get_string)("post_was_rejected","mod_moodleoverflow");case 49:if(_context.t6=_context.sent,_message=_context.t6+" ",!_nextPostURL){_context.next=61;break}return _context.t7=_message,_context.t8='<a href="'.concat(_nextPostURL,'">'),_context.next=56,(0,_str.get_string)("jump_to_next_post_needing_review","mod_moodleoverflow");case 56:_context.t9=_context.sent,_context.t10=_context.t8+_context.t9,_message=_context.t7+=_context.t10+"</a>",_context.next=65;break;case 61:return _context.t11=_message,_context.next=64,(0,_str.get_string)("there_are_no_posts_needing_review","mod_moodleoverflow");case 64:_message=_context.t11+=_context.sent;case 65:reviewRow.innerHTML=_message,_context.next=73;break;case 68:if("reject-cancel"!==action){_context.next=73;break}return reviewRow.innerHTML=".",_context.next=72,_templates.default.render("mod_moodleoverflow/review_buttons",{});case 72:reviewRow.innerHTML=_context.sent;case 73:case"end":return _context.stop()}}),_callee)})),_ref=function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))},function(_x){return _ref.apply(this,arguments)});var fn,_ref},_ajax=_interopRequireDefault(_ajax),_prefetch=_interopRequireDefault(_prefetch),_templates=_interopRequireDefault(_templates)}));
 
 //# sourceMappingURL=reviewing.min.js.map
\ No newline at end of file
diff --git a/amd/build/reviewing.min.js.map b/amd/build/reviewing.min.js.map
index 13a68e7925..d2b01e0044 100644
--- a/amd/build/reviewing.min.js.map
+++ b/amd/build/reviewing.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"reviewing.min.js","sources":["../src/reviewing.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements reviewing functionality\n *\n * @module     mod_moodleoverflow/reviewing\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Init function.\n */\nexport function init() {\n    Prefetch.prefetchTemplates(['mod_moodleoverflow/reject_post_form', 'mod_moodleoverflow/review_buttons']);\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['post_was_approved', 'jump_to_next_post_needing_review', 'there_are_no_posts_needing_review', 'post_was_rejected']);\n\n    const root = document.getElementById('moodleoverflow-posts');\n    root.onclick = async(e) => {\n        const action = e.target.getAttribute('data-moodleoverflow-action');\n\n        if (!action) {\n            return;\n        }\n\n        const post = e.target.closest('*[data-moodleoverflow-postid]');\n        const reviewRow = e.target.closest('.reviewrow');\n        const postID = post.getAttribute('data-moodleoverflow-postid');\n\n        if (action === 'approve') {\n            reviewRow.innerHTML = '.';\n            const nextPostURL = await Ajax.call([{\n                methodname: 'mod_moodleoverflow_review_approve_post',\n                args: {\n                    postid: postID,\n                }\n            }])[0];\n\n            let message = await getString('post_was_approved', 'mod_moodleoverflow') + ' ';\n            if (nextPostURL) {\n                message += `<a href=\"${nextPostURL}\">`\n                    + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n                    + \"</a>\";\n            } else {\n                message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n            }\n            reviewRow.innerHTML = message;\n            post.classList.remove(\"pendingreview\");\n        } else if (action === 'reject') {\n            reviewRow.innerHTML = '.';\n            reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/reject_post_form', {});\n        } else if (action === 'reject-submit') {\n            const rejectMessage = post.querySelector('textarea.reject-reason').value.toString().trim();\n            reviewRow.innerHTML = '.';\n            const args = {\n                postid: postID,\n                reason: rejectMessage ? rejectMessage : null\n            };\n            const nextPostURL = await Ajax.call([{\n                methodname: 'mod_moodleoverflow_review_reject_post',\n                args: args\n            }])[0];\n\n            let message = await getString('post_was_rejected', 'mod_moodleoverflow') + ' ';\n            if (nextPostURL) {\n                message += `<a href=\"${nextPostURL}\">`\n                    + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n                    + \"</a>\";\n            } else {\n                message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n            }\n            reviewRow.innerHTML = message;\n        } else if (action === 'reject-cancel') {\n            reviewRow.innerHTML = '.';\n            reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/review_buttons', {});\n        }\n    };\n}"],"names":["prefetchTemplates","prefetchStrings","document","getElementById","onclick","async","action","e","target","getAttribute","post","closest","reviewRow","postID","innerHTML","nextPostURL","Ajax","call","methodname","args","postid","message","classList","remove","Templates","render","rejectMessage","querySelector","value","toString","trim","reason"],"mappings":";;;;;;;wGA+BaA,kBAAkB,CAAC,sCAAuC,wDAC1DC,gBAAgB,qBACrB,CAAC,oBAAqB,mCAAoC,oCAAqC,sBAEtFC,SAASC,eAAe,wBAChCC,QAAUC,gBACLC,OAASC,EAAEC,OAAOC,aAAa,kCAEhCH,oBAICI,KAAOH,EAAEC,OAAOG,QAAQ,iCACxBC,UAAYL,EAAEC,OAAOG,QAAQ,cAC7BE,OAASH,KAAKD,aAAa,iCAElB,YAAXH,OAAsB,CACtBM,UAAUE,UAAY,UAChBC,kBAAoBC,cAAKC,KAAK,CAAC,CACjCC,WAAY,yCACZC,KAAM,CACFC,OAAQP,WAEZ,OAEAQ,cAAgB,mBAAU,oBAAqB,sBAAwB,IAEvEA,SADAN,YACW,mBAAYA,wBACX,mBAAU,mCAAoC,sBACpD,aAEW,mBAAU,oCAAqC,sBAEpEH,UAAUE,UAAYO,QACtBX,KAAKY,UAAUC,OAAO,sBACnB,GAAe,WAAXjB,OACPM,UAAUE,UAAY,IACtBF,UAAUE,gBAAkBU,mBAAUC,OAAO,sCAAuC,SACjF,GAAe,kBAAXnB,OAA4B,OAC7BoB,cAAgBhB,KAAKiB,cAAc,0BAA0BC,MAAMC,WAAWC,OACpFlB,UAAUE,UAAY,UAChBK,KAAO,CACTC,OAAQP,OACRkB,OAAQL,eAAgC,MAEtCX,kBAAoBC,cAAKC,KAAK,CAAC,CACjCC,WAAY,wCACZC,KAAMA,QACN,OAEAE,cAAgB,mBAAU,oBAAqB,sBAAwB,IAEvEA,SADAN,YACW,mBAAYA,wBACX,mBAAU,mCAAoC,sBACpD,aAEW,mBAAU,oCAAqC,sBAEpEH,UAAUE,UAAYO,YACJ,kBAAXf,SACPM,UAAUE,UAAY,IACtBF,UAAUE,gBAAkBU,mBAAUC,OAAO,oCAAqC"}
\ No newline at end of file
+{"version":3,"file":"reviewing.min.js","sources":["../src/reviewing.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Implements reviewing functionality\n *\n * @module     mod_moodleoverflow/reviewing\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Prefetch from 'core/prefetch';\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Init function.\n */\nexport function init() {\n    Prefetch.prefetchTemplates(['mod_moodleoverflow/reject_post_form', 'mod_moodleoverflow/review_buttons']);\n    Prefetch.prefetchStrings('mod_moodleoverflow',\n        ['post_was_approved', 'jump_to_next_post_needing_review', 'there_are_no_posts_needing_review', 'post_was_rejected']);\n\n    const root = document.getElementById('moodleoverflow-posts');\n    root.onclick = async(e) => {\n        const action = e.target.getAttribute('data-moodleoverflow-action');\n\n        if (!action) {\n            return;\n        }\n\n        const post = e.target.closest('*[data-moodleoverflow-postid]');\n        const reviewRow = e.target.closest('.reviewrow');\n        const postID = post.getAttribute('data-moodleoverflow-postid');\n\n        if (action === 'approve') {\n            reviewRow.innerHTML = '.';\n            const nextPostURL = await Ajax.call([{\n                methodname: 'mod_moodleoverflow_review_approve_post',\n                args: {\n                    postid: postID,\n                }\n            }])[0];\n\n            let message = await getString('post_was_approved', 'mod_moodleoverflow') + ' ';\n            if (nextPostURL) {\n                message += `<a href=\"${nextPostURL}\">`\n                    + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n                    + \"</a>\";\n            } else {\n                message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n            }\n            reviewRow.innerHTML = message;\n            post.classList.remove(\"pendingreview\");\n        } else if (action === 'reject') {\n            reviewRow.innerHTML = '.';\n            reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/reject_post_form', {});\n        } else if (action === 'reject-submit') {\n            const rejectMessage = post.querySelector('textarea.reject-reason').value.toString().trim();\n            reviewRow.innerHTML = '.';\n            const args = {\n                postid: postID,\n                reason: rejectMessage ? rejectMessage : null\n            };\n            const nextPostURL = await Ajax.call([{\n                methodname: 'mod_moodleoverflow_review_reject_post',\n                args: args\n            }])[0];\n\n            let message = await getString('post_was_rejected', 'mod_moodleoverflow') + ' ';\n            if (nextPostURL) {\n                message += `<a href=\"${nextPostURL}\">`\n                    + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n                    + \"</a>\";\n            } else {\n                message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n            }\n            reviewRow.innerHTML = message;\n        } else if (action === 'reject-cancel') {\n            reviewRow.innerHTML = '.';\n            reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/review_buttons', {});\n        }\n    };\n}"],"names":["prefetchTemplates","prefetchStrings","document","getElementById","onclick","e","action","target","getAttribute","post","closest","reviewRow","postID","innerHTML","Ajax","call","methodname","args","postid","nextPostURL","message","classList","remove","Templates","render","rejectMessage","querySelector","value","toString","trim","reason"],"mappings":"8iBA+BaA,kBAAkB,CAAC,sCAAuC,wDAC1DC,gBAAgB,qBACrB,CAAC,oBAAqB,mCAAoC,oCAAqC,sBAEtFC,SAASC,eAAe,wBAChCC,qCAAU,iBAAMC,yMACXC,OAASD,EAAEE,OAAOC,aAAa,gGAM/BC,KAAOJ,EAAEE,OAAOG,QAAQ,iCACxBC,UAAYN,EAAEE,OAAOG,QAAQ,cAC7BE,OAASH,KAAKD,aAAa,8BAElB,YAAXF,sCACAK,UAAUE,UAAY,qBACIC,cAAKC,KAAK,CAAC,CACjCC,WAAY,yCACZC,KAAM,CACFC,OAAQN,WAEZ,kBALEO,4CAOc,mBAAU,oBAAqB,2DAA/CC,oBAAuE,KACvED,uDACAC,uCAAuBD,oCACX,mBAAU,mCAAoC,4FAD1DC,iCAEM,yDAENA,0BAAiB,mBAAU,oCAAqC,8BAAhEA,2CAEJT,UAAUE,UAAYO,QACtBX,KAAKY,UAAUC,OAAO,mDACJ,WAAXhB,sCACPK,UAAUE,UAAY,qBACMU,mBAAUC,OAAO,sCAAuC,YAApFb,UAAUE,0DACQ,kBAAXP,sCACDmB,cAAgBhB,KAAKiB,cAAc,0BAA0BC,MAAMC,WAAWC,OACpFlB,UAAUE,UAAY,IAChBI,KAAO,CACTC,OAAQN,OACRkB,OAAQL,eAAgC,uBAElBX,cAAKC,KAAK,CAAC,CACjCC,WAAY,wCACZC,KAAMA,QACN,kBAHEE,6CAKc,mBAAU,oBAAqB,2DAA/CC,qBAAuE,KACvED,wDACAC,wCAAuBD,qCACX,mBAAU,mCAAoC,6FAD1DC,mCAEM,0DAENA,2BAAiB,mBAAU,oCAAqC,8BAAhEA,6CAEJT,UAAUE,UAAYO,2CACJ,kBAAXd,sCACPK,UAAUE,UAAY,qBACMU,mBAAUC,OAAO,oCAAqC,YAAlFb,UAAUE"}
\ No newline at end of file
diff --git a/amd/build/warnmodechange.min.js b/amd/build/warnmodechange.min.js
index 5042ba704b..370da13a32 100644
--- a/amd/build/warnmodechange.min.js
+++ b/amd/build/warnmodechange.min.js
@@ -1,10 +1,3 @@
-define("mod_moodleoverflow/warnmodechange",["exports","core/str","core/notification","core/prefetch"],(function(_exports,_str,_notification,_prefetch){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
-/**
-   * Warns on changing the subscription mode.
-   *
-   * @module     mod_moodleoverflow/warnmodechange
-   * @copyright  2022 Justus Dieckmann WWU
-   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(previousSetting){_prefetch.default.prefetchStrings("mod_moodleoverflow",["switchtooptional","switchtoauto"]),_prefetch.default.prefetchStrings("moodle",["confirm","cancel"]);const form=document.querySelector("form.mform"),select=document.getElementById("id_forcesubscribe");form.onsubmit=async e=>{const value=select.selectedOptions[0].value;value!=previousSetting&&1!=value&&3!=value&&(e.preventDefault(),await _notification.default.confirm(await(0,_str.get_string)("confirm"),await(0,_str.get_string)(0==value?"switchtooptional":"switchtoauto","mod_moodleoverflow"),await(0,_str.get_string)("confirm"),await(0,_str.get_string)("cancel"),(()=>{form.onsubmit=void 0,form.requestSubmit(e.submitter)}),void 0))}},_notification=_interopRequireDefault(_notification),_prefetch=_interopRequireDefault(_prefetch)}));
+define("mod_moodleoverflow/warnmodechange",["exports","core/str","core/notification","core/prefetch"],(function(_exports,_str,_notification,_prefetch){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(previousSetting){_prefetch.default.prefetchStrings("mod_moodleoverflow",["switchtooptional","switchtoauto"]),_prefetch.default.prefetchStrings("moodle",["confirm","cancel"]);var form=document.querySelector("form.mform"),select=document.getElementById("id_forcesubscribe");form.onsubmit=(fn=regeneratorRuntime.mark((function _callee(e){var value;return regeneratorRuntime.wrap((function(_context){for(;;)switch(_context.prev=_context.next){case 0:if((value=select.selectedOptions[0].value)!=previousSetting&&1!=value&&3!=value){_context.next=3;break}return _context.abrupt("return");case 3:return e.preventDefault(),_context.t0=_notification.default,_context.next=7,(0,_str.get_string)("confirm");case 7:return _context.t1=_context.sent,_context.next=10,(0,_str.get_string)(0==value?"switchtooptional":"switchtoauto","mod_moodleoverflow");case 10:return _context.t2=_context.sent,_context.next=13,(0,_str.get_string)("confirm");case 13:return _context.t3=_context.sent,_context.next=16,(0,_str.get_string)("cancel");case 16:return _context.t4=_context.sent,_context.t5=function(){form.onsubmit=void 0,form.requestSubmit(e.submitter)},_context.t6=void 0,_context.next=21,_context.t0.confirm.call(_context.t0,_context.t1,_context.t2,_context.t3,_context.t4,_context.t5,_context.t6);case 21:case"end":return _context.stop()}}),_callee)})),_ref=function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))},function(_x){return _ref.apply(this,arguments)});var fn,_ref},_notification=_interopRequireDefault(_notification),_prefetch=_interopRequireDefault(_prefetch)}));
 
 //# sourceMappingURL=warnmodechange.min.js.map
\ No newline at end of file
diff --git a/amd/build/warnmodechange.min.js.map b/amd/build/warnmodechange.min.js.map
index b4120395cd..f3c76304c1 100644
--- a/amd/build/warnmodechange.min.js.map
+++ b/amd/build/warnmodechange.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"warnmodechange.min.js","sources":["../src/warnmodechange.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Warns on changing the subscription mode.\n *\n * @module     mod_moodleoverflow/warnmodechange\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {get_string as getString} from 'core/str';\nimport Notification from 'core/notification';\nimport Prefetch from 'core/prefetch';\n\n/**\n * Init function.\n * @param {string} previousSetting\n */\nexport function init(previousSetting) {\n    Prefetch.prefetchStrings('mod_moodleoverflow', ['switchtooptional', 'switchtoauto']);\n    Prefetch.prefetchStrings('moodle', ['confirm', 'cancel']);\n    const form = document.querySelector('form.mform');\n    const select = document.getElementById('id_forcesubscribe');\n    form.onsubmit = async(e) => {\n        const value = select.selectedOptions[0].value;\n        if (value == previousSetting || value == 1 || value == 3) {\n            return;\n        }\n        e.preventDefault();\n        await Notification.confirm(\n            await getString('confirm'),\n            await getString(value == 0 ? 'switchtooptional' : 'switchtoauto', 'mod_moodleoverflow'),\n            await getString('confirm'),\n            await getString('cancel'),\n            () => {\n                // Prevent this listener from preventing the event again.\n                form.onsubmit = undefined;\n                form.requestSubmit(e.submitter);\n            }, undefined);\n    };\n}"],"names":["previousSetting","prefetchStrings","form","document","querySelector","select","getElementById","onsubmit","async","value","selectedOptions","e","preventDefault","Notification","confirm","undefined","requestSubmit","submitter"],"mappings":";;;;;;;oFA8BqBA,mCACRC,gBAAgB,qBAAsB,CAAC,mBAAoB,mCAC3DA,gBAAgB,SAAU,CAAC,UAAW,iBACzCC,KAAOC,SAASC,cAAc,cAC9BC,OAASF,SAASG,eAAe,qBACvCJ,KAAKK,SAAWC,gBACNC,MAAQJ,OAAOK,gBAAgB,GAAGD,MACpCA,OAAST,iBAA4B,GAATS,OAAuB,GAATA,QAG9CE,EAAEC,uBACIC,sBAAaC,cACT,mBAAU,iBACV,mBAAmB,GAATL,MAAa,mBAAqB,eAAgB,4BAC5D,mBAAU,iBACV,mBAAU,WAChB,KAEIP,KAAKK,cAAWQ,EAChBb,KAAKc,cAAcL,EAAEM,kBACtBF,GATP"}
\ No newline at end of file
+{"version":3,"file":"warnmodechange.min.js","sources":["../src/warnmodechange.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Warns on changing the subscription mode.\n *\n * @module     mod_moodleoverflow/warnmodechange\n * @copyright  2022 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {get_string as getString} from 'core/str';\nimport Notification from 'core/notification';\nimport Prefetch from 'core/prefetch';\n\n/**\n * Init function.\n * @param {string} previousSetting\n */\nexport function init(previousSetting) {\n    Prefetch.prefetchStrings('mod_moodleoverflow', ['switchtooptional', 'switchtoauto']);\n    Prefetch.prefetchStrings('moodle', ['confirm', 'cancel']);\n    const form = document.querySelector('form.mform');\n    const select = document.getElementById('id_forcesubscribe');\n    form.onsubmit = async(e) => {\n        const value = select.selectedOptions[0].value;\n        if (value == previousSetting || value == 1 || value == 3) {\n            return;\n        }\n        e.preventDefault();\n        await Notification.confirm(\n            await getString('confirm'),\n            await getString(value == 0 ? 'switchtooptional' : 'switchtoauto', 'mod_moodleoverflow'),\n            await getString('confirm'),\n            await getString('cancel'),\n            () => {\n                // Prevent this listener from preventing the event again.\n                form.onsubmit = undefined;\n                form.requestSubmit(e.submitter);\n            }, undefined);\n    };\n}"],"names":["previousSetting","prefetchStrings","form","document","querySelector","select","getElementById","onsubmit","e","value","selectedOptions","preventDefault","Notification","undefined","requestSubmit","submitter","confirm"],"mappings":"mhBA8BqBA,mCACRC,gBAAgB,qBAAsB,CAAC,mBAAoB,mCAC3DA,gBAAgB,SAAU,CAAC,UAAW,eACzCC,KAAOC,SAASC,cAAc,cAC9BC,OAASF,SAASG,eAAe,qBACvCJ,KAAKK,sCAAW,iBAAMC,sHACZC,MAAQJ,OAAOK,gBAAgB,GAAGD,QAC3BT,iBAA4B,GAATS,OAAuB,GAATA,4EAG9CD,EAAEG,6BACIC,uCACI,mBAAU,qEACV,mBAAmB,GAATH,MAAa,mBAAqB,eAAgB,iFAC5D,mBAAU,sEACV,mBAAU,+DAChB,WAEIP,KAAKK,cAAWM,EAChBX,KAAKY,cAAcN,EAAEO,6BACtBF,+BATYG"}
\ No newline at end of file
diff --git a/classes/ratings.php b/classes/ratings.php
index fbf7be5052..0c8127394e 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -259,7 +259,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
         $startother = 1;
         // Solved and helpful posts are first.
         foreach ($answerposts as $post) {
-            if ($post->markedsolution == 1 && $post->markedhelpful == 1) {
+            if ($post->markedsolution > 0 && $post->markedhelpful > 0) {
                 $sortedposts[$index] = $post;
                 $index++;
             }
@@ -277,7 +277,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
 
             // Build the group of only solved posts.
             foreach ($answerposts as $post) {
-                if ($post->markedsolution == 1 && $post->markedhelpful == 0) {
+                if ($post->markedsolution > 0 && $post->markedhelpful == 0) {
                     $sortedposts[$index] = $post;
                     $index++;
                 }
@@ -291,7 +291,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
 
             // Build the group of only helpful posts.
             foreach ($answerposts as $post) {
-                if ($post->markedsolution == 0 && $post->markedhelpful == 1) {
+                if ($post->markedsolution == 0 && $post->markedhelpful > 0) {
                     $sortedposts[$index] = $post;
                     $index++;
                 }
@@ -305,7 +305,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
 
             // Build the group of only helpful posts.
             foreach ($answerposts as $post) {
-                if ($post->markedsolution == 0 && $post->markedhelpful == 1) {
+                if ($post->markedsolution == 0 && $post->markedhelpful > 0) {
                     $sortedposts[$index] = $post;
                     $index++;
                 }
@@ -319,7 +319,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
 
             // Build the group of only solved posts.
             foreach ($answerposts as $post) {
-                if ($post->markedsolution == 1 && $post->markedhelpful == 0) {
+                if ($post->markedsolution > 0 && $post->markedhelpful == 0) {
                     $sortedposts[$index] = $post;
                     $index++;
                 }
diff --git a/tests/ratings_test.php b/tests/ratings_test.php
index 232e34afc3..c9fdd787e1 100644
--- a/tests/ratings_test.php
+++ b/tests/ratings_test.php
@@ -105,15 +105,69 @@ public function tearDown(): void {
     /**
      * Test, if rating can sort after ever group with ratingpreferences on helpful first.
      */
-    public function test_everygrouphelpfulon() {
-        // Create helpful and solved and up and downvotes ratings.
+    public function test_sort_answer_by_ratings() {
+        // Create helpful, solved, up and downvotes ratings.
         $this->create_everygroup();
-        $this->set_ratingpreferences(1);
-        // Create a array of the posts and sort them.
+
+        // Test with every group if rating
+
+        // Create a array of the posts, save the sorted post and compare them to the order that they should have.
         $posts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
+        $this->set_ratingpreferences(0);
+        $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
+        $rightorderposts = array($this->post, $this->answer1, $this->answer3, $this->answer2, $this->answer4, $this->answer6, $this->answer5);
+        $result = $this->postsorderequal($sortedposts, $rightorderposts);
+        $this->assertEquals(1, $result);
+
+        // Change the rating preference of the teacher and sort again.
+        $this->set_ratingpreferences(1);
+        $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
+        $rightorderposts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer6, $this->answer5);
+        $result = $this->postsorderequal($sortedposts, $rightorderposts);
+        $this->assertEquals(1, $result);
+
+
+        // Test without posts that are only marked as solved
+        $posts = array($this->post, $this->answer1, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
+        $this->set_ratingpreferences(0);
+        $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
+        $rightorderposts = array($this->post, $this->answer1, $this->answer3, $this->answer4, $this->answer6, $this->answer5);
+        $result = $this->postsorderequal($sortedposts, $rightorderposts);
+        $this->assertEquals(1, $result);
+
+        $this->set_ratingpreferences(1);
+        $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
+        $rightorderposts = array($this->post, $this->answer1, $this->answer3, $this->answer4, $this->answer6, $this->answer5);
+        $result = $this->postsorderequal($sortedposts, $rightorderposts);
+        $this->assertEquals(1, $result);
+
+        // Test without posts that are only marked as helpful
+        $posts = array($this->post, $this->answer1, $this->answer2, $this->answer4, $this->answer5, $this->answer6);
+        $this->set_ratingpreferences(0);
+        $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
+        $rightorderposts = array($this->post, $this->answer1, $this->answer2, $this->answer4, $this->answer6, $this->answer5);
+        $result = $this->postsorderequal($sortedposts, $rightorderposts);
+        $this->assertEquals(1, $result);
+
+        $this->set_ratingpreferences(1);
+        $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
+        $rightorderposts = array($this->post, $this->answer1, $this->answer2, $this->answer4, $this->answer6, $this->answer5);
+        $result = $this->postsorderequal($sortedposts, $rightorderposts);
+        $this->assertEquals(1, $result);
+
+        // Test without posts that are marked as both helpful and marked
+        $posts = array($this->post, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
+        $this->set_ratingpreferences(0);
+        $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
+        $rightorderposts = array($this->post, $this->answer3, $this->answer2, $this->answer4, $this->answer6, $this->answer5);
+        $result = $this->postsorderequal($sortedposts, $rightorderposts);
+        $this->assertEquals(1, $result);
+
+        $this->set_ratingpreferences(1);
         $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
-        var_dump($sortedposts);
-        $this->assertEquals(1,1);
+        $rightorderposts = array($this->post, $this->answer2, $this->answer3, $this->answer4, $this->answer6, $this->answer5);
+        $result = $this->postsorderequal($sortedposts, $rightorderposts);
+        $this->assertEquals(1, $result);
     }
 
     // Helper functions
@@ -154,6 +208,52 @@ private function helper_course_set_up() {
         $this->answer6 = $this->generator->reply_to_post($this->discussion[1], $this->user2, true);
     }
 
+
+    /**
+     * This function compares 2 arrays with posts and checks if the order is the same.
+     * 
+     * @param array $sortedposts - The sorted posts
+     * @param array $rightorderposts       - The posts with the order they should have
+     * The function returns 1 if $sortedposts matches $posts, else 0
+     */
+    private function postsorderequal($sortedposts, $rightorderposts) {
+        if (count($sortedposts) != count($rightorderposts)) {
+            return 0;
+        }
+        for ($i = 0; $i < count($sortedposts); $i++) {
+            // Get the current elements.
+            $sortedpost = current($sortedposts);
+            $post = current($rightorderposts);
+            if ($sortedpost->id == $post->id) {
+                // Go to the next elements
+                next($sortedposts);
+                next($rightorderposts);
+            } else {
+                return 0;
+            }
+        }
+        return 1;
+    }
+
+    /**
+     * sets the ratingpreferences to 1 or 0:
+     * 1 = solved posts will be shown above helpful posts.
+     * 0 = helpful posts will be shown above solved posts.
+     */
+    private function set_ratingpreferences($preference) {
+        if ($preference == 0 || $preference == 1) {
+            $this->post->ratingpreference = $preference;
+            $this->answer1->ratingpreference = $preference;
+            $this->answer2->ratingpreference = $preference;
+            $this->answer3->ratingpreference = $preference;
+            $this->answer4->ratingpreference = $preference;
+            $this->answer5->ratingpreference = $preference;
+            $this->answer6->ratingpreference = $preference;
+        }
+    }
+
+    // Creation functions, that create different rating situations of the posts in a discussion.
+
     /**
      * creates a rating of every type by adding attributes to the post:
      * - post that is solved and helpful
@@ -166,59 +266,45 @@ private function create_everygroup() {
         $this->answer1->upvotes = 0;
         $this->answer1->downvotes = 0;
         $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes;
-        $this->answer1->statusstarter = 1;
-        $this->answer1->statusteacher = 1;
+        $this->answer1->markedhelpful = 1;
+        $this->answer1->markedsolution = 1;
         
         // Answer2.
         $this->answer2->upvotes = 0;
         $this->answer2->downvotes = 0;
         $this->answer2->votesdifference = $this->answer2->upvotes - $this->answer2->downvotes;
-        $this->answer2->statusstarter = 0;
-        $this->answer2->statusteacher = 1;
+        $this->answer2->markedhelpful = 0;
+        $this->answer2->markedsolution = 1;
         
         // Answer3.
         $this->answer3->upvotes = 0;
         $this->answer3->downvotes = 0;
         $this->answer3->votesdifference = $this->answer3->upvotes - $this->answer3->downvotes;
-        $this->answer3->statusstarter = 1;
-        $this->answer3->statusteacher = 0;
+        $this->answer3->markedhelpful = 1;
+        $this->answer3->markedsolution = 0;
 
         // Answer4.
         $this->answer4->upvotes = 1;
         $this->answer4->downvotes = 0;
         $this->answer4->votesdifference = $this->answer4->upvotes - $this->answer4->downvotes;
-        $this->answer4->statusstarter = 0;
-        $this->answer4->statusteacher = 0;
+        $this->answer4->markedhelpful = 0;
+        $this->answer4->markedsolution = 0;
 
         // Answer5.
         $this->answer5->upvotes = 0;
         $this->answer5->downvotes = 1;
         $this->answer5->votesdifference = $this->answer5->upvotes - $this->answer5->downvotes;
-        $this->answer5->statusstarter = 0;
-        $this->answer5->statusteacher = 0;
+        $this->answer5->markedhelpful = 0;
+        $this->answer5->markedsolution = 0;
 
         // Answer6.
         $this->answer6->upvotes = 0;
         $this->answer6->downvotes = 0;
         $this->answer6->votesdifference = $this->answer6->upvotes - $this->answer6->downvotes;
-        $this->answer6->statusstarter = 0;
-        $this->answer6->statusteacher = 0;
+        $this->answer6->markedhelpful = 0;
+        $this->answer6->markedsolution = 0;
     }
 
-    /**
-     * sets the ratingpreferences to 1 or 0:
-     * 1 = solved posts will be shown above helpful posts.
-     * 0 = helpful posts will be shown above solved posts.
-     */
-    private function set_ratingpreferences($preference) {
-        if ($preference == 0 || $preference == 1) {
-            $this->post->ratingpreference = $preference;
-            $this->answer1->ratingpreference = $preference;
-            $this->answer2->ratingpreference = $preference;
-            $this->answer3->ratingpreference = $preference;
-            $this->answer4->ratingpreference = $preference;
-            $this->answer5->ratingpreference = $preference;
-            $this->answer6->ratingpreference = $preference;
-        }
-    }
+    //private function create_groupswithoutsolution
+    
 }

From bf935755c51335582f4480faa95ed2d0673a6ddc Mon Sep 17 00:00:00 2001
From: NinaHerrmann <nina.herrmann@uni-muenster.de>
Date: Tue, 16 May 2023 17:52:02 +0200
Subject: [PATCH 29/35] coding style

---
 classes/ratings.php    |  8 ++++----
 tests/ratings_test.php | 17 ++++++++---------
 2 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/classes/ratings.php b/classes/ratings.php
index 0c8127394e..99fb8f2366 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -229,14 +229,14 @@ public static function moodleoverflow_get_reputation($moodleoverflowid, $userid
 
     /**
      * Sort the answers of a discussion by their marks and votes.
-     * 
+     *
      * @param object $posts all the posts from a discussion.
      */
     public static function moodleoverflow_sort_answers_by_ratings($posts) {
         // Create a copy that only has the answer posts and save the parent post.
         $answerposts = $posts;
         $parentpost = array_shift($answerposts);
-        
+
         // Create an empty array for the sorted posts and add the parent post.
         $sortedposts = array();
         $sortedposts[0] = $parentpost;
@@ -264,7 +264,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
                 $index++;
             }
         }
-        //Update the indices and sort the group by votes.
+        // Update the indices and sort the group by votes.
         if ($index > $startsolvedandhelpful) {
             $startsolved = $index;
             $starthelpful = $index;
@@ -349,7 +349,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
         foreach ($sortedposts as $post) {
             $neworder[$post->id] = $post;
         }
- 
+
         // return now the sorted posts.
         return $neworder;
     }
diff --git a/tests/ratings_test.php b/tests/ratings_test.php
index c9fdd787e1..8c8250f25e 100644
--- a/tests/ratings_test.php
+++ b/tests/ratings_test.php
@@ -37,7 +37,7 @@
  * @copyright 2023 Tamaro Walter
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class ratings_test extends \advanced_testcase { 
+class ratings_test extends \advanced_testcase {
     /** @var \stdClass test course */
     private $course;
 
@@ -101,7 +101,7 @@ public function tearDown(): void {
     }
 
     // Begin of test functions
-    
+
     /**
      * Test, if rating can sort after ever group with ratingpreferences on helpful first.
      */
@@ -126,7 +126,6 @@ public function test_sort_answer_by_ratings() {
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
 
-
         // Test without posts that are only marked as solved
         $posts = array($this->post, $this->answer1, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
         $this->set_ratingpreferences(0);
@@ -171,7 +170,7 @@ public function test_sort_answer_by_ratings() {
     }
 
     // Helper functions
-    
+
     /**
      * This function creates:
      * - a course with a moodleoverflow
@@ -211,7 +210,7 @@ private function helper_course_set_up() {
 
     /**
      * This function compares 2 arrays with posts and checks if the order is the same.
-     * 
+     *
      * @param array $sortedposts - The sorted posts
      * @param array $rightorderposts       - The posts with the order they should have
      * The function returns 1 if $sortedposts matches $posts, else 0
@@ -268,14 +267,14 @@ private function create_everygroup() {
         $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes;
         $this->answer1->markedhelpful = 1;
         $this->answer1->markedsolution = 1;
-        
+
         // Answer2.
         $this->answer2->upvotes = 0;
         $this->answer2->downvotes = 0;
         $this->answer2->votesdifference = $this->answer2->upvotes - $this->answer2->downvotes;
         $this->answer2->markedhelpful = 0;
         $this->answer2->markedsolution = 1;
-        
+
         // Answer3.
         $this->answer3->upvotes = 0;
         $this->answer3->downvotes = 0;
@@ -305,6 +304,6 @@ private function create_everygroup() {
         $this->answer6->markedsolution = 0;
     }
 
-    //private function create_groupswithoutsolution
-    
+    // private function create_groupswithoutsolution
+
 }

From 97f2258698e219f5e77a36bbf6d0331482f2478e Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Tue, 16 May 2023 18:20:35 +0200
Subject: [PATCH 30/35] WIP: write test cases for ratings.php

---
 tests/ratings_test.php | 76 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 74 insertions(+), 2 deletions(-)

diff --git a/tests/ratings_test.php b/tests/ratings_test.php
index 8c8250f25e..8562642b48 100644
--- a/tests/ratings_test.php
+++ b/tests/ratings_test.php
@@ -109,7 +109,7 @@ public function test_sort_answer_by_ratings() {
         // Create helpful, solved, up and downvotes ratings.
         $this->create_everygroup();
 
-        // Test with every group if rating
+        // Test with every group of rating
 
         // Create a array of the posts, save the sorted post and compare them to the order that they should have.
         $posts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
@@ -167,6 +167,8 @@ public function test_sort_answer_by_ratings() {
         $rightorderposts = array($this->post, $this->answer2, $this->answer3, $this->answer4, $this->answer6, $this->answer5);
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
+
+        // Now test every rating group alone
     }
 
     // Helper functions
@@ -304,6 +306,76 @@ private function create_everygroup() {
         $this->answer6->markedsolution = 0;
     }
 
-    // private function create_groupswithoutsolution
+    /**
+     * Creates a rating of one group for every post in the discussion
+     * Creates up and downvotes
+     * @param string $group
+     * A Group can be:
+     * - both as solution and helpful marked posts (sh)
+     * - only solution posts (s)
+     * - only helpful (h)
+     * - no mark (o)
+     */
+    private function create_onegroup($group) {
+
+        $answers = array($this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
+        foreach ($answers as $answer) {
+            switch ($group) {
+                case 'sh':
+                    $answer->markedhelpful = 1;
+                    $answer->markedsolution = 1;
+                    break;
+                case 's':
+                    $answer->markedhelpful = 0;
+                    $answer->markedsolution = 1;
+                    break;
+                case 'h':
+                    $answer->markedhelpful = 1;
+                    $answer->markedsolution = 0;
+                    break;
+                case 'o':
+                    $answer->markedhelpful = 0;
+                    $answer->markedsolution = 0;
+                    break;
+                default:
+                    $answer->markedhelpful = 0;
+                    $answer->markedsolution = 0;
+                    break;
+            }
+        }
+
+        // Votes for the answerposts
+        // Answer1.
+        $this->answer1->upvotes = 4;
+        $this->answer1->downvotes = -4;
+        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = 0.
+
+        // Answer2.
+        $this->answer1->upvotes = 1;
+        $this->answer1->downvotes = 2;
+        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = -1.
+
+        // Answer3.
+        $this->answer1->upvotes = 3;
+        $this->answer1->downvotes = 2;
+        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = 1.
+
+        // Answer4.
+        $this->answer1->upvotes = 5;
+        $this->answer1->downvotes = 0;
+        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = 5
+
+        // Answer5.
+        $this->answer1->upvotes = 0;
+        $this->answer1->downvotes = 2;
+        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = -2.
+
+        // Answer6.
+        $this->answer1->upvotes = 4;
+        $this->answer1->downvotes = 2;
+        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = 2.
+
+        // Rightorder = answer4 , answer6, answer3, answer1, answer2, answer5.
+    }
 
 }

From cac514bef42c3f32bc56345398870ee2e225ffde Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 22 May 2023 11:08:02 +0200
Subject: [PATCH 31/35] test cases completed for ratings.php

---
 tests/ratings_test.php | 293 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 269 insertions(+), 24 deletions(-)

diff --git a/tests/ratings_test.php b/tests/ratings_test.php
index 8562642b48..062c538dd5 100644
--- a/tests/ratings_test.php
+++ b/tests/ratings_test.php
@@ -103,9 +103,10 @@ public function tearDown(): void {
     // Begin of test functions
 
     /**
-     * Test, if rating can sort after ever group with ratingpreferences on helpful first.
+     * Tests function ratings::moodleoverflow_sort_answer_by_ratings 
+     * Test case: Every group of rating exists (helful and solved posts, only helpful/solved and none)
      */
-    public function test_sort_answer_by_ratings() {
+    public function test_answersorting_everygroup() {
         // Create helpful, solved, up and downvotes ratings.
         $this->create_everygroup();
 
@@ -125,6 +126,15 @@ public function test_sort_answer_by_ratings() {
         $rightorderposts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer6, $this->answer5);
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
+    }
+
+    /**
+     * Tests function ratings::moodleoverflow_sort_answer_by_ratings
+     * Test case: One group of rating does not exist
+     */
+    public function test_answersorting_threegroups() {
+        // Create helpful, solved, up and downvotes ratings.
+        $this->create_everygroup();
 
         // Test without posts that are only marked as solved
         $posts = array($this->post, $this->answer1, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
@@ -167,8 +177,134 @@ public function test_sort_answer_by_ratings() {
         $rightorderposts = array($this->post, $this->answer2, $this->answer3, $this->answer4, $this->answer6, $this->answer5);
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
+    }
+
+    /**
+     * Tests function ratings::moodleoverflow_sort_answer_by_ratings
+     * Test case: two groups of rating do not exist
+     */
+    public function test_answersorting_twogroups() {
+        $this->set_ratingpreferences(0);
+
+        // 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);
+
+        // 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);
+
+        // 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);
 
-        // Now test every rating group alone
+        // 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);
+
+        // 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);
+
+        // 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);
+        $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);
+
+        // 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);
+    }
+
+    /**
+     * Tests function ratings::moodleoverflow_sort_answer_by_ratings
+     * Test case: Only one group of rating exists, so only:
+     * - helpful and solved posts, or
+     * - helpful, or
+     * - solved, or
+     * - not marked
+     */
+    public function test_answersorting_onegroup() {
+        $this->set_ratingpreferences(0);
+
+        // Test case 1: only solved and helpful posts.
+        $group = 'sh';
+        $this->create_onegroup($group);
+        $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->answer4, $this->answer6, $this->answer3, $this->answer1, $this->answer2, $this->answer5);
+        $result = $this->postsorderequal($sortedposts, $rightorderposts);
+        $this->assertEquals(1, $result);
+
+        // Test case 1: only solvedposts.
+        $group = 's';
+        $this->create_onegroup($group);
+        $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->answer4, $this->answer6, $this->answer3, $this->answer1, $this->answer2, $this->answer5);
+        $result = $this->postsorderequal($sortedposts, $rightorderposts);
+        $this->assertEquals(1, $result);
+
+        // Test case 1: only helpful posts.
+        $group = 'h';
+        $this->create_onegroup($group);
+        $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->answer4, $this->answer6, $this->answer3, $this->answer1, $this->answer2, $this->answer5);
+        $result = $this->postsorderequal($sortedposts, $rightorderposts);
+        $this->assertEquals(1, $result);
+
+        // Test case 1: only not marked posts.
+        $group = 'o';
+        $this->create_onegroup($group);
+        $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->answer4, $this->answer6, $this->answer3, $this->answer1, $this->answer2, $this->answer5);
+        $result = $this->postsorderequal($sortedposts, $rightorderposts);
+        $this->assertEquals(1, $result);
     }
 
     // Helper functions
@@ -317,7 +453,6 @@ private function create_everygroup() {
      * - no mark (o)
      */
     private function create_onegroup($group) {
-
         $answers = array($this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
         foreach ($answers as $answer) {
             switch ($group) {
@@ -337,45 +472,155 @@ private function create_onegroup($group) {
                     $answer->markedhelpful = 0;
                     $answer->markedsolution = 0;
                     break;
-                default:
-                    $answer->markedhelpful = 0;
-                    $answer->markedsolution = 0;
-                    break;
             }
         }
 
         // Votes for the answerposts
         // Answer1.
         $this->answer1->upvotes = 4;
-        $this->answer1->downvotes = -4;
+        $this->answer1->downvotes = 4;
         $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = 0.
 
         // Answer2.
-        $this->answer1->upvotes = 1;
-        $this->answer1->downvotes = 2;
-        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = -1.
+        $this->answer2->upvotes = 1;
+        $this->answer2->downvotes = 2;
+        $this->answer2->votesdifference = $this->answer2->upvotes - $this->answer2->downvotes; // Vd = -1.
 
         // Answer3.
-        $this->answer1->upvotes = 3;
-        $this->answer1->downvotes = 2;
-        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = 1.
+        $this->answer3->upvotes = 3;
+        $this->answer3->downvotes = 2;
+        $this->answer3->votesdifference = $this->answer3->upvotes - $this->answer3->downvotes; // Vd = 1.
 
         // Answer4.
-        $this->answer1->upvotes = 5;
-        $this->answer1->downvotes = 0;
-        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = 5
+        $this->answer4->upvotes = 5;
+        $this->answer4->downvotes = 0;
+        $this->answer4->votesdifference = $this->answer4->upvotes - $this->answer4->downvotes; // Vd = 5
 
         // Answer5.
-        $this->answer1->upvotes = 0;
-        $this->answer1->downvotes = 2;
-        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = -2.
+        $this->answer5->upvotes = 0;
+        $this->answer5->downvotes = 2;
+        $this->answer5->votesdifference = $this->answer5->upvotes - $this->answer5->downvotes; // Vd = -2.
 
         // Answer6.
-        $this->answer1->upvotes = 4;
-        $this->answer1->downvotes = 2;
-        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = 2.
+        $this->answer6->upvotes = 4;
+        $this->answer6->downvotes = 2;
+        $this->answer6->votesdifference = $this->answer6->upvotes - $this->answer6->downvotes; // Vd = 2.
 
         // Rightorder = answer4 , answer6, answer3, answer1, answer2, answer5.
     }
 
+    private function create_twogroups($group1, $group2) { 
+        // Set the first 3 answers to the first group of rating.
+        switch ($group1) {
+            case 'sh':
+                $this->answer1->markedhelpful = 1;
+                $this->answer1->markedsolution = 1;
+                $this->answer2->markedhelpful = 1;
+                $this->answer2->markedsolution = 1;
+                $this->answer3->markedhelpful = 1;
+                $this->answer3->markedsolution = 1;
+                break;
+            case 's':
+                $this->answer1->markedhelpful = 0;
+                $this->answer1->markedsolution = 1;
+                $this->answer2->markedhelpful = 0;
+                $this->answer2->markedsolution = 1;
+                $this->answer3->markedhelpful = 0;
+                $this->answer3->markedsolution = 1;
+                break;
+            case 'h':
+                $this->answer1->markedhelpful = 1;
+                $this->answer1->markedsolution = 0;
+                $this->answer2->markedhelpful = 1;
+                $this->answer2->markedsolution = 0;
+                $this->answer3->markedhelpful = 1;
+                $this->answer3->markedsolution = 0;
+                break;
+            case 'o':
+                $this->answer1->markedhelpful = 0;
+                $this->answer1->markedsolution = 0;
+                $this->answer2->markedhelpful = 0;
+                $this->answer2->markedsolution = 0;
+                $this->answer3->markedhelpful = 0;
+                $this->answer3->markedsolution = 0;
+                break;
+        }
+
+        switch ($group2) {
+            case 'sh':
+                $this->answer4->markedhelpful = 1;
+                $this->answer4->markedsolution = 1;
+                $this->answer5->markedhelpful = 1;
+                $this->answer5->markedsolution = 1;
+                $this->answer6->markedhelpful = 1;
+                $this->answer6->markedsolution = 1;
+                break;
+            case 's':
+                $this->answer4->markedhelpful = 0;
+                $this->answer4->markedsolution = 1;
+                $this->answer5->markedhelpful = 0;
+                $this->answer5->markedsolution = 1;
+                $this->answer6->markedhelpful = 0;
+                $this->answer6->markedsolution = 1;
+                break;
+            case 'h':
+                $this->answer4->markedhelpful = 1;
+                $this->answer4->markedsolution = 0;
+                $this->answer5->markedhelpful = 1;
+                $this->answer5->markedsolution = 0;
+                $this->answer6->markedhelpful = 1;
+                $this->answer6->markedsolution = 0;
+                break;
+            case 'o':
+                $this->answer4->markedhelpful = 0;
+                $this->answer4->markedsolution = 0;
+                $this->answer5->markedhelpful = 0;
+                $this->answer5->markedsolution = 0;
+                $this->answer6->markedhelpful = 0;
+                $this->answer6->markedsolution = 0;
+                break;
+        }
+
+        // Now set the up and downvotes for every answer.
+        // Answer1.
+        $this->answer1->upvotes = 3;
+        $this->answer1->downvotes = 4;
+        $this->answer1->votesdifference = $this->answer1->upvotes - $this->answer1->downvotes; // Vd = -1.
+
+        // Answer2.
+        $this->answer2->upvotes = 4;
+        $this->answer2->downvotes = 1;
+        $this->answer2->votesdifference = $this->answer2->upvotes - $this->answer2->downvotes; // Vd = 3.
+
+        // Answer3.
+        $this->answer3->upvotes = 0;
+        $this->answer3->downvotes = 2;
+        $this->answer3->votesdifference = $this->answer3->upvotes - $this->answer3->downvotes; // Vd = -2.
+
+        // Answer4.
+        $this->answer4->upvotes = 5;
+        $this->answer4->downvotes = 5;
+        $this->answer4->votesdifference = $this->answer4->upvotes - $this->answer4->downvotes; // Vd = 0
+
+        // Answer5.
+        $this->answer5->upvotes = 6;
+        $this->answer5->downvotes = 5;
+        $this->answer5->votesdifference = $this->answer5->upvotes - $this->answer5->downvotes; // Vd = 1.
+
+        // Answer6.
+        $this->answer6->upvotes = 4;
+        $this->answer6->downvotes = 2;
+        $this->answer6->votesdifference = $this->answer6->upvotes - $this->answer6->downvotes; // Vd = 2.
+
+        
+        // The Rightorder depends now on the group parameter.
+        // Rightorder (sh,s) = answer2, answer1, answer3, answer6, answer5, answer4.
+        // Rightorder (sh,h) = answer2, answer1, answer3, answer6, answer5, answer4.
+        // Rightorder (sh,o) = answer2, answer1, answer3, answer6, answer5, answer4.
+        // Rightorder (s,h) = answer6, answer5, answer4, answer2, answer1, answer3. with ratingpreference = 0.
+        // Rightorder (s,h) = answer2, answer1, answer3, answer6, answer5, answer4. with ratingpreference = 1
+        // Rightorder (s,o) = answer2, answer1, answer3, answer6, answer5, answer4.
+        // Rightorder (h,o) = answer2, answer1, answer3, answer6, answer5, answer4. 
+
+    }
 }

From 30036238ea82f589f64de85f154e6eec5ae54f82 Mon Sep 17 00:00:00 2001
From: NinaHerrmann <nina.herrmann@uni-muenster.de>
Date: Mon, 22 May 2023 11:37:51 +0200
Subject: [PATCH 32/35] force encoding to ENT_COMPAT (default in php < 8.1)

---
 classes/output/moodleoverflow_email.php | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/classes/output/moodleoverflow_email.php b/classes/output/moodleoverflow_email.php
index fc83e1cf5c..b52d653be9 100644
--- a/classes/output/moodleoverflow_email.php
+++ b/classes/output/moodleoverflow_email.php
@@ -155,15 +155,15 @@ public function export_for_template(\renderer_base $renderer, $plaintext = false
     protected function export_for_template_text(\mod_moodleoverflow_renderer $renderer) {
 
         return array(
-            'id' => html_entity_decode($this->post->id),
-            'coursename' => html_entity_decode($this->get_coursename()),
-            'courselink' => html_entity_decode($this->get_courselink()),
-            'moodleoverflowname' => html_entity_decode($this->get_moodleoverflowname()),
-            'showdiscussionname' => html_entity_decode($this->has_showdiscussionname()),
-            'discussionname' => html_entity_decode($this->get_discussionname()),
-            'subject' => html_entity_decode($this->get_subject()),
-            'authorfullname' => html_entity_decode($this->get_author_fullname()),
-            'postdate' => html_entity_decode($this->get_postdate()),
+            'id' => html_entity_decode($this->post->id, ENT_COMPAT),
+            'coursename' => html_entity_decode($this->get_coursename(), ENT_COMPAT),
+            'courselink' => html_entity_decode($this->get_courselink(), ENT_COMPAT),
+            'moodleoverflowname' => html_entity_decode($this->get_moodleoverflowname(), ENT_COMPAT),
+            'showdiscussionname' => html_entity_decode($this->has_showdiscussionname(), ENT_COMPAT),
+            'discussionname' => html_entity_decode($this->get_discussionname(), ENT_COMPAT),
+            'subject' => html_entity_decode($this->get_subject(), ENT_COMPAT),
+            'authorfullname' => html_entity_decode($this->get_author_fullname(), ENT_COMPAT),
+            'postdate' => html_entity_decode($this->get_postdate(), ENT_COMPAT),
             'firstpost' => $this->is_firstpost(),
             'canreply' => $this->canreply,
             'permalink' => $this->get_permalink(),
@@ -179,7 +179,7 @@ protected function export_for_template_text(\mod_moodleoverflow_renderer $render
             'grouppicture' => $this->get_group_picture(),
 
             // Format some components according to the renderer.
-            'message' => html_entity_decode($renderer->format_message_text($this->cm, $this->post)),
+            'message' => html_entity_decode($renderer->format_message_text($this->cm, $this->post), ENT_COMPAT),
         );
     }
 

From fdfec0e64c0d9b66f58c1298250c274096f2d405 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 22 May 2023 12:43:54 +0200
Subject: [PATCH 33/35] code checking and cleaning

---
 classes/ratings.php                      |  8 ++-
 classes/task/send_daily_mail.php         |  1 -
 locallib.php                             |  3 +
 tests/behat/behat_mod_moodleoverflow.php |  9 ++-
 tests/dailymail_test.php                 |  4 ++
 tests/ratings_test.php                   | 82 ++++++++++++++++--------
 tests/userstats_test.php                 |  4 ++
 7 files changed, 79 insertions(+), 32 deletions(-)

diff --git a/classes/ratings.php b/classes/ratings.php
index 99fb8f2366..568489514a 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -350,7 +350,7 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
             $neworder[$post->id] = $post;
         }
 
-        // return now the sorted posts.
+        // Return now the sorted posts.
         return $neworder;
     }
 
@@ -813,6 +813,12 @@ public static function moodleoverflow_user_can_rate($post, $modulecontext, $user
             && $post->reviewed == 1;
     }
 
+    /**
+     * Sorts answerposts of a discussion with quicksort algorithm
+     * @param array $posts  the posts that are being sorted
+     * @param int   $low    the index from where the sorting begins
+     * @param int   $high   the index until the array is being sorted
+     */
     private static function moodleoverflow_quicksort_post_by_votes(array &$posts, $low, $high) {
         if ($low >= $high) {
             return;
diff --git a/classes/task/send_daily_mail.php b/classes/task/send_daily_mail.php
index e2c57a57e2..7c2fa1cda9 100644
--- a/classes/task/send_daily_mail.php
+++ b/classes/task/send_daily_mail.php
@@ -23,7 +23,6 @@
  */
 namespace mod_moodleoverflow\task;
 
-defined('MOODLE_INTERNAL') || die();
 /**
  * This task sends a daily mail of unread posts
  */
diff --git a/locallib.php b/locallib.php
index 71ff6052eb..ef1ec7a7b9 100644
--- a/locallib.php
+++ b/locallib.php
@@ -921,6 +921,7 @@ 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 multiplemarks (default: multiplemarks are not allowed)
  */
 function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post, $multiplemarks = false) {
     global $USER;
@@ -1118,6 +1119,7 @@ function moodleoverflow_get_all_discussion_posts($discussionid, $tracking, $modc
  * @param bool $iscomment
  * @param array $usermapping
  * @param int $level
+ * @param bool $multiplemarks setting of multiplemarks
  * @return void|null
  * @throws coding_exception
  * @throws dml_exception
@@ -1494,6 +1496,7 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co
  * @param array  $posts          Array of posts within the discussion
  * @param bool   $iscomment      Whether the current post is a comment
  * @param array $usermapping
+ * @param bool  $multiplemarks
  * @return string
  * @throws coding_exception
  * @throws dml_exception
diff --git a/tests/behat/behat_mod_moodleoverflow.php b/tests/behat/behat_mod_moodleoverflow.php
index 709dae2ef6..56983731d4 100644
--- a/tests/behat/behat_mod_moodleoverflow.php
+++ b/tests/behat/behat_mod_moodleoverflow.php
@@ -128,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<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should exist in the "(?P<element2_string>(?:[^"]|\\")*)" moodleoverflow discussion card$/
+     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should exist in the
+     *  "(?P<element2_string>(?:[^"]|\\")*)" moodleoverflow discussion card$/
      * @throws ElementNotFoundException Thrown by behat_base::find
      * @param string $element The locator of the specified selector
      * @param string $selectortype The selector type
@@ -149,7 +150,8 @@ public function should_exist_in_the_moodleoverflow_discussion_card($element, $se
     /**
      * Click on the element of the specified type which is located inside the second element.
      *
-     * @When /^I click on "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" in the "(?P<element2_string>(?:[^"]|\\")*)" moodleoverflow discussion card$/
+     * @When /^I click on "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" in the
+     *  "(?P<element2_string>(?:[^"]|\\")*)" moodleoverflow discussion card$/
      * @param string $element Element we look for
      * @param string $selectortype The type of what we look for
      * @param string $discussiontitle The discussion title
@@ -173,7 +175,8 @@ public function i_click_on_in_the_moodleoverflow_discussion_card($element, $sele
      *
      * This step is for advanced users, use it if you don't find anything else suitable for what you need.
      *
-     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should not exist in the "(?P<element2_string>(?:[^"]|\\")*)" moodleoverflow discussion card$/
+     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should not exist in the
+     *  "(?P<element2_string>(?:[^"]|\\")*)" moodleoverflow discussion card$/
      * @throws ExpectationException
      * @param string $element The locator of the specified selector
      * @param string $selectortype The selector type
diff --git a/tests/dailymail_test.php b/tests/dailymail_test.php
index 29fc74bdce..26447424b1 100644
--- a/tests/dailymail_test.php
+++ b/tests/dailymail_test.php
@@ -134,6 +134,7 @@ private function helper_run_send_mails() {
 
     /**
      * Test if the task send_daily_mail sends a mail to the user.
+     * @covers \send_daily_mail::execute
      */
     public function test_mail_delivery() {
 
@@ -151,6 +152,7 @@ public function test_mail_delivery() {
 
     /**
      * Test if the content of the mail matches the supposed content.
+     * @covers \send_daily_mail::execute
      */
     public function test_content_of_mail_delivery() {
 
@@ -185,6 +187,7 @@ public function test_content_of_mail_delivery() {
 
     /**
      * Test if the task does not send a mail when maildigest = 0
+     * @covers \send_daily_mail::execute
      */
     public function test_mail_not_send() {
         // Creat user with daily_mail = off.
@@ -200,6 +203,7 @@ public function test_mail_not_send() {
 
     /**
      * Test if database is updated after sending a mail
+     * @covers \send_daily_mail::execute
      */
     public function test_records_removed() {
         global $DB;
diff --git a/tests/ratings_test.php b/tests/ratings_test.php
index 062c538dd5..31de91a2fe 100644
--- a/tests/ratings_test.php
+++ b/tests/ratings_test.php
@@ -100,30 +100,33 @@ public function tearDown(): void {
         \mod_moodleoverflow\subscriptions::reset_discussion_cache();
     }
 
-    // Begin of test functions
+    // Begin of test functions.
 
     /**
-     * Tests function ratings::moodleoverflow_sort_answer_by_ratings 
+     * Tests function ratings::moodleoverflow_sort_answer_by_ratings
      * Test case: Every group of rating exists (helful and solved posts, only helpful/solved and none)
+     * @covers \ratings::moodleoverflow_sort_answer_by_ratings()
      */
     public function test_answersorting_everygroup() {
         // Create helpful, solved, up and downvotes ratings.
         $this->create_everygroup();
 
-        // Test with every group of rating
+        // Test with every group of rating.
 
         // Create a array of the posts, save the sorted post and compare them to the order that they should have.
         $posts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
         $this->set_ratingpreferences(0);
         $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
-        $rightorderposts = array($this->post, $this->answer1, $this->answer3, $this->answer2, $this->answer4, $this->answer6, $this->answer5);
+        $rightorderposts = array($this->post, $this->answer1, $this->answer3, $this->answer2,
+                                 $this->answer4, $this->answer6, $this->answer5);
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
 
         // Change the rating preference of the teacher and sort again.
         $this->set_ratingpreferences(1);
         $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
-        $rightorderposts = array($this->post, $this->answer1, $this->answer2, $this->answer3, $this->answer4, $this->answer6, $this->answer5);
+        $rightorderposts = array($this->post, $this->answer1, $this->answer2, $this->answer3,
+                                 $this->answer4, $this->answer6, $this->answer5);
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
     }
@@ -131,12 +134,13 @@ public function test_answersorting_everygroup() {
     /**
      * Tests function ratings::moodleoverflow_sort_answer_by_ratings
      * Test case: One group of rating does not exist
+     * @covers \ratings::moodleoverflow_sort_answer_by_ratings()
      */
     public function test_answersorting_threegroups() {
         // Create helpful, solved, up and downvotes ratings.
         $this->create_everygroup();
 
-        // Test without posts that are only marked as solved
+        // Test without posts that are only marked as solved.
         $posts = array($this->post, $this->answer1, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
         $this->set_ratingpreferences(0);
         $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
@@ -150,7 +154,7 @@ public function test_answersorting_threegroups() {
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
 
-        // Test without posts that are only marked as helpful
+        // Test without posts that are only marked as helpful.
         $posts = array($this->post, $this->answer1, $this->answer2, $this->answer4, $this->answer5, $this->answer6);
         $this->set_ratingpreferences(0);
         $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
@@ -164,7 +168,7 @@ public function test_answersorting_threegroups() {
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
 
-        // Test without posts that are marked as both helpful and marked
+        // Test without posts that are marked as both helpful and marked.
         $posts = array($this->post, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
         $this->set_ratingpreferences(0);
         $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
@@ -182,6 +186,7 @@ public function test_answersorting_threegroups() {
     /**
      * Tests function ratings::moodleoverflow_sort_answer_by_ratings
      * Test case: two groups of rating do not exist
+     * @covers \ratings::moodleoverflow_sort_answer_by_ratings()
      */
     public function test_answersorting_twogroups() {
         $this->set_ratingpreferences(0);
@@ -192,7 +197,8 @@ public function test_answersorting_twogroups() {
         $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);
+        $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);
 
@@ -202,7 +208,8 @@ public function test_answersorting_twogroups() {
         $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);
+        $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);
 
@@ -212,7 +219,8 @@ public function test_answersorting_twogroups() {
         $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);
+        $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);
 
@@ -223,7 +231,8 @@ public function test_answersorting_twogroups() {
         $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);
+        $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);
 
@@ -234,7 +243,8 @@ public function test_answersorting_twogroups() {
         $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);
+        $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);
 
@@ -244,7 +254,8 @@ public function test_answersorting_twogroups() {
         $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);
+        $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);
 
@@ -254,7 +265,8 @@ public function test_answersorting_twogroups() {
         $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);
+        $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);
     }
@@ -266,6 +278,7 @@ public function test_answersorting_twogroups() {
      * - helpful, or
      * - solved, or
      * - not marked
+     * @covers \ratings::moodleoverflow_sort_answer_by_ratings()
      */
     public function test_answersorting_onegroup() {
         $this->set_ratingpreferences(0);
@@ -275,7 +288,8 @@ public function test_answersorting_onegroup() {
         $this->create_onegroup($group);
         $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->answer4, $this->answer6, $this->answer3, $this->answer1, $this->answer2, $this->answer5);
+        $rightorderposts = array($this->post, $this->answer4, $this->answer6, $this->answer3,
+                                 $this->answer1, $this->answer2, $this->answer5);
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
 
@@ -284,7 +298,8 @@ public function test_answersorting_onegroup() {
         $this->create_onegroup($group);
         $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->answer4, $this->answer6, $this->answer3, $this->answer1, $this->answer2, $this->answer5);
+        $rightorderposts = array($this->post, $this->answer4, $this->answer6, $this->answer3,
+                                 $this->answer1, $this->answer2, $this->answer5);
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
 
@@ -293,7 +308,8 @@ public function test_answersorting_onegroup() {
         $this->create_onegroup($group);
         $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->answer4, $this->answer6, $this->answer3, $this->answer1, $this->answer2, $this->answer5);
+        $rightorderposts = array($this->post, $this->answer4, $this->answer6, $this->answer3,
+                                 $this->answer1, $this->answer2, $this->answer5);
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
 
@@ -302,12 +318,13 @@ public function test_answersorting_onegroup() {
         $this->create_onegroup($group);
         $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->answer4, $this->answer6, $this->answer3, $this->answer1, $this->answer2, $this->answer5);
+        $rightorderposts = array($this->post, $this->answer4, $this->answer6, $this->answer3,
+                                 $this->answer1, $this->answer2, $this->answer5);
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
     }
 
-    // Helper functions
+    // Helper functions.
 
     /**
      * This function creates:
@@ -333,7 +350,7 @@ private function helper_course_set_up() {
         $this->user2 = $this->getDataGenerator()->create_user(array('firstname' => 'Ethan', 'lastname' => 'Brown'));
         $this->getDataGenerator()->enrol_user($this->user2->id, $this->course->id, 'student');
 
-        // Create a discussion, a parent post and six answers
+        // Create a discussion, a parent post and six answers.
         $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), '*');
@@ -362,7 +379,7 @@ private function postsorderequal($sortedposts, $rightorderposts) {
             $sortedpost = current($sortedposts);
             $post = current($rightorderposts);
             if ($sortedpost->id == $post->id) {
-                // Go to the next elements
+                // Go to the next elements.
                 next($sortedposts);
                 next($rightorderposts);
             } else {
@@ -376,6 +393,7 @@ private function postsorderequal($sortedposts, $rightorderposts) {
      * sets the ratingpreferences to 1 or 0:
      * 1 = solved posts will be shown above helpful posts.
      * 0 = helpful posts will be shown above solved posts.
+     * @param int  $preference  the rating preference
      */
     private function set_ratingpreferences($preference) {
         if ($preference == 0 || $preference == 1) {
@@ -494,7 +512,7 @@ private function create_onegroup($group) {
         // Answer4.
         $this->answer4->upvotes = 5;
         $this->answer4->downvotes = 0;
-        $this->answer4->votesdifference = $this->answer4->upvotes - $this->answer4->downvotes; // Vd = 5
+        $this->answer4->votesdifference = $this->answer4->upvotes - $this->answer4->downvotes; // Vd = 5.
 
         // Answer5.
         $this->answer5->upvotes = 0;
@@ -509,7 +527,18 @@ private function create_onegroup($group) {
         // Rightorder = answer4 , answer6, answer3, answer1, answer2, answer5.
     }
 
-    private function create_twogroups($group1, $group2) { 
+    /**
+     * Creates ratings of the posts of the assigned groups in the discussion.
+     * Creates up and downvotes
+     * @param string $group1
+     * @param string $group2
+     * A Group can be:
+     * - both as solution and helpful marked posts (sh)
+     * - only solution posts (s)
+     * - only helpful (h)
+     * - no mark (o)
+     */
+    private function create_twogroups($group1, $group2) {
         // Set the first 3 answers to the first group of rating.
         switch ($group1) {
             case 'sh':
@@ -600,7 +629,7 @@ private function create_twogroups($group1, $group2) {
         // Answer4.
         $this->answer4->upvotes = 5;
         $this->answer4->downvotes = 5;
-        $this->answer4->votesdifference = $this->answer4->upvotes - $this->answer4->downvotes; // Vd = 0
+        $this->answer4->votesdifference = $this->answer4->upvotes - $this->answer4->downvotes; // Vd = 0.
 
         // Answer5.
         $this->answer5->upvotes = 6;
@@ -612,7 +641,6 @@ private function create_twogroups($group1, $group2) {
         $this->answer6->downvotes = 2;
         $this->answer6->votesdifference = $this->answer6->upvotes - $this->answer6->downvotes; // Vd = 2.
 
-        
         // The Rightorder depends now on the group parameter.
         // Rightorder (sh,s) = answer2, answer1, answer3, answer6, answer5, answer4.
         // Rightorder (sh,h) = answer2, answer1, answer3, answer6, answer5, answer4.
@@ -620,7 +648,7 @@ private function create_twogroups($group1, $group2) {
         // Rightorder (s,h) = answer6, answer5, answer4, answer2, answer1, answer3. with ratingpreference = 0.
         // Rightorder (s,h) = answer2, answer1, answer3, answer6, answer5, answer4. with ratingpreference = 1
         // Rightorder (s,o) = answer2, answer1, answer3, answer6, answer5, answer4.
-        // Rightorder (h,o) = answer2, answer1, answer3, answer6, answer5, answer4. 
+        // Rightorder (h,o) = answer2, answer1, answer3, answer6, answer5, answer4.
 
     }
 }
diff --git a/tests/userstats_test.php b/tests/userstats_test.php
index 01bb9862fb..9b26db5e56 100644
--- a/tests/userstats_test.php
+++ b/tests/userstats_test.php
@@ -99,6 +99,7 @@ public function tearDown(): void {
 
     /**
      * Test, if a upvote is being counted.
+     * @covers \userstats_table
      */
     public function test_upvote() {
         // Teacher upvotes the discussion and the answer of user2.
@@ -116,6 +117,7 @@ public function test_upvote() {
 
     /**
      * Test, if a downvote is being counted.
+     * @covers \userstats_table
      */
     public function test_downvote() {
         // Teacher downvotes the discussion and the answer of user1.
@@ -133,6 +135,7 @@ public function test_downvote() {
 
     /**
      * Test, if the activity is calculated correctly.
+     * @covers \userstats_table
      */
     public function test_activity() {
         // User1 will rates 3 times.
@@ -153,6 +156,7 @@ public function test_activity() {
     }
     /**
      * Test, if the reputation is calculated correctly.
+     * @covers \userstats_table
      */
     public function test_reputation() {
         // User1 creates some ratings for user2, Teacher creates some ratings for user2.

From d7cb0d8dd580b691fa1fd9aac2d3b46808c6be63 Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 22 May 2023 13:17:59 +0200
Subject: [PATCH 34/35] last changes on version number for release

---
 classes/ratings.php    | 4 ++--
 tests/ratings_test.php | 2 +-
 version.php            | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/classes/ratings.php b/classes/ratings.php
index 568489514a..b9da34156e 100644
--- a/classes/ratings.php
+++ b/classes/ratings.php
@@ -249,8 +249,8 @@ public static function moodleoverflow_sort_answers_by_ratings($posts) {
 
         // Sort the answer posts by ratings.
         // Build groups of different types of answers (Solved and helpful, only solved/helpful, other).
-        // statusteacher == 1 means the post is marked as solved.
-        // statusstarter == 1 means the post is marked as helpful.
+        // markedsolved == 1 means the post is marked as solved.
+        // markedhelpful == 1 means the post is marked as helpful.
         // If a group is complete, sort the group.
         $index = 1;
         $startsolvedandhelpful = 1;
diff --git a/tests/ratings_test.php b/tests/ratings_test.php
index 31de91a2fe..c7fb701f6e 100644
--- a/tests/ratings_test.php
+++ b/tests/ratings_test.php
@@ -168,7 +168,7 @@ public function test_answersorting_threegroups() {
         $result = $this->postsorderequal($sortedposts, $rightorderposts);
         $this->assertEquals(1, $result);
 
-        // Test without posts that are marked as both helpful and marked.
+        // Test without posts that are marked as both helpful and solved.
         $posts = array($this->post, $this->answer2, $this->answer3, $this->answer4, $this->answer5, $this->answer6);
         $this->set_ratingpreferences(0);
         $sortedposts = ratings::moodleoverflow_sort_answers_by_ratings($posts);
diff --git a/version.php b/version.php
index 7215a84622..b70eabf999 100644
--- a/version.php
+++ b/version.php
@@ -28,7 +28,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 $plugin->component = 'mod_moodleoverflow';
-$plugin->version = 2023050802;
+$plugin->version = 2023052200;
 $plugin->release = 'v4.2-r1';
 $plugin->requires = 2020061500; // Requires Moodle 3.9+.
 $plugin->maturity = MATURITY_STABLE;

From 8e761b39cb0984d6b5ed7261b2d0d733fd9c1c1a Mon Sep 17 00:00:00 2001
From: TamaroWalter <tamarowalter@yahoo.de>
Date: Mon, 22 May 2023 13:23:40 +0200
Subject: [PATCH 35/35] behat problems fixed

---
 tests/behat/behat_mod_moodleoverflow.php | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/tests/behat/behat_mod_moodleoverflow.php b/tests/behat/behat_mod_moodleoverflow.php
index 56983731d4..709dae2ef6 100644
--- a/tests/behat/behat_mod_moodleoverflow.php
+++ b/tests/behat/behat_mod_moodleoverflow.php
@@ -128,8 +128,7 @@ 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<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should exist in the
-     *  "(?P<element2_string>(?:[^"]|\\")*)" moodleoverflow discussion card$/
+     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should exist in the "(?P<element2_string>(?:[^"]|\\")*)" moodleoverflow discussion card$/
      * @throws ElementNotFoundException Thrown by behat_base::find
      * @param string $element The locator of the specified selector
      * @param string $selectortype The selector type
@@ -150,8 +149,7 @@ public function should_exist_in_the_moodleoverflow_discussion_card($element, $se
     /**
      * Click on the element of the specified type which is located inside the second element.
      *
-     * @When /^I click on "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" in the
-     *  "(?P<element2_string>(?:[^"]|\\")*)" moodleoverflow discussion card$/
+     * @When /^I click on "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" in the "(?P<element2_string>(?:[^"]|\\")*)" moodleoverflow discussion card$/
      * @param string $element Element we look for
      * @param string $selectortype The type of what we look for
      * @param string $discussiontitle The discussion title
@@ -175,8 +173,7 @@ public function i_click_on_in_the_moodleoverflow_discussion_card($element, $sele
      *
      * This step is for advanced users, use it if you don't find anything else suitable for what you need.
      *
-     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should not exist in the
-     *  "(?P<element2_string>(?:[^"]|\\")*)" moodleoverflow discussion card$/
+     * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should not exist in the "(?P<element2_string>(?:[^"]|\\")*)" moodleoverflow discussion card$/
      * @throws ExpectationException
      * @param string $element The locator of the specified selector
      * @param string $selectortype The selector type