mirror of
https://github.com/moodle/moodle.git
synced 2025-08-08 10:26:40 +02:00
Merge branch 'MDL-64821-master-2' of https://github.com/ryanwyllie/moodle
This commit is contained in:
commit
c3122dfcf5
50 changed files with 2204 additions and 183 deletions
|
@ -147,7 +147,7 @@ class stored_file_exporter extends \core\external\exporter {
|
||||||
$filenameshort .= substr($filename, -4);
|
$filenameshort .= substr($filename, -4);
|
||||||
}
|
}
|
||||||
|
|
||||||
$icon = $this->file->is_directory() ? file_folder_icon() : file_file_icon($this->file);
|
$icon = $this->file->is_directory() ? file_folder_icon(128) : file_file_icon($this->file, 128);
|
||||||
|
|
||||||
$url = moodle_url::make_pluginfile_url(
|
$url = moodle_url::make_pluginfile_url(
|
||||||
$this->file->get_contextid(),
|
$this->file->get_contextid(),
|
||||||
|
|
|
@ -2241,6 +2241,7 @@ class core_renderer extends renderer_base {
|
||||||
if ($rating->user_can_view_aggregate()) {
|
if ($rating->user_can_view_aggregate()) {
|
||||||
|
|
||||||
$aggregatelabel = $ratingmanager->get_aggregate_label($rating->settings->aggregationmethod);
|
$aggregatelabel = $ratingmanager->get_aggregate_label($rating->settings->aggregationmethod);
|
||||||
|
$aggregatelabel = html_writer::tag('span', $aggregatelabel, array('class'=>'rating-aggregate-label'));
|
||||||
$aggregatestr = $rating->get_aggregate_string();
|
$aggregatestr = $rating->get_aggregate_string();
|
||||||
|
|
||||||
$aggregatehtml = html_writer::tag('span', $aggregatestr, array('id' => 'ratingaggregate'.$rating->itemid, 'class' => 'ratingaggregate')).' ';
|
$aggregatehtml = html_writer::tag('span', $aggregatestr, array('id' => 'ratingaggregate'.$rating->itemid, 'class' => 'ratingaggregate')).' ';
|
||||||
|
@ -2251,17 +2252,16 @@ class core_renderer extends renderer_base {
|
||||||
}
|
}
|
||||||
$aggregatehtml .= html_writer::tag('span', $countstr, array('id'=>"ratingcount{$rating->itemid}", 'class' => 'ratingcount')).' ';
|
$aggregatehtml .= html_writer::tag('span', $countstr, array('id'=>"ratingcount{$rating->itemid}", 'class' => 'ratingcount')).' ';
|
||||||
|
|
||||||
$ratinghtml .= html_writer::tag('span', $aggregatelabel, array('class'=>'rating-aggregate-label'));
|
|
||||||
if ($rating->settings->permissions->viewall && $rating->settings->pluginpermissions->viewall) {
|
if ($rating->settings->permissions->viewall && $rating->settings->pluginpermissions->viewall) {
|
||||||
|
|
||||||
$nonpopuplink = $rating->get_view_ratings_url();
|
$nonpopuplink = $rating->get_view_ratings_url();
|
||||||
$popuplink = $rating->get_view_ratings_url(true);
|
$popuplink = $rating->get_view_ratings_url(true);
|
||||||
|
|
||||||
$action = new popup_action('click', $popuplink, 'ratings', array('height' => 400, 'width' => 600));
|
$action = new popup_action('click', $popuplink, 'ratings', array('height' => 400, 'width' => 600));
|
||||||
$ratinghtml .= $this->action_link($nonpopuplink, $aggregatehtml, $action);
|
$aggregatehtml = $this->action_link($nonpopuplink, $aggregatehtml, $action);
|
||||||
} else {
|
|
||||||
$ratinghtml .= $aggregatehtml;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ratinghtml .= html_writer::tag('span', $aggregatelabel . $aggregatehtml, array('class' => 'rating-aggregate-container'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$formstart = null;
|
$formstart = null;
|
||||||
|
|
2
mod/forum/amd/build/discussion.min.js
vendored
2
mod/forum/amd/build/discussion.min.js
vendored
|
@ -1,2 +1,2 @@
|
||||||
define ("mod_forum/discussion",["jquery","core/custom_interaction_events","mod_forum/selectors"],function(a,b,c){var d=function(a){var b=a.prev(c.post.post);if(b.length){var d=b.find(c.post.post).last();if(d.length){d.focus()}else{b.focus()}}else{a.parents(c.post.post).first().focus()}},e=function(b){var d=b.find(c.post.post).first();if(d.length){d.focus()}else{var e=b.next(c.post.post);if(e.length){e.focus()}else{b.parents().toArray().forEach(function(b){var d=a(b).next(c.post.post);if(d.length){d.focus()}})}}},f=function(b){var d=a(b).closest(c.post.inpageReplyContent);return d.length?!0:!1},g=function(g){var h=g.find(c.post.post);h.each(function(b,d){var e=a(d).find(c.post.action),f=e.first();e.attr("tabindex","-1");f.attr("tabindex",0)});b.define(g,[b.events.up,b.events.down,b.events.next,b.events.previous,b.events.home,b.events.end]);g.on(b.events.up,function(b,e){var h=document.activeElement;if(f(h)){return}var i=a(h).closest(c.post.post);if(i.length){d(i)}else{g.find(c.post.post).first().focus()}e.originalEvent.preventDefault()});g.on(b.events.down,function(b,d){var h=document.activeElement;if(f(h)){return}var i=a(h).closest(c.post.post);if(i.length){e(i)}else{g.find(c.post.post).first().focus()}d.originalEvent.preventDefault()});g.on(b.events.home,function(a,b){if(f(document.activeElement)){return}g.find(c.post.post).first().focus();b.originalEvent.preventDefault()});g.on(b.events.end,function(a,b){if(f(document.activeElement)){return}g.find(c.post.post).last().focus();b.originalEvent.preventDefault()});g.on(b.events.next,c.post.action,function(b,d){var e=a(b.target),f=e.closest(c.post.actionsContainer),g=f.find(c.post.action),h=e.next(c.post.action);g.attr("tabindex","-1");if(!h.length){h=g.first()}h.attr("tabindex",0);h.focus();d.originalEvent.preventDefault()});g.on(b.events.previous,c.post.action,function(b,d){var e=a(b.target),f=e.closest(c.post.actionsContainer),g=f.find(c.post.action),h=e.prev(c.post.action);g.attr("tabindex","-1");if(!h.length){h=g.last()}h.attr("tabindex",0);h.focus();d.originalEvent.preventDefault()});g.on(b.events.home,c.post.action,function(b,d){var e=a(b.target),f=e.closest(c.post.actionsContainer),g=f.find(c.post.action),h=g.first();g.attr("tabindex","-1");h.attr("tabindex",0);h.focus();b.stopPropagation();d.originalEvent.preventDefault()});g.on(b.events.end,c.post.action,function(b,d){var e=a(b.target),f=e.closest(c.post.actionsContainer),g=f.find(c.post.action),h=g.last();g.attr("tabindex","-1");h.attr("tabindex",0);h.focus();b.stopPropagation();d.originalEvent.preventDefault()})};return{init:function init(a){g(a)}}});
|
define ("mod_forum/discussion",["jquery","core/custom_interaction_events","mod_forum/selectors"],function(a,b,c){var d=function(a){var b=a.prev(c.post.post);if(b.length){var d=b.find(c.post.post).last();if(d.length){d.focus()}else{b.focus()}}else{a.parents(c.post.post).first().focus()}},e=function(b){var d=b.find(c.post.post).first();if(d.length){d.focus()}else{var e=b.next(c.post.post);if(e.length){e.focus()}else{for(var f=b.parents(c.post.post).toArray(),g=0,h;g<f.length;g++){h=a(f[g]).next(c.post.post);if(h.length){h.focus();break}}}}},f=function(b){var d=a(b).closest(c.post.inpageReplyContent);return d.length?!0:!1},g=function(g){var h=g.find(c.post.post);h.each(function(b,d){var e=a(d).find(c.post.action),f=e.first();e.attr("tabindex","-1");f.attr("tabindex",0)});b.define(g,[b.events.up,b.events.down,b.events.next,b.events.previous,b.events.home,b.events.end]);g.on(b.events.up,function(b,e){var h=document.activeElement;if(f(h)){return}var i=a(h).closest(c.post.post);if(i.length){d(i)}else{g.find(c.post.post).first().focus()}e.originalEvent.preventDefault()});g.on(b.events.down,function(b,d){var h=document.activeElement;if(f(h)){return}var i=a(h).closest(c.post.post);if(i.length){e(i)}else{g.find(c.post.post).first().focus()}d.originalEvent.preventDefault()});g.on(b.events.home,function(a,b){if(f(document.activeElement)){return}g.find(c.post.post).first().focus();b.originalEvent.preventDefault()});g.on(b.events.end,function(a,b){if(f(document.activeElement)){return}g.find(c.post.post).last().focus();b.originalEvent.preventDefault()});g.on(b.events.next,c.post.action,function(b,d){var e=a(b.target),f=e.closest(c.post.actionsContainer),g=f.find(c.post.action),h=e.next(c.post.action);g.attr("tabindex","-1");if(!h.length){h=g.first()}h.attr("tabindex",0);h.focus();d.originalEvent.preventDefault()});g.on(b.events.previous,c.post.action,function(b,d){var e=a(b.target),f=e.closest(c.post.actionsContainer),g=f.find(c.post.action),h=e.prev(c.post.action);g.attr("tabindex","-1");if(!h.length){h=g.last()}h.attr("tabindex",0);h.focus();d.originalEvent.preventDefault()});g.on(b.events.home,c.post.action,function(b,d){var e=a(b.target),f=e.closest(c.post.actionsContainer),g=f.find(c.post.action),h=g.first();g.attr("tabindex","-1");h.attr("tabindex",0);h.focus();b.stopPropagation();d.originalEvent.preventDefault()});g.on(b.events.end,c.post.action,function(b,d){var e=a(b.target),f=e.closest(c.post.actionsContainer),g=f.find(c.post.action),h=g.last();g.attr("tabindex","-1");h.attr("tabindex",0);h.focus();b.stopPropagation();d.originalEvent.preventDefault()})};return{init:function init(a){g(a)}}});
|
||||||
//# sourceMappingURL=discussion.min.js.map
|
//# sourceMappingURL=discussion.min.js.map
|
||||||
|
|
File diff suppressed because one or more lines are too long
2
mod/forum/amd/build/discussion_modern.min.js
vendored
Normal file
2
mod/forum/amd/build/discussion_modern.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
mod/forum/amd/build/discussion_modern.min.js.map
Normal file
1
mod/forum/amd/build/discussion_modern.min.js.map
Normal file
File diff suppressed because one or more lines are too long
2
mod/forum/amd/build/inpage_reply.min.js
vendored
2
mod/forum/amd/build/inpage_reply.min.js
vendored
|
@ -1,2 +1,2 @@
|
||||||
define ("mod_forum/inpage_reply",["jquery","core/templates","core/notification","mod_forum/repository","mod_forum/selectors"],function(a,b,c,d,f){var g={THREADED:2,NESTED:3,FLAT_OLDEST_FIRST:1,FLAT_NEWEST_FIRST:-1},h={MOODLE:0},i=function(a){var b=a.find(f.post.inpageSubmitBtnText),c=a.find(f.post.loadingIconContainer),d=a.outerWidth();a.css("width",d);b.addClass("hidden");c.removeClass("hidden")},j=function(a){var b=a.find(f.post.inpageSubmitBtnText),c=a.find(f.post.loadingIconContainer);a.css("width","");b.removeClass("hidden");c.addClass("hidden")},k=function(k){k.on("click",f.post.inpageSubmitBtn,function(l){l.preventDefault();var e=a(l.currentTarget),m=e.parent().find(f.post.inpageReplyButton),n=e.parents(f.post.inpageReplyForm).get(0),o=n.elements.post.value.trim(),p=h.MOODLE,q=n.elements.reply.value,r=n.elements.subject.value,s=e.parents(f.post.forumContent),t=n.elements.privatereply!=void 0?n.elements.privatereply.checked:!1,u=parseInt(k.find(f.post.modeSelect).get(0).value),v;if(o.length){i(e);m.prop("disabled",!0);d.addDiscussionPost(q,r,o,p,t,!0).then(function(a){var b=a.messages.reduce(function(a,b){if("success"==b.type){a+="<p>"+b.message+"</p>"}return a},"");c.addNotification({message:b,type:"success"});return a}).then(function(a){n.reset();var c=a.post;v=c.id;switch(u){case g.THREADED:return b.render("mod_forum/forum_discussion_threaded_post",c);case g.NESTED:return b.render("mod_forum/forum_discussion_nested_post",c);default:return b.render("mod_forum/forum_discussion_post",c);}}).then(function(a,c){var d;if(u==g.FLAT_OLDEST_FIRST||u==g.FLAT_NEWEST_FIRST){d=s.parents(f.post.repliesContainer).children().get(0)}if(d==void 0){d=s.siblings(f.post.repliesContainer).children().get(0)}if(u==g.FLAT_NEWEST_FIRST){return b.prependNodeContents(d,a,c)}else{return b.appendNodeContents(d,a,c)}}).then(function(){j(e);m.prop("disabled",!1);return s.find(f.post.inpageReplyContent).hide()}).then(function(){location.href="#p"+v}).catch(function(a){j(e);m.prop("disabled",!1);return c.exception(a)})}})};return{init:function init(a){k(a)},CONTENT_FORMATS:h}});
|
define ("mod_forum/inpage_reply",["jquery","core/templates","core/notification","mod_forum/repository","mod_forum/selectors"],function(a,b,c,d,f){var g={MODERN:4,THREADED:2,NESTED:3,FLAT_OLDEST_FIRST:1,FLAT_NEWEST_FIRST:-1},h={POST_CREATED:"mod_forum-post-created"},i={MOODLE:0},j=function(a){var b=a.find(f.post.inpageSubmitBtnText),c=a.find(f.post.loadingIconContainer),d=a.outerWidth();a.css("width",d);b.addClass("hidden");c.removeClass("hidden")},k=function(a){var b=a.find(f.post.inpageSubmitBtnText),c=a.find(f.post.loadingIconContainer);a.css("width","");b.removeClass("hidden");c.addClass("hidden")},l=function(l){l.on("click",f.post.inpageSubmitBtn,function(m){m.preventDefault();var e=a(m.currentTarget),n=e.parent().find(f.post.inpageReplyButton),o=e.parents(f.post.inpageReplyForm).get(0),p=o.elements.post.value.trim(),q=i.MOODLE,r=o.elements.reply.value,s=o.elements.subject.value,t=e.closest(f.post.post),u=o.elements.privatereply!=void 0?o.elements.privatereply.checked:!1,v=l.find(f.post.modeSelect),w=v.length?parseInt(v.get(0).value):null,x;if(p.length){j(e);n.prop("disabled",!0);d.addDiscussionPost(r,s,p,q,u,!0).then(function(a){var b=a.messages.reduce(function(a,b){if("success"==b.type){a+="<p>"+b.message+"</p>"}return a},"");c.addNotification({message:b,type:"success"});return a}).then(function(a){o.reset();var c=a.post;x=c.id;switch(w){case g.MODERN:var d=c.capabilities;c.showactionmenu=d.controlreadstatus||d.edit||d.split||d.delete||d.export||c.urls.viewparent;return b.render("mod_forum/forum_discussion_modern_post_reply",c);case g.THREADED:return b.render("mod_forum/forum_discussion_threaded_post",c);case g.NESTED:return b.render("mod_forum/forum_discussion_nested_post",c);default:return b.render("mod_forum/forum_discussion_post",c);}}).then(function(a,c){var d=t.find(f.post.repliesContainer).first();if(w==g.FLAT_NEWEST_FIRST){return b.prependNodeContents(d,a,c)}else{return b.appendNodeContents(d,a,c)}}).then(function(){e.trigger(h.POST_CREATED,x);k(e);n.prop("disabled",!1);return t.find(f.post.inpageReplyContent).hide()}).then(function(){location.href="#p"+x}).catch(function(a){k(e);n.prop("disabled",!1);return c.exception(a)})}})};return{init:function init(a){l(a)},CONTENT_FORMATS:i,EVENTS:h}});
|
||||||
//# sourceMappingURL=inpage_reply.min.js.map
|
//# sourceMappingURL=inpage_reply.min.js.map
|
||||||
|
|
File diff suppressed because one or more lines are too long
2
mod/forum/amd/build/selectors.min.js
vendored
2
mod/forum/amd/build/selectors.min.js
vendored
|
@ -1,2 +1,2 @@
|
||||||
define ("mod_forum/selectors",[],function(){return{subscription:{toggle:"[data-type='subscription-toggle'][data-action='toggle']"},summary:{actions:"[data-container='discussion-summary-actions']"},post:{post:"[data-region=\"post\"]",action:"[data-region=\"post-action\"]",actionsContainer:"[data-region=\"post-actions-container\"]",forumCoreContent:"[data-region-content='forum-post-core']",forumContent:"[data-content='forum-post']",forumSubject:"[data-region-content='forum-post-core-subject']",inpageReplyButton:"button",inpageReplyLink:"[data-action='collapsible-link']",inpageReplyContent:"[data-content='inpage-reply-content']",inpageReplyForm:"form[data-content='inpage-reply-form']",inpageSubmitBtn:"[data-action='forum-inpage-submit']",inpageSubmitBtnText:"[data-region='submit-text']",loadingIconContainer:"[data-region='loading-icon-container']",repliesContainer:"[data-region='replies-container']",modeSelect:"select[name='mode']"},lock:{toggle:"[data-action='toggle'][data-type='lock-toggle']",icon:"[data-region='locked-icon']"},favourite:{toggle:"[data-type='favorite-toggle'][data-action='toggle']"},pin:{toggle:"[data-type='pin-toggle'][data-action='toggle']"}}});
|
define ("mod_forum/selectors",[],function(){return{subscription:{toggle:"[data-type='subscription-toggle'][data-action='toggle']"},summary:{actions:"[data-container='discussion-summary-actions']"},post:{post:"[data-region=\"post\"]",action:"[data-region=\"post-action\"]",actionsContainer:"[data-region=\"post-actions-container\"]",authorName:"[data-region=\"author-name\"]",forumCoreContent:"[data-region-content='forum-post-core']",forumContent:"[data-content='forum-post']",forumSubject:"[data-region-content='forum-post-core-subject']",inpageReplyButton:"button",inpageReplyLink:"[data-action='collapsible-link']",inpageReplyCancelButton:"[data-action='cancel-inpage-reply']",inpageReplyCreateButton:"[data-action='create-inpage-reply']",inpageReplyContainer:"[data-region=\"inpage-reply-container\"]",inpageReplyContent:"[data-content='inpage-reply-content']",inpageReplyForm:"form[data-content='inpage-reply-form']",inpageSubmitBtn:"[data-action='forum-inpage-submit']",inpageSubmitBtnText:"[data-region='submit-text']",loadingIconContainer:"[data-region='loading-icon-container']",repliesContainer:"[data-region='replies-container']",replyCount:"[data-region=\"reply-count\"]",modeSelect:"select[name='mode']",showReplies:"[data-action=\"show-replies\"]",hideReplies:"[data-action=\"hide-replies\"]",repliesVisibilityToggleContainer:"[data-region=\"replies-visibility-toggle-container\"]"},lock:{toggle:"[data-action='toggle'][data-type='lock-toggle']",icon:"[data-region='locked-icon']"},favourite:{toggle:"[data-type='favorite-toggle'][data-action='toggle']"},pin:{toggle:"[data-type='pin-toggle'][data-action='toggle']"},discussion:{tools:"[data-container=\"discussion-tools\"]"}}});
|
||||||
//# sourceMappingURL=selectors.min.js.map
|
//# sourceMappingURL=selectors.min.js.map
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":3,"sources":["../src/selectors.js"],"names":["define","subscription","toggle","summary","actions","post","action","actionsContainer","forumCoreContent","forumContent","forumSubject","inpageReplyButton","inpageReplyLink","inpageReplyContent","inpageReplyForm","inpageSubmitBtn","inpageSubmitBtnText","loadingIconContainer","repliesContainer","modeSelect","lock","icon","favourite","pin"],"mappings":"AAuBAA,OAAM,uBAAC,EAAD,CAAK,UAAW,CAClB,MAAO,CACHC,YAAY,CAAE,CACVC,MAAM,CAAE,yDADE,CADX,CAIHC,OAAO,CAAE,CACLC,OAAO,CAAE,+CADJ,CAJN,CAOHC,IAAI,CAAE,CACFA,IAAI,CAAE,wBADJ,CAEFC,MAAM,CAAE,+BAFN,CAGFC,gBAAgB,CAAE,0CAHhB,CAIFC,gBAAgB,CAAE,yCAJhB,CAKFC,YAAY,CAAE,6BALZ,CAMFC,YAAY,CAAE,iDANZ,CAOFC,iBAAiB,CAAE,QAPjB,CAQFC,eAAe,CAAE,kCARf,CASFC,kBAAkB,CAAE,uCATlB,CAUFC,eAAe,CAAE,wCAVf,CAWFC,eAAe,CAAE,qCAXf,CAYFC,mBAAmB,CAAE,6BAZnB,CAaFC,oBAAoB,CAAE,wCAbpB,CAcFC,gBAAgB,CAAE,mCAdhB,CAeFC,UAAU,CAAE,qBAfV,CAPH,CAwBHC,IAAI,CAAE,CACFlB,MAAM,CAAE,iDADN,CAEFmB,IAAI,CAAE,6BAFJ,CAxBH,CA4BHC,SAAS,CAAE,CACPpB,MAAM,CAAE,qDADD,CA5BR,CA+BHqB,GAAG,CAAE,CACDrB,MAAM,CAAE,gDADP,CA/BF,CAmCV,CApCK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Common CSS selectors for the forum UI.\n *\n * @module mod_forum/selectors\n * @package mod_forum\n * @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n subscription: {\n toggle: \"[data-type='subscription-toggle'][data-action='toggle']\",\n },\n summary: {\n actions: \"[data-container='discussion-summary-actions']\"\n },\n post: {\n post: '[data-region=\"post\"]',\n action: '[data-region=\"post-action\"]',\n actionsContainer: '[data-region=\"post-actions-container\"]',\n forumCoreContent: \"[data-region-content='forum-post-core']\",\n forumContent: \"[data-content='forum-post']\",\n forumSubject: \"[data-region-content='forum-post-core-subject']\",\n inpageReplyButton: \"button\",\n inpageReplyLink: \"[data-action='collapsible-link']\",\n inpageReplyContent: \"[data-content='inpage-reply-content']\",\n inpageReplyForm: \"form[data-content='inpage-reply-form']\",\n inpageSubmitBtn: \"[data-action='forum-inpage-submit']\",\n inpageSubmitBtnText: \"[data-region='submit-text']\",\n loadingIconContainer: \"[data-region='loading-icon-container']\",\n repliesContainer: \"[data-region='replies-container']\",\n modeSelect: \"select[name='mode']\"\n },\n lock: {\n toggle: \"[data-action='toggle'][data-type='lock-toggle']\",\n icon: \"[data-region='locked-icon']\"\n },\n favourite: {\n toggle: \"[data-type='favorite-toggle'][data-action='toggle']\",\n },\n pin: {\n toggle: \"[data-type='pin-toggle'][data-action='toggle']\",\n },\n };\n});\n"],"file":"selectors.min.js"}
|
{"version":3,"sources":["../src/selectors.js"],"names":["define","subscription","toggle","summary","actions","post","action","actionsContainer","authorName","forumCoreContent","forumContent","forumSubject","inpageReplyButton","inpageReplyLink","inpageReplyCancelButton","inpageReplyCreateButton","inpageReplyContainer","inpageReplyContent","inpageReplyForm","inpageSubmitBtn","inpageSubmitBtnText","loadingIconContainer","repliesContainer","replyCount","modeSelect","showReplies","hideReplies","repliesVisibilityToggleContainer","lock","icon","favourite","pin","discussion","tools"],"mappings":"AAuBAA,OAAM,uBAAC,EAAD,CAAK,UAAW,CAClB,MAAO,CACHC,YAAY,CAAE,CACVC,MAAM,CAAE,yDADE,CADX,CAIHC,OAAO,CAAE,CACLC,OAAO,CAAE,+CADJ,CAJN,CAOHC,IAAI,CAAE,CACFA,IAAI,CAAE,wBADJ,CAEFC,MAAM,CAAE,+BAFN,CAGFC,gBAAgB,CAAE,0CAHhB,CAIFC,UAAU,CAAE,+BAJV,CAKFC,gBAAgB,CAAE,yCALhB,CAMFC,YAAY,CAAE,6BANZ,CAOFC,YAAY,CAAE,iDAPZ,CAQFC,iBAAiB,CAAE,QARjB,CASFC,eAAe,CAAE,kCATf,CAUFC,uBAAuB,CAAE,qCAVvB,CAWFC,uBAAuB,CAAE,qCAXvB,CAYFC,oBAAoB,CAAE,0CAZpB,CAaFC,kBAAkB,CAAE,uCAblB,CAcFC,eAAe,CAAE,wCAdf,CAeFC,eAAe,CAAE,qCAff,CAgBFC,mBAAmB,CAAE,6BAhBnB,CAiBFC,oBAAoB,CAAE,wCAjBpB,CAkBFC,gBAAgB,CAAE,mCAlBhB,CAmBFC,UAAU,CAAE,+BAnBV,CAoBFC,UAAU,CAAE,qBApBV,CAqBFC,WAAW,CAAE,gCArBX,CAsBFC,WAAW,CAAE,gCAtBX,CAuBFC,gCAAgC,CAAE,uDAvBhC,CAPH,CAgCHC,IAAI,CAAE,CACF1B,MAAM,CAAE,iDADN,CAEF2B,IAAI,CAAE,6BAFJ,CAhCH,CAoCHC,SAAS,CAAE,CACP5B,MAAM,CAAE,qDADD,CApCR,CAuCH6B,GAAG,CAAE,CACD7B,MAAM,CAAE,gDADP,CAvCF,CA0CH8B,UAAU,CAAE,CACRC,KAAK,CAAE,uCADC,CA1CT,CA8CV,CA/CK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Common CSS selectors for the forum UI.\n *\n * @module mod_forum/selectors\n * @package mod_forum\n * @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n subscription: {\n toggle: \"[data-type='subscription-toggle'][data-action='toggle']\",\n },\n summary: {\n actions: \"[data-container='discussion-summary-actions']\"\n },\n post: {\n post: '[data-region=\"post\"]',\n action: '[data-region=\"post-action\"]',\n actionsContainer: '[data-region=\"post-actions-container\"]',\n authorName: '[data-region=\"author-name\"]',\n forumCoreContent: \"[data-region-content='forum-post-core']\",\n forumContent: \"[data-content='forum-post']\",\n forumSubject: \"[data-region-content='forum-post-core-subject']\",\n inpageReplyButton: \"button\",\n inpageReplyLink: \"[data-action='collapsible-link']\",\n inpageReplyCancelButton: \"[data-action='cancel-inpage-reply']\",\n inpageReplyCreateButton: \"[data-action='create-inpage-reply']\",\n inpageReplyContainer: '[data-region=\"inpage-reply-container\"]',\n inpageReplyContent: \"[data-content='inpage-reply-content']\",\n inpageReplyForm: \"form[data-content='inpage-reply-form']\",\n inpageSubmitBtn: \"[data-action='forum-inpage-submit']\",\n inpageSubmitBtnText: \"[data-region='submit-text']\",\n loadingIconContainer: \"[data-region='loading-icon-container']\",\n repliesContainer: \"[data-region='replies-container']\",\n replyCount: '[data-region=\"reply-count\"]',\n modeSelect: \"select[name='mode']\",\n showReplies: '[data-action=\"show-replies\"]',\n hideReplies: '[data-action=\"hide-replies\"]',\n repliesVisibilityToggleContainer: '[data-region=\"replies-visibility-toggle-container\"]'\n },\n lock: {\n toggle: \"[data-action='toggle'][data-type='lock-toggle']\",\n icon: \"[data-region='locked-icon']\"\n },\n favourite: {\n toggle: \"[data-type='favorite-toggle'][data-action='toggle']\",\n },\n pin: {\n toggle: \"[data-type='pin-toggle'][data-action='toggle']\",\n },\n discussion: {\n tools: '[data-container=\"discussion-tools\"]'\n }\n };\n});\n"],"file":"selectors.min.js"}
|
|
@ -88,14 +88,16 @@ function(
|
||||||
// No siblings either. That means we're the lowest level reply in a thread
|
// No siblings either. That means we're the lowest level reply in a thread
|
||||||
// so we need to walk back up the tree of posts and find an ancestor post that
|
// so we need to walk back up the tree of posts and find an ancestor post that
|
||||||
// has a sibling post we can focus.
|
// has a sibling post we can focus.
|
||||||
currentPost.parents().toArray().forEach(function(parent) {
|
var parentPosts = currentPost.parents(Selectors.post.post).toArray();
|
||||||
var ancestorSiblingPost = $(parent).next(Selectors.post.post);
|
|
||||||
|
for (var i = 0; i < parentPosts.length; i++) {
|
||||||
|
var ancestorSiblingPost = $(parentPosts[i]).next(Selectors.post.post);
|
||||||
|
|
||||||
if (ancestorSiblingPost.length) {
|
if (ancestorSiblingPost.length) {
|
||||||
ancestorSiblingPost.focus();
|
ancestorSiblingPost.focus();
|
||||||
return;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
415
mod/forum/amd/src/discussion_modern.js
Normal file
415
mod/forum/amd/src/discussion_modern.js
Normal file
|
@ -0,0 +1,415 @@
|
||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module for viewing a discussion in modern view.
|
||||||
|
*
|
||||||
|
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
import $ from 'jquery';
|
||||||
|
import AutoRows from 'core/auto_rows';
|
||||||
|
import CustomEvents from 'core/custom_interaction_events';
|
||||||
|
import Notification from 'core/notification';
|
||||||
|
import Templates from 'core/templates';
|
||||||
|
import Discussion from 'mod_forum/discussion';
|
||||||
|
import InPageReply from 'mod_forum/inpage_reply';
|
||||||
|
import LockToggle from 'mod_forum/lock_toggle';
|
||||||
|
import FavouriteToggle from 'mod_forum/favourite_toggle';
|
||||||
|
import Pin from 'mod_forum/pin_toggle';
|
||||||
|
import Selectors from 'mod_forum/selectors';
|
||||||
|
|
||||||
|
const ANIMATION_DURATION = 150;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the closest post container element from the given element.
|
||||||
|
*
|
||||||
|
* @param {Object} element jQuery element to search from
|
||||||
|
* @return {Object} jQuery element
|
||||||
|
*/
|
||||||
|
const getPostContainer = (element) => {
|
||||||
|
return element.closest(Selectors.post.post);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the closest post container element from the given element.
|
||||||
|
*
|
||||||
|
* @param {Object} element jQuery element to search from
|
||||||
|
* @param {Number} id Id of the post to find.
|
||||||
|
* @return {Object} jQuery element
|
||||||
|
*/
|
||||||
|
const getPostContainerById = (element, id) => {
|
||||||
|
return element.find(`${Selectors.post.post}[data-post-id=${id}]`);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the parent post container elements from the given element.
|
||||||
|
*
|
||||||
|
* @param {Object} element jQuery element to search from
|
||||||
|
* @return {Object} jQuery element
|
||||||
|
*/
|
||||||
|
const getParentPostContainers = (element) => {
|
||||||
|
return element.parents(Selectors.post.post);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the post content container element from the post container element.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
* @return {Object} jQuery element
|
||||||
|
*/
|
||||||
|
const getPostContentContainer = (postContainer) => {
|
||||||
|
return postContainer.children().not(Selectors.post.repliesContainer).find(Selectors.post.forumCoreContent);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the in page reply container element from the post container element.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
* @return {Object} jQuery element
|
||||||
|
*/
|
||||||
|
const getInPageReplyContainer = (postContainer) => {
|
||||||
|
return postContainer.children().filter(Selectors.post.inpageReplyContainer);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the in page reply form element from the post container element.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
* @return {Object} jQuery element
|
||||||
|
*/
|
||||||
|
const getInPageReplyForm = (postContainer) => {
|
||||||
|
return getInPageReplyContainer(postContainer).find(Selectors.post.inpageReplyContent);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the in page reply create (reply) button element from the post container element.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
* @return {Object} jQuery element
|
||||||
|
*/
|
||||||
|
const getInPageReplyCreateButton = (postContainer) => {
|
||||||
|
return getPostContentContainer(postContainer).find(Selectors.post.inpageReplyCreateButton);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the replies visibility toggle container (show/hide replies button container) element
|
||||||
|
* from the post container element.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
* @return {Object} jQuery element
|
||||||
|
*/
|
||||||
|
const getRepliesVisibilityToggleContainer = (postContainer) => {
|
||||||
|
return postContainer.children(Selectors.post.repliesVisibilityToggleContainer);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the replies container element from the post container element.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
* @return {Object} jQuery element
|
||||||
|
*/
|
||||||
|
const getRepliesContainer = (postContainer) => {
|
||||||
|
return postContainer.children(Selectors.post.repliesContainer);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the post has any replies.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
* @return {Bool}
|
||||||
|
*/
|
||||||
|
const hasReplies = (postContainer) => {
|
||||||
|
return getRepliesContainer(postContainer).children().length > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the show replies button element from the replies visibility toggle container element.
|
||||||
|
*
|
||||||
|
* @param {Object} replyVisibilityToggleContainer jQuery element for the toggle container
|
||||||
|
* @return {Object} jQuery element
|
||||||
|
*/
|
||||||
|
const getShowRepliesButton = (replyVisibilityToggleContainer) => {
|
||||||
|
return replyVisibilityToggleContainer.find(Selectors.post.showReplies);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hide replies button element from the replies visibility toggle container element.
|
||||||
|
*
|
||||||
|
* @param {Object} replyVisibilityToggleContainer jQuery element for the toggle container
|
||||||
|
* @return {Object} jQuery element
|
||||||
|
*/
|
||||||
|
const getHideRepliesButton = (replyVisibilityToggleContainer) => {
|
||||||
|
return replyVisibilityToggleContainer.find(Selectors.post.hideReplies);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the replies are visible.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
* @return {Bool}
|
||||||
|
*/
|
||||||
|
const repliesVisible = (postContainer) => {
|
||||||
|
const repliesContainer = getRepliesContainer(postContainer);
|
||||||
|
return repliesContainer.is(':visible');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the post replies.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
* @param {Number|null} postIdToSee Id of the post to scroll into view (if any)
|
||||||
|
*/
|
||||||
|
const showReplies = (postContainer, postIdToSee = null) => {
|
||||||
|
const repliesContainer = getRepliesContainer(postContainer);
|
||||||
|
const replyVisibilityToggleContainer = getRepliesVisibilityToggleContainer(postContainer);
|
||||||
|
const showButton = getShowRepliesButton(replyVisibilityToggleContainer);
|
||||||
|
const hideButton = getHideRepliesButton(replyVisibilityToggleContainer);
|
||||||
|
|
||||||
|
showButton.addClass('hidden');
|
||||||
|
hideButton.removeClass('hidden');
|
||||||
|
|
||||||
|
repliesContainer.slideDown({
|
||||||
|
duration: ANIMATION_DURATION,
|
||||||
|
queue: false,
|
||||||
|
complete: () => {
|
||||||
|
if (postIdToSee) {
|
||||||
|
const postContainerToSee = getPostContainerById(repliesContainer, postIdToSee);
|
||||||
|
if (postContainerToSee.length) {
|
||||||
|
postContainerToSee[0].scrollIntoView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).css('display', 'none').fadeIn(ANIMATION_DURATION);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the post replies.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
*/
|
||||||
|
const hideReplies = (postContainer) => {
|
||||||
|
const repliesContainer = getRepliesContainer(postContainer);
|
||||||
|
const replyVisibilityToggleContainer = getRepliesVisibilityToggleContainer(postContainer);
|
||||||
|
const showButton = getShowRepliesButton(replyVisibilityToggleContainer);
|
||||||
|
const hideButton = getHideRepliesButton(replyVisibilityToggleContainer);
|
||||||
|
|
||||||
|
showButton.removeClass('hidden');
|
||||||
|
hideButton.addClass('hidden');
|
||||||
|
|
||||||
|
repliesContainer.slideUp({
|
||||||
|
duration: ANIMATION_DURATION,
|
||||||
|
queue: false
|
||||||
|
}).fadeOut(ANIMATION_DURATION);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Variable to hold the showInPageReplyForm function after it's built. */
|
||||||
|
let showInPageReplyForm = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the showInPageReplyForm function with the given additional template context.
|
||||||
|
*
|
||||||
|
* @param {Object} additionalTemplateContext Additional render context for the in page reply template.
|
||||||
|
* @return {Function}
|
||||||
|
*/
|
||||||
|
const buildShowInPageReplyFormFunction = (additionalTemplateContext) => {
|
||||||
|
/**
|
||||||
|
* Show the in page reply form in the given in page reply container. The form
|
||||||
|
* display will be animated.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
*/
|
||||||
|
return async (postContainer) => {
|
||||||
|
|
||||||
|
const inPageReplyContainer = getInPageReplyContainer(postContainer);
|
||||||
|
const repliesVisibilityToggleContainer = getRepliesVisibilityToggleContainer(postContainer);
|
||||||
|
const inPageReplyCreateButton = getInPageReplyCreateButton(postContainer);
|
||||||
|
|
||||||
|
if (!hasInPageReplyForm(inPageReplyContainer)) {
|
||||||
|
try {
|
||||||
|
const html = await renderInPageReplyTemplate(additionalTemplateContext, inPageReplyCreateButton, postContainer);
|
||||||
|
Templates.appendNodeContents(inPageReplyContainer, html, '');
|
||||||
|
} catch (e) {
|
||||||
|
Notification.exception(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inPageReplyCreateButton.fadeOut(ANIMATION_DURATION, () => {
|
||||||
|
const inPageReplyForm = getInPageReplyForm(postContainer);
|
||||||
|
inPageReplyForm.slideDown({
|
||||||
|
duration: ANIMATION_DURATION,
|
||||||
|
queue: false,
|
||||||
|
complete: () => {
|
||||||
|
inPageReplyForm.find('textarea').focus();
|
||||||
|
}
|
||||||
|
}).css('display', 'none').fadeIn(ANIMATION_DURATION);
|
||||||
|
|
||||||
|
if (repliesVisibilityToggleContainer.length && hasReplies(postContainer)) {
|
||||||
|
repliesVisibilityToggleContainer.fadeIn(ANIMATION_DURATION);
|
||||||
|
hideReplies(postContainer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the in page reply form in the given in page reply container. The form
|
||||||
|
* display will be animated.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
* @param {Number|null} postIdToSee Id of the post to scroll into view (if any)
|
||||||
|
*/
|
||||||
|
const hideInPageReplyForm = (postContainer, postIdToSee = null) => {
|
||||||
|
const inPageReplyForm = getInPageReplyForm(postContainer);
|
||||||
|
const inPageReplyCreateButton = getInPageReplyCreateButton(postContainer);
|
||||||
|
const repliesVisibilityToggleContainer = getRepliesVisibilityToggleContainer(postContainer);
|
||||||
|
|
||||||
|
if (repliesVisibilityToggleContainer.length && hasReplies(postContainer)) {
|
||||||
|
repliesVisibilityToggleContainer.fadeOut(ANIMATION_DURATION);
|
||||||
|
if (!repliesVisible(postContainer)) {
|
||||||
|
showReplies(postContainer, postIdToSee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inPageReplyForm.slideUp({
|
||||||
|
duration: ANIMATION_DURATION,
|
||||||
|
queue: false,
|
||||||
|
complete: () => {
|
||||||
|
inPageReplyCreateButton.fadeIn(ANIMATION_DURATION);
|
||||||
|
}
|
||||||
|
}).fadeOut(200);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the in page reply container contains the in page reply form.
|
||||||
|
*
|
||||||
|
* @param {Object} inPageReplyContainer jQuery element for the in page reply container
|
||||||
|
* @return {Bool}
|
||||||
|
*/
|
||||||
|
const hasInPageReplyForm = (inPageReplyContainer) => {
|
||||||
|
return inPageReplyContainer.find(Selectors.post.inpageReplyContent).length > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the template to generate the in page reply form HTML.
|
||||||
|
*
|
||||||
|
* @param {Object} additionalTemplateContext Additional render context for the in page reply template
|
||||||
|
* @param {Object} button jQuery element for the reply button that was clicked
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
* @return {Object} jQuery promise
|
||||||
|
*/
|
||||||
|
const renderInPageReplyTemplate = (additionalTemplateContext, button, postContainer) => {
|
||||||
|
const postContentContainer = getPostContentContainer(postContainer);
|
||||||
|
const currentSubject = postContentContainer.find(Selectors.post.forumSubject).text();
|
||||||
|
const currentAuthorName = postContentContainer.find(Selectors.post.authorName).text();
|
||||||
|
const context = {
|
||||||
|
postid: postContainer.data('post-id'),
|
||||||
|
"reply_url": button.attr('data-href'),
|
||||||
|
sesskey: M.cfg.sesskey,
|
||||||
|
parentsubject: currentSubject,
|
||||||
|
parentauthorname: currentAuthorName,
|
||||||
|
canreplyprivately: button.data('can-reply-privately'),
|
||||||
|
postformat: InPageReply.CONTENT_FORMATS.MOODLE,
|
||||||
|
...additionalTemplateContext
|
||||||
|
};
|
||||||
|
|
||||||
|
return Templates.render('mod_forum/inpage_reply_modern', context);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the total reply count in the show/hide replies buttons for the post.
|
||||||
|
*
|
||||||
|
* @param {Object} postContainer jQuery element for the post container
|
||||||
|
*/
|
||||||
|
const incrementTotalReplyCount = (postContainer) => {
|
||||||
|
getRepliesVisibilityToggleContainer(postContainer).find(Selectors.post.replyCount).each((index, element) => {
|
||||||
|
const currentCount = parseInt(element.innerText, 10);
|
||||||
|
element.innerText = currentCount + 1;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create all of the event listeners for the discussion.
|
||||||
|
*
|
||||||
|
* @param {Object} root jQuery element for the discussion container
|
||||||
|
*/
|
||||||
|
const registerEventListeners = (root) => {
|
||||||
|
CustomEvents.define(root, [CustomEvents.events.activate]);
|
||||||
|
// Auto expanding text area for in page reply.
|
||||||
|
AutoRows.init(root);
|
||||||
|
|
||||||
|
// Reply button is clicked.
|
||||||
|
root.on(CustomEvents.events.activate, Selectors.post.inpageReplyCreateButton, (e, data) => {
|
||||||
|
data.originalEvent.preventDefault();
|
||||||
|
const postContainer = getPostContainer($(e.currentTarget));
|
||||||
|
showInPageReplyForm(postContainer);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel in page reply button.
|
||||||
|
root.on(CustomEvents.events.activate, Selectors.post.inpageReplyCancelButton, (e, data) => {
|
||||||
|
data.originalEvent.preventDefault();
|
||||||
|
const postContainer = getPostContainer($(e.currentTarget));
|
||||||
|
hideInPageReplyForm(postContainer);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show replies button clicked.
|
||||||
|
root.on(CustomEvents.events.activate, Selectors.post.showReplies, (e, data) => {
|
||||||
|
data.originalEvent.preventDefault();
|
||||||
|
const postContainer = getPostContainer($(e.target));
|
||||||
|
showReplies(postContainer);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hide replies button clicked.
|
||||||
|
root.on(CustomEvents.events.activate, Selectors.post.hideReplies, (e, data) => {
|
||||||
|
data.originalEvent.preventDefault();
|
||||||
|
const postContainer = getPostContainer($(e.target));
|
||||||
|
hideReplies(postContainer);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Post created with in page reply.
|
||||||
|
root.on(InPageReply.EVENTS.POST_CREATED, Selectors.post.inpageSubmitBtn, (e, newPostId) => {
|
||||||
|
const currentTarget = $(e.currentTarget);
|
||||||
|
const postContainer = getPostContainer(currentTarget);
|
||||||
|
const postContainers = getParentPostContainers(currentTarget);
|
||||||
|
hideInPageReplyForm(postContainer, newPostId);
|
||||||
|
|
||||||
|
postContainers.each((index, container) => {
|
||||||
|
incrementTotalReplyCount($(container));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the javascript for the discussion in modern display mode.
|
||||||
|
*
|
||||||
|
* @param {Object} root jQuery element for the discussion container
|
||||||
|
* @param {Object} context Additional render context for the in page reply template
|
||||||
|
*/
|
||||||
|
export const init = (root, context) => {
|
||||||
|
// Build the showInPageReplyForm function with the additional render context.
|
||||||
|
showInPageReplyForm = buildShowInPageReplyFormFunction(context);
|
||||||
|
// Add discussion event listeners.
|
||||||
|
registerEventListeners(root);
|
||||||
|
// Initialise default discussion javascript (keyboard nav etc).
|
||||||
|
Discussion.init(root);
|
||||||
|
// Add in page reply javascript.
|
||||||
|
InPageReply.init(root);
|
||||||
|
|
||||||
|
// Initialise the settings menu javascript.
|
||||||
|
const discussionToolsContainer = root.find(Selectors.discussion.tools);
|
||||||
|
LockToggle.init(discussionToolsContainer);
|
||||||
|
FavouriteToggle.init(discussionToolsContainer);
|
||||||
|
Pin.init(discussionToolsContainer);
|
||||||
|
};
|
|
@ -36,12 +36,17 @@ define([
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var DISPLAYCONSTANTS = {
|
var DISPLAYCONSTANTS = {
|
||||||
|
MODERN: 4,
|
||||||
THREADED: 2,
|
THREADED: 2,
|
||||||
NESTED: 3,
|
NESTED: 3,
|
||||||
FLAT_OLDEST_FIRST: 1,
|
FLAT_OLDEST_FIRST: 1,
|
||||||
FLAT_NEWEST_FIRST: -1
|
FLAT_NEWEST_FIRST: -1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var EVENTS = {
|
||||||
|
POST_CREATED: 'mod_forum-post-created'
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moodle formats taken from the FORMAT_* constants declared in lib/weblib.php.
|
* Moodle formats taken from the FORMAT_* constants declared in lib/weblib.php.
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
|
@ -97,9 +102,10 @@ define([
|
||||||
var topreferredformat = true;
|
var topreferredformat = true;
|
||||||
var postid = form.elements.reply.value;
|
var postid = form.elements.reply.value;
|
||||||
var subject = form.elements.subject.value;
|
var subject = form.elements.subject.value;
|
||||||
var currentRoot = submitButton.parents(Selectors.post.forumContent);
|
var currentRoot = submitButton.closest(Selectors.post.post);
|
||||||
var isprivatereply = form.elements.privatereply != undefined ? form.elements.privatereply.checked : false;
|
var isprivatereply = form.elements.privatereply != undefined ? form.elements.privatereply.checked : false;
|
||||||
var mode = parseInt(root.find(Selectors.post.modeSelect).get(0).value);
|
var modeSelector = root.find(Selectors.post.modeSelect);
|
||||||
|
var mode = modeSelector.length ? parseInt(modeSelector.get(0).value) : null;
|
||||||
var newid;
|
var newid;
|
||||||
|
|
||||||
if (message.length) {
|
if (message.length) {
|
||||||
|
@ -125,7 +131,17 @@ define([
|
||||||
form.reset();
|
form.reset();
|
||||||
var post = context.post;
|
var post = context.post;
|
||||||
newid = post.id;
|
newid = post.id;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
case DISPLAYCONSTANTS.MODERN:
|
||||||
|
var capabilities = post.capabilities;
|
||||||
|
post.showactionmenu = capabilities.controlreadstatus ||
|
||||||
|
capabilities.edit ||
|
||||||
|
capabilities.split ||
|
||||||
|
capabilities.delete ||
|
||||||
|
capabilities.export ||
|
||||||
|
post.urls.viewparent;
|
||||||
|
return Templates.render('mod_forum/forum_discussion_modern_post_reply', post);
|
||||||
case DISPLAYCONSTANTS.THREADED:
|
case DISPLAYCONSTANTS.THREADED:
|
||||||
return Templates.render('mod_forum/forum_discussion_threaded_post', post);
|
return Templates.render('mod_forum/forum_discussion_threaded_post', post);
|
||||||
case DISPLAYCONSTANTS.NESTED:
|
case DISPLAYCONSTANTS.NESTED:
|
||||||
|
@ -135,16 +151,7 @@ define([
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(function(html, js) {
|
.then(function(html, js) {
|
||||||
var repliesnode;
|
var repliesnode = currentRoot.find(Selectors.post.repliesContainer).first();
|
||||||
|
|
||||||
// Try and get the replies-container which can either be a sibling OR parent if it's flat
|
|
||||||
if (mode == DISPLAYCONSTANTS.FLAT_OLDEST_FIRST || mode == DISPLAYCONSTANTS.FLAT_NEWEST_FIRST) {
|
|
||||||
repliesnode = currentRoot.parents(Selectors.post.repliesContainer).children().get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (repliesnode == undefined) {
|
|
||||||
repliesnode = currentRoot.siblings(Selectors.post.repliesContainer).children().get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == DISPLAYCONSTANTS.FLAT_NEWEST_FIRST) {
|
if (mode == DISPLAYCONSTANTS.FLAT_NEWEST_FIRST) {
|
||||||
return Templates.prependNodeContents(repliesnode, html, js);
|
return Templates.prependNodeContents(repliesnode, html, js);
|
||||||
|
@ -153,6 +160,7 @@ define([
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
|
submitButton.trigger(EVENTS.POST_CREATED, newid);
|
||||||
hideSubmitButtonLoadingIcon(submitButton);
|
hideSubmitButtonLoadingIcon(submitButton);
|
||||||
allButtons.prop('disabled', false);
|
allButtons.prop('disabled', false);
|
||||||
return currentRoot.find(Selectors.post.inpageReplyContent).hide();
|
return currentRoot.find(Selectors.post.inpageReplyContent).hide();
|
||||||
|
@ -174,6 +182,7 @@ define([
|
||||||
init: function(root) {
|
init: function(root) {
|
||||||
registerEventListeners(root);
|
registerEventListeners(root);
|
||||||
},
|
},
|
||||||
CONTENT_FORMATS: CONTENT_FORMATS
|
CONTENT_FORMATS: CONTENT_FORMATS,
|
||||||
|
EVENTS: EVENTS
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -33,18 +33,26 @@ define([], function() {
|
||||||
post: '[data-region="post"]',
|
post: '[data-region="post"]',
|
||||||
action: '[data-region="post-action"]',
|
action: '[data-region="post-action"]',
|
||||||
actionsContainer: '[data-region="post-actions-container"]',
|
actionsContainer: '[data-region="post-actions-container"]',
|
||||||
|
authorName: '[data-region="author-name"]',
|
||||||
forumCoreContent: "[data-region-content='forum-post-core']",
|
forumCoreContent: "[data-region-content='forum-post-core']",
|
||||||
forumContent: "[data-content='forum-post']",
|
forumContent: "[data-content='forum-post']",
|
||||||
forumSubject: "[data-region-content='forum-post-core-subject']",
|
forumSubject: "[data-region-content='forum-post-core-subject']",
|
||||||
inpageReplyButton: "button",
|
inpageReplyButton: "button",
|
||||||
inpageReplyLink: "[data-action='collapsible-link']",
|
inpageReplyLink: "[data-action='collapsible-link']",
|
||||||
|
inpageReplyCancelButton: "[data-action='cancel-inpage-reply']",
|
||||||
|
inpageReplyCreateButton: "[data-action='create-inpage-reply']",
|
||||||
|
inpageReplyContainer: '[data-region="inpage-reply-container"]',
|
||||||
inpageReplyContent: "[data-content='inpage-reply-content']",
|
inpageReplyContent: "[data-content='inpage-reply-content']",
|
||||||
inpageReplyForm: "form[data-content='inpage-reply-form']",
|
inpageReplyForm: "form[data-content='inpage-reply-form']",
|
||||||
inpageSubmitBtn: "[data-action='forum-inpage-submit']",
|
inpageSubmitBtn: "[data-action='forum-inpage-submit']",
|
||||||
inpageSubmitBtnText: "[data-region='submit-text']",
|
inpageSubmitBtnText: "[data-region='submit-text']",
|
||||||
loadingIconContainer: "[data-region='loading-icon-container']",
|
loadingIconContainer: "[data-region='loading-icon-container']",
|
||||||
repliesContainer: "[data-region='replies-container']",
|
repliesContainer: "[data-region='replies-container']",
|
||||||
modeSelect: "select[name='mode']"
|
replyCount: '[data-region="reply-count"]',
|
||||||
|
modeSelect: "select[name='mode']",
|
||||||
|
showReplies: '[data-action="show-replies"]',
|
||||||
|
hideReplies: '[data-action="hide-replies"]',
|
||||||
|
repliesVisibilityToggleContainer: '[data-region="replies-visibility-toggle-container"]'
|
||||||
},
|
},
|
||||||
lock: {
|
lock: {
|
||||||
toggle: "[data-action='toggle'][data-type='lock-toggle']",
|
toggle: "[data-action='toggle'][data-type='lock-toggle']",
|
||||||
|
@ -56,5 +64,8 @@ define([], function() {
|
||||||
pin: {
|
pin: {
|
||||||
toggle: "[data-type='pin-toggle'][data-action='toggle']",
|
toggle: "[data-type='pin-toggle'][data-action='toggle']",
|
||||||
},
|
},
|
||||||
|
discussion: {
|
||||||
|
tools: '[data-container="discussion-tools"]'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -51,6 +51,7 @@ class author {
|
||||||
'lastname' => $author->get_last_name(),
|
'lastname' => $author->get_last_name(),
|
||||||
'fullname' => $author->get_full_name(),
|
'fullname' => $author->get_full_name(),
|
||||||
'email' => $author->get_email(),
|
'email' => $author->get_email(),
|
||||||
|
'deleted' => $author->is_deleted(),
|
||||||
'middlename' => $author->get_middle_name(),
|
'middlename' => $author->get_middle_name(),
|
||||||
'firstnamephonetic' => $author->get_first_name_phonetic(),
|
'firstnamephonetic' => $author->get_first_name_phonetic(),
|
||||||
'lastnamephonetic' => $author->get_last_name_phonetic(),
|
'lastnamephonetic' => $author->get_last_name_phonetic(),
|
||||||
|
|
|
@ -45,6 +45,8 @@ class author {
|
||||||
private $fullname;
|
private $fullname;
|
||||||
/** @var string $email Email */
|
/** @var string $email Email */
|
||||||
private $email;
|
private $email;
|
||||||
|
/** @var bool $deleted Deleted */
|
||||||
|
private $deleted;
|
||||||
/** @var string $middlename Middle name */
|
/** @var string $middlename Middle name */
|
||||||
private $middlename;
|
private $middlename;
|
||||||
/** @var string $firstnamephonetic Phonetic spelling of first name */
|
/** @var string $firstnamephonetic Phonetic spelling of first name */
|
||||||
|
@ -78,6 +80,7 @@ class author {
|
||||||
string $lastname,
|
string $lastname,
|
||||||
string $fullname,
|
string $fullname,
|
||||||
string $email,
|
string $email,
|
||||||
|
bool $deleted,
|
||||||
string $middlename = null,
|
string $middlename = null,
|
||||||
string $firstnamephonetic = null,
|
string $firstnamephonetic = null,
|
||||||
string $lastnamephonetic = null,
|
string $lastnamephonetic = null,
|
||||||
|
@ -90,6 +93,7 @@ class author {
|
||||||
$this->lastname = $lastname;
|
$this->lastname = $lastname;
|
||||||
$this->fullname = $fullname;
|
$this->fullname = $fullname;
|
||||||
$this->email = $email;
|
$this->email = $email;
|
||||||
|
$this->deleted = $deleted;
|
||||||
$this->middlename = $middlename;
|
$this->middlename = $middlename;
|
||||||
$this->firstnamephonetic = $firstnamephonetic;
|
$this->firstnamephonetic = $firstnamephonetic;
|
||||||
$this->lastnamephonetic = $lastnamephonetic;
|
$this->lastnamephonetic = $lastnamephonetic;
|
||||||
|
@ -151,6 +155,15 @@ class author {
|
||||||
return $this->email;
|
return $this->email;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the author deleted?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function is_deleted() : bool {
|
||||||
|
return !empty($this->deleted);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the middle name.
|
* Return the middle name.
|
||||||
*
|
*
|
||||||
|
|
|
@ -91,6 +91,12 @@ class author extends exporter {
|
||||||
'default' => null,
|
'default' => null,
|
||||||
'null' => NULL_ALLOWED
|
'null' => NULL_ALLOWED
|
||||||
],
|
],
|
||||||
|
'isdeleted' => [
|
||||||
|
'type' => PARAM_BOOL,
|
||||||
|
'optional' => true,
|
||||||
|
'default' => null,
|
||||||
|
'null' => NULL_ALLOWED
|
||||||
|
],
|
||||||
'groups' => [
|
'groups' => [
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'optional' => true,
|
'optional' => true,
|
||||||
|
@ -143,6 +149,18 @@ class author extends exporter {
|
||||||
$context = $this->related['context'];
|
$context = $this->related['context'];
|
||||||
|
|
||||||
if ($this->canview) {
|
if ($this->canview) {
|
||||||
|
if ($author->is_deleted()) {
|
||||||
|
return [
|
||||||
|
'id' => $author->get_id(),
|
||||||
|
'fullname' => get_string('deleteduser', 'mod_forum'),
|
||||||
|
'isdeleted' => true,
|
||||||
|
'groups' => [],
|
||||||
|
'urls' => [
|
||||||
|
'profile' => ($urlfactory->get_author_profile_url($author))->out(false),
|
||||||
|
'profileimage' => ($urlfactory->get_author_profile_image_url($author, $authorcontextid))->out(false)
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} else {
|
||||||
$groups = array_map(function($group) use ($urlfactory, $context) {
|
$groups = array_map(function($group) use ($urlfactory, $context) {
|
||||||
$imageurl = null;
|
$imageurl = null;
|
||||||
$groupurl = null;
|
$groupurl = null;
|
||||||
|
@ -167,17 +185,20 @@ class author extends exporter {
|
||||||
return [
|
return [
|
||||||
'id' => $author->get_id(),
|
'id' => $author->get_id(),
|
||||||
'fullname' => $author->get_full_name(),
|
'fullname' => $author->get_full_name(),
|
||||||
|
'isdeleted' => false,
|
||||||
'groups' => $groups,
|
'groups' => $groups,
|
||||||
'urls' => [
|
'urls' => [
|
||||||
'profile' => ($urlfactory->get_author_profile_url($author))->out(false),
|
'profile' => ($urlfactory->get_author_profile_url($author))->out(false),
|
||||||
'profileimage' => ($urlfactory->get_author_profile_image_url($author, $authorcontextid))->out(false)
|
'profileimage' => ($urlfactory->get_author_profile_image_url($author, $authorcontextid))->out(false)
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// The author should be anonymised.
|
// The author should be anonymised.
|
||||||
return [
|
return [
|
||||||
'id' => null,
|
'id' => null,
|
||||||
'fullname' => get_string('forumauthorhidden', 'mod_forum'),
|
'fullname' => get_string('forumauthorhidden', 'mod_forum'),
|
||||||
|
'isdeleted' => null,
|
||||||
'groups' => [],
|
'groups' => [],
|
||||||
'urls' => [
|
'urls' => [
|
||||||
'profile' => null,
|
'profile' => null,
|
||||||
|
|
|
@ -386,7 +386,7 @@ class post extends exporter {
|
||||||
$author,
|
$author,
|
||||||
$authorcontextid,
|
$authorcontextid,
|
||||||
$authorgroups,
|
$authorgroups,
|
||||||
($canview && !$isdeleted),
|
$canview,
|
||||||
$this->related
|
$this->related
|
||||||
);
|
);
|
||||||
$exportedauthor = $authorexporter->export($output);
|
$exportedauthor = $authorexporter->export($output);
|
||||||
|
@ -402,10 +402,6 @@ class post extends exporter {
|
||||||
$subject = $isdeleted ? get_string('forumsubjectdeleted', 'forum') : get_string('forumsubjecthidden', 'forum');
|
$subject = $isdeleted ? get_string('forumsubjectdeleted', 'forum') : get_string('forumsubjecthidden', 'forum');
|
||||||
$message = $isdeleted ? get_string('forumbodydeleted', 'forum') : get_string('forumbodyhidden', 'forum');
|
$message = $isdeleted ? get_string('forumbodydeleted', 'forum') : get_string('forumbodyhidden', 'forum');
|
||||||
$timecreated = null;
|
$timecreated = null;
|
||||||
|
|
||||||
if ($isdeleted) {
|
|
||||||
$exportedauthor->fullname = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$replysubject = $subject;
|
$replysubject = $subject;
|
||||||
|
|
|
@ -172,6 +172,7 @@ class entity {
|
||||||
$record->lastname,
|
$record->lastname,
|
||||||
fullname($record),
|
fullname($record),
|
||||||
$record->email,
|
$record->email,
|
||||||
|
$record->deleted,
|
||||||
$record->middlename,
|
$record->middlename,
|
||||||
$record->firstnamephonetic,
|
$record->firstnamephonetic,
|
||||||
$record->lastnamephonetic,
|
$record->lastnamephonetic,
|
||||||
|
|
|
@ -134,6 +134,8 @@ class renderer {
|
||||||
$this->legacydatamapperfactory,
|
$this->legacydatamapperfactory,
|
||||||
$this->exporterfactory,
|
$this->exporterfactory,
|
||||||
$this->vaultfactory,
|
$this->vaultfactory,
|
||||||
|
$this->urlfactory,
|
||||||
|
$this->entityfactory,
|
||||||
$capabilitymanager,
|
$capabilitymanager,
|
||||||
$ratingmanager,
|
$ratingmanager,
|
||||||
$this->entityfactory->get_exported_posts_sorter(),
|
$this->entityfactory->get_exported_posts_sorter(),
|
||||||
|
@ -141,7 +143,7 @@ class renderer {
|
||||||
$notifications,
|
$notifications,
|
||||||
function($discussion, $user, $forum) {
|
function($discussion, $user, $forum) {
|
||||||
$exportbuilder = $this->builderfactory->get_exported_discussion_builder();
|
$exportbuilder = $this->builderfactory->get_exported_discussion_builder();
|
||||||
return $exportedposts = $exportbuilder->build(
|
return $exportbuilder->build(
|
||||||
$user,
|
$user,
|
||||||
$forum,
|
$forum,
|
||||||
$discussion
|
$discussion
|
||||||
|
@ -180,6 +182,9 @@ class renderer {
|
||||||
case FORUM_MODE_NESTED:
|
case FORUM_MODE_NESTED:
|
||||||
$template = 'mod_forum/forum_discussion_nested_posts';
|
$template = 'mod_forum/forum_discussion_nested_posts';
|
||||||
break;
|
break;
|
||||||
|
case FORUM_MODE_MODERN:
|
||||||
|
$template = 'mod_forum/forum_discussion_modern_posts';
|
||||||
|
break;
|
||||||
default;
|
default;
|
||||||
$template = 'mod_forum/forum_discussion_posts';
|
$template = 'mod_forum/forum_discussion_posts';
|
||||||
break;
|
break;
|
||||||
|
@ -192,12 +197,17 @@ class renderer {
|
||||||
// Post process the exported posts for our template. This function will add the "replies"
|
// Post process the exported posts for our template. This function will add the "replies"
|
||||||
// and "hasreplies" properties to the exported posts. It will also sort them into the
|
// and "hasreplies" properties to the exported posts. It will also sort them into the
|
||||||
// reply tree structure if the display mode requires it.
|
// reply tree structure if the display mode requires it.
|
||||||
function($exportedposts, $forums) use ($displaymode, $readonly, $exportedpostssorter) {
|
function($exportedposts, $forums, $discussions) use ($displaymode, $readonly, $exportedpostssorter) {
|
||||||
$forum = array_shift($forums);
|
$forum = array_shift($forums);
|
||||||
$seenfirstunread = false;
|
$seenfirstunread = false;
|
||||||
$postcount = count($exportedposts);
|
$postcount = count($exportedposts);
|
||||||
|
$discussionsbyid = array_reduce($discussions, function($carry, $discussion) {
|
||||||
|
$carry[$discussion->get_id()] = $discussion;
|
||||||
|
return $carry;
|
||||||
|
}, []);
|
||||||
$exportedposts = array_map(
|
$exportedposts = array_map(
|
||||||
function($exportedpost) use ($forum, $readonly, $seenfirstunread) {
|
function($exportedpost) use ($forum, $discussionsbyid, $readonly, $seenfirstunread, $displaymode) {
|
||||||
|
$discussion = $discussionsbyid[$exportedpost->discussionid] ?? null;
|
||||||
if ($forum->get_type() == 'single' && !$exportedpost->hasparent) {
|
if ($forum->get_type() == 'single' && !$exportedpost->hasparent) {
|
||||||
// Remove the author from any posts that don't have a parent.
|
// Remove the author from any posts that don't have a parent.
|
||||||
unset($exportedpost->author);
|
unset($exportedpost->author);
|
||||||
|
@ -209,6 +219,7 @@ class renderer {
|
||||||
$exportedpost->hasreplycount = false;
|
$exportedpost->hasreplycount = false;
|
||||||
$exportedpost->hasreplies = false;
|
$exportedpost->hasreplies = false;
|
||||||
$exportedpost->replies = [];
|
$exportedpost->replies = [];
|
||||||
|
$exportedpost->discussionlocked = $discussion ? $discussion->is_locked() : null;
|
||||||
|
|
||||||
$exportedpost->isfirstunread = false;
|
$exportedpost->isfirstunread = false;
|
||||||
if (!$seenfirstunread && $exportedpost->unread) {
|
if (!$seenfirstunread && $exportedpost->unread) {
|
||||||
|
@ -216,16 +227,35 @@ class renderer {
|
||||||
$seenfirstunread = true;
|
$seenfirstunread = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($displaymode === FORUM_MODE_MODERN) {
|
||||||
|
$exportedpost->showactionmenu = $exportedpost->capabilities['controlreadstatus'] ||
|
||||||
|
$exportedpost->capabilities['edit'] ||
|
||||||
|
$exportedpost->capabilities['split'] ||
|
||||||
|
$exportedpost->capabilities['delete'] ||
|
||||||
|
$exportedpost->capabilities['export'] ||
|
||||||
|
!empty($exportedpost->urls['viewparent']);
|
||||||
|
}
|
||||||
|
|
||||||
return $exportedpost;
|
return $exportedpost;
|
||||||
},
|
},
|
||||||
$exportedposts
|
$exportedposts
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($displaymode === FORUM_MODE_NESTED || $displaymode === FORUM_MODE_THREADED) {
|
if (
|
||||||
|
$displaymode === FORUM_MODE_NESTED ||
|
||||||
|
$displaymode === FORUM_MODE_THREADED ||
|
||||||
|
$displaymode === FORUM_MODE_MODERN
|
||||||
|
) {
|
||||||
$sortedposts = $exportedpostssorter->sort_into_children($exportedposts);
|
$sortedposts = $exportedpostssorter->sort_into_children($exportedposts);
|
||||||
$sortintoreplies = function($nestedposts) use (&$sortintoreplies) {
|
$sortintoreplies = function($nestedposts) use (&$sortintoreplies) {
|
||||||
return array_map(function($postdata) use (&$sortintoreplies) {
|
return array_map(function($postdata) use (&$sortintoreplies) {
|
||||||
[$post, $replies] = $postdata;
|
[$post, $replies] = $postdata;
|
||||||
|
$totalreplycount = 0;
|
||||||
|
|
||||||
|
if (empty($replies)) {
|
||||||
|
$post->replies = [];
|
||||||
|
$post->hasreplies = false;
|
||||||
|
} else {
|
||||||
$sortedreplies = $sortintoreplies($replies);
|
$sortedreplies = $sortintoreplies($replies);
|
||||||
// Set the parent author name on the replies. This is used for screen
|
// Set the parent author name on the replies. This is used for screen
|
||||||
// readers to help them identify the structure of the discussion.
|
// readers to help them identify the structure of the discussion.
|
||||||
|
@ -239,8 +269,17 @@ class renderer {
|
||||||
}
|
}
|
||||||
return $reply;
|
return $reply;
|
||||||
}, $sortedreplies);
|
}, $sortedreplies);
|
||||||
|
|
||||||
|
$totalreplycount = array_reduce($sortedreplies, function($carry, $reply) {
|
||||||
|
return $carry + 1 + $reply->totalreplycount;
|
||||||
|
}, $totalreplycount);
|
||||||
|
|
||||||
$post->replies = $sortedreplies;
|
$post->replies = $sortedreplies;
|
||||||
$post->hasreplies = !empty($post->replies);
|
$post->hasreplies = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post->totalreplycount = $totalreplycount;
|
||||||
|
|
||||||
return $post;
|
return $post;
|
||||||
}, $nestedposts);
|
}, $nestedposts);
|
||||||
};
|
};
|
||||||
|
@ -583,6 +622,8 @@ class renderer {
|
||||||
$this->legacydatamapperfactory,
|
$this->legacydatamapperfactory,
|
||||||
$this->exporterfactory,
|
$this->exporterfactory,
|
||||||
$this->vaultfactory,
|
$this->vaultfactory,
|
||||||
|
$this->urlfactory,
|
||||||
|
$this->entityfactory,
|
||||||
$capabilitymanager,
|
$capabilitymanager,
|
||||||
$ratingmanager,
|
$ratingmanager,
|
||||||
$this->entityfactory->get_exported_posts_sorter(),
|
$this->entityfactory->get_exported_posts_sorter(),
|
||||||
|
|
|
@ -30,8 +30,10 @@ use mod_forum\local\entities\discussion as discussion_entity;
|
||||||
use mod_forum\local\entities\forum as forum_entity;
|
use mod_forum\local\entities\forum as forum_entity;
|
||||||
use mod_forum\local\entities\post as post_entity;
|
use mod_forum\local\entities\post as post_entity;
|
||||||
use mod_forum\local\entities\sorter as sorter_entity;
|
use mod_forum\local\entities\sorter as sorter_entity;
|
||||||
|
use mod_forum\local\factories\entity as entity_factory;
|
||||||
use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
|
use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
|
||||||
use mod_forum\local\factories\exporter as exporter_factory;
|
use mod_forum\local\factories\exporter as exporter_factory;
|
||||||
|
use mod_forum\local\factories\url as url_factory;
|
||||||
use mod_forum\local\factories\vault as vault_factory;
|
use mod_forum\local\factories\vault as vault_factory;
|
||||||
use mod_forum\local\managers\capability as capability_manager;
|
use mod_forum\local\managers\capability as capability_manager;
|
||||||
use mod_forum\local\renderers\posts as posts_renderer;
|
use mod_forum\local\renderers\posts as posts_renderer;
|
||||||
|
@ -80,6 +82,10 @@ class discussion {
|
||||||
private $exporterfactory;
|
private $exporterfactory;
|
||||||
/** @var vault_factory $vaultfactory Vault factory */
|
/** @var vault_factory $vaultfactory Vault factory */
|
||||||
private $vaultfactory;
|
private $vaultfactory;
|
||||||
|
/** @var url_factory $urlfactory URL factory */
|
||||||
|
private $urlfactory;
|
||||||
|
/** @var entity_factory $entityfactory Entity factory */
|
||||||
|
private $entityfactory;
|
||||||
/** @var capability_manager $capabilitymanager Capability manager */
|
/** @var capability_manager $capabilitymanager Capability manager */
|
||||||
private $capabilitymanager;
|
private $capabilitymanager;
|
||||||
/** @var rating_manager $ratingmanager Rating manager */
|
/** @var rating_manager $ratingmanager Rating manager */
|
||||||
|
@ -105,11 +111,14 @@ class discussion {
|
||||||
* @param legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory
|
* @param legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory
|
||||||
* @param exporter_factory $exporterfactory Exporter factory
|
* @param exporter_factory $exporterfactory Exporter factory
|
||||||
* @param vault_factory $vaultfactory Vault factory
|
* @param vault_factory $vaultfactory Vault factory
|
||||||
|
* @param url_factory $urlfactory URL factory
|
||||||
|
* @param entity_factory $entityfactory Entity factory
|
||||||
* @param capability_manager $capabilitymanager Capability manager
|
* @param capability_manager $capabilitymanager Capability manager
|
||||||
* @param rating_manager $ratingmanager Rating manager
|
* @param rating_manager $ratingmanager Rating manager
|
||||||
* @param sorter_entity $exportedpostsorter Sorter for the exported posts
|
* @param sorter_entity $exportedpostsorter Sorter for the exported posts
|
||||||
* @param moodle_url $baseurl The base URL for the discussion
|
* @param moodle_url $baseurl The base URL for the discussion
|
||||||
* @param array $notifications List of HTML notifications to display
|
* @param array $notifications List of HTML notifications to display
|
||||||
|
* @param callable|null $postprocessfortemplate Post processing for template callback
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
forum_entity $forum,
|
forum_entity $forum,
|
||||||
|
@ -121,6 +130,8 @@ class discussion {
|
||||||
legacy_data_mapper_factory $legacydatamapperfactory,
|
legacy_data_mapper_factory $legacydatamapperfactory,
|
||||||
exporter_factory $exporterfactory,
|
exporter_factory $exporterfactory,
|
||||||
vault_factory $vaultfactory,
|
vault_factory $vaultfactory,
|
||||||
|
url_factory $urlfactory,
|
||||||
|
entity_factory $entityfactory,
|
||||||
capability_manager $capabilitymanager,
|
capability_manager $capabilitymanager,
|
||||||
rating_manager $ratingmanager,
|
rating_manager $ratingmanager,
|
||||||
sorter_entity $exportedpostsorter,
|
sorter_entity $exportedpostsorter,
|
||||||
|
@ -138,6 +149,8 @@ class discussion {
|
||||||
$this->legacydatamapperfactory = $legacydatamapperfactory;
|
$this->legacydatamapperfactory = $legacydatamapperfactory;
|
||||||
$this->exporterfactory = $exporterfactory;
|
$this->exporterfactory = $exporterfactory;
|
||||||
$this->vaultfactory = $vaultfactory;
|
$this->vaultfactory = $vaultfactory;
|
||||||
|
$this->urlfactory = $urlfactory;
|
||||||
|
$this->entityfactory = $entityfactory;
|
||||||
$this->capabilitymanager = $capabilitymanager;
|
$this->capabilitymanager = $capabilitymanager;
|
||||||
$this->ratingmanager = $ratingmanager;
|
$this->ratingmanager = $ratingmanager;
|
||||||
$this->notifications = $notifications;
|
$this->notifications = $notifications;
|
||||||
|
@ -169,6 +182,8 @@ class discussion {
|
||||||
|
|
||||||
$displaymode = $this->displaymode;
|
$displaymode = $this->displaymode;
|
||||||
$capabilitymanager = $this->capabilitymanager;
|
$capabilitymanager = $this->capabilitymanager;
|
||||||
|
$urlfactory = $this->urlfactory;
|
||||||
|
$entityfactory = $this->entityfactory;
|
||||||
|
|
||||||
// Make sure we can render.
|
// Make sure we can render.
|
||||||
if (!$capabilitymanager->can_view_discussions($user)) {
|
if (!$capabilitymanager->can_view_discussions($user)) {
|
||||||
|
@ -212,7 +227,22 @@ class discussion {
|
||||||
$exporteddiscussion['html']['movediscussion'] = $this->get_move_discussion_html();
|
$exporteddiscussion['html']['movediscussion'] = $this->get_move_discussion_html();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->renderer->render_from_template('mod_forum/forum_discussion', $exporteddiscussion);
|
if (!empty($user->id)) {
|
||||||
|
$loggedinuser = $entityfactory->get_author_from_stdClass($user);
|
||||||
|
$exporteddiscussion['loggedinuser'] = [
|
||||||
|
'firstname' => $loggedinuser->get_first_name(),
|
||||||
|
'fullname' => $loggedinuser->get_full_name(),
|
||||||
|
'profileimageurl' => ($urlfactory->get_author_profile_image_url($loggedinuser, null))->out(false)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->displaymode === FORUM_MODE_MODERN) {
|
||||||
|
$template = 'mod_forum/forum_discussion_modern';
|
||||||
|
} else {
|
||||||
|
$template = 'mod_forum/forum_discussion';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->renderer->render_from_template($template, $exporteddiscussion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -120,8 +120,8 @@ class discussion_list extends db_table_vault {
|
||||||
// - Most recent editor.
|
// - Most recent editor.
|
||||||
$thistable = new dml_table(self::TABLE, $alias, $alias);
|
$thistable = new dml_table(self::TABLE, $alias, $alias);
|
||||||
$posttable = new dml_table('forum_posts', 'fp', 'p_');
|
$posttable = new dml_table('forum_posts', 'fp', 'p_');
|
||||||
$firstauthorfields = \user_picture::fields('fa', null, self::FIRST_AUTHOR_ID_ALIAS, self::FIRST_AUTHOR_ALIAS);
|
$firstauthorfields = \user_picture::fields('fa', ['deleted'], self::FIRST_AUTHOR_ID_ALIAS, self::FIRST_AUTHOR_ALIAS);
|
||||||
$latestuserfields = \user_picture::fields('la', null, self::LATEST_AUTHOR_ID_ALIAS, self::LATEST_AUTHOR_ALIAS);
|
$latestuserfields = \user_picture::fields('la', ['deleted'], self::LATEST_AUTHOR_ID_ALIAS, self::LATEST_AUTHOR_ALIAS);
|
||||||
|
|
||||||
$fields = implode(', ', [
|
$fields = implode(', ', [
|
||||||
$thistable->get_field_select(),
|
$thistable->get_field_select(),
|
||||||
|
|
|
@ -65,7 +65,7 @@ class extract_user {
|
||||||
$alias = $this->alias;
|
$alias = $this->alias;
|
||||||
|
|
||||||
return array_map(function($record) use ($idalias, $alias) {
|
return array_map(function($record) use ($idalias, $alias) {
|
||||||
return user_picture::unalias($record, null, $idalias, $alias);
|
return user_picture::unalias($record, ['deleted'], $idalias, $alias);
|
||||||
}, $records);
|
}, $records);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -291,13 +291,20 @@ if ($node && $post->get_id() != $discussion->get_first_post_id()) {
|
||||||
$node->add(format_string($post->get_subject()), $PAGE->url);
|
$node->add(format_string($post->get_subject()), $PAGE->url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ismoderndisplaymode = $displaymode == FORUM_MODE_MODERN;
|
||||||
$PAGE->set_title("$course->shortname: " . format_string($discussion->get_name()));
|
$PAGE->set_title("$course->shortname: " . format_string($discussion->get_name()));
|
||||||
$PAGE->set_heading($course->fullname);
|
$PAGE->set_heading($course->fullname);
|
||||||
|
if ($ismoderndisplaymode) {
|
||||||
|
$PAGE->add_body_class('modern-display-mode reset-style');
|
||||||
|
} else {
|
||||||
$PAGE->set_button(forum_search_form($course));
|
$PAGE->set_button(forum_search_form($course));
|
||||||
|
}
|
||||||
|
|
||||||
echo $OUTPUT->header();
|
echo $OUTPUT->header();
|
||||||
|
if (!$ismoderndisplaymode) {
|
||||||
echo $OUTPUT->heading(format_string($forum->get_name()), 2);
|
echo $OUTPUT->heading(format_string($forum->get_name()), 2);
|
||||||
echo $OUTPUT->heading(format_string($discussion->get_name()), 3, 'discussionname');
|
echo $OUTPUT->heading(format_string($discussion->get_name()), 3, 'discussionname');
|
||||||
|
}
|
||||||
|
|
||||||
$rendererfactory = mod_forum\local\container::get_renderer_factory();
|
$rendererfactory = mod_forum\local\container::get_renderer_factory();
|
||||||
$discussionrenderer = $rendererfactory->get_discussion_renderer($forum, $discussion, $displaymode);
|
$discussionrenderer = $rendererfactory->get_discussion_renderer($forum, $discussion, $displaymode);
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$string['activityoverview'] = 'There are new forum posts';
|
$string['activityoverview'] = 'There are new forum posts';
|
||||||
|
$string['actionsforpost'] = 'Actions for post';
|
||||||
$string['addanewdiscussion'] = 'Add a new discussion topic';
|
$string['addanewdiscussion'] = 'Add a new discussion topic';
|
||||||
$string['addanewquestion'] = 'Add a new question';
|
$string['addanewquestion'] = 'Add a new question';
|
||||||
$string['addanewtopic'] = 'Add a new topic';
|
$string['addanewtopic'] = 'Add a new topic';
|
||||||
|
@ -43,10 +44,13 @@ $string['areaattachment'] = 'Attachments';
|
||||||
$string['areapost'] = 'Messages';
|
$string['areapost'] = 'Messages';
|
||||||
$string['attachment'] = 'Attachment';
|
$string['attachment'] = 'Attachment';
|
||||||
$string['attachmentname'] = 'Attachment {$a}';
|
$string['attachmentname'] = 'Attachment {$a}';
|
||||||
|
$string['attachmentnameandfilesize'] = '{$a->name} ({$a->size})';
|
||||||
$string['attachment_help'] = 'You can optionally attach one or more files to a forum post. If you attach an image, it will be displayed after the message.';
|
$string['attachment_help'] = 'You can optionally attach one or more files to a forum post. If you attach an image, it will be displayed after the message.';
|
||||||
$string['attachmentnopost'] = 'You cannot export attachments without a post id';
|
$string['attachmentnopost'] = 'You cannot export attachments without a post id';
|
||||||
$string['attachments'] = 'Attachments';
|
$string['attachments'] = 'Attachments';
|
||||||
$string['attachmentswordcount'] = 'Attachments and word count';
|
$string['attachmentswordcount'] = 'Attachments and word count';
|
||||||
|
$string['authorreplyingprivatelytoauthor'] = '{$a->respondant} replying privately to {$a->author}';
|
||||||
|
$string['authorreplyingtoauthor'] = '{$a->respondant} replying to {$a->author}';
|
||||||
$string['availability'] = 'Availability';
|
$string['availability'] = 'Availability';
|
||||||
$string['blockafter'] = 'Post threshold for blocking';
|
$string['blockafter'] = 'Post threshold for blocking';
|
||||||
$string['blockafter_help'] = 'This setting specifies the maximum number of posts which a user can post in the given time period. Users with the capability mod/forum:postwithoutthrottling are exempt from post limits.';
|
$string['blockafter_help'] = 'This setting specifies the maximum number of posts which a user can post in the given time period. Users with the capability mod/forum:postwithoutthrottling are exempt from post limits.';
|
||||||
|
@ -57,6 +61,7 @@ $string['blogforum'] = 'Standard forum displayed in a blog-like format';
|
||||||
$string['bynameondate'] = 'by {$a->name} - {$a->date}';
|
$string['bynameondate'] = 'by {$a->name} - {$a->date}';
|
||||||
$string['cachedef_forum_is_tracked'] = 'Forum tracking status for user';
|
$string['cachedef_forum_is_tracked'] = 'Forum tracking status for user';
|
||||||
$string['calendardue'] = '{$a} is due';
|
$string['calendardue'] = '{$a} is due';
|
||||||
|
$string['cancelreply'] = 'Cancel reply';
|
||||||
$string['cannotadd'] = 'Could not add the discussion for this forum';
|
$string['cannotadd'] = 'Could not add the discussion for this forum';
|
||||||
$string['cannotadddiscussion'] = 'Adding discussions to this forum requires group membership.';
|
$string['cannotadddiscussion'] = 'Adding discussions to this forum requires group membership.';
|
||||||
$string['cannotadddiscussionall'] = 'You do not have permission to add a new discussion topic for all participants.';
|
$string['cannotadddiscussionall'] = 'You do not have permission to add a new discussion topic for all participants.';
|
||||||
|
@ -144,6 +149,7 @@ $string['delete'] = 'Delete';
|
||||||
$string['deleteddiscussion'] = 'The discussion topic has been deleted';
|
$string['deleteddiscussion'] = 'The discussion topic has been deleted';
|
||||||
$string['deletedpost'] = 'The post has been deleted';
|
$string['deletedpost'] = 'The post has been deleted';
|
||||||
$string['deletedposts'] = 'Those posts have been deleted';
|
$string['deletedposts'] = 'Those posts have been deleted';
|
||||||
|
$string['deleteduser'] = 'Deleted user';
|
||||||
$string['deletesure'] = 'Are you sure you want to delete this post?';
|
$string['deletesure'] = 'Are you sure you want to delete this post?';
|
||||||
$string['deletesureplural'] = 'Are you sure you want to delete this post and all replies? ({$a} posts)';
|
$string['deletesureplural'] = 'Are you sure you want to delete this post and all replies? ({$a} posts)';
|
||||||
$string['digestmailheader'] = 'This is your daily digest of new posts from the {$a->sitename} forums. To change your default forum email preferences, go to {$a->userprefs}.';
|
$string['digestmailheader'] = 'This is your daily digest of new posts from the {$a->sitename} forums. To change your default forum email preferences, go to {$a->userprefs}.';
|
||||||
|
@ -311,6 +317,7 @@ $string['forum:viewsubscribers'] = 'View subscribers';
|
||||||
$string['generalforum'] = 'Standard forum for general use';
|
$string['generalforum'] = 'Standard forum for general use';
|
||||||
$string['generalforums'] = 'General forums';
|
$string['generalforums'] = 'General forums';
|
||||||
$string['hiddenforumpost'] = 'Hidden forum post';
|
$string['hiddenforumpost'] = 'Hidden forum post';
|
||||||
|
$string['hidepreviousrepliescount'] = 'Hide previous replies ({$a})';
|
||||||
$string['indicator:cognitivedepth'] = 'Forum cognitive';
|
$string['indicator:cognitivedepth'] = 'Forum cognitive';
|
||||||
$string['indicator:cognitivedepth_help'] = 'This indicator is based on the cognitive depth reached by the student in a Forum activity.';
|
$string['indicator:cognitivedepth_help'] = 'This indicator is based on the cognitive depth reached by the student in a Forum activity.';
|
||||||
$string['indicator:socialbreadth'] = 'Forum social';
|
$string['indicator:socialbreadth'] = 'Forum social';
|
||||||
|
@ -370,6 +377,7 @@ $string['messageprovider:posts'] = 'Subscribed forum posts';
|
||||||
$string['missingsearchterms'] = 'The following search terms occur only in the HTML markup of this message:';
|
$string['missingsearchterms'] = 'The following search terms occur only in the HTML markup of this message:';
|
||||||
$string['modeflatnewestfirst'] = 'Display replies flat, with newest first';
|
$string['modeflatnewestfirst'] = 'Display replies flat, with newest first';
|
||||||
$string['modeflatoldestfirst'] = 'Display replies flat, with oldest first';
|
$string['modeflatoldestfirst'] = 'Display replies flat, with oldest first';
|
||||||
|
$string['modemodern'] = 'Display replies in modern form';
|
||||||
$string['modenested'] = 'Display replies in nested form';
|
$string['modenested'] = 'Display replies in nested form';
|
||||||
$string['modethreaded'] = 'Display replies in threaded form';
|
$string['modethreaded'] = 'Display replies in threaded form';
|
||||||
$string['modulename'] = 'Forum';
|
$string['modulename'] = 'Forum';
|
||||||
|
@ -555,6 +563,8 @@ $string['replies'] = 'Replies';
|
||||||
$string['repliesmany'] = '{$a} replies so far';
|
$string['repliesmany'] = '{$a} replies so far';
|
||||||
$string['repliesone'] = '{$a} reply so far';
|
$string['repliesone'] = '{$a} reply so far';
|
||||||
$string['reply'] = 'Reply';
|
$string['reply'] = 'Reply';
|
||||||
|
$string['replyauthorself'] = '{$a} (you)';
|
||||||
|
$string['replyingtoauthor'] = 'Replying to {$a}...';
|
||||||
$string['replyplaceholder'] = 'Write your reply...';
|
$string['replyplaceholder'] = 'Write your reply...';
|
||||||
$string['replyforum'] = 'Reply to forum';
|
$string['replyforum'] = 'Reply to forum';
|
||||||
$string['replytopostbyemail'] = 'You can reply to this via email.';
|
$string['replytopostbyemail'] = 'You can reply to this via email.';
|
||||||
|
@ -595,6 +605,7 @@ $string['seeallposts'] = 'See all posts made by this user';
|
||||||
$string['settings'] = 'Settings';
|
$string['settings'] = 'Settings';
|
||||||
$string['shortpost'] = 'Short post';
|
$string['shortpost'] = 'Short post';
|
||||||
$string['showingcountoftotaldiscussions'] = 'Showing {$a->count} of {$a->total} discussions';
|
$string['showingcountoftotaldiscussions'] = 'Showing {$a->count} of {$a->total} discussions';
|
||||||
|
$string['showpreviousrepliescount'] = 'Show previous replies ({$a})';
|
||||||
$string['showsubscribers'] = 'Show/edit current subscribers';
|
$string['showsubscribers'] = 'Show/edit current subscribers';
|
||||||
$string['singleforum'] = 'A single simple discussion';
|
$string['singleforum'] = 'A single simple discussion';
|
||||||
$string['smallmessage'] = '{$a->user} posted in {$a->forumname}';
|
$string['smallmessage'] = '{$a->user} posted in {$a->forumname}';
|
||||||
|
@ -649,6 +660,7 @@ If set to optional, participants can choose whether to turn tracking on or off v
|
||||||
If \'Allow forced read tracking\' is enabled in the site administration, then a further option is available - forced. This means that tracking is always on, regardless of users\' forum preferences.';
|
If \'Allow forced read tracking\' is enabled in the site administration, then a further option is available - forced. This means that tracking is always on, regardless of users\' forum preferences.';
|
||||||
$string['unlockdiscussion'] = 'Unlock this discussion';
|
$string['unlockdiscussion'] = 'Unlock this discussion';
|
||||||
$string['unread'] = 'Unread';
|
$string['unread'] = 'Unread';
|
||||||
|
$string['unreadpost'] = 'Unread post';
|
||||||
$string['unreadposts'] = 'Unread posts';
|
$string['unreadposts'] = 'Unread posts';
|
||||||
$string['unreadpostsnumber'] = '{$a} unread posts';
|
$string['unreadpostsnumber'] = '{$a} unread posts';
|
||||||
$string['unreadpostsone'] = '1 unread post';
|
$string['unreadpostsone'] = '1 unread post';
|
||||||
|
|
|
@ -32,6 +32,7 @@ define('FORUM_MODE_FLATOLDEST', 1);
|
||||||
define('FORUM_MODE_FLATNEWEST', -1);
|
define('FORUM_MODE_FLATNEWEST', -1);
|
||||||
define('FORUM_MODE_THREADED', 2);
|
define('FORUM_MODE_THREADED', 2);
|
||||||
define('FORUM_MODE_NESTED', 3);
|
define('FORUM_MODE_NESTED', 3);
|
||||||
|
define('FORUM_MODE_MODERN', 4);
|
||||||
|
|
||||||
define('FORUM_CHOOSESUBSCRIBE', 0);
|
define('FORUM_CHOOSESUBSCRIBE', 0);
|
||||||
define('FORUM_FORCESUBSCRIBE', 1);
|
define('FORUM_FORCESUBSCRIBE', 1);
|
||||||
|
@ -5184,7 +5185,8 @@ function forum_get_layout_modes() {
|
||||||
return array (FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'),
|
return array (FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'),
|
||||||
FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'),
|
FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'),
|
||||||
FORUM_MODE_THREADED => get_string('modethreaded', 'forum'),
|
FORUM_MODE_THREADED => get_string('modethreaded', 'forum'),
|
||||||
FORUM_MODE_NESTED => get_string('modenested', 'forum'));
|
FORUM_MODE_NESTED => get_string('modenested', 'forum'),
|
||||||
|
FORUM_MODE_MODERN => get_string('modemodern', 'forum'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
<div class="ml-auto dropdown">
|
<div class="ml-auto dropdown">
|
||||||
<button class="{{^settings.excludetext}}dropdown-toggle{{/settings.excludetext}} m-t-0 p-t-0 btn btn-link btn-icon"
|
<button class="{{^settings.excludetext}}dropdown-toggle{{/settings.excludetext}} m-t-0 p-t-0 btn btn-link"
|
||||||
type="button"
|
type="button"
|
||||||
role="button"
|
role="button"
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
|
|
70
mod/forum/templates/forum_discussion_modern.mustache
Normal file
70
mod/forum/templates/forum_discussion_modern.mustache
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
{{!
|
||||||
|
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_forum/forum_discussion_modern
|
||||||
|
|
||||||
|
Template for displaying a single forum discussion.
|
||||||
|
|
||||||
|
Classes required for JS:
|
||||||
|
* none
|
||||||
|
|
||||||
|
Data attributes required for JS:
|
||||||
|
* none
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
<div id="discussion-container-{{uniqid}}" data-content="forum-discussion">
|
||||||
|
{{#html}}
|
||||||
|
<div class="mb-5">
|
||||||
|
<div class="d-flex flex-wrap">
|
||||||
|
<div>{{{modeselectorform}}}</div>
|
||||||
|
<div class="ml-auto d-flex align-items-middle" data-container="discussion-tools">
|
||||||
|
<div>{{{subscribe}}}</div>
|
||||||
|
<div class="pl-1">{{> mod_forum/forum_action_menu}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex mt-2">
|
||||||
|
<div>{{{movediscussion}}}</div>
|
||||||
|
<div {{#movediscussion}}class="ml-2"{{/movediscussion}}>{{{exportdiscussion}}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/html}}
|
||||||
|
|
||||||
|
{{#notifications}}
|
||||||
|
{{> core/notification}}
|
||||||
|
{{/notifications}}
|
||||||
|
|
||||||
|
{{{html.posts}}}
|
||||||
|
</div>
|
||||||
|
{{#js}}
|
||||||
|
require(['jquery', 'mod_forum/discussion_modern'], function($, Discussion) {
|
||||||
|
var root = $('#discussion-container-{{uniqid}}');
|
||||||
|
Discussion.init(root, {
|
||||||
|
{{#loggedinuser}}
|
||||||
|
loggedinuser: {
|
||||||
|
profileimageurl: '{{{profileimageurl}}}',
|
||||||
|
fullname: '{{fullname}}',
|
||||||
|
firstname: '{{firstname}}'
|
||||||
|
}
|
||||||
|
{{/loggedinuser}}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
{{/js}}
|
362
mod/forum/templates/forum_discussion_modern_first_post.mustache
Normal file
362
mod/forum/templates/forum_discussion_modern_first_post.mustache
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
{{!
|
||||||
|
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_forum/forum_discussion_modern_first_post
|
||||||
|
|
||||||
|
Template to render a single post from a discussion.
|
||||||
|
|
||||||
|
Classes required for JS:
|
||||||
|
* none
|
||||||
|
|
||||||
|
Data attributes required for JS:
|
||||||
|
* none
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<article
|
||||||
|
id="p{{id}}"
|
||||||
|
class="forum-post-container"
|
||||||
|
data-post-id="{{id}}"
|
||||||
|
data-region="post"
|
||||||
|
data-target="{{id}}-target"
|
||||||
|
tabindex="0"
|
||||||
|
aria-labelledby="post-header-{{id}}"
|
||||||
|
aria-describedby="post-content-{{id}}"
|
||||||
|
>
|
||||||
|
<!-- The firstpost and starter classes below aren't used for anything other than to identify the first post in behat -->
|
||||||
|
<div
|
||||||
|
class="d-flex focus-target mb-4 {{#firstpost}}firstpost starter{{/firstpost}}"
|
||||||
|
aria-label='{{#str}} postbyuser, mod_forum, {"post": "{{subject}}", "user": "{{author.fullname}}"} {{/str}}'
|
||||||
|
data-post-id="{{id}}" data-content="forum-post"
|
||||||
|
>
|
||||||
|
{{#isfirstunread}}<a id="unread" aria-hidden="true"></a>{{/isfirstunread}}
|
||||||
|
|
||||||
|
<div class="author-image-container d-inline-block pt-2">
|
||||||
|
{{^isdeleted}}
|
||||||
|
{{#author}}
|
||||||
|
{{#urls.profileimage}}
|
||||||
|
<img
|
||||||
|
class="rounded-circle w-100"
|
||||||
|
src="{{{.}}}"
|
||||||
|
alt="{{#str}} pictureof, core, {{author.fullname}} {{/str}}"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
{{/urls.profileimage}}
|
||||||
|
{{/author}}
|
||||||
|
{{/isdeleted}}
|
||||||
|
|
||||||
|
{{#unread}}
|
||||||
|
<div class="icon-size-4 text-info text-center mt-3 icon-no-margin">
|
||||||
|
<span
|
||||||
|
data-toggle="tooltip"
|
||||||
|
data-placement="left"
|
||||||
|
title="{{#str}} unreadpost, mod_forum {{/str}}"
|
||||||
|
tabindex="0"
|
||||||
|
>{{#pix}} i/flagged, core {{/pix}}</span>
|
||||||
|
</div>
|
||||||
|
{{/unread}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-column w-100" data-region-content="forum-post-core">
|
||||||
|
<header id="post-header-{{uniqid}}">
|
||||||
|
{{^isdeleted}}
|
||||||
|
<div class="d-flex flex-wrap align-items-center mb-3">
|
||||||
|
<address class="mb-0 mr-2" tabindex="-1">
|
||||||
|
{{#author}}
|
||||||
|
<h4 class="h6 d-lg-inline-block mb-0 author-header mr-1">
|
||||||
|
{{#parentauthorname}}
|
||||||
|
{{#isprivatereply}}
|
||||||
|
<span class="text-danger">
|
||||||
|
{{#str}}
|
||||||
|
authorreplyingprivatelytoauthor, mod_forum, {
|
||||||
|
"respondant":"<a class='font-weight-bold author-name' data-region='author-name' href='{{{urls.profile}}}'>{{fullname}}</a>",
|
||||||
|
"author":"{{parentauthorname}}"
|
||||||
|
}
|
||||||
|
{{/str}}
|
||||||
|
</span>
|
||||||
|
{{/isprivatereply}}
|
||||||
|
{{^isprivatereply}}
|
||||||
|
{{#str}}
|
||||||
|
authorreplyingtoauthor, mod_forum, {
|
||||||
|
"respondant":"<a class='font-weight-bold author-name' data-region='author-name' href='{{{urls.profile}}}'>{{fullname}}</a>",
|
||||||
|
"author":"{{parentauthorname}}"
|
||||||
|
}
|
||||||
|
{{/str}}
|
||||||
|
{{/isprivatereply}}
|
||||||
|
{{/parentauthorname}}
|
||||||
|
{{^parentauthorname}}
|
||||||
|
<a class='font-weight-bold author-name' data-region='author-name' href="{{{urls.profile}}}">{{fullname}}</a>
|
||||||
|
{{/parentauthorname}}
|
||||||
|
</h4>
|
||||||
|
{{/author}}
|
||||||
|
<time class="text-muted">
|
||||||
|
{{#userdate}} {{timecreated}}, {{#str}} strftimerecentfull, core_langconfig {{/str}} {{/userdate}}
|
||||||
|
</time>
|
||||||
|
</address>
|
||||||
|
|
||||||
|
<div class="d-flex align-items-center ml-auto">
|
||||||
|
{{#author.groups}}
|
||||||
|
{{#urls.image}}
|
||||||
|
<div class="mr-2">
|
||||||
|
{{#urls.group}}
|
||||||
|
<a href="{{urls.group}}" aria-label='{{#str}} memberofgroup, group, {{name}}{{/str}}'>
|
||||||
|
<img
|
||||||
|
class="rounded-circle group-image"
|
||||||
|
src="{{{urls.image}}}"
|
||||||
|
alt="{{#str}} pictureof, core, {{name}} {{/str}}"
|
||||||
|
aria-hidden="true"
|
||||||
|
title="{{#str}} pictureof, core, {{name}} {{/str}}"
|
||||||
|
>
|
||||||
|
</a>
|
||||||
|
{{/urls.group}}
|
||||||
|
{{^urls.group}}
|
||||||
|
<img class="rounded-circle group-image"
|
||||||
|
src="{{{urls.image}}}"
|
||||||
|
alt="{{#str}} pictureof, core, {{name}} {{/str}}"
|
||||||
|
title="{{#str}} pictureof, core, {{name}} {{/str}}"
|
||||||
|
>
|
||||||
|
{{/urls.group}}
|
||||||
|
</div>
|
||||||
|
{{/urls.image}}
|
||||||
|
{{/author.groups}}
|
||||||
|
|
||||||
|
{{^readonly}}
|
||||||
|
{{#capabilities.view}}
|
||||||
|
<a
|
||||||
|
href="{{{urls.view}}}"
|
||||||
|
class="d-inline-block mr-1 icon-no-margin"
|
||||||
|
title="{{#str}} permanentlinktopost, mod_forum {{/str}}"
|
||||||
|
>
|
||||||
|
{{#pix}} e/insert_edit_link, core {{/pix}}
|
||||||
|
</a>
|
||||||
|
{{/capabilities.view}}
|
||||||
|
|
||||||
|
{{#showactionmenu}}
|
||||||
|
<div class="dropdown">
|
||||||
|
<button
|
||||||
|
class="btn btn-icon text-muted icon-no-margin icon-size-3"
|
||||||
|
type="button"
|
||||||
|
id="post-actions-menu-{{uniqid}}"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label="{{#str}} actionsforpost, mod_forum {{/str}}"
|
||||||
|
>
|
||||||
|
{{#pix}} i/moremenu {{/pix}}
|
||||||
|
</button>
|
||||||
|
<!-- inline style to fix RTL placement bug -->
|
||||||
|
<div class="dropdown-menu" aria-labelledby="post-actions-menu-{{uniqid}}" style="right: auto">
|
||||||
|
{{#capabilities}}
|
||||||
|
{{#controlreadstatus}}
|
||||||
|
{{#unread}}
|
||||||
|
<a
|
||||||
|
href="{{{urls.markasread}}}"
|
||||||
|
class="dropdown-item"
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
{{#str}} markread, mod_forum {{/str}}
|
||||||
|
</a>
|
||||||
|
{{/unread}}
|
||||||
|
{{^unread}}
|
||||||
|
<a
|
||||||
|
href="{{{urls.markasunread}}}"
|
||||||
|
class="dropdown-item"
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
{{#str}} markunread, mod_forum {{/str}}
|
||||||
|
</a>
|
||||||
|
{{/unread}}
|
||||||
|
{{/controlreadstatus}}
|
||||||
|
{{#urls.viewparent}}
|
||||||
|
<a
|
||||||
|
href="{{{.}}}"
|
||||||
|
class="dropdown-item"
|
||||||
|
title="{{#str}} permanentlinktoparentpost, mod_forum {{/str}}"
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
{{#str}} parent, mod_forum {{/str}}
|
||||||
|
</a>
|
||||||
|
{{/urls.viewparent}}
|
||||||
|
{{#edit}}
|
||||||
|
<a
|
||||||
|
href="{{{urls.edit}}}"
|
||||||
|
class="dropdown-item"
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
{{#str}} edit, mod_forum {{/str}}
|
||||||
|
</a>
|
||||||
|
{{/edit}}
|
||||||
|
{{#split}}
|
||||||
|
<a
|
||||||
|
href="{{{urls.split}}}"
|
||||||
|
class="dropdown-item"
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
{{#str}} prune, mod_forum {{/str}}
|
||||||
|
</a>
|
||||||
|
{{/split}}
|
||||||
|
{{#delete}}
|
||||||
|
<a
|
||||||
|
href="{{{urls.delete}}}"
|
||||||
|
class="dropdown-item"
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
{{#str}} delete, mod_forum {{/str}}
|
||||||
|
</a>
|
||||||
|
{{/delete}}
|
||||||
|
{{#export}}
|
||||||
|
<a
|
||||||
|
href="{{{urls.export}}}"
|
||||||
|
class="dropdown-item"
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
{{#str}} addtoportfolio, core_portfolio {{/str}}
|
||||||
|
</a>
|
||||||
|
{{/export}}
|
||||||
|
{{/capabilities}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/showactionmenu}}
|
||||||
|
{{/readonly}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/isdeleted}}
|
||||||
|
{{$subject}}
|
||||||
|
<h2
|
||||||
|
class="h1 font-weight-bold post-subject mt-n2"
|
||||||
|
data-region-content="forum-post-core-subject"
|
||||||
|
data-reply-subject="{{replysubject}}"
|
||||||
|
>{{{subject}}}</h2>
|
||||||
|
{{/subject}}
|
||||||
|
{{#hasreplycount}}
|
||||||
|
<span class="sr-only">{{#str}} numberofreplies, mod_forum, {{replycount}} {{/str}}</span>
|
||||||
|
{{/hasreplycount}}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="post-message" id="post-content-{{id}}">
|
||||||
|
{{{message}}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{^isdeleted}}
|
||||||
|
{{#attachments}}
|
||||||
|
{{#isimage}}
|
||||||
|
<div class="attachedimages">
|
||||||
|
<img
|
||||||
|
src="{{{url}}}"
|
||||||
|
alt="{{#str}} attachmentname, mod_forum, {{filename}} {{/str}}"
|
||||||
|
style="max-width: 100%"
|
||||||
|
>
|
||||||
|
{{#urls.export}}
|
||||||
|
<a href="{{{.}}}" title="{{#str}} addtoportfolio, core_portfolio {{/str}}">
|
||||||
|
{{#pix}} t/portfolioadd, core {{/pix}}
|
||||||
|
</a>
|
||||||
|
{{/urls.export}}
|
||||||
|
{{#html.plagiarism}}
|
||||||
|
<div>{{{.}}}</div>
|
||||||
|
{{/html.plagiarism}}
|
||||||
|
</div>
|
||||||
|
{{/isimage}}
|
||||||
|
{{/attachments}}
|
||||||
|
|
||||||
|
{{#attachments}}
|
||||||
|
{{^isimage}}
|
||||||
|
<div class="mt-3">
|
||||||
|
<span class="icon-size-5">{{#pix}} {{icon}}, core {{/pix}}</span>
|
||||||
|
<div class="align-bottom d-inline-block">
|
||||||
|
<a
|
||||||
|
href="{{{url}}}"
|
||||||
|
aria-label="{{#str}} attachmentname, mod_forum, {{filename}} {{/str}}"
|
||||||
|
class="font-weight-bold"
|
||||||
|
>
|
||||||
|
{{#str}} attachmentnameandfilesize, mod_forum, {"name": "{{filename}}", "size": "{{filesizeformatted}}"} {{/str}}
|
||||||
|
</a>
|
||||||
|
{{#urls.export}}
|
||||||
|
<a class="icon-no-margin" href="{{{.}}}" title="{{#str}} exportattachmentname, mod_forum, {{filename}} {{/str}}">
|
||||||
|
{{#pix}} t/portfolioadd, core {{/pix}}
|
||||||
|
</a>
|
||||||
|
{{/urls.export}}
|
||||||
|
{{#html.plagiarism}}
|
||||||
|
<div>{{{.}}}</div>
|
||||||
|
{{/html.plagiarism}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/isimage}}
|
||||||
|
{{/attachments}}
|
||||||
|
|
||||||
|
<div class="d-flex mt-3 align-items-center">
|
||||||
|
{{#html.rating}}
|
||||||
|
<div>{{{.}}}</div>
|
||||||
|
{{/html.rating}}
|
||||||
|
<div class="ml-auto d-flex flex-column">
|
||||||
|
{{#haswordcount}}
|
||||||
|
<span class="ml-auto badge badge-light">
|
||||||
|
{{#str}} numwords, core, {{wordcount}} {{/str}}
|
||||||
|
</span>
|
||||||
|
{{/haswordcount}}
|
||||||
|
{{#html.taglist}}
|
||||||
|
<div class="d-inline-block ml-auto {{#haswordcount}}mt-2{{/haswordcount}}">{{{.}}}</div>
|
||||||
|
{{/html.taglist}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/isdeleted}}
|
||||||
|
|
||||||
|
{{$footer}}
|
||||||
|
{{^isdeleted}}
|
||||||
|
{{^readonly}}
|
||||||
|
<div class="d-flex mt-3">
|
||||||
|
{{#capabilities.reply}}
|
||||||
|
<button
|
||||||
|
class="btn btn-primary btn-lg"
|
||||||
|
data-href="{{{urls.reply}}}"
|
||||||
|
data-post-id="{{id}}"
|
||||||
|
data-action="create-inpage-reply"
|
||||||
|
data-can-reply-privately="{{capabilities.canreplyprivately}}"
|
||||||
|
>
|
||||||
|
{{#str}} reply, mod_forum {{/str}}
|
||||||
|
</button>
|
||||||
|
{{/capabilities.reply}}
|
||||||
|
{{^capabilities.reply}}
|
||||||
|
{{#discussionlocked}}
|
||||||
|
<button class="btn btn-secondary btn-lg disabled" disabled>
|
||||||
|
{{#str}} reply, mod_forum {{/str}}
|
||||||
|
</button>
|
||||||
|
{{/discussionlocked}}
|
||||||
|
{{/capabilities.reply}}
|
||||||
|
</div>
|
||||||
|
{{/readonly}}
|
||||||
|
{{/isdeleted}}
|
||||||
|
{{#discussionlocked}}
|
||||||
|
<div><span class="badge badge-danger mt-2">{{#str}} locked, mod_forum {{/str}}</span></div>
|
||||||
|
{{/discussionlocked}}
|
||||||
|
{{/footer}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{$replies}}
|
||||||
|
<div class="indent inline-reply-container" data-region="inpage-reply-container"></div>
|
||||||
|
|
||||||
|
<div class="indent replies-container" data-region="replies-container">
|
||||||
|
{{#hasreplies}}
|
||||||
|
{{#replies}}
|
||||||
|
{{> mod_forum/forum_discussion_modern_post_reply }}
|
||||||
|
{{/replies}}
|
||||||
|
{{/hasreplies}}
|
||||||
|
</div>
|
||||||
|
{{/replies}}
|
||||||
|
</article>
|
|
@ -0,0 +1,85 @@
|
||||||
|
{{!
|
||||||
|
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_forum/forum_discussion_post
|
||||||
|
|
||||||
|
Template to render a single post from a discussion.
|
||||||
|
|
||||||
|
Classes required for JS:
|
||||||
|
* none
|
||||||
|
|
||||||
|
Data attributes required for JS:
|
||||||
|
* none
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{{< mod_forum/forum_discussion_modern_first_post }}
|
||||||
|
{{$subject}}
|
||||||
|
<h3
|
||||||
|
{{#isdeleted}}class="h6 font-weight-bold"{{/isdeleted}}
|
||||||
|
{{^isdeleted}}class="sr-only"{{/isdeleted}}
|
||||||
|
data-region-content="forum-post-core-subject"
|
||||||
|
>{{{subject}}}</h3>
|
||||||
|
{{/subject}}
|
||||||
|
{{$footer}}
|
||||||
|
{{^isdeleted}}
|
||||||
|
{{^readonly}}
|
||||||
|
{{#capabilities.reply}}
|
||||||
|
<div class="d-flex mt-1">
|
||||||
|
<button
|
||||||
|
class="font-weight-bold btn btn-link btn-lg px-0"
|
||||||
|
data-href="{{{urls.reply}}}"
|
||||||
|
data-post-id="{{id}}"
|
||||||
|
data-action="create-inpage-reply"
|
||||||
|
data-can-reply-privately="{{capabilities.canreplyprivately}}"
|
||||||
|
>
|
||||||
|
{{#str}} reply, mod_forum {{/str}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{{/capabilities.reply}}
|
||||||
|
{{/readonly}}
|
||||||
|
{{/isdeleted}}
|
||||||
|
{{/footer}}
|
||||||
|
{{$replies}}
|
||||||
|
<div class="indent my-4" data-region="replies-visibility-toggle-container" style="display: none">
|
||||||
|
<button class="btn btn-link pl-0" data-action="show-replies">
|
||||||
|
{{#str}}
|
||||||
|
showpreviousrepliescount,
|
||||||
|
mod_forum,
|
||||||
|
<span data-region="reply-count">{{#totalreplycount}}{{.}}{{/totalreplycount}}{{^totalreplycount}}0{{/totalreplycount}}</span>
|
||||||
|
{{/str}}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-link hidden pl-0" data-action="hide-replies">
|
||||||
|
{{#str}}
|
||||||
|
hidepreviousrepliescount,
|
||||||
|
mod_forum,
|
||||||
|
<span data-region="reply-count">{{#totalreplycount}}{{.}}{{/totalreplycount}}{{^totalreplycount}}0{{/totalreplycount}}</span>
|
||||||
|
{{/str}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="indent replies-container" data-region="replies-container">
|
||||||
|
{{#hasreplies}}
|
||||||
|
{{#replies}}
|
||||||
|
{{> mod_forum/forum_discussion_modern_post_reply }}
|
||||||
|
{{/replies}}
|
||||||
|
{{/hasreplies}}
|
||||||
|
</div>
|
||||||
|
<div class="indent inline-reply-container" data-region="inpage-reply-container"></div>
|
||||||
|
{{/replies}}
|
||||||
|
{{/ mod_forum/forum_discussion_modern_first_post }}
|
34
mod/forum/templates/forum_discussion_modern_posts.mustache
Normal file
34
mod/forum/templates/forum_discussion_modern_posts.mustache
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{{!
|
||||||
|
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_forum/forum_discussion_modern_posts
|
||||||
|
|
||||||
|
Template to render a list of posts for a discussion.
|
||||||
|
|
||||||
|
Classes required for JS:
|
||||||
|
* none
|
||||||
|
|
||||||
|
Data attributes required for JS:
|
||||||
|
* none
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{{#posts}}
|
||||||
|
{{> mod_forum/forum_discussion_modern_first_post }}
|
||||||
|
{{/posts}}
|
|
@ -31,7 +31,7 @@
|
||||||
}}
|
}}
|
||||||
{{< mod_forum/forum_discussion_post }}
|
{{< mod_forum/forum_discussion_post }}
|
||||||
{{$replies}}
|
{{$replies}}
|
||||||
<div class="indent">
|
<div class="indent" data-region="replies-container">
|
||||||
{{#replies}}
|
{{#replies}}
|
||||||
{{> mod_forum/forum_discussion_nested_post }}
|
{{> mod_forum/forum_discussion_nested_post }}
|
||||||
{{/replies}}
|
{{/replies}}
|
||||||
|
|
|
@ -314,9 +314,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-region="replies-container">
|
|
||||||
{{$replies}}
|
{{$replies}}
|
||||||
<div>
|
<div data-region="replies-container">
|
||||||
{{#hasreplies}}
|
{{#hasreplies}}
|
||||||
{{#replies}}
|
{{#replies}}
|
||||||
{{> mod_forum/forum_discussion_post }}
|
{{> mod_forum/forum_discussion_post }}
|
||||||
|
@ -324,5 +323,4 @@
|
||||||
{{/hasreplies}}
|
{{/hasreplies}}
|
||||||
</div>
|
</div>
|
||||||
{{/replies}}
|
{{/replies}}
|
||||||
</div>
|
|
||||||
</article>
|
</article>
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
{{< mod_forum/forum_discussion_post }}
|
{{< mod_forum/forum_discussion_post }}
|
||||||
{{$replies}}
|
{{$replies}}
|
||||||
<!-- The forumthread class is only added for behat -->
|
<!-- The forumthread class is only added for behat -->
|
||||||
<div class="indent forumthread post-replies">
|
<div class="indent forumthread post-replies" data-region="replies-container">
|
||||||
{{#replies}}
|
{{#replies}}
|
||||||
{{> mod_forum/forum_discussion_threaded_post }}
|
{{> mod_forum/forum_discussion_threaded_post }}
|
||||||
{{/replies}}
|
{{/replies}}
|
||||||
|
|
104
mod/forum/templates/inpage_reply_modern.mustache
Normal file
104
mod/forum/templates/inpage_reply_modern.mustache
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
{{!
|
||||||
|
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_forum/inpage_reply_modern
|
||||||
|
|
||||||
|
In page reply HTML for the "modern" discussion display mode.
|
||||||
|
|
||||||
|
Classes required for JS:
|
||||||
|
* none
|
||||||
|
|
||||||
|
Data attributes required for JS:
|
||||||
|
* none
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"postid": 0,
|
||||||
|
"reply_url": "",
|
||||||
|
"sesskey": "",
|
||||||
|
"parentsubject": ""
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<div
|
||||||
|
class="mt-4 mb-4"
|
||||||
|
data-content="inpage-reply-content"
|
||||||
|
style="display: none;"
|
||||||
|
>
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="author-image-container">
|
||||||
|
{{#loggedinuser}}
|
||||||
|
<img
|
||||||
|
class="rounded-circle w-100"
|
||||||
|
src="{{{profileimageurl}}}"
|
||||||
|
alt="{{#str}} pictureof, core, {{fullname}} {{/str}}"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
{{/loggedinuser}}
|
||||||
|
</div>
|
||||||
|
<div class="w-100">
|
||||||
|
{{#loggedinuser}}
|
||||||
|
<h4 class="h5 font-weight-bold reply-author">{{#str}} replyauthorself, mod_forum, {{firstname}}{{/str}}</h4>
|
||||||
|
{{/loggedinuser}}
|
||||||
|
<form data-post-id="{{postid}}" data-content="inpage-reply-form" action="{{{reply_url}}}">
|
||||||
|
<textarea
|
||||||
|
name="post"
|
||||||
|
rows="3"
|
||||||
|
class="w-100"
|
||||||
|
{{#parentauthorname}}
|
||||||
|
placeholder="{{#str}} replyingtoauthor, forum, {{.}} {{/str}}"
|
||||||
|
{{/parentauthorname}}
|
||||||
|
{{^parentauthorname}}
|
||||||
|
placeholder="{{#str}} replyplaceholder, forum {{/str}}"
|
||||||
|
{{/parentauthorname}}
|
||||||
|
data-auto-rows
|
||||||
|
data-min-rows="3"
|
||||||
|
data-max-rows="10"
|
||||||
|
></textarea>
|
||||||
|
<input type="hidden" name="postformat" value="{{postformat}}"/>
|
||||||
|
<input type="hidden" name="subject" value="{{#str}} inpagereplysubject, forum, {{parentsubject}} {{/str}}"/>
|
||||||
|
<input type="hidden" name="reply" value="{{postid}}"/>
|
||||||
|
<input type="hidden" name="sesskey" value="{{sesskey}}"/>
|
||||||
|
<div class="d-flex mt-3 align-items-center flex-wrap">
|
||||||
|
<button class="btn btn-primary" data-action="forum-inpage-submit">
|
||||||
|
<span data-region="submit-text">{{#str}} post, core {{/str}}</span>
|
||||||
|
<span data-region="loading-icon-container" class="hidden">{{> core/loading }}</span>
|
||||||
|
</button>
|
||||||
|
<button data-action="forum-advanced-reply" class="btn btn-link mr-auto" type="submit">
|
||||||
|
{{#str}} advanced, core {{/str}}
|
||||||
|
</button>
|
||||||
|
{{#canreplyprivately}}
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<span class="switch">
|
||||||
|
<input name="privatereply" type="checkbox" id="private-reply-checkbox-{{uniqid}}">
|
||||||
|
<label class="mb-0" for="private-reply-checkbox-{{uniqid}}">
|
||||||
|
{{#str}} privatereply, forum {{/str}}
|
||||||
|
</label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{/canreplyprivately}}
|
||||||
|
<button
|
||||||
|
class="btn btn-icon icon-no-margin icon-size-4 text-muted"
|
||||||
|
title="{{#str}} cancelreply, mod_forum {{/str}}"
|
||||||
|
data-action="cancel-inpage-reply"
|
||||||
|
>
|
||||||
|
{{#pix}} i/delete, core {{/pix}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -47,6 +47,7 @@ class mod_forum_entities_author_testcase extends advanced_testcase {
|
||||||
'person',
|
'person',
|
||||||
'test person',
|
'test person',
|
||||||
'test@example.com',
|
'test@example.com',
|
||||||
|
false,
|
||||||
'middle',
|
'middle',
|
||||||
'tteeeeest',
|
'tteeeeest',
|
||||||
'ppppeeerssson',
|
'ppppeeerssson',
|
||||||
|
@ -60,6 +61,7 @@ class mod_forum_entities_author_testcase extends advanced_testcase {
|
||||||
$this->assertEquals('person', $author->get_last_name());
|
$this->assertEquals('person', $author->get_last_name());
|
||||||
$this->assertEquals('test person', $author->get_full_name());
|
$this->assertEquals('test person', $author->get_full_name());
|
||||||
$this->assertEquals('test@example.com', $author->get_email());
|
$this->assertEquals('test@example.com', $author->get_email());
|
||||||
|
$this->assertEquals(false, $author->is_deleted());
|
||||||
$this->assertEquals('middle', $author->get_middle_name());
|
$this->assertEquals('middle', $author->get_middle_name());
|
||||||
$this->assertEquals('tteeeeest', $author->get_first_name_phonetic());
|
$this->assertEquals('tteeeeest', $author->get_first_name_phonetic());
|
||||||
$this->assertEquals('ppppeeerssson', $author->get_last_name_phonetic());
|
$this->assertEquals('ppppeeerssson', $author->get_last_name_phonetic());
|
||||||
|
|
|
@ -49,7 +49,8 @@ class mod_forum_entities_discussion_summary_testcase extends advanced_testcase {
|
||||||
'test',
|
'test',
|
||||||
'person',
|
'person',
|
||||||
'test person',
|
'test person',
|
||||||
'test@example.com'
|
'test@example.com',
|
||||||
|
false
|
||||||
);
|
);
|
||||||
$lastauthor = new author_entity(
|
$lastauthor = new author_entity(
|
||||||
2,
|
2,
|
||||||
|
@ -57,7 +58,8 @@ class mod_forum_entities_discussion_summary_testcase extends advanced_testcase {
|
||||||
'test 2',
|
'test 2',
|
||||||
'person 2',
|
'person 2',
|
||||||
'test 2 person 2',
|
'test 2 person 2',
|
||||||
'test2@example.com'
|
'test2@example.com',
|
||||||
|
false
|
||||||
);
|
);
|
||||||
$discussion = new discussion_entity(
|
$discussion = new discussion_entity(
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -57,7 +57,8 @@ class mod_forum_exporters_author_testcase extends advanced_testcase {
|
||||||
'test',
|
'test',
|
||||||
'user',
|
'user',
|
||||||
'test user',
|
'test user',
|
||||||
'test@example.com'
|
'test@example.com',
|
||||||
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
$exporter = new author_exporter($author, 1, [], true, [
|
$exporter = new author_exporter($author, 1, [], true, [
|
||||||
|
@ -95,7 +96,8 @@ class mod_forum_exporters_author_testcase extends advanced_testcase {
|
||||||
'test',
|
'test',
|
||||||
'user',
|
'user',
|
||||||
'test user',
|
'test user',
|
||||||
'test@example.com'
|
'test@example.com',
|
||||||
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
$group = $datagenerator->create_group(['courseid' => $course->id]);
|
$group = $datagenerator->create_group(['courseid' => $course->id]);
|
||||||
|
@ -132,7 +134,8 @@ class mod_forum_exporters_author_testcase extends advanced_testcase {
|
||||||
'test',
|
'test',
|
||||||
'user',
|
'user',
|
||||||
'test user',
|
'test user',
|
||||||
'test@example.com'
|
'test@example.com',
|
||||||
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
$group = $datagenerator->create_group(['courseid' => $course->id]);
|
$group = $datagenerator->create_group(['courseid' => $course->id]);
|
||||||
|
|
|
@ -548,6 +548,7 @@ class mod_forum_external_testcase extends externallib_advanced_testcase {
|
||||||
$exporteduser2 = [
|
$exporteduser2 = [
|
||||||
'id' => (int) $user2->id,
|
'id' => (int) $user2->id,
|
||||||
'fullname' => fullname($user2),
|
'fullname' => fullname($user2),
|
||||||
|
'isdeleted' => false,
|
||||||
'groups' => [],
|
'groups' => [],
|
||||||
'urls' => [
|
'urls' => [
|
||||||
'profile' => $urlfactory->get_author_profile_url($user2entity),
|
'profile' => $urlfactory->get_author_profile_url($user2entity),
|
||||||
|
@ -562,6 +563,7 @@ class mod_forum_external_testcase extends externallib_advanced_testcase {
|
||||||
'id' => (int) $user3->id,
|
'id' => (int) $user3->id,
|
||||||
'fullname' => fullname($user3),
|
'fullname' => fullname($user3),
|
||||||
'groups' => [],
|
'groups' => [],
|
||||||
|
'isdeleted' => false,
|
||||||
'urls' => [
|
'urls' => [
|
||||||
'profile' => $urlfactory->get_author_profile_url($user3entity),
|
'profile' => $urlfactory->get_author_profile_url($user3entity),
|
||||||
'profileimage' => $urlfactory->get_author_profile_image_url($user3entity),
|
'profileimage' => $urlfactory->get_author_profile_image_url($user3entity),
|
||||||
|
@ -642,6 +644,16 @@ class mod_forum_external_testcase extends externallib_advanced_testcase {
|
||||||
|
|
||||||
// Delete one user, to test that we still receive posts by this user.
|
// Delete one user, to test that we still receive posts by this user.
|
||||||
delete_user($user3);
|
delete_user($user3);
|
||||||
|
$exporteduser3 = [
|
||||||
|
'id' => (int) $user3->id,
|
||||||
|
'fullname' => get_string('deleteduser', 'mod_forum'),
|
||||||
|
'groups' => [],
|
||||||
|
'isdeleted' => true,
|
||||||
|
'urls' => [
|
||||||
|
'profile' => $urlfactory->get_author_profile_url($user3entity),
|
||||||
|
'profileimage' => $urlfactory->get_author_profile_image_url($user3entity),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
// Create what we expect to be returned when querying the discussion.
|
// Create what we expect to be returned when querying the discussion.
|
||||||
$expectedposts = array(
|
$expectedposts = array(
|
||||||
|
|
|
@ -76,12 +76,24 @@ $cm = \cm_info::create($coursemodule);
|
||||||
|
|
||||||
require_course_login($course, true, $cm);
|
require_course_login($course, true, $cm);
|
||||||
|
|
||||||
|
$istypesingle = 'single' === $forum->get_type();
|
||||||
|
|
||||||
|
if ($mode) {
|
||||||
|
set_user_preference('forum_displaymode', $mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
$displaymode = get_user_preferences('forum_displaymode', $CFG->forum_displaymode);
|
||||||
|
|
||||||
$PAGE->set_context($forum->get_context());
|
$PAGE->set_context($forum->get_context());
|
||||||
$PAGE->set_title($forum->get_name());
|
$PAGE->set_title($forum->get_name());
|
||||||
$PAGE->add_body_class('forumtype-' . $forum->get_type());
|
$PAGE->add_body_class('forumtype-' . $forum->get_type());
|
||||||
$PAGE->set_heading($course->fullname);
|
$PAGE->set_heading($course->fullname);
|
||||||
$PAGE->set_button(forum_search_form($course, $search));
|
$PAGE->set_button(forum_search_form($course, $search));
|
||||||
|
|
||||||
|
if ($istypesingle && $displaymode == FORUM_MODE_MODERN) {
|
||||||
|
$PAGE->add_body_class('modern-display-mode reset-style');
|
||||||
|
}
|
||||||
|
|
||||||
if (empty($cm->visible) && !has_capability('moodle/course:viewhiddenactivities', $forum->get_context())) {
|
if (empty($cm->visible) && !has_capability('moodle/course:viewhiddenactivities', $forum->get_context())) {
|
||||||
redirect(
|
redirect(
|
||||||
$urlfactory->get_course_url_from_forum($forum),
|
$urlfactory->get_course_url_from_forum($forum),
|
||||||
|
@ -125,16 +137,10 @@ if (!empty($CFG->enablerssfeeds) && !empty($CFG->forum_enablerssfeeds) && $forum
|
||||||
echo $OUTPUT->header();
|
echo $OUTPUT->header();
|
||||||
echo $OUTPUT->heading(format_string($forum->get_name()), 2);
|
echo $OUTPUT->heading(format_string($forum->get_name()), 2);
|
||||||
|
|
||||||
if ('single' !== $forum->get_type() && !empty($forum->get_intro())) {
|
if (!$istypesingle && !empty($forum->get_intro())) {
|
||||||
echo $OUTPUT->box(format_module_intro('forum', $forumrecord, $cm->id), 'generalbox', 'intro');
|
echo $OUTPUT->box(format_module_intro('forum', $forumrecord, $cm->id), 'generalbox', 'intro');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($mode) {
|
|
||||||
set_user_preference('forum_displaymode', $mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
$displaymode = get_user_preferences('forum_displaymode', $CFG->forum_displaymode);
|
|
||||||
|
|
||||||
if ($sortorder) {
|
if ($sortorder) {
|
||||||
set_user_preference('forum_discussionlistsortorder', $sortorder);
|
set_user_preference('forum_discussionlistsortorder', $sortorder);
|
||||||
}
|
}
|
||||||
|
|
2
theme/boost/amd/build/loader.min.js
vendored
2
theme/boost/amd/build/loader.min.js
vendored
|
@ -1,2 +1,2 @@
|
||||||
define ("theme_boost/loader",["jquery","./tether","core/event"],function(a,b,c){window.jQuery=a;window.Tether=b;M.util.js_pending("theme_boost/loader:children");require(["theme_boost/aria","theme_boost/pending","theme_boost/util","theme_boost/alert","theme_boost/button","theme_boost/carousel","theme_boost/collapse","theme_boost/dropdown","theme_boost/modal","theme_boost/scrollspy","theme_boost/tab","theme_boost/tooltip","theme_boost/popover"],function(b){a("body").popover({trigger:"focus",selector:"[data-toggle=popover][data-trigger!=hover]"});a("html").popover({container:"body",selector:"[data-toggle=popover][data-trigger=hover]",trigger:"hover",delay:{hide:500}});a.fn.dropdown.Constructor.Default.flip=!1;a("a[data-toggle=\"tab\"]").on("shown.bs.tab",function(b){var c=a(b.target).attr("href");if(history.replaceState){history.replaceState(null,null,c)}else{location.hash=c}});var d=window.location.hash;if(d){a(".nav-link[href=\""+d+"\"]").tab("show")}c.getLegacyEvents().done(function(b){a(document).on(b.FILTER_CONTENT_UPDATED,function(){a("body").popover({selector:"[data-toggle=\"popover\"]",trigger:"focus"})})});b.init();M.util.js_complete("theme_boost/loader:children")});return{}});
|
define ("theme_boost/loader",["jquery","./tether","core/event"],function(a,b,c){window.jQuery=a;window.Tether=b;M.util.js_pending("theme_boost/loader:children");require(["theme_boost/aria","theme_boost/pending","theme_boost/util","theme_boost/alert","theme_boost/button","theme_boost/carousel","theme_boost/collapse","theme_boost/dropdown","theme_boost/modal","theme_boost/scrollspy","theme_boost/tab","theme_boost/tooltip","theme_boost/popover"],function(b){a("body").popover({trigger:"focus",selector:"[data-toggle=popover][data-trigger!=hover]"});a("html").popover({container:"body",selector:"[data-toggle=popover][data-trigger=hover]",trigger:"hover",delay:{hide:500}});a("html").tooltip({container:"body",selector:"[data-toggle=\"tooltip\"]"});a.fn.dropdown.Constructor.Default.flip=!1;a("a[data-toggle=\"tab\"]").on("shown.bs.tab",function(b){var c=a(b.target).attr("href");if(history.replaceState){history.replaceState(null,null,c)}else{location.hash=c}});var d=window.location.hash;if(d){a(".nav-link[href=\""+d+"\"]").tab("show")}c.getLegacyEvents().done(function(b){a(document).on(b.FILTER_CONTENT_UPDATED,function(){a("body").popover({selector:"[data-toggle=\"popover\"]",trigger:"focus"})})});b.init();M.util.js_complete("theme_boost/loader:children")});return{}});
|
||||||
//# sourceMappingURL=loader.min.js.map
|
//# sourceMappingURL=loader.min.js.map
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -59,6 +59,11 @@ define(['jquery', './tether', 'core/event'], function(jQuery, Tether, Event) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jQuery("html").tooltip({
|
||||||
|
container: "body",
|
||||||
|
selector: '[data-toggle="tooltip"]'
|
||||||
|
});
|
||||||
|
|
||||||
// Disables flipping the dropdowns up and getting hidden behind the navbar.
|
// Disables flipping the dropdowns up and getting hidden behind the navbar.
|
||||||
jQuery.fn.dropdown.Constructor.Default.flip = false;
|
jQuery.fn.dropdown.Constructor.Default.flip = false;
|
||||||
|
|
||||||
|
|
|
@ -46,3 +46,23 @@ p.arrow_button {
|
||||||
margin: 0 0 10px 5px;
|
margin: 0 0 10px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn.btn-icon {
|
||||||
|
@extend .btn-link;
|
||||||
|
|
||||||
|
height: $icon-width;
|
||||||
|
width: $icon-width;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
@include hover-focus {
|
||||||
|
background-color: $gray-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $size, $length in $iconsizes {
|
||||||
|
&.icon-size-#{$size} {
|
||||||
|
height: ($length + 20px) !important; /* stylelint-disable-line declaration-no-important */
|
||||||
|
width: ($length + 20px) !important; /* stylelint-disable-line declaration-no-important */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2158,13 +2158,13 @@ div.editor_atto_toolbar button .icon {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$switch-height: (1rem * .8) !default;
|
$switch-height: 1.25rem !default;
|
||||||
$switch-height-half: ($switch-height / 2) !default;
|
$switch-height-half: ($switch-height / 2) !default;
|
||||||
$switch-border-radius: $switch-height !default;
|
$switch-border-radius: $switch-height !default;
|
||||||
$switch-bg: $custom-control-indicator-bg !default;
|
$switch-bg: $gray-300 !default;
|
||||||
$switch-checked-bg: map-get($theme-colors, 'primary') !default;
|
$switch-checked-bg: map-get($theme-colors, 'primary') !default;
|
||||||
$switch-disabled-bg: $custom-control-indicator-disabled-bg !default;
|
$switch-disabled-bg: $gray-200 !default;
|
||||||
$switch-disabled-color: $custom-control-label-disabled-color !default;
|
$switch-disabled-color: $gray-600 !default;
|
||||||
$switch-thumb-bg: $white !default;
|
$switch-thumb-bg: $white !default;
|
||||||
$switch-thumb-border-radius: 50% !default;
|
$switch-thumb-border-radius: 50% !default;
|
||||||
$switch-thumb-padding: 2px !default;
|
$switch-thumb-padding: 2px !default;
|
||||||
|
|
|
@ -173,6 +173,208 @@ select {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$author-image-width: 70px;
|
||||||
|
$author-image-margin: 24px;
|
||||||
|
$author-image-width-sm: 30px;
|
||||||
|
$author-image-margin-sm: 8px;
|
||||||
|
|
||||||
|
/** Gently highlight the selected post by changing it's background to blue and then fading it out. */
|
||||||
|
@keyframes background-highlight {
|
||||||
|
from {
|
||||||
|
background-color: rgba(0, 123, 255, 0.5);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.path-mod-forum.modern-display-mode {
|
||||||
|
.discussionsubscription {
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: inherit;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preload-subscribe,
|
||||||
|
.preload-unsubscribe {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-message {
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indent {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reset the badge styling back to pill style. */
|
||||||
|
.badge {
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
padding-left: .5rem;
|
||||||
|
padding-right: .5rem;
|
||||||
|
border-radius: 10rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-light {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
color: #5b5b5b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Style the ratings like a badge. */
|
||||||
|
.rating-aggregate-container {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
color: #5b5b5b;
|
||||||
|
padding: .25em .5em;
|
||||||
|
line-height: 1;
|
||||||
|
margin-right: .5rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 10rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ratinginput {
|
||||||
|
padding: .25em 1.75rem 0.25em .75em;
|
||||||
|
line-height: 1;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 10rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-image {
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
margin-right: 0;
|
||||||
|
float: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Don't show the discussion locked alert in this mode because it's already indicated with a badge. */
|
||||||
|
.alert.discussionlocked {
|
||||||
|
@extend .sr-only;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fix muted text contrast ratios for accessibility. */
|
||||||
|
.text-muted,
|
||||||
|
.dimmed_text {
|
||||||
|
color: #707070 !important; /* stylelint-disable-line declaration-no-important */
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-header {
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
.author-name {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Make the tag list text screen reader visible only */
|
||||||
|
.tag_list > b {
|
||||||
|
@extend .sr-only;
|
||||||
|
}
|
||||||
|
|
||||||
|
:target > .focus-target {
|
||||||
|
animation-name: background-highlight;
|
||||||
|
animation-duration: 1s;
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
animation-iteration-count: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forum-post-container {
|
||||||
|
.replies-container {
|
||||||
|
.forum-post-container {
|
||||||
|
border-top: 1px solid #dee2e6;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
|
||||||
|
.replies-container .forum-post-container {
|
||||||
|
border-top: none;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-reply-container .reply-author {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-message p:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-image-container {
|
||||||
|
width: $author-image-width;
|
||||||
|
margin-right: $author-image-margin;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-reply-container textarea {
|
||||||
|
border: 0;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indent {
|
||||||
|
/**
|
||||||
|
* The first post and first set of replies have a larger author image so offset the 2nd
|
||||||
|
* set of replies by the image width + margin to ensure they align.
|
||||||
|
*/
|
||||||
|
.indent {
|
||||||
|
padding-left: $author-image-width + $author-image-margin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduce the size of the the author image for all second level replies (and below).
|
||||||
|
*/
|
||||||
|
.author-image-container {
|
||||||
|
width: $author-image-width-sm;
|
||||||
|
margin-right: $author-image-margin-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust the indentation offset for all 3rd level replies and below for the smaller author image.
|
||||||
|
*/
|
||||||
|
.indent {
|
||||||
|
padding-left: $author-image-width-sm + $author-image-margin-sm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop indenting the replies after the 5th reply.
|
||||||
|
*/
|
||||||
|
.indent .indent .indent {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Extra small devices (portrait phones, less than 576px). */
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
#page-mod-forum-discuss.modern-display-mode {
|
||||||
|
.forum-post-container {
|
||||||
|
.author-image-container {
|
||||||
|
width: $author-image-width-sm;
|
||||||
|
margin-right: $author-image-margin-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indent {
|
||||||
|
.indent {
|
||||||
|
padding-left: $author-image-width-sm + $author-image-margin-sm;
|
||||||
|
|
||||||
|
.indent .indent {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-image {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// End styling for mod_forum.
|
||||||
|
|
||||||
.maincalendar .calendarmonth td,
|
.maincalendar .calendarmonth td,
|
||||||
.maincalendar .calendarmonth th {
|
.maincalendar .calendarmonth th {
|
||||||
border: 1px dotted $table-border-color;
|
border: 1px dotted $table-border-color;
|
||||||
|
|
|
@ -184,3 +184,76 @@ body:not(.jsenabled) .langmenu:hover > .dropdown-menu,
|
||||||
.active.carousel-item-left {
|
.active.carousel-item-left {
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all of the forced style on the page.
|
||||||
|
* - Remove borders on header and content.
|
||||||
|
* - Remove most of the vertical padding.
|
||||||
|
* - Make the content region flex grow so it pushes things like the
|
||||||
|
* next activity selector to the bottom of the page.
|
||||||
|
*/
|
||||||
|
$allow-reset-style: true !default;
|
||||||
|
|
||||||
|
@if $allow-reset-style {
|
||||||
|
body.reset-style {
|
||||||
|
#page-header {
|
||||||
|
.card {
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
.page-header-headings {
|
||||||
|
h1 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
padding-top: 0 !important; /* stylelint-disable-line declaration-no-important */
|
||||||
|
padding-bottom: 0 !important; /* stylelint-disable-line declaration-no-important */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-content {
|
||||||
|
padding-bottom: 0 !important; /* stylelint-disable-line declaration-no-important */
|
||||||
|
|
||||||
|
#region-main-box {
|
||||||
|
#region-main {
|
||||||
|
border: none;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding-left: $card-spacer-x;
|
||||||
|
padding-right: $card-spacer-x;
|
||||||
|
vertical-align: top;
|
||||||
|
|
||||||
|
div[role="main"] {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-navigation {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-blocks {
|
||||||
|
width: calc(100% - #{$blocks-plus-gutter});
|
||||||
|
|
||||||
|
@include media-breakpoint-down(lg) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-region="blocks-column"] {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-down(lg) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1 +1,4 @@
|
||||||
// General variables for all presets
|
// General variables for all presets
|
||||||
|
|
||||||
|
// Disable the Boost theme reset styling and fixed width content.
|
||||||
|
$allow-reset-style: false;
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue