diff --git a/amd/build/show_post.min.js b/amd/build/show_post.min.js new file mode 100644 index 0000000000..ffa000f9af --- /dev/null +++ b/amd/build/show_post.min.js @@ -0,0 +1,11 @@ +define("mod_moodleoverflow/show_post",["exports","core/str","core/prefetch"],function(_exports,_str,_prefetch){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(){(0,_prefetch.prefetchStrings)("moodleoverflow",["showpost_expand","showpost_collapse"]),postElement.setAttribute("expanded","false"),postElement.style.maxHeight="0px",addEventListener()}; +/** + * JavaScript for + * + * @module mod_moodleoverflow/show_post + * @copyright 2025 Tamaro Walter + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +const showpostButton=document.getElementById("moodleoverflow_showpost"),postElement=document.getElementById("moodleoverflow_original_post"),Selectors_actions={showpostbutton:'[data-action="mod_moodleoverflow/showpost_button"]'};const addEventListener=()=>{document.addEventListener("click",async e=>{e.target.closest(Selectors_actions.showpostbutton)&&("true"===postElement.getAttribute("expanded")?(showpostButton.textContent=await(0,_str.getString)("showpost_expand","moodleoverflow"),postElement.style.maxHeight="0px",postElement.setAttribute("expanded","false")):(showpostButton.textContent=await(0,_str.getString)("showpost_collapse","moodleoverflow"),postElement.style.maxHeight=`${postElement.scrollHeight}px`,postElement.setAttribute("expanded","true")))})}}); + +//# sourceMappingURL=show_post.min.js.map \ No newline at end of file diff --git a/amd/build/show_post.min.js.map b/amd/build/show_post.min.js.map new file mode 100644 index 0000000000..e5a054aa7c --- /dev/null +++ b/amd/build/show_post.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"show_post.min.js","sources":["../src/show_post.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\nimport {getString} from \"core/str\";\nimport {prefetchStrings} from 'core/prefetch';\n/**\n * JavaScript for\n *\n * @module mod_moodleoverflow/show_post\n * @copyright 2025 Tamaro Walter\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst showpostButton = document.getElementById('moodleoverflow_showpost');\nconst postElement = document.getElementById('moodleoverflow_original_post');\n\nconst Selectors = {\n actions: {\n showpostbutton: '[data-action=\"mod_moodleoverflow/showpost_button\"]',\n },\n};\n\n/**\n * Init function.\n */\nexport function init() {\n prefetchStrings('moodleoverflow', ['showpost_expand', 'showpost_collapse',]);\n postElement.setAttribute('expanded', 'false');\n postElement.style.maxHeight = '0px';\n addEventListener();\n}\n\n/**\n * Event listener.\n */\nconst addEventListener = () => {\n document.addEventListener('click', async e => {\n if (e.target.closest(Selectors.actions.showpostbutton)) {\n if (postElement.getAttribute('expanded') === 'true') {\n showpostButton.textContent = await getString('showpost_expand', 'moodleoverflow');\n postElement.style.maxHeight = '0px';\n postElement.setAttribute('expanded', 'false');\n } else {\n showpostButton.textContent = await getString('showpost_collapse', 'moodleoverflow');\n postElement.style.maxHeight = `${postElement.scrollHeight}px`;\n postElement.setAttribute('expanded', 'true');\n }\n }\n });\n};\n"],"names":["prefetchStrings","postElement","setAttribute","style","maxHeight","addEventListener","showpostButton","document","getElementById","Selectors","showpostbutton","async","e","target","closest","getAttribute","textContent","getString","scrollHeight"],"mappings":"qLAqCO,YACH,EAAAA,UAAAA,iBAAgB,iBAAkB,CAAC,kBAAmB,sBACtDC,YAAYC,aAAa,WAAY,SACrCD,YAAYE,MAAMC,UAAY,MAC9BC,kBACJ;;;;;;;;AAjBA,MAAMC,eAAiBC,SAASC,eAAe,2BACzCP,YAAcM,SAASC,eAAe,gCAEtCC,kBACO,CACLC,eAAgB,sDAiBxB,MAAML,iBAAmBA,KACrBE,SAASF,iBAAiB,QAASM,UAC3BC,EAAEC,OAAOC,QAAQL,kBAAkBC,kBACU,SAAzCT,YAAYc,aAAa,aACzBT,eAAeU,kBAAoB,EAAAC,gBAAU,kBAAmB,kBAChEhB,YAAYE,MAAMC,UAAY,MAC9BH,YAAYC,aAAa,WAAY,WAErCI,eAAeU,kBAAqB,EAAAC,gBAAU,oBAAqB,kBACnEhB,YAAYE,MAAMC,UAAY,GAAGH,YAAYiB,iBAC7CjB,YAAYC,aAAa,WAAY,YAInD"} \ No newline at end of file diff --git a/amd/src/show_post.js b/amd/src/show_post.js new file mode 100644 index 0000000000..87dd2e7039 --- /dev/null +++ b/amd/src/show_post.js @@ -0,0 +1,62 @@ +// 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 . + +import {getString} from "core/str"; +import {prefetchStrings} from 'core/prefetch'; +/** + * JavaScript for + * + * @module mod_moodleoverflow/show_post + * @copyright 2025 Tamaro Walter + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +const showpostButton = document.getElementById('moodleoverflow_showpost'); +const postElement = document.getElementById('moodleoverflow_original_post'); + +const Selectors = { + actions: { + showpostbutton: '[data-action="mod_moodleoverflow/showpost_button"]', + }, +}; + +/** + * Init function. + */ +export function init() { + prefetchStrings('moodleoverflow', ['showpost_expand', 'showpost_collapse',]); + postElement.setAttribute('expanded', 'false'); + postElement.style.maxHeight = '0px'; + addEventListener(); +} + +/** + * Event listener. + */ +const addEventListener = () => { + document.addEventListener('click', async e => { + if (e.target.closest(Selectors.actions.showpostbutton)) { + if (postElement.getAttribute('expanded') === 'true') { + showpostButton.textContent = await getString('showpost_expand', 'moodleoverflow'); + postElement.style.maxHeight = '0px'; + postElement.setAttribute('expanded', 'false'); + } else { + showpostButton.textContent = await getString('showpost_collapse', 'moodleoverflow'); + postElement.style.maxHeight = `${postElement.scrollHeight}px`; + postElement.setAttribute('expanded', 'true'); + } + } + }); +}; diff --git a/classes/post/post.php b/classes/post/post.php index 0d4f97c188..0879c985fc 100644 --- a/classes/post/post.php +++ b/classes/post/post.php @@ -26,8 +26,12 @@ namespace mod_moodleoverflow\post; use coding_exception; +use context_module; +use core\output\html_writer; use core_user\fields; use dml_exception; +use mod_moodleoverflow\anonymous; +use mod_moodleoverflow\capabilities; use mod_moodleoverflow\event\post_deleted; use mod_moodleoverflow\ratings; use mod_moodleoverflow\readtracking; @@ -550,6 +554,58 @@ public function moodleoverflow_get_attachments(): array { return $attachments; } + /** + * Get a link to the users profile. + * Returns a html link embedded in the users name. + * @return moodle_url + * @throws moodle_exception + */ + public function get_userlink(): string { + global $USER, $DB; + $this->existence_check(); + + $courseid = $this->get_discussion()->get_courseid(); + $modulecontext = context_module::instance($this->get_coursemodule()->id); + $userid = $this->get_userid(); + + if (anonymous::is_post_anonymous($this->get_discussion()->get_db_object(), $this->get_moodleoverflow(), $userid)) { + if ($userid == $USER->id) { + $fullname = get_string('anonym_you', 'mod_moodleoverflow'); + $profilelink = new moodle_url('/user/view.php', ['id' => $userid, 'course' => $courseid]); + return html_writer::link($profilelink, $fullname); + } else { + $usermapping = anonymous::get_userid_mapping($this->get_moodleoverflow(), $this->get_discussionid()); + return $usermapping[$userid]; + } + } + $user = $DB->get_record('user', ['id' => $userid]); + $fullname = fullname($user, capabilities::has('moodle/site:viewfullnames', $modulecontext)); + $profilelink = new moodle_url('/user/view.php', ['id' => $userid, 'course' => $courseid]); + return html_writer::link($profilelink, $fullname); + } + + /** + * Returns the post message in a formatted way ready to display. + * @return string + * @throws moodle_exception + */ + public function get_message_formatted(): string { + $context = context_module::instance($this->get_coursemodule()->id); + $message = file_rewrite_pluginfile_urls( + $this->message, + 'pluginfile.php', + $context->id, + 'mod_moodleoverflow', + 'post', + $this->get_id(), + ['includetoken' => true] + ); + $options = new stdClass(); + $options->para = true; + $options->context = $context; + return format_text($message, $this->messageformat, $options); + } + // Getter. /** @@ -707,14 +763,13 @@ public function moodleoverflow_get_post_ratings(): object { $discussionid = $this->get_discussion()->get_id(); $postratings = ratings::moodleoverflow_get_ratings_by_discussion($discussionid, $this->id); - $ratingsobject = new stdClass(); - $ratingsobject->upvotes = $postratings->upvotes; - $ratingsobject->downvotes = $postratings->downvotes; - $ratingsobject->votesdifference = $postratings->upvotes - $postratings->downvotes; - $ratingsobject->markedhelpful = $postratings->ishelpful; - $ratingsobject->markedsolution = $postratings->issolved; - - return $ratingsobject; + return (object) [ + 'upvotes' => $postratings->upvotes, + 'downvotes' => $postratings->downvotes, + 'votesdifference' => $postratings->upvotes - $postratings->downvotes, + 'markedhelpful' => $postratings->ishelpful, + 'markedsolution' => $postratings->issolved, + ]; } /** diff --git a/classes/post/post_control.php b/classes/post/post_control.php index 9e087b5984..0a77a02c68 100644 --- a/classes/post/post_control.php +++ b/classes/post/post_control.php @@ -27,6 +27,7 @@ // Import namespace from the locallib, needs a check later which namespaces are really needed. use coding_exception; +use context_module; use core\notification; use dml_exception; use html_writer; @@ -671,6 +672,28 @@ public function build_postform(array $pageparams): mod_moodleoverflow_post_form return $mformpost; } + /** + * Display the original post when a user replies to it. + * + * @throws moodle_exception|dml_exception + */ + public function display_original_post(): string { + global $PAGE, $DB; + if ($this->interaction == 'reply') { + $PAGE->requires->js_call_amd('mod_moodleoverflow/show_post', 'init'); + $post = post::from_record($DB->get_record('moodleoverflow_posts', ['id' => $this->info->relatedpost->get_id()])); + $data = (object) [ + 'postid' => $post->get_id(), + 'postcontent' => $post->get_message_formatted(), + 'attachments' => $post->moodleoverflow_get_attachments(), + 'byname' => $post->get_userlink(), + 'byshortdate' => userdate($post->modified, get_string('strftimedatetimeshort', 'core_langconfig')), + ]; + return $PAGE->get_renderer('mod_moodleoverflow')->render_post_original($data); + } + return ''; + } + // Helper functions. // Error handling functions. diff --git a/lang/en/moodleoverflow.php b/lang/en/moodleoverflow.php index 8bb130628d..8229795b4c 100644 --- a/lang/en/moodleoverflow.php +++ b/lang/en/moodleoverflow.php @@ -270,6 +270,7 @@ $string['nowtracking'] = '{$a->name} is now tracking \'{$a->moodleoverflow}\'.'; $string['oldpostdays'] = 'Read after days'; $string['original_post'] = 'Original post'; +$string['original_post_reply'] = 'You are adressing the post from {$a}:'; $string['parent'] = 'Show parent'; $string['pending_review'] = 'Pending review'; $string['pending_review_but_cannot_now'] = 'Pending review, but cannot be approved until {$a} after the creation of this post to allow the author a bit of time to edit it.'; @@ -369,6 +370,8 @@ $string['scalefactor_help'] = 'The user rating is divided by the scale factor to obtain each user\'s grade. If the resulting grade is greater than the maximum grade, the value is limited to the specified maximum grade'; $string['scalefactorerror'] = 'Scale factor must be a positive integer different than 0'; $string['seeuserstats'] = 'View user statistics'; +$string['showpost_collapse'] = 'collapse'; +$string['showpost_expand'] = 'expand'; $string['showuserstats'] = 'Show cumulative user statistics'; $string['smallmessage'] = '{$a->user} posted in {$a->moodleoverflowname}'; $string['starterrating'] = 'Helpful'; diff --git a/post.php b/post.php index 2e2e42d386..6d48971b1a 100644 --- a/post.php +++ b/post.php @@ -168,5 +168,6 @@ // Display all. echo $OUTPUT->header(); +echo $postcontrol->display_original_post(); $mformpost->display(); echo $OUTPUT->footer(); diff --git a/renderer.php b/renderer.php index 293f010f17..943bfe5e52 100644 --- a/renderer.php +++ b/renderer.php @@ -79,6 +79,17 @@ public function render_post($data) { return $this->render_from_template('mod_moodleoverflow/post', $data); } + /** + * Renders a simplified version of any post. Used to display the post a reply is referring to. + * + * @param object $data The submitted variables. + * + * @return bool|string + */ + public function render_post_original(object $data): bool|string { + return $this->render_from_template('mod_moodleoverflow/post_original', $data); + } + /** * Display a moodleoverflow post in the relevant context. * diff --git a/styles.css b/styles.css index d6f90bdb9f..f32e5b34c8 100644 --- a/styles.css +++ b/styles.css @@ -123,6 +123,25 @@ white-space: nowrap; } +#moodleoverflow_original_post { + overflow: hidden; + max-height: 0; + transition: max-height 0.4s ease; +} + +#moodleoverflow_showpost { + cursor: pointer; + background-color: #6a737b; + font-size: 0.8rem; +} + +.original-post-text { + margin-bottom: 5px; + overflow-wrap: break-word; + width: 100%; + word-wrap: break-word; +} + /* * The Index Page. */ diff --git a/templates/post_original.mustache b/templates/post_original.mustache new file mode 100644 index 0000000000..e18df59a4c --- /dev/null +++ b/templates/post_original.mustache @@ -0,0 +1,72 @@ +{{! + 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 . +}} +{{! + @template mod_moodleoverflow/post_original + + Moodleoverflow post original template. + In the post.php this template is used to show the post that a new answer post is referring to. + It's a simpler version of the post template. + + Example context (json): + { + } +}} + +{{! Start the post. Mark it read or unread. }} +

Reply to discussion

+
+
+
+ +
+
+ {{{ postcontent }}} +
+
+ {{#attachments}} + {{#image}} + +
+ {{/image}} + {{^image}} + + {{{icon}}} + + + {{filename}} + + {{/image}} +
+ {{/attachments}} +
+
+
+
+
diff --git a/tests/behat/reply_post.feature b/tests/behat/reply_post.feature new file mode 100644 index 0000000000..f9fd6f8a07 --- /dev/null +++ b/tests/behat/reply_post.feature @@ -0,0 +1,12 @@ +@mod @mod_moodleoverflow @javascript +Feature: Replying to posts in a moodleoverflow + + Scenario: While replying to a post I need to see the original post I'm answering to + Given I add a moodleoverflow discussion with posts from different users + When I navigate as "teacher1" to "C1" "Moodleoverflow 1" "Discussion 1" + And I click on "Answer" "link" + Then I should see "You are adressing the post from Tamaro Walter:" + When I click on "#moodleoverflow_showpost" "css_element" + Then I should see "Message from teacher" + When I click on "#moodleoverflow_showpost" "css_element" + Then I should not see "Message from teacher" \ No newline at end of file