From 92b57213202f605d31cb6a41268680dcd4844aeb Mon Sep 17 00:00:00 2001 From: Justus Dieckmann Date: Sun, 10 Jul 2022 18:47:19 +0200 Subject: [PATCH 01/15] Create groundwork for reviewable posts --- classes/readtracking.php | 8 ++- db/access.php | 11 ++++ db/install.xml | 2 + db/upgrade.php | 24 ++++++++ lib.php | 5 +- locallib.php | 123 ++++++++++++++++++++++++--------------- markposts.php | 2 +- post.php | 2 +- version.php | 2 +- 9 files changed, 124 insertions(+), 55 deletions(-) diff --git a/classes/readtracking.php b/classes/readtracking.php index 44b65db2da3..3cc6ba61a98 100644 --- a/classes/readtracking.php +++ b/classes/readtracking.php @@ -24,6 +24,7 @@ namespace mod_moodleoverflow; +use context_module; use moodle_exception; /** @@ -136,7 +137,7 @@ public static function moodleoverflow_mark_moodleoverflow_read($cm, $userid = nu foreach ($discussions as $discussionid => $amount) { // Mark the discussion as read. - if (!self::moodleoverflow_mark_discussion_read($discussionid, $userid)) { + if (!self::moodleoverflow_mark_discussion_read($discussionid, context_module::instance($cm->id), $userid)) { throw new moodle_exception('markreadfailed', 'moodleoverflow'); return false; @@ -150,13 +151,14 @@ public static function moodleoverflow_mark_moodleoverflow_read($cm, $userid = nu * Marks a specific discussion as read by a specific user. * * @param int $discussionid + * @param context_module $modcontext * @param null $userid */ - public static function moodleoverflow_mark_discussion_read($discussionid, $userid = null) { + public static function moodleoverflow_mark_discussion_read($discussionid, $modcontext, $userid = null) { global $USER; // Get all posts. - $posts = moodleoverflow_get_all_discussion_posts($discussionid, true); + $posts = moodleoverflow_get_all_discussion_posts($discussionid, true, $modcontext); // If no user is submitted, use the current one. if (!isset($userid)) { diff --git a/db/access.php b/db/access.php index 16f3ca34f9a..17259912da9 100644 --- a/db/access.php +++ b/db/access.php @@ -234,4 +234,15 @@ 'clonepermissionsfrom' => 'mod/forum:createattachment' ), + 'mod/moodleoverflow:reviewpost' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array( + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ), + 'clonepermissionsfrom' => 'moodle/course:manageactivities' + ), + ); diff --git a/db/install.xml b/db/install.xml index 31ba26467c5..0486317c060 100644 --- a/db/install.xml +++ b/db/install.xml @@ -26,6 +26,7 @@ + @@ -67,6 +68,7 @@ + diff --git a/db/upgrade.php b/db/upgrade.php index f8aac2eec32..2dbb3d62cc5 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -195,5 +195,29 @@ function xmldb_moodleoverflow_upgrade($oldversion) { upgrade_mod_savepoint(true, 2021111700, 'moodleoverflow'); } + if ($oldversion < 2022070600) { + + // Define field needsreview to be added to moodleoverflow. + $table = new xmldb_table('moodleoverflow'); + $field = new xmldb_field('needsreview', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'anonymous'); + + // Conditionally launch add field needsreview. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Define field reviewed to be added to moodleoverflow_posts. + $table = new xmldb_table('moodleoverflow_posts'); + $field = new xmldb_field('reviewed', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'mailed'); + + // Conditionally launch add field reviewed. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Moodleoverflow savepoint reached. + upgrade_mod_savepoint(true, 2022070600, 'moodleoverflow'); + } + return true; } diff --git a/lib.php b/lib.php index d29fa21dae4..00c5de957d6 100644 --- a/lib.php +++ b/lib.php @@ -1006,6 +1006,7 @@ function moodleoverflow_get_unmailed_posts($starttime, $endtime) { // Set params for the sql query. $params = array(); $params['mailed'] = MOODLEOVERFLOW_MAILED_PENDING; + $params['reviewed'] = 1; $params['ptimestart'] = $starttime; $params['ptimeend'] = $endtime; @@ -1013,7 +1014,7 @@ function moodleoverflow_get_unmailed_posts($starttime, $endtime) { $sql = "SELECT p.*, d.course, d.moodleoverflow FROM {moodleoverflow_posts} p JOIN {moodleoverflow_discussions} d ON d.id = p.discussion - WHERE p.mailed = :mailed AND p.created >= :ptimestart AND p.created < :ptimeend + WHERE p.mailed = :mailed AND p.created >= :ptimestart AND p.created < :ptimeend AND p.reviewed = :reviewed ORDER BY p.modified ASC"; return $DB->get_records_sql($sql, $params); @@ -1039,6 +1040,8 @@ function moodleoverflow_mark_old_posts_as_mailed($endtime) { $params['endtime'] = $endtime; $params['mailedpending'] = MOODLEOVERFLOW_MAILED_PENDING; + // TODO do something about this. Add field timereviewed to post? / Make reviewed contain either null or timestamp? + // Define the sql query. $sql = "UPDATE {moodleoverflow_posts} SET mailed = :mailedsuccess diff --git a/locallib.php b/locallib.php index ecd379ea57a..8852e9a78a3 100644 --- a/locallib.php +++ b/locallib.php @@ -43,8 +43,6 @@ function moodleoverflow_get_discussions($cm, $page = -1, $perpage = 0) { global $DB, $CFG; - $params = array($cm->instance); - // User must have the permission to view the discussions. $modcontext = context_module::instance($cm->id); if (!has_capability('mod/moodleoverflow:viewdiscussion', $modcontext)) { @@ -69,7 +67,7 @@ function moodleoverflow_get_discussions($cm, $page = -1, $perpage = 0) { } else { $allnames = get_all_user_name_fields(true, 'u'); } - $postdata = 'p.id, p.modified, p.discussion, p.userid'; + $postdata = 'p.id, p.modified, p.discussion, p.userid, p.reviewed'; $discussiondata = 'd.name, d.timemodified, d.timestart, d.usermodified'; $userdata = 'u.email, u.picture, u.imagealt'; @@ -81,15 +79,23 @@ function moodleoverflow_get_discussions($cm, $page = -1, $perpage = 0) { $usermodifiedfields = get_all_user_name_fields(true, 'um', null, 'um') . ', um.email AS umemail, um.picture AS umpicture, um.imagealt AS umimagealt'; } - $usermodifiedtable = " LEFT JOIN {user} um ON (d.usermodified = um.id)"; + + $params = [$cm->instance]; + $whereconditions = ['d.moodleoverflow = ?', 'p.parent = 0']; + + if (!has_capability('mod/moodleoverflow:reviewpost', $modcontext)) { + $whereconditions[] = 'p.reviewed = 1'; + } + + $wheresql = join(' AND ', $whereconditions); // Retrieve and return all discussions from the database. $sql = "SELECT $postdata, $discussiondata, $allnames, $userdata, $usermodifiedfields FROM {moodleoverflow_discussions} d JOIN {moodleoverflow_posts} p ON p.discussion = d.id LEFT JOIN {user} u ON p.userid = u.id - $usermodifiedtable - WHERE d.moodleoverflow = ? AND p.parent = 0 + LEFT JOIN {user} um ON d.usermodified = um.id + WHERE $wheresql ORDER BY d.timestart DESC, d.id DESC"; return $DB->get_records_sql($sql, $params, $limitfrom, $limitamount); @@ -426,12 +432,21 @@ function moodleoverflow_user_can_post_discussion($moodleoverflow, $cm = null, $c function moodleoverflow_get_discussions_count($cm) { global $DB; - $params = array($cm->instance); + $modcontext = context_module::instance($cm->id); + + $params = [$cm->instance]; + $whereconditions = ['d.moodleoverflow = ?', 'p.parent = 0']; + + if (!has_capability('mod/moodleoverflow:reviewpost', $modcontext)) { + $whereconditions[] = 'p.reviewed = 1'; + } + + $wheresql = join(' AND ', $whereconditions); $sql = 'SELECT COUNT(d.id) FROM {moodleoverflow_discussions} d JOIN {moodleoverflow_posts} p ON p.discussion = d.id - WHERE d.moodleoverflow = ? AND p.parent = 0'; + WHERE ' . $wheresql; return $DB->get_field_sql($sql, $params); } @@ -447,29 +462,39 @@ function moodleoverflow_get_discussions_unread($cm) { global $DB, $USER; // Get the current timestamp and the oldpost-timestamp. - $params = array(); $now = round(time(), -2); $cutoffdate = $now - (get_config('moodleoverflow', 'oldpostdays') * 24 * 60 * 60); + $modcontext = context_module::instance($cm->id); + + $whereconditions = ['d.moodleoverflow = :instance', 'p.modified >= :cutoffdate', 'r.id is NULL']; + $params = [ + 'userid' => $USER->id, + 'instance' => $cm->instance, + 'cutoffdate' => $cutoffdate + ]; + + if (!has_capability('mod/moodleoverflow:reviewpost', $modcontext)) { + $whereconditions[] = 'p.reviewed = 1'; + } + + $wheresql = join(' AND ', $whereconditions); + // Define the sql-query. $sql = "SELECT d.id, COUNT(p.id) AS unread - FROM {moodleoverflow_discussions} d - JOIN {moodleoverflow_posts} p ON p.discussion = d.id - LEFT JOIN {moodleoverflow_read} r ON (r.postid = p.id AND r.userid = :userid) - WHERE d.moodleoverflow = :instance - AND p.modified >= :cutoffdate AND r.id is NULL - GROUP BY d.id"; - $params['userid'] = $USER->id; - $params['instance'] = $cm->instance; - $params['cutoffdate'] = $cutoffdate; + FROM {moodleoverflow_discussions} d + JOIN {moodleoverflow_posts} p ON p.discussion = d.id + LEFT JOIN {moodleoverflow_read} r ON (r.postid = p.id AND r.userid = :userid) + WHERE $wheresql + GROUP BY d.id"; // Return the unread messages as an array. if ($unreads = $DB->get_records_sql($sql, $params)) { + $returnarray = []; foreach ($unreads as $unread) { - $unreads[$unread->id] = $unread->unread; + $returnarray[$unread->id] = $unread->unread; } - - return $unreads; + return $returnarray; } else { // If there are no unread messages, return an empty array. @@ -516,11 +541,10 @@ function moodleoverflow_get_post_full($postid) { * @param object $discussion * @param object $post * @param object $cm - * @param null $user * * @return bool */ -function moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $cm, $user = null) { +function moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $cm) { global $USER, $DB; // Retrieve the modulecontext. @@ -570,7 +594,7 @@ function moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $ // Check if the user can view the discussion. $canviewdiscussion = !empty($cm->cache->caps['mod/moodleoverflow:viewdiscussion']) || - has_capability('mod/moodleoverflow:viewdiscussion', $modulecontext, $user->id); + has_capability('mod/moodleoverflow:viewdiscussion', $modulecontext, $user); if (!$canviewdiscussion && !has_all_capabilities(array('moodle/user:viewdetails', 'moodle/user:readuserposts'), context_user::instance($post->userid)) @@ -578,6 +602,10 @@ function moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $ return false; } + if ($post->reviewed == 0 && !has_capability('mod/moodleoverflow:reviewpost', $modulecontext, $user)) { + return false; + } + // Check the coursemodule settings. if (isset($cm->uservisible)) { if (!$cm->uservisible) { @@ -681,6 +709,8 @@ function moodleoverflow_add_discussion($discussion, $modulecontext, $userid = nu $post->moodleoverflow = $moodleoverflow->id; $post->course = $moodleoverflow->course; + // TODO set reviewed = 0 if moodleoverflow is reviewable + // Submit the post to the database and get its id. $post->id = $DB->insert_record('moodleoverflow_posts', $post); @@ -823,7 +853,7 @@ function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discuss $istracked = \mod_moodleoverflow\readtracking::moodleoverflow_is_tracked($moodleoverflow); // Retrieve all posts of the discussion. - $posts = moodleoverflow_get_all_discussion_posts($discussion->id, $istracked); + $posts = moodleoverflow_get_all_discussion_posts($discussion->id, $istracked, $modulecontext); $usermapping = anonymous::get_userid_mapping($moodleoverflow, $discussion->id); @@ -883,10 +913,11 @@ function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discuss * * @param int $discussionid The ID of the discussion * @param boolean $tracking Whether tracking is activated + * @param context_module $modcontext Context of the module * * @return array */ -function moodleoverflow_get_all_discussion_posts($discussionid, $tracking) { +function moodleoverflow_get_all_discussion_posts($discussionid, $tracking, $modcontext) { global $DB, $USER, $CFG; // Initiate tracking settings. @@ -909,6 +940,12 @@ function moodleoverflow_get_all_discussion_posts($discussionid, $tracking) { $allnames = get_all_user_name_fields(true, 'u'); } + $additionalwhere = ''; + + if (!has_capability('mod/moodleoverflow:reviewpost', $modcontext)) { + $additionalwhere = ' AND p.reviewed = 1 '; + } + // Create the sql array. $sql = "SELECT p.*, m.ratingpreference, $allnames, d.name as subject, u.email, u.picture, u.imagealt $trackingselector FROM {moodleoverflow_posts} p @@ -916,7 +953,7 @@ function moodleoverflow_get_all_discussion_posts($discussionid, $tracking) { LEFT JOIN {moodleoverflow_discussions} d ON d.id = p.discussion LEFT JOIN {moodleoverflow} m on m.id = d.moodleoverflow $trackingjoin - WHERE p.discussion = :discussion + WHERE p.discussion = :discussion $additionalwhere ORDER BY p.created ASC"; $params['discussion'] = $discussionid; @@ -1535,6 +1572,8 @@ function moodleoverflow_add_new_post($post) { $post->totalscore = 0; } + // TODO add reiview = 0 if enabled on moodleoverflow module. + // Add the post to the database. $post->id = $DB->insert_record('moodleoverflow_posts', $post); $DB->set_field('moodleoverflow_posts', 'message', $post->message, array('id' => $post->id)); @@ -1616,35 +1655,22 @@ function moodleoverflow_update_post($newpost) { /** * Count all replies of a post. * - * @param object $post The post object - * @param bool $recursive Whether the deletion should be recursive + * @param object $post The post object + * @param bool $onlyreviewed Whether to count only reviewed posts. * * @return int Amount of replies */ -function moodleoverflow_count_replies($post, $recursive = null) { +function moodleoverflow_count_replies($post, $onlyreviewed) { global $DB; - // Initiate the variable. - $count = 0; - - // Count the posts recursively? - if (isset($recursive)) { - // Get all the direct children. - if ($childposts = $DB->get_records('moodleoverflow_posts', array('parent' => $post->id))) { + $conditions = ['parent' => $post->id]; - // And count their children as well. - foreach ($childposts as $childpost) { - $count++; - $count += moodleoverflow_count_replies($childpost, true); - } - } - } else { - // Just count the direct children. - $count += $DB->count_records('moodleoverflow_posts', array('parent' => $post->id)); + if ($onlyreviewed) { + $conditions['reviewed'] = '1'; } // Return the amount of replies. - return $count; + return $DB->count_records('moodleoverflow_posts', $conditions); } /** @@ -1773,10 +1799,11 @@ function moodleoverflow_discussion_update_last_post($discussionid) { return false; } - // Find the last post of the discussion. + // Find the last reviewed post of the discussion. (even if user has review capability, because it is written to DB) $sql = "SELECT id, userid, modified FROM {moodleoverflow_posts} WHERE discussion = ? + AND reviewed = 1 ORDER BY modified DESC"; // Find the new last post of the discussion. diff --git a/markposts.php b/markposts.php index 00f7889d7fb..9c1ba8b1d1e 100644 --- a/markposts.php +++ b/markposts.php @@ -106,7 +106,7 @@ } // Mark all the discussions read. - if (!\mod_moodleoverflow\readtracking::moodleoverflow_mark_discussion_read($discussionid, $user->id)) { + if (!\mod_moodleoverflow\readtracking::moodleoverflow_mark_discussion_read($discussionid, context_module::instance($cm->id), $user->id)) { // Display an error, if something failes. $message = get_string('markreadfailed', 'moodleoverflow'); diff --git a/post.php b/post.php index 606c5b45d1c..283b008f732 100644 --- a/post.php +++ b/post.php @@ -360,7 +360,7 @@ } // Count all replies of this post. - $replycount = moodleoverflow_count_replies($post); + $replycount = moodleoverflow_count_replies($post, has_capability('mod/moodleoverflow:reviewpost', $modulecontext)); // Has the user confirmed the deletion? if (!empty($confirm) AND confirm_sesskey()) { diff --git a/version.php b/version.php index b9d08476254..75f1fcc5150 100644 --- a/version.php +++ b/version.php @@ -28,7 +28,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'mod_moodleoverflow'; -$plugin->version = 2022041801; +$plugin->version = 2022070600; $plugin->release = 'v4.0-r2'; $plugin->requires = 2020061500; // Requires Moodle 3.9+. $plugin->maturity = MATURITY_STABLE; From 952f7b5a4cd46a421a92d41da886d31205385b40 Mon Sep 17 00:00:00 2001 From: Justus Dieckmann Date: Sun, 17 Jul 2022 22:03:00 +0200 Subject: [PATCH 02/15] Roughly working reviews (Approve + Reject without mail sending) --- amd/build/functions.min.js | 4 +- amd/build/functions.min.js.map | 2 +- amd/build/reviewing.min.js | 10 ++ amd/build/reviewing.min.js.map | 1 + amd/src/functions.js | 4 +- amd/src/reviewing.js | 101 ++++++++++++++++ classes/readtracking.php | 80 +------------ classes/review.php | 118 +++++++++++++++++++ db/services.php | 18 ++- discussion.php | 6 + externallib.php | 91 +++++++++++++++ lang/en/moodleoverflow.php | 21 ++++ lib.php | 13 ++- locallib.php | 106 +++++++++++------ mod_form.php | 13 +++ post.php | 8 +- renderer.php | 2 +- settings.php | 4 + styles.css | 115 +++--------------- templates/answer.mustache | 169 +++++++++++++++------------ templates/comment.mustache | 123 ++++++++++--------- templates/discussion_list.mustache | 67 ++++++----- templates/question.mustache | 102 ---------------- templates/reject_email_html.mustache | 88 ++++++++++++++ templates/reject_post_form.mustache | 40 +++++++ templates/review_buttons.mustache | 35 ++++++ version.php | 2 +- view.php | 2 + 28 files changed, 858 insertions(+), 487 deletions(-) create mode 100644 amd/build/reviewing.min.js create mode 100644 amd/build/reviewing.min.js.map create mode 100644 amd/src/reviewing.js create mode 100644 classes/review.php delete mode 100644 templates/question.mustache create mode 100644 templates/reject_email_html.mustache create mode 100644 templates/reject_post_form.mustache create mode 100644 templates/review_buttons.mustache diff --git a/amd/build/functions.min.js b/amd/build/functions.min.js index f7e8d3db86b..8adfcc8dcb7 100644 --- a/amd/build/functions.min.js +++ b/amd/build/functions.min.js @@ -1,10 +1,10 @@ /** * Ajax functions for moodleoverflow * - * @module mod/moodleoverflow + * @module mod_moodleoverflow/functions * @copyright 2017 Tamara Gunkel * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("mod_moodleoverflow/functions",["jquery","core/ajax","core/templates","core/notification","core/config","core/url","core/str"],(function($,ajax,templates,notification,Cfg,Url,str){var t={recordvote:function(discussionid,ratingid,userid,event){var postid=$(event.target).closest(".moodleoverflowpost").prev().attr("id");postid=postid.substring(1);var vote=ajax.call([{methodname:"mod_moodleoverflow_record_vote",args:{discussionid:discussionid,postid:postid,ratingid:ratingid,sesskey:Cfg.sesskey}}]);return vote[0].done((function(response){var parentdiv=$(event.target).parent().parent();2===ratingid?(parentdiv.children("a:first-of-type").children().attr("src",Url.imageUrl("vote/upvoted","moodleoverflow")),parentdiv.children("a:nth-of-type(2)").children().attr("src",Url.imageUrl("vote/downvote","moodleoverflow"))):1===ratingid?(parentdiv.children("a:first-of-type").children().attr("src",Url.imageUrl("vote/upvote","moodleoverflow")),parentdiv.children("a:nth-of-type(2)").children().attr("src",Url.imageUrl("vote/downvoted","moodleoverflow"))):(parentdiv.children("a:first-of-type").children().attr("src",Url.imageUrl("vote/upvote","moodleoverflow")),parentdiv.children("a:nth-of-type(2)").children().attr("src",Url.imageUrl("vote/downvote","moodleoverflow"))),parentdiv.children("p").text(response.postrating),templates.replaceNode($(".user-details,.author").find('a[href*="id='+userid+'"]').siblings("span"),""+response.raterreputation+"",""),response.ownerid&&userid!==response.ownerid&&templates.replaceNode($(".user-details,.author").find('a[href*="id='+response.ownerid+'"]').siblings("span"),""+response.ownerreputation+"","")})).fail(notification.exception),vote},clickevent:function(discussionid,userid){$(".upvote").on("click",(function(event){$(event.target).is("a")&&(event.target=$(event.target).children()),$(event.target).parent().attr("class").indexOf("active")>=0?t.recordvote(discussionid,20,userid,event):t.recordvote(discussionid,2,userid,event),$(event.target).parent().toggleClass("active"),$(event.target).parent().nextAll("a").removeClass("active")})),$(".downvote").on("click",(function(event){$(event.target).is("a")&&(event.target=$(event.target).children()),$(event.target).parent().attr("class").indexOf("active")>=0?t.recordvote(discussionid,10,userid,event):t.recordvote(discussionid,1,userid,event),$(event.target).parent().toggleClass("active"),$(event.target).parent().prevAll("a").removeClass("active")})),$(".marksolved").on("click",(function(event){var post=$(event.target).parents(".moodleoverflowpost");post.hasClass("statusteacher")||post.hasClass("statusboth")?t.recordvote(discussionid,30,userid,event)[0].then((function(){t.removeSolvedFromPost(post)})):t.recordvote(discussionid,3,userid,event)[0].then((function(){t.removeOtherSolved(post.parent().parent()),post.hasClass("statusstarter")?(post.removeClass("statusstarter"),post.addClass("statusboth")):post.addClass("statusteacher");var promiseStringNotSolved=str.get_string("marknotsolved","mod_moodleoverflow");$.when(promiseStringNotSolved).done((function(string){$(event.target).text(string)})),t.redoStatus(post)}))})),$(".markhelpful").on("click",(function(event){var post=$(event.target).parents(".moodleoverflowpost");post.hasClass("statusstarter")||post.hasClass("statusboth")?t.recordvote(discussionid,40,userid,event)[0].then((function(){t.removeHelpfulFromPost(post)})):t.recordvote(discussionid,4,userid,event)[0].then((function(){t.removeOtherHelpful(post.parent().parent()),post.hasClass("statusteacher")?(post.removeClass("statusteacher"),post.addClass("statusboth")):post.addClass("statusstarter");var promiseStringNotHelpful=str.get_string("marknothelpful","mod_moodleoverflow");$.when(promiseStringNotHelpful).done((function(string){$(event.target).text(string)})),t.redoStatus(post)}))}))},removeHelpfulFromPost:function(post){post.hasClass("statusstarter")?post.removeClass("statusstarter"):(post.removeClass("statusboth"),post.addClass("statusteacher")),t.redoStatus(post);var promiseHelpful=str.get_string("markhelpful","mod_moodleoverflow");$.when(promiseHelpful).done((function(string){post.find(".markhelpful").text(string)}))},removeOtherHelpful:function(root){var formerhelpful=root.find(".statusstarter, .statusboth");formerhelpful.length>0&&t.removeHelpfulFromPost(formerhelpful)},removeSolvedFromPost:function(post){post.hasClass("statusteacher")?post.removeClass("statusteacher"):(post.removeClass("statusboth"),post.addClass("statusstarter")),t.redoStatus(post);var promiseHelpful=str.get_string("marksolved","mod_moodleoverflow");$.when(promiseHelpful).done((function(string){post.find(".marksolved").text(string)}))},removeOtherSolved:function(root){var formersolution=root.find(".statusteacher, .statusboth");formersolution.length>0&&t.removeSolvedFromPost(formersolution)},redoStatus:function(post){if($(post).hasClass("statusboth")){str.get_strings([{key:"teacherrating",component:"mod_moodleoverflow"},{key:"starterrating",component:"mod_moodleoverflow"},{key:"bestanswer",component:"mod_moodleoverflow"}]).then((function(results){var circle=templates.renderPix("status/c_circle","mod_moodleoverflow",results[0]),box=templates.renderPix("status/b_box","mod_moodleoverflow",results[1]);return $.when(box,circle).done((function(boxImg,circleImg){screen.width>600?post.find(".status").html(boxImg+circleImg+results[2]):post.find(".status").html(boxImg+circleImg)})),results}))}else if($(post).hasClass("statusteacher")){str.get_strings([{key:"teacherrating",component:"mod_moodleoverflow"},{key:"solvedanswer",component:"mod_moodleoverflow"}]).then((function(results){var circle=templates.renderPix("status/c_outline","mod_moodleoverflow",results[0]);return $.when(circle).done((function(circleImg){screen.width>600?post.find(".status").html(circleImg+results[1]):post.find(".status").html(circleImg)})),results}))}else if($(post).hasClass("statusstarter")){str.get_strings([{key:"starterrating",component:"mod_moodleoverflow"},{key:"helpfulanswer",component:"mod_moodleoverflow"}]).then((function(results){var box=templates.renderPix("status/b_outline","mod_moodleoverflow",results[0]);return $.when(box).done((function(boxImg){screen.width>600?post.find(".status").html(boxImg+results[1]):post.find(".status").html(boxImg)})),results}))}else post.find(".status").html("")}};return t})); +define("mod_moodleoverflow/functions",["jquery","core/ajax","core/templates","core/notification","core/config","core/url","core/str"],(function($,ajax,templates,notification,Cfg,Url,str){var t={recordvote:function(discussionid,ratingid,userid,event){var postid=$(event.target).closest(".moodleoverflowpost").attr("id");postid=postid.substring(1);var vote=ajax.call([{methodname:"mod_moodleoverflow_record_vote",args:{discussionid:discussionid,postid:postid,ratingid:ratingid,sesskey:Cfg.sesskey}}]);return vote[0].done((function(response){var parentdiv=$(event.target).parent().parent();2===ratingid?(parentdiv.children("a:first-of-type").children().attr("src",Url.imageUrl("vote/upvoted","moodleoverflow")),parentdiv.children("a:nth-of-type(2)").children().attr("src",Url.imageUrl("vote/downvote","moodleoverflow"))):1===ratingid?(parentdiv.children("a:first-of-type").children().attr("src",Url.imageUrl("vote/upvote","moodleoverflow")),parentdiv.children("a:nth-of-type(2)").children().attr("src",Url.imageUrl("vote/downvoted","moodleoverflow"))):(parentdiv.children("a:first-of-type").children().attr("src",Url.imageUrl("vote/upvote","moodleoverflow")),parentdiv.children("a:nth-of-type(2)").children().attr("src",Url.imageUrl("vote/downvote","moodleoverflow"))),parentdiv.children("p").text(response.postrating),templates.replaceNode($(".user-details,.author").find('a[href*="id='+userid+'"]').siblings("span"),""+response.raterreputation+"",""),response.ownerid&&userid!==response.ownerid&&templates.replaceNode($(".user-details,.author").find('a[href*="id='+response.ownerid+'"]').siblings("span"),""+response.ownerreputation+"","")})).fail(notification.exception),vote},clickevent:function(discussionid,userid){$(".upvote").on("click",(function(event){$(event.target).is("a")&&(event.target=$(event.target).children()),$(event.target).parent().attr("class").indexOf("active")>=0?t.recordvote(discussionid,20,userid,event):t.recordvote(discussionid,2,userid,event),$(event.target).parent().toggleClass("active"),$(event.target).parent().nextAll("a").removeClass("active")})),$(".downvote").on("click",(function(event){$(event.target).is("a")&&(event.target=$(event.target).children()),$(event.target).parent().attr("class").indexOf("active")>=0?t.recordvote(discussionid,10,userid,event):t.recordvote(discussionid,1,userid,event),$(event.target).parent().toggleClass("active"),$(event.target).parent().prevAll("a").removeClass("active")})),$(".marksolved").on("click",(function(event){var post=$(event.target).parents(".moodleoverflowpost");post.hasClass("statusteacher")||post.hasClass("statusboth")?t.recordvote(discussionid,30,userid,event)[0].then((function(){t.removeSolvedFromPost(post)})):t.recordvote(discussionid,3,userid,event)[0].then((function(){t.removeOtherSolved(post.parent().parent()),post.hasClass("statusstarter")?(post.removeClass("statusstarter"),post.addClass("statusboth")):post.addClass("statusteacher");var promiseStringNotSolved=str.get_string("marknotsolved","mod_moodleoverflow");$.when(promiseStringNotSolved).done((function(string){$(event.target).text(string)})),t.redoStatus(post)}))})),$(".markhelpful").on("click",(function(event){var post=$(event.target).parents(".moodleoverflowpost");post.hasClass("statusstarter")||post.hasClass("statusboth")?t.recordvote(discussionid,40,userid,event)[0].then((function(){t.removeHelpfulFromPost(post)})):t.recordvote(discussionid,4,userid,event)[0].then((function(){t.removeOtherHelpful(post.parent().parent()),post.hasClass("statusteacher")?(post.removeClass("statusteacher"),post.addClass("statusboth")):post.addClass("statusstarter");var promiseStringNotHelpful=str.get_string("marknothelpful","mod_moodleoverflow");$.when(promiseStringNotHelpful).done((function(string){$(event.target).text(string)})),t.redoStatus(post)}))}))},removeHelpfulFromPost:function(post){post.hasClass("statusstarter")?post.removeClass("statusstarter"):(post.removeClass("statusboth"),post.addClass("statusteacher")),t.redoStatus(post);var promiseHelpful=str.get_string("markhelpful","mod_moodleoverflow");$.when(promiseHelpful).done((function(string){post.find(".markhelpful").text(string)}))},removeOtherHelpful:function(root){var formerhelpful=root.find(".statusstarter, .statusboth");formerhelpful.length>0&&t.removeHelpfulFromPost(formerhelpful)},removeSolvedFromPost:function(post){post.hasClass("statusteacher")?post.removeClass("statusteacher"):(post.removeClass("statusboth"),post.addClass("statusstarter")),t.redoStatus(post);var promiseHelpful=str.get_string("marksolved","mod_moodleoverflow");$.when(promiseHelpful).done((function(string){post.find(".marksolved").text(string)}))},removeOtherSolved:function(root){var formersolution=root.find(".statusteacher, .statusboth");formersolution.length>0&&t.removeSolvedFromPost(formersolution)},redoStatus:function(post){if($(post).hasClass("statusboth")){str.get_strings([{key:"teacherrating",component:"mod_moodleoverflow"},{key:"starterrating",component:"mod_moodleoverflow"},{key:"bestanswer",component:"mod_moodleoverflow"}]).then((function(results){var circle=templates.renderPix("status/c_circle","mod_moodleoverflow",results[0]),box=templates.renderPix("status/b_box","mod_moodleoverflow",results[1]);return $.when(box,circle).done((function(boxImg,circleImg){screen.width>600?post.find(".status").html(boxImg+circleImg+results[2]):post.find(".status").html(boxImg+circleImg)})),results}))}else if($(post).hasClass("statusteacher")){str.get_strings([{key:"teacherrating",component:"mod_moodleoverflow"},{key:"solvedanswer",component:"mod_moodleoverflow"}]).then((function(results){var circle=templates.renderPix("status/c_outline","mod_moodleoverflow",results[0]);return $.when(circle).done((function(circleImg){screen.width>600?post.find(".status").html(circleImg+results[1]):post.find(".status").html(circleImg)})),results}))}else if($(post).hasClass("statusstarter")){str.get_strings([{key:"starterrating",component:"mod_moodleoverflow"},{key:"helpfulanswer",component:"mod_moodleoverflow"}]).then((function(results){var box=templates.renderPix("status/b_outline","mod_moodleoverflow",results[0]);return $.when(box).done((function(boxImg){screen.width>600?post.find(".status").html(boxImg+results[1]):post.find(".status").html(boxImg)})),results}))}else post.find(".status").html("")}};return t})); //# sourceMappingURL=functions.min.js.map \ No newline at end of file diff --git a/amd/build/functions.min.js.map b/amd/build/functions.min.js.map index bdaff9eb1fa..8edc3539b36 100644 --- a/amd/build/functions.min.js.map +++ b/amd/build/functions.min.js.map @@ -1 +1 @@ -{"version":3,"file":"functions.min.js","sources":["../src/functions.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 .\n\n/**\n * Ajax functions for moodleoverflow\n *\n * @module mod/moodleoverflow\n * @copyright 2017 Tamara Gunkel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/config', 'core/url', 'core/str'],\n function($, ajax, templates, notification, Cfg, Url, str) {\n\n var RATING_SOLVED = 3;\n var RATING_REMOVE_SOLVED = 30;\n var RATING_HELPFUL = 4;\n var RATING_REMOVE_HELPFUL = 40;\n\n var t = {\n\n /**\n * Reoords a upvote / downvote.\n * @param {int} discussionid\n * @param {int} ratingid\n * @param {int} userid\n * @param {event} event\n * @returns {string}\n */\n recordvote: function(discussionid, ratingid, userid, event) {\n var target = $(event.target).closest('.moodleoverflowpost').prev();\n var postid = target.attr('id');\n postid = postid.substring(1);\n\n var vote = ajax.call([{\n methodname: 'mod_moodleoverflow_record_vote',\n args: {\n discussionid: discussionid,\n postid: postid,\n ratingid: ratingid,\n sesskey: Cfg.sesskey\n }\n }\n ]);\n\n vote[0].done(function(response) {\n\n var parentdiv = $(event.target).parent().parent();\n // Update Votes.\n if (ratingid === 2) {\n parentdiv.children('a:first-of-type').children().attr(\n 'src', Url.imageUrl('vote/upvoted', 'moodleoverflow'));\n parentdiv.children('a:nth-of-type(2)').children().attr(\n 'src', Url.imageUrl('vote/downvote', 'moodleoverflow'));\n } else if (ratingid === 1) {\n parentdiv.children('a:first-of-type').children().attr(\n 'src', Url.imageUrl('vote/upvote', 'moodleoverflow'));\n parentdiv.children('a:nth-of-type(2)').children().attr(\n 'src', Url.imageUrl('vote/downvoted', 'moodleoverflow'));\n } else {\n parentdiv.children('a:first-of-type').children().attr(\n 'src', Url.imageUrl('vote/upvote', 'moodleoverflow'));\n parentdiv.children('a:nth-of-type(2)').children().attr(\n 'src', Url.imageUrl('vote/downvote', 'moodleoverflow'));\n }\n\n parentdiv.children('p').text(response.postrating);\n\n // Update user reputation.\n templates.replaceNode($('.user-details,.author').find('a[href*=\"id=' + userid + '\"]')\n .siblings('span'), '' + response.raterreputation + '', \"\");\n if (response.ownerid && userid !== response.ownerid) {\n templates.replaceNode($('.user-details,.author').find('a[href*=\"id=' + response.ownerid + '\"]')\n .siblings('span'), '' + response.ownerreputation + '', \"\");\n }\n }).fail(notification.exception);\n\n return vote;\n },\n\n /**\n * Initializes the clickevent on upvotes / downvotes.\n * @param {int} discussionid\n * @param {int} userid\n */\n clickevent: function(discussionid, userid) {\n $(\".upvote\").on(\"click\", function(event) {\n if ($(event.target).is('a')) {\n event.target = $(event.target).children();\n }\n\n if ($(event.target).parent().attr('class').indexOf('active') >= 0) {\n t.recordvote(discussionid, 20, userid, event);\n } else {\n t.recordvote(discussionid, 2, userid, event);\n }\n $(event.target).parent().toggleClass('active');\n $(event.target).parent().nextAll('a').removeClass('active');\n });\n\n $(\".downvote\").on(\"click\", function(event) {\n if ($(event.target).is('a')) {\n event.target = $(event.target).children();\n }\n\n if ($(event.target).parent().attr('class').indexOf('active') >= 0) {\n t.recordvote(discussionid, 10, userid, event);\n } else {\n t.recordvote(discussionid, 1, userid, event);\n }\n $(event.target).parent().toggleClass('active');\n $(event.target).parent().prevAll('a').removeClass('active');\n });\n\n $(\".marksolved\").on(\"click\", function(event) {\n var post = $(event.target).parents('.moodleoverflowpost');\n\n if (post.hasClass('statusteacher') || post.hasClass('statusboth')) {\n // Remove solution mark.\n t.recordvote(discussionid, RATING_REMOVE_SOLVED, userid, event)[0].then(function() {\n t.removeSolvedFromPost(post);\n });\n } else {\n // Add solution mark.\n t.recordvote(discussionid, RATING_SOLVED, userid, event)[0].then(function() {\n // Remove other solution mark in dom.\n t.removeOtherSolved(post.parent().parent());\n if (post.hasClass('statusstarter')) {\n post.removeClass('statusstarter');\n post.addClass('statusboth');\n } else {\n post.addClass('statusteacher');\n }\n\n var promiseStringNotSolved = str.get_string('marknotsolved', 'mod_moodleoverflow');\n $.when(promiseStringNotSolved).done(function(string) {\n $(event.target).text(string);\n });\n t.redoStatus(post);\n });\n }\n\n\n });\n\n $(\".markhelpful\").on(\"click\", function(event) {\n var post = $(event.target).parents('.moodleoverflowpost');\n\n if (post.hasClass('statusstarter') || post.hasClass('statusboth')) {\n // Remove helpful mark.\n t.recordvote(discussionid, RATING_REMOVE_HELPFUL, userid, event)[0].then(function() {\n t.removeHelpfulFromPost(post);\n });\n } else {\n // Add helpful mark.\n t.recordvote(discussionid, RATING_HELPFUL, userid, event)[0].then(function() {\n // Remove other helpful mark in dom.\n t.removeOtherHelpful(post.parent().parent());\n if (post.hasClass('statusteacher')) {\n post.removeClass('statusteacher');\n post.addClass('statusboth');\n } else {\n post.addClass('statusstarter');\n }\n\n var promiseStringNotHelpful = str.get_string('marknothelpful', 'mod_moodleoverflow');\n $.when(promiseStringNotHelpful).done(function(string) {\n $(event.target).text(string);\n });\n t.redoStatus(post);\n });\n }\n\n });\n },\n\n removeHelpfulFromPost: function (post) {\n if (post.hasClass('statusstarter')) {\n post.removeClass('statusstarter');\n } else {\n post.removeClass('statusboth');\n post.addClass('statusteacher');\n }\n\n t.redoStatus(post);\n\n var promiseHelpful = str.get_string('markhelpful', 'mod_moodleoverflow');\n $.when(promiseHelpful).done(function (string) {\n post.find('.markhelpful').text(string);\n });\n },\n\n removeOtherHelpful: function(root) {\n var formerhelpful = root.find('.statusstarter, .statusboth');\n if (formerhelpful.length > 0) {\n t.removeHelpfulFromPost(formerhelpful);\n }\n },\n\n removeSolvedFromPost: function(post) {\n if (post.hasClass('statusteacher')) {\n post.removeClass('statusteacher');\n } else {\n post.removeClass('statusboth');\n post.addClass('statusstarter');\n }\n\n t.redoStatus(post);\n\n var promiseHelpful = str.get_string('marksolved', 'mod_moodleoverflow');\n $.when(promiseHelpful).done(function(string) {\n post.find('.marksolved').text(string);\n });\n },\n\n removeOtherSolved: function(root) {\n var formersolution = root.find('.statusteacher, .statusboth');\n if (formersolution.length > 0) {\n t.removeSolvedFromPost(formersolution);\n }\n },\n\n /**\n * Redoes the post status\n * @param {object} post dom with .moodleoverflowpost which status should be redone\n */\n redoStatus: function(post) {\n if ($(post).hasClass('statusboth')) {\n var statusBothRequest = [\n {key: 'teacherrating', component: 'mod_moodleoverflow'},\n {key: 'starterrating', component: 'mod_moodleoverflow'},\n {key: 'bestanswer', component: 'mod_moodleoverflow'}\n ];\n str.get_strings(statusBothRequest).then(function(results) {\n var circle = templates.renderPix('status/c_circle', 'mod_moodleoverflow', results[0]);\n var box = templates.renderPix('status/b_box', 'mod_moodleoverflow', results[1]);\n $.when(box, circle).done(function(boxImg, circleImg) {\n if (screen.width > 600) {\n post.find('.status').html(boxImg + circleImg + results[2]);\n } else {\n post.find('.status').html(boxImg + circleImg);\n }\n });\n return results;\n });\n } else if ($(post).hasClass('statusteacher')) {\n var statusTeacherRequest = [\n {key: 'teacherrating', component: 'mod_moodleoverflow'},\n {key: 'solvedanswer', component: 'mod_moodleoverflow'}\n ];\n str.get_strings(statusTeacherRequest).then(function(results) {\n var circle = templates.renderPix('status/c_outline', 'mod_moodleoverflow', results[0]);\n $.when(circle).done(function(circleImg) {\n if (screen.width > 600) {\n post.find('.status').html(circleImg + results[1]);\n } else {\n post.find('.status').html(circleImg);\n }\n\n });\n return results;\n });\n } else if ($(post).hasClass('statusstarter')) {\n var statusStarterRequest = [\n {key: 'starterrating', component: 'mod_moodleoverflow'},\n {key: 'helpfulanswer', component: 'mod_moodleoverflow'}\n ];\n str.get_strings(statusStarterRequest).then(function(results) {\n var box = templates.renderPix('status/b_outline', 'mod_moodleoverflow', results[0]);\n $.when(box).done(function(boxImg) {\n if (screen.width > 600) {\n post.find('.status').html(boxImg + results[1]);\n } else {\n post.find('.status').html(boxImg);\n }\n });\n return results;\n });\n } else {\n post.find('.status').html('');\n }\n\n }\n };\n\n return t;\n});\n"],"names":["define","$","ajax","templates","notification","Cfg","Url","str","t","recordvote","discussionid","ratingid","userid","event","postid","target","closest","prev","attr","substring","vote","call","methodname","args","sesskey","done","response","parentdiv","parent","children","imageUrl","text","postrating","replaceNode","find","siblings","raterreputation","ownerid","ownerreputation","fail","exception","clickevent","on","is","indexOf","toggleClass","nextAll","removeClass","prevAll","post","parents","hasClass","then","removeSolvedFromPost","removeOtherSolved","addClass","promiseStringNotSolved","get_string","when","string","redoStatus","removeHelpfulFromPost","removeOtherHelpful","promiseStringNotHelpful","promiseHelpful","root","formerhelpful","length","formersolution","get_strings","key","component","results","circle","renderPix","box","boxImg","circleImg","screen","width","html"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"functions.min.js","sources":["../src/functions.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 .\n\n/**\n * Ajax functions for moodleoverflow\n *\n * @module mod_moodleoverflow/functions\n * @copyright 2017 Tamara Gunkel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/config', 'core/url', 'core/str'],\n function($, ajax, templates, notification, Cfg, Url, str) {\n\n var RATING_SOLVED = 3;\n var RATING_REMOVE_SOLVED = 30;\n var RATING_HELPFUL = 4;\n var RATING_REMOVE_HELPFUL = 40;\n\n var t = {\n\n /**\n * Reoords a upvote / downvote.\n * @param {int} discussionid\n * @param {int} ratingid\n * @param {int} userid\n * @param {event} event\n * @returns {string}\n */\n recordvote: function(discussionid, ratingid, userid, event) {\n var target = $(event.target).closest('.moodleoverflowpost');\n var postid = target.attr('id');\n postid = postid.substring(1);\n\n var vote = ajax.call([{\n methodname: 'mod_moodleoverflow_record_vote',\n args: {\n discussionid: discussionid,\n postid: postid,\n ratingid: ratingid,\n sesskey: Cfg.sesskey\n }\n }\n ]);\n\n vote[0].done(function(response) {\n\n var parentdiv = $(event.target).parent().parent();\n // Update Votes.\n if (ratingid === 2) {\n parentdiv.children('a:first-of-type').children().attr(\n 'src', Url.imageUrl('vote/upvoted', 'moodleoverflow'));\n parentdiv.children('a:nth-of-type(2)').children().attr(\n 'src', Url.imageUrl('vote/downvote', 'moodleoverflow'));\n } else if (ratingid === 1) {\n parentdiv.children('a:first-of-type').children().attr(\n 'src', Url.imageUrl('vote/upvote', 'moodleoverflow'));\n parentdiv.children('a:nth-of-type(2)').children().attr(\n 'src', Url.imageUrl('vote/downvoted', 'moodleoverflow'));\n } else {\n parentdiv.children('a:first-of-type').children().attr(\n 'src', Url.imageUrl('vote/upvote', 'moodleoverflow'));\n parentdiv.children('a:nth-of-type(2)').children().attr(\n 'src', Url.imageUrl('vote/downvote', 'moodleoverflow'));\n }\n\n parentdiv.children('p').text(response.postrating);\n\n // Update user reputation.\n templates.replaceNode($('.user-details,.author').find('a[href*=\"id=' + userid + '\"]')\n .siblings('span'), '' + response.raterreputation + '', \"\");\n if (response.ownerid && userid !== response.ownerid) {\n templates.replaceNode($('.user-details,.author').find('a[href*=\"id=' + response.ownerid + '\"]')\n .siblings('span'), '' + response.ownerreputation + '', \"\");\n }\n }).fail(notification.exception);\n\n return vote;\n },\n\n /**\n * Initializes the clickevent on upvotes / downvotes.\n * @param {int} discussionid\n * @param {int} userid\n */\n clickevent: function(discussionid, userid) {\n $(\".upvote\").on(\"click\", function(event) {\n if ($(event.target).is('a')) {\n event.target = $(event.target).children();\n }\n\n if ($(event.target).parent().attr('class').indexOf('active') >= 0) {\n t.recordvote(discussionid, 20, userid, event);\n } else {\n t.recordvote(discussionid, 2, userid, event);\n }\n $(event.target).parent().toggleClass('active');\n $(event.target).parent().nextAll('a').removeClass('active');\n });\n\n $(\".downvote\").on(\"click\", function(event) {\n if ($(event.target).is('a')) {\n event.target = $(event.target).children();\n }\n\n if ($(event.target).parent().attr('class').indexOf('active') >= 0) {\n t.recordvote(discussionid, 10, userid, event);\n } else {\n t.recordvote(discussionid, 1, userid, event);\n }\n $(event.target).parent().toggleClass('active');\n $(event.target).parent().prevAll('a').removeClass('active');\n });\n\n $(\".marksolved\").on(\"click\", function(event) {\n var post = $(event.target).parents('.moodleoverflowpost');\n\n if (post.hasClass('statusteacher') || post.hasClass('statusboth')) {\n // Remove solution mark.\n t.recordvote(discussionid, RATING_REMOVE_SOLVED, userid, event)[0].then(function() {\n t.removeSolvedFromPost(post);\n });\n } else {\n // Add solution mark.\n t.recordvote(discussionid, RATING_SOLVED, userid, event)[0].then(function() {\n // Remove other solution mark in dom.\n t.removeOtherSolved(post.parent().parent());\n if (post.hasClass('statusstarter')) {\n post.removeClass('statusstarter');\n post.addClass('statusboth');\n } else {\n post.addClass('statusteacher');\n }\n\n var promiseStringNotSolved = str.get_string('marknotsolved', 'mod_moodleoverflow');\n $.when(promiseStringNotSolved).done(function(string) {\n $(event.target).text(string);\n });\n t.redoStatus(post);\n });\n }\n\n\n });\n\n $(\".markhelpful\").on(\"click\", function(event) {\n var post = $(event.target).parents('.moodleoverflowpost');\n\n if (post.hasClass('statusstarter') || post.hasClass('statusboth')) {\n // Remove helpful mark.\n t.recordvote(discussionid, RATING_REMOVE_HELPFUL, userid, event)[0].then(function() {\n t.removeHelpfulFromPost(post);\n });\n } else {\n // Add helpful mark.\n t.recordvote(discussionid, RATING_HELPFUL, userid, event)[0].then(function() {\n // Remove other helpful mark in dom.\n t.removeOtherHelpful(post.parent().parent());\n if (post.hasClass('statusteacher')) {\n post.removeClass('statusteacher');\n post.addClass('statusboth');\n } else {\n post.addClass('statusstarter');\n }\n\n var promiseStringNotHelpful = str.get_string('marknothelpful', 'mod_moodleoverflow');\n $.when(promiseStringNotHelpful).done(function(string) {\n $(event.target).text(string);\n });\n t.redoStatus(post);\n });\n }\n\n });\n },\n\n removeHelpfulFromPost: function (post) {\n if (post.hasClass('statusstarter')) {\n post.removeClass('statusstarter');\n } else {\n post.removeClass('statusboth');\n post.addClass('statusteacher');\n }\n\n t.redoStatus(post);\n\n var promiseHelpful = str.get_string('markhelpful', 'mod_moodleoverflow');\n $.when(promiseHelpful).done(function (string) {\n post.find('.markhelpful').text(string);\n });\n },\n\n removeOtherHelpful: function(root) {\n var formerhelpful = root.find('.statusstarter, .statusboth');\n if (formerhelpful.length > 0) {\n t.removeHelpfulFromPost(formerhelpful);\n }\n },\n\n removeSolvedFromPost: function(post) {\n if (post.hasClass('statusteacher')) {\n post.removeClass('statusteacher');\n } else {\n post.removeClass('statusboth');\n post.addClass('statusstarter');\n }\n\n t.redoStatus(post);\n\n var promiseHelpful = str.get_string('marksolved', 'mod_moodleoverflow');\n $.when(promiseHelpful).done(function(string) {\n post.find('.marksolved').text(string);\n });\n },\n\n removeOtherSolved: function(root) {\n var formersolution = root.find('.statusteacher, .statusboth');\n if (formersolution.length > 0) {\n t.removeSolvedFromPost(formersolution);\n }\n },\n\n /**\n * Redoes the post status\n * @param {object} post dom with .moodleoverflowpost which status should be redone\n */\n redoStatus: function(post) {\n if ($(post).hasClass('statusboth')) {\n var statusBothRequest = [\n {key: 'teacherrating', component: 'mod_moodleoverflow'},\n {key: 'starterrating', component: 'mod_moodleoverflow'},\n {key: 'bestanswer', component: 'mod_moodleoverflow'}\n ];\n str.get_strings(statusBothRequest).then(function(results) {\n var circle = templates.renderPix('status/c_circle', 'mod_moodleoverflow', results[0]);\n var box = templates.renderPix('status/b_box', 'mod_moodleoverflow', results[1]);\n $.when(box, circle).done(function(boxImg, circleImg) {\n if (screen.width > 600) {\n post.find('.status').html(boxImg + circleImg + results[2]);\n } else {\n post.find('.status').html(boxImg + circleImg);\n }\n });\n return results;\n });\n } else if ($(post).hasClass('statusteacher')) {\n var statusTeacherRequest = [\n {key: 'teacherrating', component: 'mod_moodleoverflow'},\n {key: 'solvedanswer', component: 'mod_moodleoverflow'}\n ];\n str.get_strings(statusTeacherRequest).then(function(results) {\n var circle = templates.renderPix('status/c_outline', 'mod_moodleoverflow', results[0]);\n $.when(circle).done(function(circleImg) {\n if (screen.width > 600) {\n post.find('.status').html(circleImg + results[1]);\n } else {\n post.find('.status').html(circleImg);\n }\n\n });\n return results;\n });\n } else if ($(post).hasClass('statusstarter')) {\n var statusStarterRequest = [\n {key: 'starterrating', component: 'mod_moodleoverflow'},\n {key: 'helpfulanswer', component: 'mod_moodleoverflow'}\n ];\n str.get_strings(statusStarterRequest).then(function(results) {\n var box = templates.renderPix('status/b_outline', 'mod_moodleoverflow', results[0]);\n $.when(box).done(function(boxImg) {\n if (screen.width > 600) {\n post.find('.status').html(boxImg + results[1]);\n } else {\n post.find('.status').html(boxImg);\n }\n });\n return results;\n });\n } else {\n post.find('.status').html('');\n }\n\n }\n };\n\n return t;\n});\n"],"names":["define","$","ajax","templates","notification","Cfg","Url","str","t","recordvote","discussionid","ratingid","userid","event","postid","target","closest","attr","substring","vote","call","methodname","args","sesskey","done","response","parentdiv","parent","children","imageUrl","text","postrating","replaceNode","find","siblings","raterreputation","ownerid","ownerreputation","fail","exception","clickevent","on","is","indexOf","toggleClass","nextAll","removeClass","prevAll","post","parents","hasClass","then","removeSolvedFromPost","removeOtherSolved","addClass","promiseStringNotSolved","get_string","when","string","redoStatus","removeHelpfulFromPost","removeOtherHelpful","promiseStringNotHelpful","promiseHelpful","root","formerhelpful","length","formersolution","get_strings","key","component","results","circle","renderPix","box","boxImg","circleImg","screen","width","html"],"mappings":";;;;;;;AAsBAA,sCAAO,CAAC,SAAU,YAAa,iBAAkB,oBAAqB,cAAe,WAAY,aAC7F,SAASC,EAAGC,KAAMC,UAAWC,aAAcC,IAAKC,IAAKC,SAOjDC,EAAI,CAUJC,WAAY,SAASC,aAAcC,SAAUC,OAAQC,WAE7CC,OADSb,EAAEY,MAAME,QAAQC,QAAQ,uBACjBC,KAAK,MACzBH,OAASA,OAAOI,UAAU,OAEtBC,KAAOjB,KAAKkB,KAAK,CAAC,CAClBC,WAAY,iCACZC,KAAM,CACFZ,aAAcA,aACdI,OAAQA,OACRH,SAAUA,SACVY,QAASlB,IAAIkB,mBAKrBJ,KAAK,GAAGK,MAAK,SAASC,cAEdC,UAAYzB,EAAEY,MAAME,QAAQY,SAASA,SAExB,IAAbhB,UACAe,UAAUE,SAAS,mBAAmBA,WAAWX,KAC7C,MAAOX,IAAIuB,SAAS,eAAgB,mBACxCH,UAAUE,SAAS,oBAAoBA,WAAWX,KAC9C,MAAOX,IAAIuB,SAAS,gBAAiB,oBACrB,IAAblB,UACPe,UAAUE,SAAS,mBAAmBA,WAAWX,KAC7C,MAAOX,IAAIuB,SAAS,cAAe,mBACvCH,UAAUE,SAAS,oBAAoBA,WAAWX,KAC9C,MAAOX,IAAIuB,SAAS,iBAAkB,qBAE1CH,UAAUE,SAAS,mBAAmBA,WAAWX,KAC7C,MAAOX,IAAIuB,SAAS,cAAe,mBACvCH,UAAUE,SAAS,oBAAoBA,WAAWX,KAC9C,MAAOX,IAAIuB,SAAS,gBAAiB,oBAG7CH,UAAUE,SAAS,KAAKE,KAAKL,SAASM,YAGtC5B,UAAU6B,YAAY/B,EAAE,yBAAyBgC,KAAK,eAAiBrB,OAAS,MAC3EsB,SAAS,QAAS,SAAWT,SAASU,gBAAkB,UAAW,IACpEV,SAASW,SAAWxB,SAAWa,SAASW,SACxCjC,UAAU6B,YAAY/B,EAAE,yBAAyBgC,KAAK,eAAiBR,SAASW,QAAU,MACrFF,SAAS,QAAS,SAAWT,SAASY,gBAAkB,UAAW,OAE7EC,KAAKlC,aAAamC,WAEdpB,MAQXqB,WAAY,SAAS9B,aAAcE,QAC/BX,EAAE,WAAWwC,GAAG,SAAS,SAAS5B,OAC1BZ,EAAEY,MAAME,QAAQ2B,GAAG,OACnB7B,MAAME,OAASd,EAAEY,MAAME,QAAQa,YAG/B3B,EAAEY,MAAME,QAAQY,SAASV,KAAK,SAAS0B,QAAQ,WAAa,EAC5DnC,EAAEC,WAAWC,aAAc,GAAIE,OAAQC,OAEvCL,EAAEC,WAAWC,aAAc,EAAGE,OAAQC,OAE1CZ,EAAEY,MAAME,QAAQY,SAASiB,YAAY,UACrC3C,EAAEY,MAAME,QAAQY,SAASkB,QAAQ,KAAKC,YAAY,aAGtD7C,EAAE,aAAawC,GAAG,SAAS,SAAS5B,OAC5BZ,EAAEY,MAAME,QAAQ2B,GAAG,OACnB7B,MAAME,OAASd,EAAEY,MAAME,QAAQa,YAG/B3B,EAAEY,MAAME,QAAQY,SAASV,KAAK,SAAS0B,QAAQ,WAAa,EAC5DnC,EAAEC,WAAWC,aAAc,GAAIE,OAAQC,OAEvCL,EAAEC,WAAWC,aAAc,EAAGE,OAAQC,OAE1CZ,EAAEY,MAAME,QAAQY,SAASiB,YAAY,UACrC3C,EAAEY,MAAME,QAAQY,SAASoB,QAAQ,KAAKD,YAAY,aAGtD7C,EAAE,eAAewC,GAAG,SAAS,SAAS5B,WAC9BmC,KAAO/C,EAAEY,MAAME,QAAQkC,QAAQ,uBAE/BD,KAAKE,SAAS,kBAAoBF,KAAKE,SAAS,cAEhD1C,EAAEC,WAAWC,aAxGF,GAwGsCE,OAAQC,OAAO,GAAGsC,MAAK,WACpE3C,EAAE4C,qBAAqBJ,SAI3BxC,EAAEC,WAAWC,aA9GT,EA8GsCE,OAAQC,OAAO,GAAGsC,MAAK,WAE7D3C,EAAE6C,kBAAkBL,KAAKrB,SAASA,UAC9BqB,KAAKE,SAAS,kBACdF,KAAKF,YAAY,iBACjBE,KAAKM,SAAS,eAEdN,KAAKM,SAAS,qBAGdC,uBAAyBhD,IAAIiD,WAAW,gBAAiB,sBAC7DvD,EAAEwD,KAAKF,wBAAwB/B,MAAK,SAASkC,QACzCzD,EAAEY,MAAME,QAAQe,KAAK4B,WAEzBlD,EAAEmD,WAAWX,YAOzB/C,EAAE,gBAAgBwC,GAAG,SAAS,SAAS5B,WAC/BmC,KAAO/C,EAAEY,MAAME,QAAQkC,QAAQ,uBAE/BD,KAAKE,SAAS,kBAAoBF,KAAKE,SAAS,cAEhD1C,EAAEC,WAAWC,aArID,GAqIsCE,OAAQC,OAAO,GAAGsC,MAAK,WACrE3C,EAAEoD,sBAAsBZ,SAI5BxC,EAAEC,WAAWC,aA3IR,EA2IsCE,OAAQC,OAAO,GAAGsC,MAAK,WAE9D3C,EAAEqD,mBAAmBb,KAAKrB,SAASA,UAC/BqB,KAAKE,SAAS,kBACdF,KAAKF,YAAY,iBACjBE,KAAKM,SAAS,eAEdN,KAAKM,SAAS,qBAGdQ,wBAA0BvD,IAAIiD,WAAW,iBAAkB,sBAC/DvD,EAAEwD,KAAKK,yBAAyBtC,MAAK,SAASkC,QAC1CzD,EAAEY,MAAME,QAAQe,KAAK4B,WAEzBlD,EAAEmD,WAAWX,aAO7BY,sBAAuB,SAAUZ,MACzBA,KAAKE,SAAS,iBACdF,KAAKF,YAAY,kBAEjBE,KAAKF,YAAY,cACjBE,KAAKM,SAAS,kBAGlB9C,EAAEmD,WAAWX,UAETe,eAAiBxD,IAAIiD,WAAW,cAAe,sBACnDvD,EAAEwD,KAAKM,gBAAgBvC,MAAK,SAAUkC,QAClCV,KAAKf,KAAK,gBAAgBH,KAAK4B,YAIvCG,mBAAoB,SAASG,UACrBC,cAAgBD,KAAK/B,KAAK,+BAC1BgC,cAAcC,OAAS,GACvB1D,EAAEoD,sBAAsBK,gBAIhCb,qBAAsB,SAASJ,MACvBA,KAAKE,SAAS,iBACdF,KAAKF,YAAY,kBAEjBE,KAAKF,YAAY,cACjBE,KAAKM,SAAS,kBAGlB9C,EAAEmD,WAAWX,UAETe,eAAiBxD,IAAIiD,WAAW,aAAc,sBAClDvD,EAAEwD,KAAKM,gBAAgBvC,MAAK,SAASkC,QACjCV,KAAKf,KAAK,eAAeH,KAAK4B,YAItCL,kBAAmB,SAASW,UACpBG,eAAiBH,KAAK/B,KAAK,+BAC3BkC,eAAeD,OAAS,GACxB1D,EAAE4C,qBAAqBe,iBAQ/BR,WAAY,SAASX,SACb/C,EAAE+C,MAAME,SAAS,cAAe,CAMhC3C,IAAI6D,YALoB,CACpB,CAACC,IAAK,gBAAiBC,UAAW,sBAClC,CAACD,IAAK,gBAAiBC,UAAW,sBAClC,CAACD,IAAK,aAAcC,UAAW,wBAEAnB,MAAK,SAASoB,aACzCC,OAASrE,UAAUsE,UAAU,kBAAmB,qBAAsBF,QAAQ,IAC9EG,IAAMvE,UAAUsE,UAAU,eAAgB,qBAAsBF,QAAQ,WAC5EtE,EAAEwD,KAAKiB,IAAKF,QAAQhD,MAAK,SAASmD,OAAQC,WAClCC,OAAOC,MAAQ,IACf9B,KAAKf,KAAK,WAAW8C,KAAKJ,OAASC,UAAYL,QAAQ,IAEvDvB,KAAKf,KAAK,WAAW8C,KAAKJ,OAASC,cAGpCL,gBAER,GAAItE,EAAE+C,MAAME,SAAS,iBAAkB,CAK1C3C,IAAI6D,YAJuB,CACvB,CAACC,IAAK,gBAAiBC,UAAW,sBAClC,CAACD,IAAK,eAAgBC,UAAW,wBAECnB,MAAK,SAASoB,aAC5CC,OAASrE,UAAUsE,UAAU,mBAAoB,qBAAsBF,QAAQ,WACnFtE,EAAEwD,KAAKe,QAAQhD,MAAK,SAASoD,WACrBC,OAAOC,MAAQ,IACf9B,KAAKf,KAAK,WAAW8C,KAAKH,UAAYL,QAAQ,IAE9CvB,KAAKf,KAAK,WAAW8C,KAAKH,cAI3BL,gBAER,GAAItE,EAAE+C,MAAME,SAAS,iBAAkB,CAK1C3C,IAAI6D,YAJuB,CACvB,CAACC,IAAK,gBAAiBC,UAAW,sBAClC,CAACD,IAAK,gBAAiBC,UAAW,wBAEAnB,MAAK,SAASoB,aAC5CG,IAAMvE,UAAUsE,UAAU,mBAAoB,qBAAsBF,QAAQ,WAChFtE,EAAEwD,KAAKiB,KAAKlD,MAAK,SAASmD,QAClBE,OAAOC,MAAQ,IACf9B,KAAKf,KAAK,WAAW8C,KAAKJ,OAASJ,QAAQ,IAE3CvB,KAAKf,KAAK,WAAW8C,KAAKJ,WAG3BJ,gBAGXvB,KAAKf,KAAK,WAAW8C,KAAK,aAM/BvE"} \ No newline at end of file diff --git a/amd/build/reviewing.min.js b/amd/build/reviewing.min.js new file mode 100644 index 00000000000..321046c3a82 --- /dev/null +++ b/amd/build/reviewing.min.js @@ -0,0 +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}} +/** + * 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"]),window.console.log("INIT1!");document.getElementById("moodleoverflow-posts").onclick=async e=>{const action=e.target.getAttribute("data-moodleoverflow-action");if(!action)return;window.console.log("INIT!");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?'')+await(0,_str.get_string)("jump_to_next_post_needing_review","mod_moodleoverflow")+"":await(0,_str.get_string)("there_are_no_posts_needing_review","mod_moodleoverflow"),reviewRow.innerHTML=message}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};rejectMessage&&(args.reason=rejectMessage);const 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?'')+await(0,_str.get_string)("jump_to_next_post_needing_review","mod_moodleoverflow")+"":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 new file mode 100644 index 00000000000..2608254c22a --- /dev/null +++ b/amd/build/reviewing.min.js.map @@ -0,0 +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 .\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 window.console.log('INIT1!');\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 window.console.log('INIT!');\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 += ``\n + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n + \"\";\n } else {\n message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow');\n }\n reviewRow.innerHTML = message;\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 };\n if (rejectMessage) {\n args.reason = rejectMessage;\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 += ``\n + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow')\n + \"\";\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","window","console","log","document","getElementById","onclick","async","action","e","target","getAttribute","post","closest","reviewRow","postID","innerHTML","nextPostURL","Ajax","call","methodname","args","postid","message","Templates","render","rejectMessage","querySelector","value","toString","trim","reason"],"mappings":";;;;;;;wGA+BaA,kBAAkB,CAAC,sCAAuC,wDAC1DC,gBAAgB,qBACrB,CAAC,oBAAqB,mCAAoC,oCAAqC,sBAEnGC,OAAOC,QAAQC,IAAI,UAENC,SAASC,eAAe,wBAChCC,QAAUC,MAAAA,UACLC,OAASC,EAAEC,OAAOC,aAAa,kCAEhCH,cAILP,OAAOC,QAAQC,IAAI,eAEbS,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,aACnB,GAAe,WAAXf,OACPM,UAAUE,UAAY,IACtBF,UAAUE,gBAAkBQ,mBAAUC,OAAO,sCAAuC,SACjF,GAAe,kBAAXjB,OAA4B,OAC7BkB,cAAgBd,KAAKe,cAAc,0BAA0BC,MAAMC,WAAWC,OACpFhB,UAAUE,UAAY,UAChBK,KAAO,CACTC,OAAQP,QAERW,gBACAL,KAAKU,OAASL,qBAEZT,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,gBAAkBQ,mBAAUC,OAAO,oCAAqC"} \ No newline at end of file diff --git a/amd/src/functions.js b/amd/src/functions.js index 90b80fe9bb6..21a7f8ead57 100644 --- a/amd/src/functions.js +++ b/amd/src/functions.js @@ -16,7 +16,7 @@ /** * Ajax functions for moodleoverflow * - * @module mod/moodleoverflow + * @module mod_moodleoverflow/functions * @copyright 2017 Tamara Gunkel * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -39,7 +39,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/conf * @returns {string} */ recordvote: function(discussionid, ratingid, userid, event) { - var target = $(event.target).closest('.moodleoverflowpost').prev(); + var target = $(event.target).closest('.moodleoverflowpost'); var postid = target.attr('id'); postid = postid.substring(1); diff --git a/amd/src/reviewing.js b/amd/src/reviewing.js new file mode 100644 index 00000000000..ba12a97656f --- /dev/null +++ b/amd/src/reviewing.js @@ -0,0 +1,101 @@ +// 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 . + +/** + * 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 + */ +import Ajax from 'core/ajax'; +import Prefetch from 'core/prefetch'; +import Templates from 'core/templates'; +import {get_string as getString} from 'core/str'; + +/** + * Init function. + */ +export function init() { + Prefetch.prefetchTemplates(['mod_moodleoverflow/reject_post_form', 'mod_moodleoverflow/review_buttons']); + Prefetch.prefetchStrings('mod_moodleoverflow', + ['post_was_approved', 'jump_to_next_post_needing_review', 'there_are_no_posts_needing_review', 'post_was_rejected']); + + window.console.log('INIT1!'); + + const root = document.getElementById('moodleoverflow-posts'); + root.onclick = async(e) => { + const action = e.target.getAttribute('data-moodleoverflow-action'); + + if (!action) { + return; + } + + window.console.log('INIT!'); + + const post = e.target.closest('*[data-moodleoverflow-postid]'); + const reviewRow = e.target.closest('.reviewrow'); + const postID = post.getAttribute('data-moodleoverflow-postid'); + + if (action === 'approve') { + reviewRow.innerHTML = '.'; + const nextPostURL = await Ajax.call([{ + methodname: 'mod_moodleoverflow_review_approve_post', + args: { + postid: postID, + } + }])[0]; + + let message = await getString('post_was_approved', 'mod_moodleoverflow') + ' '; + if (nextPostURL) { + message += `` + + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow') + + ""; + } else { + message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow'); + } + reviewRow.innerHTML = message; + } else if (action === 'reject') { + reviewRow.innerHTML = '.'; + reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/reject_post_form', {}); + } else if (action === 'reject-submit') { + const rejectMessage = post.querySelector('textarea.reject-reason').value.toString().trim(); + reviewRow.innerHTML = '.'; + const args = { + postid: postID + }; + if (rejectMessage) { + args.reason = rejectMessage; + } + const nextPostURL = await Ajax.call([{ + methodname: 'mod_moodleoverflow_review_reject_post', + args: args + }])[0]; + + let message = await getString('post_was_rejected', 'mod_moodleoverflow') + ' '; + if (nextPostURL) { + message += `` + + await getString('jump_to_next_post_needing_review', 'mod_moodleoverflow') + + ""; + } else { + message += await getString('there_are_no_posts_needing_review', 'mod_moodleoverflow'); + } + reviewRow.innerHTML = message; + } else if (action === 'reject-cancel') { + reviewRow.innerHTML = '.'; + reviewRow.innerHTML = await Templates.render('mod_moodleoverflow/review_buttons', {}); + } + }; +} \ No newline at end of file diff --git a/classes/readtracking.php b/classes/readtracking.php index 3cc6ba61a98..ed5d807de9d 100644 --- a/classes/readtracking.php +++ b/classes/readtracking.php @@ -471,39 +471,11 @@ public static function get_untracked_moodleoverflows($userid, $courseid) { * @return int|mixed */ public static function moodleoverflow_count_unread_posts_moodleoverflow($cm, $course) { - global $CFG, $DB, $USER; - - // Create a cache. - static $readcache = array(); + global $DB, $USER; // Get the moodleoverflow ids. $moodleoverflowid = $cm->instance; - // Check whether the cache is already set. - if (!isset($readcache[$course->id])) { - - // Create a cache for the course. - $readcache[$course->id] = array(); - - // Count the unread posts in the course. - $counts = self::moodleoverflow_count_unread_posts_course($USER->id, $course->id); - if ($counts) { - - // Loop through all unread posts. - foreach ($counts as $count) { - $readcache[$course->id][$count->id] = $count->unread; - } - } - } - - // Check whether there are no unread post for this moodleoverflow. - if (empty($readcache[$course->id][$moodleoverflowid])) { - return 0; - } - - // Require the course library. - require_once($CFG->dirroot . '/course/lib.php'); - // Get the current timestamp and the cutoffdate. $now = round(time(), -2); $cutoffdate = $now - (get_config('moodleoverflow', 'oldpostdays') * 24 * 60 * 60); @@ -514,57 +486,9 @@ public static function moodleoverflow_count_unread_posts_moodleoverflow($cm, $co FROM {moodleoverflow_posts} p JOIN {moodleoverflow_discussions} d ON p.discussion = d.id LEFT JOIN {moodleoverflow_read} r ON (r.postid = p.id AND r.userid = ?) - WHERE d.moodleoverflow = ? AND p.modified >= ? AND r.id IS NULL"; + WHERE d.moodleoverflow = ? AND p.modified >= ? AND r.id IS NULL AND p.reviewed = 1"; // Return the number of unread posts per moodleoverflow. return $DB->get_field_sql($sql, $params); } - - /** - * Get an array of unread posts within a course. - * - * @param int $userid The user ID - * @param int $courseid The course ID - * - * @return array Array of unread posts within a course - */ - public static function moodleoverflow_count_unread_posts_course($userid, $courseid) { - global $DB; - - // Get the current timestamp and calculate the cutoffdate. - $now = round(time(), -2); - $cutoffdate = $now - (get_config('moodleoverflow', 'oldpostdays') * 24 * 60 * 60); - - // Set parameters for the sql-query. - $params = array($userid, $userid, $courseid, $cutoffdate, $userid); - - // Check if forced readtracking is allowed. - if (get_config('moodleoverflow', 'allowforcedreadtracking')) { - $trackingsql = "AND (m.trackingtype = " . MOODLEOVERFLOW_TRACKING_FORCED . - " OR (m.trackingtype = " . MOODLEOVERFLOW_TRACKING_OPTIONAL . " AND tm.id IS NULL))"; - } else { - $trackingsql = "AND ((m.trackingtype = " . MOODLEOVERFLOW_TRACKING_OPTIONAL . " OR m.trackingtype = " . - MOODLEOVERFLOW_TRACKING_FORCED . ") AND tm.id IS NULL)"; - } - - // Define the sql-query. - $sql = "SELECT m.id, COUNT(p.id) AS unread - FROM {moodleoverflow_posts} p - JOIN {moodleoverflow_discussions} d ON d.id = p.discussion - JOIN {moodleoverflow} m ON m.id = d.moodleoverflow - JOIN {course} c ON c.id = m.course - LEFT JOIN {moodleoverflow_read} r ON (r.postid = p.id AND r.userid = ?) - LEFT JOIN {moodleoverflow_tracking} tm ON (tm.userid = ? AND tm.moodleoverflowid = m.id) - WHERE m.course = ? AND p.modified >= ? AND r.id IS NULL $trackingsql - GROUP BY m.id"; - - // Get the amount of unread post within a course. - $return = $DB->get_records_sql($sql, $params); - if ($return) { - return $return; - } - - // Else return nothing. - return array(); - } } diff --git a/classes/review.php b/classes/review.php new file mode 100644 index 00000000000..831a8fde493 --- /dev/null +++ b/classes/review.php @@ -0,0 +1,118 @@ +. + +/** + * Moodleoverflow anonymous related class. + * + * @package mod_moodleoverflow + * @copyright 2021 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_moodleoverflow; + +/** + * Class for Moodleoverflow anonymity + * + * @package mod_moodleoverflow + * @copyright 2021 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class review { + + /** + * Nothing has to be reviewed. + */ + const NOTHING = 0; + /** + * New questions (= discussions) have to be reviewed. + */ + const QUESTIONS = 1; + /** + * Everything has to be reviewed. + */ + const EVERYTHING = 2; + + /** + * Returns the review level of the given moodleoverflow instance, considering the global allowreview setting. + * @param object $moodleoverflow + * @return int + */ + public static function get_review_level(object $moodleoverflow): int { + if (get_config('moodleoverflow', 'allowreview') == '1') { + return $moodleoverflow->needsreview; + } else { + return self::NOTHING; + } + } + + /** + * Returns a short review info for the discussion. + * @param int $discussionid The discussionid. + * @return object {"count": amount needing review (int) , "first": first postid needing review (int)} + */ + public static function get_short_review_info_for_discussion(int $discussionid) { + global $DB; + + return $DB->get_record_sql( + 'SELECT COUNT(*) as count, MIN(id) AS first ' . + 'FROM {moodleoverflow_posts} ' . + 'WHERE discussion = :discussionid AND reviewed = 0', + ['discussionid' => $discussionid] + ); + } + + /** + * @param $afterpostid + * @return string|null + */ + public static function get_first_review_post($afterpostid = null) { + global $DB; + + $params = []; + $orderby = ''; + $addwhere = ''; + + if ($afterpostid) { + $afterdiscussionid = $DB->get_field('moodleoverflow_posts', 'discussion', ['id' => $afterpostid], + MUST_EXIST); + + $orderby = 'CASE WHEN (discussion > :afterdiscussionid OR (discussion = :afterdiscussionid2 AND id > :afterpostid)) THEN 0 ELSE 1 END, '; + $params['afterdiscussionid'] = $afterdiscussionid; + $params['afterdiscussionid2'] = $afterdiscussionid; + $params['afterpostid'] = $afterpostid; + + $addwhere = ' AND id != :notpostid '; + $params['notpostid'] = $afterpostid; + } + $record = $DB->get_record_sql( + 'SELECT id as postid, discussion as discussionid FROM {moodleoverflow_posts} ' . + "WHERE reviewed = 0 $addwhere " . + "ORDER BY $orderby discussion, id " . + 'LIMIT 1', + $params + ); + if ($record) { + return (new \moodle_url('/mod/moodleoverflow/discussion.php', [ + 'd' => $record->discussionid + ], 'p' . $record->postid))->out(false); + } else { + return null; + } + } + + +} diff --git a/db/services.php b/db/services.php index 7bcba3e5eba..e1c585e6303 100644 --- a/db/services.php +++ b/db/services.php @@ -33,5 +33,21 @@ 'type' => 'write', 'ajax' => true, 'capabilities' => 'mod/moodleoverflow:ratepost' - ) + ), + 'mod_moodleoverflow_review_approve_post' => array( + 'classname' => 'mod_moodleoverflow_external', + 'methodname' => 'review_approve_post', + 'classpath' => 'mod/moodleoverflow/externallib.php', + 'description' => 'Approves a post', + 'type' => 'write', + 'ajax' => true, + ), + 'mod_moodleoverflow_review_reject_post' => array( + 'classname' => 'mod_moodleoverflow_external', + 'methodname' => 'review_reject_post', + 'classpath' => 'mod/moodleoverflow/externallib.php', + 'description' => 'Rejects a post', + 'type' => 'write', + 'ajax' => true, + ), ); diff --git a/discussion.php b/discussion.php index cd162d5fb1f..c5f68e030b5 100644 --- a/discussion.php +++ b/discussion.php @@ -129,6 +129,8 @@ $node->add(format_string($post->subject), $PAGE->url); } +$PAGE->requires->js_call_amd('mod_moodleoverflow/reviewing', 'init'); + // Initiate the page. $PAGE->set_title($course->shortname . ': ' . format_string($discussion->name)); $PAGE->set_heading($course->fullname); @@ -158,6 +160,10 @@ echo "
"; +echo '
'; + moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discussion, $post, $canreply); +echo '
'; + echo $OUTPUT->footer(); diff --git a/externallib.php b/externallib.php index 1b56d144db6..595b4c17541 100644 --- a/externallib.php +++ b/externallib.php @@ -25,6 +25,7 @@ defined('MOODLE_INTERNAL') || die; require_once("$CFG->libdir/externallib.php"); +require_once($CFG->dirroot . '/mod/moodleoverflow/locallib.php'); /** * Class implementing the external API, esp. for AJAX functions. @@ -143,4 +144,94 @@ public static function record_vote($discussionid, $postid, $ratingid, $sesskey) return $params; } + + /** + * Returns description of method parameters. + * @return external_function_parameters + */ + public static function review_approve_post_parameters() { + return new external_function_parameters([ + 'postid' => new external_value(PARAM_INT, 'id of post') + ]); + } + + /** + * Returns description of return value. + * @return external_value + */ + public static function review_approve_post_returns() { + return new external_value(PARAM_TEXT, 'the url of the next post to review'); + } + + public static function review_approve_post($postid) { + global $DB; + + $params = self::validate_parameters(self::review_approve_post_parameters(), ['postid' => $postid]); + $postid = $params['postid']; + + $post = $DB->get_record('moodleoverflow_posts', ['id' => $postid], '*', MUST_EXIST); + $moodleoverflow = $DB->get_record_sql( + 'SELECT m.* FROM {moodleoverflow} m ' . + 'JOIN {moodleoverflow_discussions} d ON d.moodleoverflow = m.id ' . + 'WHERE d.id = :discussionid', + ['discussionid' => $post->discussion], MUST_EXIST + ); + $cm = get_coursemodule_from_instance('moodleoverflow', $moodleoverflow->id); + $context = context_module::instance($cm->id); + + require_capability('mod/moodleoverflow:reviewpost', $context); + + $post->reviewed = 1; + + $DB->update_record('moodleoverflow_posts', $post); + + return \mod_moodleoverflow\review::get_first_review_post($post->id); + } + + /** + * Returns description of method parameters. + * @return external_function_parameters + */ + public static function review_reject_post_parameters() { + return new external_function_parameters([ + 'postid' => new external_value(PARAM_INT, 'id of post'), + 'reason' => new external_value(PARAM_RAW, 'reason of rejection', VALUE_OPTIONAL) + ]); + } + + /** + * Returns description of return value. + * @return external_value + */ + public static function review_reject_post_returns() { + return new external_value(PARAM_TEXT, 'the url of the next post to review'); + } + + public static function review_reject_post($postid) { + global $DB; + + $params = self::validate_parameters(self::review_reject_post_parameters(), ['postid' => $postid]); + $postid = $params['postid']; + + $post = $DB->get_record('moodleoverflow_posts', ['id' => $postid], '*', MUST_EXIST); + $moodleoverflow = $DB->get_record_sql( + 'SELECT m.* FROM {moodleoverflow} m ' . + 'JOIN {moodleoverflow_discussions} d ON d.moodleoverflow = m.id ' . + 'WHERE d.id = :discussionid', + ['discussionid' => $post->discussion], MUST_EXIST + ); + $cm = get_coursemodule_from_instance('moodleoverflow', $moodleoverflow->id); + $context = context_module::instance($cm->id); + + require_capability('mod/moodleoverflow:reviewpost', $context); + + // Has to be done before deleting the post. + $url = \mod_moodleoverflow\review::get_first_review_post($post->id); + + moodleoverflow_delete_post($post, true, $cm, $moodleoverflow); + + // email_to_user(core_user::get_user($post->userid), core_user::get_noreply_user(), ) + + return $url; + } } diff --git a/lang/en/moodleoverflow.php b/lang/en/moodleoverflow.php index 5c6f5154c64..d0148eb15ef 100644 --- a/lang/en/moodleoverflow.php +++ b/lang/en/moodleoverflow.php @@ -418,3 +418,24 @@ $string['desc:only_questions'] = 'The name of questioners will not be displayed in their question and comments.'; $string['desc:anonymous'] = 'No names will be displayed.'; $string['resetanonymous_warning'] = 'Are you sure? If you are in production, this is most certainly a bad decision because your students and teachers posted their questions and answers, believing they would remain anonymous.

{$a->fullanoncount} forums are currently fully anonymized, and in {$a->questionanoncount} additional forums the questioners are anonymized.

In all these forums, the real names of posters will be displayed again, even in already existing posts!

There is no way of reverting those changes!
'; + +// Review feature +$string['review'] = 'Review'; +$string['review_help'] = 'Select what has to be approved by a teacher before being shown to students.'; +$string['nothing'] = 'Nothing'; +$string['questions'] = 'Questions'; +$string['questions_and_posts'] = 'Questions and answers'; +$string['allowreview'] = 'Allow reviews'; +$string['allowreview_desc'] = 'Allow teachers to enable that all posts (or only all questions) have to be reviewed by them in order to be published.'; +$string['amount_waiting_for_review'] = '{$a} post(s) need to be reviewed!'; +$string['pending_review'] = 'Pending review'; +$string['post_was_approved'] = 'The post was approved.'; +$string['post_was_rejected'] = 'The post was rejected.'; +$string['jump_to_next_post_needing_review'] = 'Jump to next post needing to be reviewed.'; +$string['there_are_no_posts_needing_review'] = 'There are no more posts needing to be reviewed.'; +$string['give_a_reason'] = 'Give a reason (optional)'; +$string['approve'] = 'Approve'; +$string['reject'] = 'Reject'; + +$string['rejected_email_subject'] = 'One of your posts in {$a->course} {$a->activity} has been rejected.'; +$string['rejected_email_content'] = 'This post has been rejected in {$a->course} {$a->activity}'; diff --git a/lib.php b/lib.php index 00c5de957d6..7a3330b462c 100644 --- a/lib.php +++ b/lib.php @@ -1006,7 +1006,6 @@ function moodleoverflow_get_unmailed_posts($starttime, $endtime) { // Set params for the sql query. $params = array(); $params['mailed'] = MOODLEOVERFLOW_MAILED_PENDING; - $params['reviewed'] = 1; $params['ptimestart'] = $starttime; $params['ptimeend'] = $endtime; @@ -1014,7 +1013,7 @@ function moodleoverflow_get_unmailed_posts($starttime, $endtime) { $sql = "SELECT p.*, d.course, d.moodleoverflow FROM {moodleoverflow_posts} p JOIN {moodleoverflow_discussions} d ON d.id = p.discussion - WHERE p.mailed = :mailed AND p.created >= :ptimestart AND p.created < :ptimeend AND p.reviewed = :reviewed + WHERE p.mailed = :mailed AND p.created >= :ptimestart AND p.created < :ptimeend AND p.reviewed = 1 ORDER BY p.modified ASC"; return $DB->get_records_sql($sql, $params); @@ -1213,3 +1212,13 @@ function moodleoverflow_grade_item_update($moodleoverflow, $grades=null) { return $gradeupdate; } + +/** + * Map icons for font-awesome themes. + */ +function moodleoverflow_get_fontawesome_icon_map() { + return [ + 'mod_moodleoverflow:i/commenting' => 'fa-commenting', + 'mod_moodleoverflow:i/pending-big' => 'fa-clock-o text-danger moodleoverflow-icon-big' + ]; +} diff --git a/locallib.php b/locallib.php index 8852e9a78a3..b8de82ae792 100644 --- a/locallib.php +++ b/locallib.php @@ -26,6 +26,7 @@ */ use mod_moodleoverflow\anonymous; +use mod_moodleoverflow\review; defined('MOODLE_INTERNAL') || die(); @@ -41,7 +42,7 @@ * @return array */ function moodleoverflow_get_discussions($cm, $page = -1, $perpage = 0) { - global $DB, $CFG; + global $DB, $CFG, $USER; // User must have the permission to view the discussions. $modcontext = context_module::instance($cm->id); @@ -84,7 +85,8 @@ function moodleoverflow_get_discussions($cm, $page = -1, $perpage = 0) { $whereconditions = ['d.moodleoverflow = ?', 'p.parent = 0']; if (!has_capability('mod/moodleoverflow:reviewpost', $modcontext)) { - $whereconditions[] = 'p.reviewed = 1'; + $whereconditions[] = '(p.reviewed = 1 OR p.userid = ?)'; + $params[] = $USER->id; } $wheresql = join(' AND ', $whereconditions); @@ -131,6 +133,7 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = - // Check some capabilities. $canstartdiscussion = moodleoverflow_user_can_post_discussion($moodleoverflow, $cm, $context); $canviewdiscussions = has_capability('mod/moodleoverflow:viewdiscussion', $context); + $canreviewposts = has_capability('mod/moodleoverflow:reviewpost', $context); // Print a button if the user is capable of starting // a new discussion or if the selfenrol is aviable. @@ -157,7 +160,7 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = - } // Get the number of replies for each discussion. - $replies = moodleoverflow_count_discussion_replies($moodleoverflow->id); + $replies = moodleoverflow_count_discussion_replies($cm); // Check whether the moodleoverflow instance can be tracked and is tracked. if ($cantrack = \mod_moodleoverflow\readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow)) { @@ -288,6 +291,7 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = - // Get the amount of replies and the link to the discussion. $preparedarray[$i]['replyamount'] = $discussion->replies; + $preparedarray[$i]['questionunderreview'] = $discussion->reviewed == 0; // Are there unread messages? Create a link to them. $preparedarray[$i]['unreadamount'] = $discussion->unread; @@ -334,6 +338,14 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = - } } + if ($canreviewposts) { + $reviewinfo = review::get_short_review_info_for_discussion($discussion->discussion); + $preparedarray[$i]['needreview'] = $reviewinfo->count; + $preparedarray[$i]['reviewlink'] = (new moodle_url('/mod/moodleoverflow/discussion.php', [ + 'd' => $discussion->discussion + ], 'p' . $reviewinfo->first))->out(false); + } + // Add all created data to an array. $preparedarray[$i]['statusstarter'] = $statusstarter; @@ -352,6 +364,7 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = - $mustachedata = new stdClass(); $mustachedata->cantrack = $cantrack; $mustachedata->canviewdiscussions = $canviewdiscussions; + $mustachedata->canreview = $canreviewposts; $mustachedata->discussions = $preparedarray; $mustachedata->hasdiscussions = (count($discussions) >= 0) ? true : false; $mustachedata->istracked = $istracked; @@ -370,20 +383,32 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = - /** * Returns an array of counts of replies for each discussion. * - * @param int $moodleoverflowid + * @param object $cm coursemodule * * @return array */ -function moodleoverflow_count_discussion_replies($moodleoverflowid) { - global $DB; +function moodleoverflow_count_discussion_replies($cm) { + global $DB, $USER; + + $modcontext = context_module::instance($cm->id); + + $params = [$cm->instance]; + $whereconditions = ['d.moodleoverflow = ?', 'p.parent > 0']; + + if (!has_capability('mod/moodleoverflow:reviewpost', $modcontext)) { + $whereconditions[] = '(p.reviewed = 1 OR p.userid = ?)'; + $params[] = $USER->id; + } + + $wheresql = join(' AND ', $whereconditions); $sql = "SELECT p.discussion, COUNT(p.id) AS replies, MAX(p.id) AS lastpostid FROM {moodleoverflow_posts} p JOIN {moodleoverflow_discussions} d ON p.discussion = d.id - WHERE p.parent > 0 AND d.moodleoverflow = ? + WHERE $wheresql GROUP BY p.discussion"; - return $DB->get_records_sql($sql, array($moodleoverflowid)); + return $DB->get_records_sql($sql, $params); } /** @@ -430,7 +455,7 @@ function moodleoverflow_user_can_post_discussion($moodleoverflow, $cm = null, $c * @return array */ function moodleoverflow_get_discussions_count($cm) { - global $DB; + global $DB, $USER; $modcontext = context_module::instance($cm->id); @@ -438,7 +463,8 @@ function moodleoverflow_get_discussions_count($cm) { $whereconditions = ['d.moodleoverflow = ?', 'p.parent = 0']; if (!has_capability('mod/moodleoverflow:reviewpost', $modcontext)) { - $whereconditions[] = 'p.reviewed = 1'; + $whereconditions[] = '(p.reviewed = 1 OR p.userid = ?)'; + $params[] = $USER->id; } $wheresql = join(' AND ', $whereconditions); @@ -475,7 +501,8 @@ function moodleoverflow_get_discussions_unread($cm) { ]; if (!has_capability('mod/moodleoverflow:reviewpost', $modcontext)) { - $whereconditions[] = 'p.reviewed = 1'; + $whereconditions[] = '(p.reviewed = 1 OR p.userid = :userid2)'; + $params['userid2'] = $USER->id; } $wheresql = join(' AND ', $whereconditions); @@ -602,7 +629,8 @@ function moodleoverflow_user_can_see_post($moodleoverflow, $discussion, $post, $ return false; } - if ($post->reviewed == 0 && !has_capability('mod/moodleoverflow:reviewpost', $modulecontext, $user)) { + if (!($post->reviewed == 1 || $post->userid == $user->id || + has_capability('mod/moodleoverflow:reviewpost', $modulecontext, $user))) { return false; } @@ -709,7 +737,9 @@ function moodleoverflow_add_discussion($discussion, $modulecontext, $userid = nu $post->moodleoverflow = $moodleoverflow->id; $post->course = $moodleoverflow->course; - // TODO set reviewed = 0 if moodleoverflow is reviewable + if (review::get_review_level($moodleoverflow) > review::QUESTIONS) { + $post->reviewed = 0; + } // Submit the post to the database and get its id. $post->id = $DB->insert_record('moodleoverflow_posts', $post); @@ -904,8 +934,12 @@ function moodleoverflow_print_discussion($course, $cm, $moodleoverflow, $discuss } echo "

$answerstring

"; + echo '
'; + // Print the other posts. echo moodleoverflow_print_posts_nested($course, $cm, $moodleoverflow, $discussion, $post, $canreply, $istracked, $posts, null, $usermapping); + + echo '
'; } /** @@ -943,7 +977,8 @@ function moodleoverflow_get_all_discussion_posts($discussionid, $tracking, $modc $additionalwhere = ''; if (!has_capability('mod/moodleoverflow:reviewpost', $modcontext)) { - $additionalwhere = ' AND p.reviewed = 1 '; + $additionalwhere = ' AND (p.reviewed = 1 OR p.userid = :userid2) '; + $params['userid2'] = $USER->id; } // Create the sql array. @@ -1078,6 +1113,7 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co $cm->cache->caps['mod/moodleoverflow:viewanyrating'] = has_capability('mod/moodleoverflow:viewanyrating', $modulecontext); $cm->cache->caps['moodle/site:viewfullnames'] = has_capability('moodle/site:viewfullnames', $modulecontext); $cm->cache->caps['mod/moodleoverflow:marksolved'] = has_capability('mod/moodleoverflow:marksolved', $modulecontext); + $cm->cache->caps['mod/moodleoverflow:reviewpost'] = has_capability('mod/moodleoverflow:reviewpost', $modulecontext); } // Check if the user has the capability to see posts. @@ -1354,6 +1390,9 @@ function moodleoverflow_print_post($post, $discussion, $moodleoverflow, $cm, $co $options->trusted = false; $options->context = $modulecontext; + $mustachedata->needsreview = !$post->reviewed; + $mustachedata->canreview = $cm->cache->caps['mod/moodleoverflow:reviewpost']; + // Prepare the post. $mustachedata->postcontent = format_text($post->message, $post->messageformat, $options, $course->id); @@ -1572,11 +1611,13 @@ function moodleoverflow_add_new_post($post) { $post->totalscore = 0; } - // TODO add reiview = 0 if enabled on moodleoverflow module. + if (review::get_review_level($moodleoverflow) == review::EVERYTHING) { + $post->reviewed = 0; + } // Add the post to the database. $post->id = $DB->insert_record('moodleoverflow_posts', $post); - $DB->set_field('moodleoverflow_posts', 'message', $post->message, array('id' => $post->id)); + $DB->set_field('moodleoverflow_posts', 'message', $post->message, array('id' => $post->id)); // ?? moodleoverflow_add_attachment($post, $moodleoverflow, $cm); // Update the discussion. @@ -1624,10 +1665,12 @@ function moodleoverflow_update_post($newpost) { } } - // Update the date and the user of the post and the discussion. - $post->modified = time(); - $discussion->timemodified = $post->modified; - $discussion->usermodified = $post->userid; + if (isset($newpost->reviewed) ? $newpost->reviewed : $post->reviewed) { + // Update the date and the user of the post and the discussion. + $post->modified = time(); + $discussion->timemodified = $post->modified; + $discussion->usermodified = $post->userid; + } // When editing the starting post of a discussion. if (!$post->parent) { @@ -1696,7 +1739,7 @@ function moodleoverflow_delete_discussion($discussion, $course, $cm, $moodleover foreach ($posts as $post) { $post->course = $discussion->course; $post->moodleoverflow = $discussion->moodleoverflow; - if (!moodleoverflow_delete_post($post, 'ignore', $course, $cm, $moodleoverflow)) { + if (!moodleoverflow_delete_post($post, 'ignore', $cm, $moodleoverflow)) { // If the deletion failed, change the pointer. $result = false; @@ -1720,28 +1763,21 @@ function moodleoverflow_delete_discussion($discussion, $course, $cm, $moodleover /** * Deletes a single moodleoverflow post. * - * @param int $post The post ID - * @param array $children The child posts - * @param object $course The course object. + * @param object $post The post ID + * @param bool $deletechildren The child posts * @param object $cm The course module - * @param int $moodleoverflow The moodleoverflow ID + * @param object $moodleoverflow The moodleoverflow ID * * @return bool Whether the deletion was successful */ -function moodleoverflow_delete_post($post, $children, $course, $cm, $moodleoverflow) { +function moodleoverflow_delete_post($post, $deletechildren, $cm, $moodleoverflow) { global $DB, $USER; // Iterate through all children and delete them. $childposts = $DB->get_records('moodleoverflow_posts', array('parent' => $post->id)); - if (($children !== 'ignore') AND $childposts) { - if ($children) { - foreach ($childposts as $childpost) { - moodleoverflow_delete_post($childpost, true, $course, $cm, $moodleoverflow); - } - - } else { - // If there are no children, return false. - return false; + if ($deletechildren AND $childposts) { + foreach ($childposts as $childpost) { + moodleoverflow_delete_post($childpost, true, $cm, $moodleoverflow); } } diff --git a/mod_form.php b/mod_form.php index 94107763161..a49187c1109 100644 --- a/mod_form.php +++ b/mod_form.php @@ -26,6 +26,7 @@ */ use mod_moodleoverflow\anonymous; +use mod_moodleoverflow\review; defined('MOODLE_INTERNAL') || die(); @@ -88,6 +89,18 @@ public function definition() { $mform->setDefault('anonymous', anonymous::NOT_ANONYMOUS); } + if (get_config('moodleoverflow', 'allowreview') == 1) { + $possiblesettings = [ + review::NOTHING => get_string('nothing', 'moodleoverflow'), + review::QUESTIONS => get_string('questions', 'moodleoverflow'), + review::EVERYTHING => get_string('questions_and_posts', 'moodleoverflow') + ]; + + $mform->addElement('select', 'needsreview', get_string('review', 'moodleoverflow'), $possiblesettings); + $mform->addHelpButton('needsreview', 'review', 'moodleoverflow'); + $mform->setDefault('needsreview', review::NOTHING); + } + // Attachments. $mform->addElement('header', 'attachmentshdr', get_string('attachments', 'moodleoverflow')); diff --git a/post.php b/post.php index 283b008f732..2fcaf602143 100644 --- a/post.php +++ b/post.php @@ -22,6 +22,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +global $CFG, $USER, $DB, $PAGE, $SESSION, $OUTPUT; + // Include config and locallib. require_once(dirname(dirname(dirname(__FILE__))) . '/config.php'); require_once(dirname(__FILE__) . '/locallib.php'); @@ -360,7 +362,7 @@ } // Count all replies of this post. - $replycount = moodleoverflow_count_replies($post, has_capability('mod/moodleoverflow:reviewpost', $modulecontext)); + $replycount = moodleoverflow_count_replies($post, false); // Has the user confirmed the deletion? if (!empty($confirm) AND confirm_sesskey()) { @@ -396,7 +398,7 @@ redirect("view.php?m=$discussion->moodleoverflow"); exit; - } else if (moodleoverflow_delete_post($post, $deleteanypost, $course, $cm, $moodleoverflow)) { + } else if (moodleoverflow_delete_post($post, $deleteanypost, $cm, $moodleoverflow)) { // Delete a single post. // Redirect back to the discussion. $discussionurl = new moodle_url('/mod/moodleoverflow/discussion.php', array('d' => $discussion->id)); @@ -451,6 +453,7 @@ // Last posibility: the action is not known. throw new moodle_exception('unknownaction'); + die(); } // Second step: The user must be logged on properly. Must be enrolled to the course as well. @@ -527,6 +530,7 @@ $postmessage = empty($post->message) ? null : $post->message; // Set data for the form. +// TODO Refactor. $param1 = (isset($discussion->id) ? array($discussion->id) : array()); $param2 = (isset($post->format) ? array('format' => $post->format) : array()); $param3 = (isset($discussion->timestart) ? array('timestart' => $discussion->timestart) : array()); diff --git a/renderer.php b/renderer.php index 7ce983ebb5d..6790e97726b 100644 --- a/renderer.php +++ b/renderer.php @@ -66,7 +66,7 @@ public function render_post_dummy_cantsee($data) { * @return bool|string */ public function render_question($data) { - return $this->render_from_template('mod_moodleoverflow/question', $data); + return $this->render_from_template('mod_moodleoverflow/answer', $data); } /** diff --git a/settings.php b/settings.php index 09957e082f7..b299227a9d0 100644 --- a/settings.php +++ b/settings.php @@ -93,6 +93,10 @@ $settings->add(new admin_setting_configcheckbox('moodleoverflow/allowratingchange', get_string('allowratingchange', 'moodleoverflow'), get_string('configallowratingchange', 'moodleoverflow'), 1)); + // Allow teachers to enable review before publish. + $settings->add(new admin_setting_configcheckbox('moodleoverflow/allowreview', + get_string('allowreview', 'moodleoverflow'), get_string('allowreview_desc', 'moodleoverflow'), 1)); + // Set scales for the reputation. $votesettings = []; diff --git a/styles.css b/styles.css index 9f517a65910..b6a8e8484e1 100644 --- a/styles.css +++ b/styles.css @@ -129,77 +129,17 @@ * The styles for the discussion.php */ .moodleoverflowpost { - border: 1px solid #000; - display: block; margin: 0 0 1em 0; max-width: 100%; - padding: 0; position: relative; -} - -.moodleoverflowpost .row { - position: relative; - width: 100%; -} - -.moodleoverflowpost .row .left { - float: left; - overflow: hidden; - width: 43px; -} - -.moodleoverflowpost .row .left + .no-overflow { - max-width: calc(100% - 43px); -} - -.moodleoverflowpost .row .left .votes { - display: block; - margin: 0 8px 0 0; - text-align: center; -} - -.moodleoverflowpost .row .left .votes p { - margin-bottom: 0; - margin-top: 0; -} - -.moodleoverflowpost .row .left .votes .icon { - color: #0a0; - height: 24px; - margin: 0; - width: 24px; -} - -.moodleoverflowpost .row .topic, -.moodleoverflowpost .row .content-mask, -.moodleoverflowpost .row .options { - margin-left: 43px; + border-radius: 0; + display: flex; } .moodleoverflowpost .picture img { margin: 4px; } -.moodleoverflowpost .options .commands, -.moodleoverflowpost .content .attachments, -.moodleoverflowpost .options .footer, -.moodleoverflowpost .options .link { - text-align: right; -} - -.moodleoverflowpost .options .moodleoverflow-post-rating { - float: left; -} - -.moodleoverflowpost .content .posting { - max-width: 100%; - overflow: auto; -} - -.moodleoverflowpost .content .attachedimages img { - max-width: 100%; -} - .moodleoverflowpost .post-word-count { font-size: .85em; font-style: italic; @@ -210,7 +150,7 @@ padding: 0 .3em; } -:target ~ .moodleoverflowpost:before { +.moodleoverflowpost:target:before { background: #0070a8; bottom: -1px; content: ""; @@ -221,14 +161,6 @@ width: 4px; } -.moodleoverflowpost { - border-radius: 0; - display: flex; - margin-bottom: 5px; - position: relative; - padding: 6px; -} - .moodleoverflowpost.statusboth:before { background: linear-gradient(80deg, rgba(234, 133, 22, 1) 50%, rgba(112, 160, 52, 1) 50%); bottom: -1px; @@ -296,23 +228,6 @@ margin-right: 10px; } -.moodleoverflowpost .content .posting.fullpost { - margin-top: 8px; -} - -.moodleoverflowpost .row.side { - clear: both; -} - -.moodleoverflowpost .options .commands { - margin-left: 0; -} - -.moodleoverflowpost .options .moodleoverflow-post-rating .icon { - height: 20px; - width: 24px; -} - .moodleoverflowpost .subject { font-weight: 700; } @@ -326,15 +241,6 @@ span.unread { border-bottom: 1px solid #ddd; } -.moodleoverflowpost .row { - margin-left: 0; -} - -.moodleoverflowpost .row:before, -.moodleoverflowpost .row:after { - content: none; -} - .moodleoverflowpost .topic .author { color: #848d95; } @@ -531,8 +437,23 @@ span.unread { border-color: #900000; } +.moodleoverflowpost.pendingreview > :first-child { + opacity: 0.6; + transition: opacity 200ms linear; +} + +.moodleoverflowpost.pendingreview:hover > :first-child { + opacity: 1; +} + @media (max-width: 600px) { .hide-600 { display: none; } +} + +.icon.moodleoverflow-icon-big { + font-size: 2em; + width: 32px; + height: 32px; } \ No newline at end of file diff --git a/templates/answer.mustache b/templates/answer.mustache index 193ac4ea624..be755f0f9ab 100644 --- a/templates/answer.mustache +++ b/templates/answer.mustache @@ -30,93 +30,108 @@ {{/ isfirstunread}} -{{! Print the anchor to the post. }} - - {{! Start the post. Mark it read or unread. }} -
- {{#showvotes}} -
-
- {{> mod_moodleoverflow/postvoting }} +
+
+ {{^needsreview}} + {{#showvotes}} +
+
+ {{> mod_moodleoverflow/postvoting }} +
+
+ {{/showvotes}} + {{^showvotes}} +
+ {{/showvotes}} + {{/needsreview}} + {{#needsreview}} +
+ {{#pix}}i/pending-big, mod_moodleoverflow, {{#str}}pending_review, mod_moodleoverflow{{/str}}{{/pix}}
-
- {{/showvotes}} - {{^showvotes}} -
- {{/showvotes}} -
-
- {{{ postcontent }}} -
-
- {{#attachments}} - {{#image}} - + {{/needsreview}} +
+
+ {{{ postcontent }}} +
+
+ {{#attachments}} + {{#image}} + +
+ {{/image}} + {{^image}} + + {{{icon}}} + + + {{filename}} + + {{/image}}
- {{/image}} - {{^image}} - - {{{icon}}} - - - {{filename}} - - {{/image}} -
- {{/attachments}} -
-