From 5bb4a5c090f3bd72780a8c0cb475cd5f71761f2f Mon Sep 17 00:00:00 2001 From: NinaHerrmann <nina.herrmann@uni-muenster.de> Date: Fri, 19 May 2023 13:38:28 +0200 Subject: [PATCH 1/2] WIP add link to overview to profile page TODO put content on pages --- lang/en/moodleoverflow.php | 9 +++++++- lib.php | 35 +++++++++++++++++++++++++++++- settings.php | 5 +++++ viewcontributingposts.php | 43 +++++++++++++++++++++++++++++++++++++ viewstartingdiscussion.php | 44 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 viewcontributingposts.php create mode 100644 viewstartingdiscussion.php diff --git a/lang/en/moodleoverflow.php b/lang/en/moodleoverflow.php index 13d0e76ec1..aca5b27209 100644 --- a/lang/en/moodleoverflow.php +++ b/lang/en/moodleoverflow.php @@ -196,6 +196,14 @@ $string['answer'] = '{$a} Answer'; $string['answers'] = '{$a} Answers'; +// Strings for Overview Pages +$string['overviewdiscussions'] = 'Overview of all started discussions'; +$string['overviewposts'] = 'Overview of all posts'; +$string['viewdiscussions'] = 'View started Moodleoverflow discussions'; +$string['viewposts'] = 'View answers to Moodleoverflow discussions'; +$string['showoverviewprofilpage'] = 'Link Overview on Profile Page'; +$string['showoverviewprofilpage_desc'] = 'Show Links to Overview Pages in User Profile Page'; + // Strings for the readtracking.php. $string['markreadfailed'] = 'A post of the discussion could not be marked as read.'; $string['markdiscussionreadsuccessful'] = 'The discussion has been marked as read.'; @@ -465,7 +473,6 @@ $string['your_post_was_rejected_with_reason'] = 'Your post was rejected with the following reason:'; $string['original_post'] = 'Original post'; - // Daily mail message. $string['digestunreadpost'] = 'Course: {$a->linktocourse} -> {$a->linktoforum}, Topic: {$a->linktodiscussion} has {$a->unreadposts} unread posts.'; diff --git a/lib.php b/lib.php index 5ee67061d7..d2b46a8792 100644 --- a/lib.php +++ b/lib.php @@ -552,7 +552,40 @@ function moodleoverflow_extend_settings_navigation(settings_navigation $settings } } } - +/** + * Insert Overview of discussions and post in the Miscellaneous block in the my profile page. + * + * @param tree $tree tree + * @param stdClass $user user + * @param int $iscurrentuser iscurrentuser + */ +function moodleoverflow_myprofile_navigation(core_user\output\myprofile\tree $tree, $user, $iscurrentuser, $course) { + global $DB, $CFG, $USER; + if (isguestuser($user)) { + // Coopy from forum - the guest user cannot post, so it is not possible to view any posts. + // May as well just bail aggressively here. + return false; + } + if (get_config('moodleoverflow', 'showoverviewprofilpage')) { + $viewdiscussionurl = new moodle_url('/mod/moodleoverflow/viewstartingdiscussion.php'); + if (!empty($course)) { + $viewdiscussionurl->param('course', $course->id); + } + $discussionnode = new core_user\output\myprofile\node('miscellaneous', 'moodleoverflowdiscussions', + get_string('viewdiscussions', 'moodleoverflow'), null, $viewdiscussionurl); + $tree->add_node($discussionnode); + $viewposturl = new moodle_url('/mod/moodleoverflow/viewcontributingposts.php'); + if (!empty($course)) { + $viewposturl->param('course', $course->id); + } + $postnode = new core_user\output\myprofile\node('miscellaneous', 'moodleoverflowposts', + get_string('viewposts', 'moodleoverflow'), null, $viewposturl); + $tree->add_node($postnode); + return true; + } else { + return false; + } +} /** * Determine the current context if one wa not already specified. * diff --git a/settings.php b/settings.php index 02376d7b9f..941ec44741 100644 --- a/settings.php +++ b/settings.php @@ -97,6 +97,11 @@ $settings->add(new admin_setting_configcheckbox('moodleoverflow/allowreview', get_string('allowreview', 'moodleoverflow'), get_string('allowreview_desc', 'moodleoverflow'), 1)); + // Allow teachers to enable review before publish. + $settings->add(new admin_setting_configcheckbox('moodleoverflow/showoverviewprofilpage', + get_string('showoverviewprofilpage', 'moodleoverflow'), + get_string('showoverviewprofilpage_desc', 'moodleoverflow'), 1)); + $settings->add(new admin_setting_configtext('moodleoverflow/reviewpossibleaftertime', get_string('reviewpossibleaftertime', 'moodleoverflow'), get_string('reviewpossibleaftertime_desc', 'moodleoverflow'), 1800, PARAM_INT)); diff --git a/viewcontributingposts.php b/viewcontributingposts.php new file mode 100644 index 0000000000..cba854c9e6 --- /dev/null +++ b/viewcontributingposts.php @@ -0,0 +1,43 @@ +<?php +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see <http://www.gnu.org/licenses/>. + +/** + * Prints a particular instance of moodleoverflow + * + * You can have a rather longer description of the file as well, + * if you like, and it can span multiple lines. + * + * @package mod_moodleoverflow + * @copyright 2017 Kennet Winter <k_wint10@uni-muenster.de> + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +// Include config and locallib. +require_once(__DIR__.'/../../config.php'); +global $PAGE, $OUTPUT; +$params = array(); +$PAGE->set_url('/mod/moodleoverflow/viewcontributingposts.php', $params); +require_login(); + +// Systemwide context as all post in moodleoverflows are displayed. +$PAGE->set_context(context_system::instance()); + +$PAGE->set_title(get_string('overviewposts', 'mod_moodleoverflow')); +$PAGE->set_heading(get_string('overviewposts', 'mod_moodleoverflow')); + +echo $OUTPUT->header(); +echo "hello world"; +echo $OUTPUT->footer(); diff --git a/viewstartingdiscussion.php b/viewstartingdiscussion.php new file mode 100644 index 0000000000..12df6426df --- /dev/null +++ b/viewstartingdiscussion.php @@ -0,0 +1,44 @@ +<?php +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see <http://www.gnu.org/licenses/>. + +/** + * Prints a particular instance of moodleoverflow + * + * You can have a rather longer description of the file as well, + * if you like, and it can span multiple lines. + * + * @package mod_moodleoverflow + * @copyright 2017 Kennet Winter <k_wint10@uni-muenster.de> + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +// Include config and locallib. +require_once(__DIR__.'/../../config.php'); + +global $PAGE, $OUTPUT; +$params = array(); +$PAGE->set_url('/mod/moodleoverflow/viewstartingdiscussion.php', $params); +require_login(); + +// Systemwide context as all started discussion in moodleoverflows are displayed. +$PAGE->set_context(context_system::instance()); + +$PAGE->set_title(get_string('overviewdiscussions', 'mod_moodleoverflow')); +$PAGE->set_heading(get_string('overviewdiscussions', 'mod_moodleoverflow')); + +echo $OUTPUT->header(); +echo "hello world"; +echo $OUTPUT->footer(); \ No newline at end of file From 98a93c6800b4ee8cfaf8f7f1f87e149ffb37570b Mon Sep 17 00:00:00 2001 From: NinaHerrmann <nina.herrmann@uni-muenster.de> Date: Fri, 19 May 2023 16:32:19 +0200 Subject: [PATCH 2/2] WIP started startingdiscussion overview page TODO check for unread post in normal overview check for redirects in overviewpage implement second page - nice to have filter for pages --- lib.php | 2 +- locallib.php | 422 ++++++++++++--------- renderer.php | 10 + styles.css | 3 + templates/discussion_started_list.mustache | 149 ++++++++ viewcontributingposts.php | 7 + viewstartingdiscussion.php | 59 ++- 7 files changed, 464 insertions(+), 188 deletions(-) create mode 100644 templates/discussion_started_list.mustache diff --git a/lib.php b/lib.php index d2b46a8792..0b900a34b7 100644 --- a/lib.php +++ b/lib.php @@ -569,7 +569,7 @@ function moodleoverflow_myprofile_navigation(core_user\output\myprofile\tree $tr if (get_config('moodleoverflow', 'showoverviewprofilpage')) { $viewdiscussionurl = new moodle_url('/mod/moodleoverflow/viewstartingdiscussion.php'); if (!empty($course)) { - $viewdiscussionurl->param('course', $course->id); + $viewdiscussionurl->param('courseid', $course->id); } $discussionnode = new core_user\output\myprofile\node('miscellaneous', 'moodleoverflowdiscussions', get_string('viewdiscussions', 'moodleoverflow'), null, $viewdiscussionurl); diff --git a/locallib.php b/locallib.php index 71ff6052eb..5bb4aeacbf 100644 --- a/locallib.php +++ b/locallib.php @@ -174,9 +174,6 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = - echo $OUTPUT->paging_bar($numberofdiscussions, $page, $perpage, "view.php?id=$cm->id"); } - // Get the number of replies for each discussion. - $replies = moodleoverflow_count_discussion_replies($cm); - // Check whether the moodleoverflow instance can be tracked and is tracked. if ($cantrack = \mod_moodleoverflow\readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow)) { $istracked = \mod_moodleoverflow\readtracking::moodleoverflow_is_tracked($moodleoverflow); @@ -213,9 +210,6 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = - echo $OUTPUT->paging_bar($numberofdiscussions, $page, $perpage, "view.php?id=$cm->id"); } - // Get the number of replies for each discussion. - $replies = moodleoverflow_count_discussion_replies($cm); - // Check whether the user can subscribe to the discussion. $cansubtodiscussion = false; if ((!is_guest($context, $USER) && isloggedin()) && has_capability('mod/moodleoverflow:viewdiscussion', $context) @@ -230,221 +224,241 @@ function moodleoverflow_print_latest_discussions($moodleoverflow, $cm, $page = - } // Iterate through every visible discussion. - $i = 0; $preparedarray = array(); foreach ($discussions as $discussion) { - $preparedarray[$i] = array(); + $preparedarray[] = prepare_data_for_discussions($discussion, $istracked, $canreviewposts, $moodleoverflow, $context, $cm); + } - // Handle anonymized discussions. - if ($discussion->userid == 0) { - $discussion->name = get_string('privacy:anonym_discussion_name', 'mod_moodleoverflow'); - } + // Include the renderer. + $renderer = $PAGE->get_renderer('mod_moodleoverflow'); - // Set the amount of replies for every discussion. - if (!empty($replies[$discussion->discussion])) { - $discussion->replies = $replies[$discussion->discussion]->replies; - $discussion->lastpostid = $replies[$discussion->discussion]->lastpostid; - } else { - $discussion->replies = 0; - } + // Collect the needed data being submitted to the template. + $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; + $mustachedata->markallread = $markallread; + $mustachedata->cansubtodiscussion = $cansubtodiscussion; + $mustachedata->canmovetopic = $canmovetopic; + $mustachedata->cannormoveorsub = ((!$canmovetopic) && (!$cansubtodiscussion)); + // Print the template. + echo $renderer->render_discussion_list($mustachedata); + + // Show the paging bar if paging is activated. + if ($page != -1) { + echo $OUTPUT->paging_bar($numberofdiscussions, $page, $perpage, "view.php?id=$cm->id"); + } +} + +/** + * @param array $discussions + * @param bool $istracked + * @param bool $canreviewposts + * @param object $moodleoverflow + * @return array + * @throws coding_exception + * @throws moodle_exception + */ +function prepare_data_for_discussions($discussion, $istracked, $canreviewposts, $moodleoverflow, $context, $cm, $onlystarted = false): stdClass { + global $CFG, $OUTPUT, $USER, $DB; + // Get the number of replies for each discussion. + $replies = moodleoverflow_count_discussion_replies($cm); + // Handle anonymized discussions. + if ($discussion->userid == 0) { + $discussion->name = get_string('privacy:anonym_discussion_name', 'mod_moodleoverflow'); + } - // Set the right text. - $preparedarray[$i]['answertext'] = ($discussion->replies == 1) ? 'answer' : 'answers'; + // Set the amount of replies for every discussion. + if (!empty($replies[$discussion->discussion])) { + $discussion->replies = $replies[$discussion->discussion]->replies; + $discussion->lastpostid = $replies[$discussion->discussion]->lastpostid; + } else { + $discussion->replies = 0; + } - // Set the amount of unread messages for each discussion. - if (!$istracked) { - $discussion->unread = '-'; - } else if (empty($USER)) { + // Set the right text. + $discussion->answertext = ($discussion->replies == 1) ? 'answer' : 'answers'; + // Set the amount of unread messages for each discussion. + if (!$istracked) { + $discussion->unread = '-'; + } else if (empty($USER)) { + $discussion->unread = 0; + } else { + if (empty($unreads[$discussion->discussion])) { $discussion->unread = 0; } else { - if (empty($unreads[$discussion->discussion])) { - $discussion->unread = 0; - } else { - $discussion->unread = $unreads[$discussion->discussion]; - } + $discussion->unread = $unreads[$discussion->discussion]; } + } - // Check if the question owner marked the question as helpful. - $markedhelpful = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussion->discussion, false); - $preparedarray[$i]['starterlink'] = null; - if ($markedhelpful) { - $link = '/mod/moodleoverflow/discussion.php?d='; - $markedhelpful = $markedhelpful[array_key_first($markedhelpful)]; + // Check if the question owner marked the question as helpful. + $markedhelpful = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussion->discussion, false); + $discussion->starterlink = null; + if ($markedhelpful) { + $link = '/mod/moodleoverflow/discussion.php?d='; + $markedhelpful = $markedhelpful[array_key_first($markedhelpful)]; - $preparedarray[$i]['starterlink'] = new moodle_url($link . - $markedhelpful->discussionid . '#p' . $markedhelpful->postid); - } + $discussion->starterlink = new moodle_url($link . + $markedhelpful->discussionid . '#p' . $markedhelpful->postid); + } - // Check if a teacher marked a post as solved. - $markedsolution = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussion->discussion, true); - $preparedarray[$i]['teacherlink'] = null; - if ($markedsolution) { - $link = '/mod/moodleoverflow/discussion.php?d='; - $markedsolution = $markedsolution[array_key_first($markedsolution)]; + // Check if a teacher marked a post as solved. + $markedsolution = \mod_moodleoverflow\ratings::moodleoverflow_discussion_is_solved($discussion->discussion, true); + $discussion->teacherlink = null; + if ($markedsolution) { + $link = '/mod/moodleoverflow/discussion.php?d='; + $markedsolution = $markedsolution[array_key_first($markedsolution)]; - $preparedarray[$i]['teacherlink'] = new moodle_url($link . - $markedsolution->discussionid . '#p' . $markedsolution->postid); - } + $discussion->teacherlink = new moodle_url($link . + $markedsolution->discussionid . '#p' . $markedsolution->postid); + } - // Check if a single post was marked by the question owner and a teacher. - $statusboth = false; - if ($markedhelpful && $markedsolution) { - if ($markedhelpful->postid == $markedsolution->postid) { - $statusboth = true; - } + // Check if a single post was marked by the question owner and a teacher. + $statusboth = false; + if ($markedhelpful && $markedsolution) { + if ($markedhelpful->postid == $markedsolution->postid) { + $statusboth = true; } + } - // Get the amount of votes for the discussion. + // Get the amount of votes for the discussion. + if (!$onlystarted) { $votes = \mod_moodleoverflow\ratings::moodleoverflow_get_ratings_by_discussion($discussion->discussion, $discussion->id); - $votes = $votes->upvotes - $votes->downvotes; - $preparedarray[$i]['votetext'] = ($votes == 1) ? 'vote' : 'votes'; + } else { + $votes = \mod_moodleoverflow\ratings::moodleoverflow_get_ratings_by_discussion($discussion->id, $discussion->firstpost); + } + $votes = $votes->upvotes - $votes->downvotes; + $discussion->votetext = ($votes == 1) ? 'vote' : 'votes'; + $discussion->votes = $votes; - // Use the discussions name instead of the subject of the first post. - $discussion->subject = $discussion->name; + // Use the discussions name instead of the subject of the first post. + $discussion->subject = $discussion->name; - // Format the subjectname and the link to the topic. - $preparedarray[$i]['subjecttext'] = format_string($discussion->subject); - $preparedarray[$i]['subjectlink'] = $CFG->wwwroot . '/mod/moodleoverflow/discussion.php?d=' . $discussion->discussion; + // Format the subjectname and the link to the topic. + $discussion->subjecttext = format_string($discussion->subject); + $discussion->subjectlink = $CFG->wwwroot . '/mod/moodleoverflow/discussion.php?d=' . $discussion->discussion; - // Get information about the user who started the discussion. - $startuser = new stdClass(); - if ($CFG->branch >= 311) { - $startuserfields = \core_user\fields::get_picture_fields(); - } else { - $startuserfields = explode(',', user_picture::fields()); - } + // Get information about the user who started the discussion. + $startuser = new stdClass(); + if ($CFG->branch >= 311) { + $startuserfields = \core_user\fields::get_picture_fields(); + } else { + $startuserfields = explode(',', user_picture::fields()); + } - $startuser = username_load_fields_from_object($startuser, $discussion, null, $startuserfields); - $startuser->id = $discussion->userid; + $startuser = username_load_fields_from_object($startuser, $discussion, null, $startuserfields); + $startuser->id = $discussion->userid; - // Discussion was anonymized. - if ($startuser->id == 0 || $moodleoverflow->anonymous != anonymous::NOT_ANONYMOUS) { - // Get his picture, his name and the link to his profile. - if ($startuser->id == $USER->id) { - $preparedarray[$i]['username'] = get_string('anonym_you', 'mod_moodleoverflow'); - // Needs to be included for reputation to update properly. - $preparedarray[$i]['userlink'] = $CFG->wwwroot . '/user/view.php?id=' . - $discussion->userid . '&course=' . $moodleoverflow->course; + if ($onlystarted) { + $startuser = $DB->get_record('user', array('id' => $discussion->userid)); + } + // Discussion was anonymized. + if ($startuser->id == 0 || $moodleoverflow->anonymous != anonymous::NOT_ANONYMOUS) { + // Get his picture, his name and the link to his profile. + if ($startuser->id == $USER->id) { + $discussion->username = get_string('anonym_you', 'mod_moodleoverflow'); + // Needs to be included for reputation to update properly. + $discussion->userlink = $CFG->wwwroot . '/user/view.php?id=' . + $discussion->userid . '&course=' . $moodleoverflow->course; - } else { - $preparedarray[$i]['username'] = get_string('privacy:anonym_user_name', 'mod_moodleoverflow'); - $preparedarray[$i]['userlink'] = null; - } } else { - // Get his picture, his name and the link to his profile. - $preparedarray[$i]['picture'] = $OUTPUT->user_picture($startuser, array('courseid' => $moodleoverflow->course, - 'link' => false)); - $preparedarray[$i]['username'] = fullname($startuser, has_capability('moodle/site:viewfullnames', $context)); - $preparedarray[$i]['userlink'] = $CFG->wwwroot . '/user/view.php?id=' . - $discussion->userid . '&course=' . $moodleoverflow->course; + $discussion->username = get_string('privacy:anonym_user_name', 'mod_moodleoverflow'); + $discussion->userlink = null; } - - // 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; - $preparedarray[$i]['unread'] = ($preparedarray[$i]['unreadamount'] > 0) ? true : false; - $preparedarray[$i]['unreadlink'] = $CFG->wwwroot . - '/mod/moodleoverflow/discussion.php?d=' . $discussion->discussion . '#unread'; - $link = '/mod/moodleoverflow/markposts.php?m='; - $preparedarray[$i]['markreadlink'] = $CFG->wwwroot . $link . $moodleoverflow->id . '&d=' . $discussion->discussion; - - // Check the date of the latest post. Just in case the database is not consistent. - $usedate = (empty($discussion->timemodified)) ? $discussion->modified : $discussion->timemodified; - - // Get the name and the link to the profile of the user, that is related to the latest post. - $usermodified = new stdClass(); - $usermodified->id = $discussion->usermodified; - - if ($usermodified->id == 0 || $moodleoverflow->anonymous) { - if ($usermodified->id == $USER->id) { - $preparedarray[$i]['lastpostusername'] = null; - $preparedarray[$i]['lastpostuserlink'] = null; - } else { - $preparedarray[$i]['lastpostusername'] = null; - $preparedarray[$i]['lastpostuserlink'] = null; - } + } else { + // Get his picture, his name and the link to his profile. + $discussion->picture = $OUTPUT->user_picture($startuser, array('courseid' => $moodleoverflow->course, + 'link' => false)); + $discussion->username = fullname($startuser, has_capability('moodle/site:viewfullnames', $context)); + $discussion->userlink = $CFG->wwwroot . '/user/view.php?id=' . + $discussion->userid . '&course=' . $moodleoverflow->course; + } + + // Get the amount of replies and the link to the discussion. + $discussion->replyamount = $discussion->replies; + $discussion->questionunderreview = $discussion->reviewed == 0; + + // Are there unread messages? Create a link to them. + $discussion->unreadamount = $discussion->unread; + $discussion->unread = ($discussion->unreadamount > 0) ? true : false; + $discussion->unreadlink = $CFG->wwwroot . + '/mod/moodleoverflow/discussion.php?d=' . $discussion->discussion . '#unread'; + $link = '/mod/moodleoverflow/markposts.php?m='; + $discussion->markreadlink = $CFG->wwwroot . $link . $moodleoverflow->id . '&d=' . $discussion->discussion; + + // Check the date of the latest post. Just in case the database is not consistent. + $usedate = (empty($discussion->timemodified)) ? $discussion->modified : $discussion->timemodified; + + // Get the name and the link to the profile of the user, that is related to the latest post. + $usermodified = new stdClass(); + $usermodified->id = $discussion->usermodified; + + if ($usermodified->id == 0 || $moodleoverflow->anonymous) { + if ($usermodified->id == $USER->id) { + $discussion->lastpostusername = null; + $discussion->lastpostuserlink = null; } else { - $usermodified = username_load_fields_from_object($usermodified, $discussion, 'um'); - $preparedarray[$i]['lastpostusername'] = fullname($usermodified); - $preparedarray[$i]['lastpostuserlink'] = $CFG->wwwroot . '/user/view.php?id=' . - $discussion->usermodified . '&course=' . $moodleoverflow->course; + $discussion->lastpostusername = null; + $discussion->lastpostuserlink = null; } - - // Get the date of the latest post of the discussion. - $parenturl = (empty($discussion->lastpostid)) ? '' : '&parent=' . $discussion->lastpostid; - $preparedarray[$i]['lastpostdate'] = userdate($usedate, get_string('strftimerecentfull')); - $preparedarray[$i]['lastpostlink'] = $preparedarray[$i]['subjectlink'] . $parenturl; - - // Check whether the discussion is subscribed. - $preparedarray[$i]['discussionsubicon'] = false; - if ((!is_guest($context, $USER) && isloggedin()) && has_capability('mod/moodleoverflow:viewdiscussion', $context)) { - // Discussion subscription. - if (\mod_moodleoverflow\subscriptions::is_subscribable($moodleoverflow, $context)) { - $preparedarray[$i]['discussionsubicon'] = \mod_moodleoverflow\subscriptions::get_discussion_subscription_icon( - $moodleoverflow, $context, $discussion->discussion); - } + } else { + $usermodified = username_load_fields_from_object($usermodified, $discussion, 'um'); + if ($onlystarted) { + $usermodified = $DB->get_record('user', array('id' => $discussion->usermodified)); } - - 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); + $discussion->lastpostusername = fullname($usermodified); + $discussion->lastpostuserlink = $CFG->wwwroot . '/user/view.php?id=' . + $discussion->usermodified . '&course=' . $moodleoverflow->course; + } + + // Get the date of the latest post of the discussion. + $parenturl = (empty($discussion->lastpostid)) ? '' : '&parent=' . $discussion->lastpostid; + $discussion->lastpostdate = userdate($usedate, get_string('strftimerecentfull')); + $discussion->lastpostlink = $discussion->subjectlink . $parenturl; + + // Check whether the discussion is subscribed. + $discussion->discussionsubicon = false; + if ((!is_guest($context, $USER) && isloggedin()) && has_capability('mod/moodleoverflow:viewdiscussion', $context)) { + // Discussion subscription. + if (\mod_moodleoverflow\subscriptions::is_subscribable($moodleoverflow, $context)) { + $discussion->discussionsubicon = \mod_moodleoverflow\subscriptions::get_discussion_subscription_icon( + $moodleoverflow, $context, $discussion->discussion); } + } - // Build linktopopup to move a topic. - $linktopopup = $CFG->wwwroot . '/mod/moodleoverflow/view.php?id=' . $cm->id . '&movetopopup=' . $discussion->discussion; - $preparedarray[$i]['linktopopup'] = $linktopopup; - - // Add all created data to an array. - $preparedarray[$i]['markedhelpful'] = $markedhelpful; - $preparedarray[$i]['markedsolution'] = $markedsolution; - $preparedarray[$i]['statusboth'] = $statusboth; - $preparedarray[$i]['votes'] = $votes; + if ($canreviewposts) { + $reviewinfo = review::get_short_review_info_for_discussion($discussion->discussion); + $discussion->needreview = $reviewinfo->count; + $discussion->reviewlink = (new moodle_url('/mod/moodleoverflow/discussion.php', [ + 'd' => $discussion->discussion + ], 'p' . $reviewinfo->first))->out(false); + } - // Did the user rated this post? - $rating = \mod_moodleoverflow\ratings::moodleoverflow_user_rated($discussion->firstpost); + // Build linktopopup to move a topic. + $linktopopup = $CFG->wwwroot . '/mod/moodleoverflow/view.php?id=' . $cm->id . '&movetopopup=' . $discussion->discussion; + $discussion->linktopopup = $linktopopup; - $firstpost = moodleoverflow_get_post_full($discussion->firstpost); + // Add all created data to an array. + $discussion->markedhelpful = $markedhelpful; + $discussion->markedsolution = $markedsolution; + $discussion->statusboth = $statusboth; - $preparedarray[$i]['userupvoted'] = ($rating->rating ?? null) == RATING_UPVOTE; - $preparedarray[$i]['userdownvoted'] = ($rating->rating ?? null) == RATING_DOWNVOTE; - $preparedarray[$i]['canchange'] = \mod_moodleoverflow\ratings::moodleoverflow_user_can_rate($firstpost, $context) && - $startuser->id != $USER->id; - $preparedarray[$i]['postid'] = $discussion->firstpost; + // Did the user rated this post? + $rating = \mod_moodleoverflow\ratings::moodleoverflow_user_rated($discussion->firstpost); - // Go to the next discussion. - $i++; - } + $firstpost = moodleoverflow_get_post_full($discussion->firstpost); - // Include the renderer. - $renderer = $PAGE->get_renderer('mod_moodleoverflow'); + $discussion->userupvoted = ($rating->rating ?? null) == RATING_UPVOTE; + $discussion->userdownvoted = ($rating->rating ?? null) == RATING_DOWNVOTE; + $discussion->canchange = \mod_moodleoverflow\ratings::moodleoverflow_user_can_rate($firstpost, $context) && + $startuser->id != $USER->id; + $discussion->postid = $discussion->firstpost; - // Collect the needed data being submitted to the template. - $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; - $mustachedata->markallread = $markallread; - $mustachedata->cansubtodiscussion = $cansubtodiscussion; - $mustachedata->canmovetopic = $canmovetopic; - $mustachedata->cannormoveorsub = ((!$canmovetopic) && (!$cansubtodiscussion)); - // Print the template. - echo $renderer->render_discussion_list($mustachedata); - - // Show the paging bar if paging is activated. - if ($page != -1) { - echo $OUTPUT->paging_bar($numberofdiscussions, $page, $perpage, "view.php?id=$cm->id"); - } + return $discussion; } - /** * Prints a popup with a menu of other moodleoverflow in the course. * Menu to move a topic to another moodleoverflow forum. @@ -639,6 +653,46 @@ function moodleoverflow_get_discussions_unread($cm) { } } +/** + * Gets the number of unread post for a single discussion. + * @param int $discussionid + * @param $cm + * @return false|mixed + * @throws coding_exception + * @throws dml_exception + */ +function get_discussion_unread($discussionid, $cm) { + global $USER, $DB; + $now = round(time(), -2); + $cutoffdate = $now - (get_config('moodleoverflow', 'oldpostdays') * 24 * 60 * 60); + + $params = [ + 'userid' => $USER->id, + 'cutoffdate' => $cutoffdate, + 'discussionid' => $discussionid + ]; + + $modcontext = context_module::instance($cm->id); + $whereconditions = ['p.modified >= :cutoffdate', 'r.id is NULL', 'd.id = :discussionid']; + + if (!has_capability('mod/moodleoverflow:reviewpost', $modcontext)) { + $whereconditions[] = '(p.reviewed = 1 OR p.userid = :userid2)'; + $params['userid2'] = $USER->id; + } + + $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 $wheresql + GROUP BY d.id"; + + return $DB->get_record_sql($sql, $params); +} + /** * Gets a post with all info ready for moodleoverflow_print_post. * Most of these joins are just to get the forum id. diff --git a/renderer.php b/renderer.php index 905bc3d12d..ed9e6bb81d 100644 --- a/renderer.php +++ b/renderer.php @@ -46,6 +46,16 @@ class mod_moodleoverflow_renderer extends plugin_renderer_base { public function render_discussion_list($data) { return $this->render_from_template('mod_moodleoverflow/discussions', $data); } + /** + * Display the discussion list for the viewstartingdiscussion.php. + * + * @param object $data The prepared variables. + * + * @return string + */ + public function render_discussion_started_list($data) { + return $this->render_from_template('mod_moodleoverflow/discussion_started_list', $data); + } /** * Display the forum list in the view.php if a discussion needs to be moved to another forum. diff --git a/styles.css b/styles.css index f0c92ff5bf..2adfa93825 100644 --- a/styles.css +++ b/styles.css @@ -12,6 +12,9 @@ /* * The discussionlist in the view.php */ +.moodleoverflowheaderlist { + border-spacing: 0; +} .moodleoverflowheaderlist th.header.replies .iconsmall { margin: 0 .3em; diff --git a/templates/discussion_started_list.mustache b/templates/discussion_started_list.mustache new file mode 100644 index 0000000000..14673f18a3 --- /dev/null +++ b/templates/discussion_started_list.mustache @@ -0,0 +1,149 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see <http://www.gnu.org/licenses/>. +}} +{{! + @template mod_moodleoverflow/discussion_list + + Moodleoverflow discussion_list template. + The purpose of this template is to render a list of discussions for the view.php. + + Example context (json): + { + } +}} + +{{! There are no discussions. Print the string that specifies it. }} +{{^hasdiscussions}} + <div class="moodleoverflowdiscussions"> + ( {{#str}} nodiscussions, moodleoverflow {{/str}} ) + </div> +{{/hasdiscussions}} + +{{! There are discussions. Start to print the table. }} +{{#hasdiscussions}} + + {{#discussions}} + <div class="moodleoverflowdiscussion d-flex border p-2 mb-3" data-moodleoverflow-postid="{{postid}}"> + <div class="leftbox text-center mr-2"> + <div class="votes text-center mb-1"> + {{> mod_moodleoverflow/postvoting }} + </div> + <div class="status moodleoverflow-icon-no-margin"> + {{# questionunderreview }} + {{#pix}}i/duration, moodle, {{#str}}pending_review, mod_moodleoverflow{{/str}}{{/pix}} + {{/ questionunderreview }} + {{^ questionunderreview }} + {{#markedsolution}} + <a href="{{teacherlink}}">{{! avoid whitespace + !}}{{# pix}} i/status-solved, moodleoverflow, {{#str}}containsteacherrating, moodleoverflow{{/str}} {{/ pix}}{{! + !}}</a> + {{/markedsolution}} + {{#markedhelpful}} + <a href="{{starterlink}}">{{! avoid whitespace + !}}{{# pix}} i/status-helpful, moodleoverflow, {{#str}}containsstarterrating, moodleoverflow{{/str}} {{/ pix}}{{! + !}}</a> + {{/ markedhelpful }} + {{/ questionunderreview }} + </div> + <div class="d-flex flex-colum flex-wrap justify-content-around"> + {{# canreview }} + {{# needreview }} + <div class="reviews my-1"> + <a href="{{{reviewlink}}}" class="text-danger bold" + title="{{#str}}amount_waiting_for_review, mod_moodleoverflow, {{needreview}}{{/str}}"> + {{#pix}}i/commenting, mod_moodleoverflow{{/pix}}{{ needreview }} + </a> + </div> + {{/ needreview }} + {{/ canreview }} + <div class="replies my-1" title="{{#str}}answers, mod_moodleoverflow, {{replyamount}}{{/str}}"> + <span><span class="reply-correction">{{#pix}}i/reply, mod_moodleoverflow {{/pix}}</span>{{ replyamount }}</span> + </div> + </div> + + </div> + <div class="w-100 d-flex flex-column justify-content-between"> + <div class="d-flex justify-content-between"> + <a class="subject mb-3 mr-3" href="{{{subjectlink}}}">{{{ subjecttext }}}</a> + + <div class="pt-2 text-right"> + {{#cansubtodiscussion}} + <div class="discussionsubscription">{{{discussionsubicon}}}</div> + {{/cansubtodiscussion}} + {{#canmovetopic}} + <div class="discussionmove"><a href='{{ linktopopup }}'>{{#pix}} i/arrow-right, core, {{#str}}movetopicicon, moodleoverflow{{/str}} {{/pix}}</a></div> + {{/canmovetopic}} + {{#unread}} + <div class="mod_moodleoverflow-no-wrap unread-part d-flex align-items-baseline"> + <a class="mark-read" href="{{markreadlink}}" + title="{{#str}}markallread, moodleoverflow{{/str}}"> + {{#pix}}i/delete, core {{/pix}} + </a> + <a class="d-inline-block mt-3" href="{{ unreadlink }}" + title="{{#str}}amount_unread_posts_in_discussion, mod_moodleoverflow, {{unreadamount}} {{/str}}"> + <span class="fa fa-envelope icon mr-0 moodleoverflow-icon-1_5x text-muted"> + </span><span class="unread-bubble"> + {{ unreadamount }} + </span> + </a> + </div> + {{/unread}} + </div> + </div> + <div class="d-flex justify-content-between flex-wrap moodleoverflow-gap-small"> + {{#userlink}} + <a href="{{{userlink}}}" class="user-info d-flex questioner px-2 py-1 align-items-center"> + {{/userlink}} + {{^userlink}} + <div class="user-info d-flex questioner px-2 py-1 align-items-center"> + {{/userlink}} + + {{# picture }} + <div class="user-avatar"> + {{{ picture }}} + </div> + {{/ picture }} + <div class="user-details"> + {{{ username }}} + </div> + {{^userlink}} + </div> + {{/userlink}} + {{#userlink}} + </a> + {{/userlink}} + <div class="d-flex flex-wrap align-items-center"> + <span class="text-muted">Last post:</span> + <div> + <a href="{{{lastpostlink}}}" class="user-info d-flex px-2 py-1 align-items-center"> + {{# lastpostuserpicture }} + <div class="user-avatar"> + {{{ lastpostuserpicture }}} + </div> + {{/ lastpostuserpicture }} + <div class="user-details line-height-3"> + {{{ lastpostusername }}}<br> + {{{ lastpostdate }}} + </div> + </a> + </div> + </div> + </div> + </div> + </div> + {{/discussions}} + +{{/hasdiscussions}} diff --git a/viewcontributingposts.php b/viewcontributingposts.php index cba854c9e6..83086ce397 100644 --- a/viewcontributingposts.php +++ b/viewcontributingposts.php @@ -28,7 +28,14 @@ // Include config and locallib. require_once(__DIR__.'/../../config.php'); global $PAGE, $OUTPUT; +// If invoked from a course show the discussions from the course. +$courseid = optional_param('courseid', 0, PARAM_INT); $params = array(); + +if ($courseid) { + $params['courseid'] = $courseid; +} + $PAGE->set_url('/mod/moodleoverflow/viewcontributingposts.php', $params); require_login(); diff --git a/viewstartingdiscussion.php b/viewstartingdiscussion.php index 12df6426df..c73a8d38be 100644 --- a/viewstartingdiscussion.php +++ b/viewstartingdiscussion.php @@ -27,9 +27,17 @@ // Include config and locallib. require_once(__DIR__.'/../../config.php'); +require_once(__DIR__.'/locallib.php'); +use mod_moodleoverflow\readtracking; -global $PAGE, $OUTPUT; +global $PAGE, $OUTPUT, $USER, $DB; +// If invoked from a course show the discussions from the course. +$courseid = optional_param('courseid', 0, PARAM_INT); $params = array(); +if ($courseid) { + $params['courseid'] = $courseid; +} + $PAGE->set_url('/mod/moodleoverflow/viewstartingdiscussion.php', $params); require_login(); @@ -39,6 +47,51 @@ $PAGE->set_title(get_string('overviewdiscussions', 'mod_moodleoverflow')); $PAGE->set_heading(get_string('overviewdiscussions', 'mod_moodleoverflow')); +// Get all started discussions (in a course). +$discussions = $DB->get_records('moodleoverflow_discussions', array('userid' => $USER->id)); +// Collect the needed data being submitted to the template. +$discussionswithdetails = array(); +$tracking = new readtracking(); echo $OUTPUT->header(); -echo "hello world"; -echo $OUTPUT->footer(); \ No newline at end of file + +foreach ($discussions as $discussion) { + $moodleoverflow = $DB->get_record('moodleoverflow', array('id' => $discussion->moodleoverflow)); + $cm = get_coursemodule_from_instance('moodleoverflow', $moodleoverflow->id, $discussion->course, + false, MUST_EXIST); + $context = context_module::instance($cm->id); + + $canreviewposts = has_capability('mod/moodleoverflow:reviewpost', $context); + + if (has_capability('mod/moodleoverflow:viewdiscussion', $context)) { + if ($cantrack = \mod_moodleoverflow\readtracking::moodleoverflow_can_track_moodleoverflows($moodleoverflow)) { + $istracked = \mod_moodleoverflow\readtracking::moodleoverflow_is_tracked($moodleoverflow); + } else { + $istracked = false; + } + $discussion->discussion = $discussion->id; + + $firstpost = $DB->get_record('moodleoverflow_posts', array('id' => $discussion->firstpost)); + $discussion->reviewed = $firstpost->reviewed; + $newdiscussion = prepare_data_for_discussions($discussion, $istracked, $canreviewposts, $moodleoverflow, $context, $cm, true); + $newdiscussion->istracked = $istracked; + $newdiscussion->cantrack = $cantrack; + if ($unreadpost = get_discussion_unread($discussion->id, $cm)) { + $newdiscussion->unread = true; + $newdiscussion->unreadamount = $unreadpost->unread; + } + $newdiscussion->cansubtodiscussion = false; + if ((!is_guest($context, $USER) && isloggedin()) && has_capability('mod/moodleoverflow:viewdiscussion', $context) + && \mod_moodleoverflow\subscriptions::is_subscribable($moodleoverflow, $context)) { + $newdiscussion->cansubtodiscussion = true; + } + array_push($discussionswithdetails, $newdiscussion); + } +} +$mustachedata = new stdClass(); +$mustachedata->discussions = $discussionswithdetails; +$mustachedata->hasdiscussions = count($discussionswithdetails) >= 0; + +// Include the renderer. +$renderer = $PAGE->get_renderer('mod_moodleoverflow'); +echo $renderer->render_discussion_started_list($mustachedata); +echo $OUTPUT->footer();