diff --git a/mod/forum/report/summary/amd/build/filters.min.js b/mod/forum/report/summary/amd/build/filters.min.js new file mode 100644 index 00000000000..123433ea14b --- /dev/null +++ b/mod/forum/report/summary/amd/build/filters.min.js @@ -0,0 +1,2 @@ +define ("forumreport_summary/filters",["exports","jquery","core/popper"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=d(b);c=d(c);function d(a){return a&&a.__esModule?a:{default:a}}var e=function(a){a=(0,b.default)(a);(0,b.default)(document).ready(function(){(0,b.default)(".loading-icon").hide();(0,b.default)("#summaryreport").removeClass("hidden")});(0,b.default)(a).on("click",".filter-clear",function(a){var b=a.target.parentNode.parentNode.parentElement.querySelectorAll("input[type=\"checkbox\"]:checked");b.forEach(function(a){a.checked=!1})});var d=function(a){var c=(0,b.default)("#filtersform").attr("action");if(a){a.preventDefault();var d=a.target.search.substr(1);c+="&"+d}(0,b.default)("#filtersform").attr("action",c);(0,b.default)("#filtersform").submit()};(0,b.default)(".resettable").on("click","a",function(a){d(a)});(0,b.default)("thead").on("click","a",function(a){d(a)});(0,b.default)(".pagination").on("click","a",function(a){d(a)});var e=function(a){var b=document.getElementById(a),c=b.querySelectorAll("input[type=\"checkbox\"]:not(:checked)");c.forEach(function(a){a.checked=!0})};(0,b.default)("#filter-groups-popover .select-all").on("click",function(){e("filter-groups-popover")});(0,b.default)("#filter-groups-button").on("click",function(){var a=document.querySelector("#filter-groups-button"),d=document.querySelector("#filter-groups-popover");new c.default(a,d,{placement:"bottom"});(0,b.default)("#filter-groups-popover").removeClass("hidden")});(0,b.default)(a).on("click","#filter-groups-popover .filter-save",function(){(0,b.default)("#filter-groups-popover").addClass("hidden");d(!1)})};a.init=e}); +//# sourceMappingURL=filters.min.js.map diff --git a/mod/forum/report/summary/amd/build/filters.min.js.map b/mod/forum/report/summary/amd/build/filters.min.js.map new file mode 100644 index 00000000000..4b9132aeeeb --- /dev/null +++ b/mod/forum/report/summary/amd/build/filters.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/filters.js"],"names":["init","root","document","ready","hide","removeClass","on","event","selected","target","parentNode","parentElement","querySelectorAll","forEach","checkbox","checked","generateWithFilters","newLink","attr","preventDefault","filterParams","search","substr","submit","selectAll","checkboxdiv","targetdiv","getElementById","deselected","referenceElement","querySelector","popperContent","Popper","placement","addClass"],"mappings":"qKAwBA,OACA,O,mDAEO,GAAMA,CAAAA,CAAI,CAAG,SAACC,CAAD,CAAU,CAC1BA,CAAI,CAAG,cAAEA,CAAF,CAAP,CAIA,cAAEC,QAAF,EAAYC,KAAZ,CAAkB,UAAW,CACzB,cAAE,eAAF,EAAmBC,IAAnB,GACA,cAAE,gBAAF,EAAoBC,WAApB,CAAgC,QAAhC,CACH,CAHD,EAQA,cAAEJ,CAAF,EAAQK,EAAR,CAAW,OAAX,CAAoB,eAApB,CAAqC,SAASC,CAAT,CAAgB,CAEjD,GAAIC,CAAAA,CAAQ,CAAGD,CAAK,CAACE,MAAN,CAAaC,UAAb,CAAwBA,UAAxB,CAAmCC,aAAnC,CAAiDC,gBAAjD,CAAkE,kCAAlE,CAAf,CAEAJ,CAAQ,CAACK,OAAT,CAAiB,SAASC,CAAT,CAAmB,CAChCA,CAAQ,CAACC,OAAT,GACH,CAFD,CAGH,CAPD,EAUA,GAAIC,CAAAA,CAAmB,CAAG,SAACT,CAAD,CAAW,CACjC,GAAIU,CAAAA,CAAO,CAAG,cAAE,cAAF,EAAkBC,IAAlB,CAAuB,QAAvB,CAAd,CAEA,GAAIX,CAAJ,CAAW,CACPA,CAAK,CAACY,cAAN,GAEA,GAAIC,CAAAA,CAAY,CAAGb,CAAK,CAACE,MAAN,CAAaY,MAAb,CAAoBC,MAApB,CAA2B,CAA3B,CAAnB,CACAL,CAAO,EAAI,IAAMG,CACpB,CAED,cAAE,cAAF,EAAkBF,IAAlB,CAAuB,QAAvB,CAAiCD,CAAjC,EACA,cAAE,cAAF,EAAkBM,MAAlB,EACH,CAZD,CAeA,cAAE,aAAF,EAAiBjB,EAAjB,CAAoB,OAApB,CAA6B,GAA7B,CAAkC,SAASC,CAAT,CAAgB,CAC9CS,CAAmB,CAACT,CAAD,CACtB,CAFD,EAKA,cAAE,OAAF,EAAWD,EAAX,CAAc,OAAd,CAAuB,GAAvB,CAA4B,SAASC,CAAT,CAAgB,CACxCS,CAAmB,CAACT,CAAD,CACtB,CAFD,EAKA,cAAE,aAAF,EAAiBD,EAAjB,CAAoB,OAApB,CAA6B,GAA7B,CAAkC,SAASC,CAAT,CAAgB,CAC9CS,CAAmB,CAACT,CAAD,CACtB,CAFD,EAKA,GAAIiB,CAAAA,CAAS,CAAG,SAACC,CAAD,CAAiB,IACzBC,CAAAA,CAAS,CAAGxB,QAAQ,CAACyB,cAAT,CAAwBF,CAAxB,CADa,CAEzBG,CAAU,CAAGF,CAAS,CAACd,gBAAV,CAA2B,wCAA3B,CAFY,CAI7BgB,CAAU,CAACf,OAAX,CAAmB,SAASC,CAAT,CAAmB,CAClCA,CAAQ,CAACC,OAAT,GACH,CAFD,CAGH,CAPD,CAYA,cAAE,oCAAF,EAAwCT,EAAxC,CAA2C,OAA3C,CAAoD,UAAW,CAC3DkB,CAAS,CAAC,uBAAD,CACZ,CAFD,EAKA,cAAE,uBAAF,EAA2BlB,EAA3B,CAA8B,OAA9B,CAAuC,UAAW,CAE9C,GAAIuB,CAAAA,CAAgB,CAAG3B,QAAQ,CAAC4B,aAAT,CAAuB,uBAAvB,CAAvB,CACIC,CAAa,CAAG7B,QAAQ,CAAC4B,aAAT,CAAuB,wBAAvB,CADpB,CAGA,GAAIE,UAAJ,CAAWH,CAAX,CAA6BE,CAA7B,CAA4C,CAACE,SAAS,CAAE,QAAZ,CAA5C,EAGA,cAAE,wBAAF,EAA4B5B,WAA5B,CAAwC,QAAxC,CACH,CATD,EAYA,cAAEJ,CAAF,EAAQK,EAAR,CAAW,OAAX,CAAoB,qCAApB,CAA2D,UAAW,CAElE,cAAE,wBAAF,EAA4B4B,QAA5B,CAAqC,QAArC,EAGAlB,CAAmB,IACtB,CAND,CAOH,CAzFM,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module responsible for handling forum summary report filters.\n *\n * @module forumreport_summary/filters\n * @package forumreport_summary\n * @copyright 2019 Michael Hawkins \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Popper from 'core/popper';\n\nexport const init = (root) => {\n root = $(root);\n\n // Hide loading spinner and show report once page is ready.\n // This ensures filters can be applied when sorting by columns.\n $(document).ready(function() {\n $('.loading-icon').hide();\n $('#summaryreport').removeClass('hidden');\n });\n\n // Generic filter handlers.\n\n // Event handler to clear filters.\n $(root).on(\"click\", \".filter-clear\", function(event) {\n // Clear checkboxes.\n let selected = event.target.parentNode.parentNode.parentElement.querySelectorAll('input[type=\"checkbox\"]:checked');\n\n selected.forEach(function(checkbox) {\n checkbox.checked = false;\n });\n });\n\n // Called to override click event to trigger a proper generate request with filtering.\n var generateWithFilters = (event) => {\n var newLink = $('#filtersform').attr('action');\n\n if (event) {\n event.preventDefault();\n\n let filterParams = event.target.search.substr(1);\n newLink += '&' + filterParams;\n }\n\n $('#filtersform').attr('action', newLink);\n $('#filtersform').submit();\n };\n\n // Override 'reset table preferences' so it generates with filters.\n $('.resettable').on(\"click\", \"a\", function(event) {\n generateWithFilters(event);\n });\n\n // Override table heading sort links so they generate with filters.\n $('thead').on(\"click\", \"a\", function(event) {\n generateWithFilters(event);\n });\n\n // Override pagination page links so they generate with filters.\n $('.pagination').on(\"click\", \"a\", function(event) {\n generateWithFilters(event);\n });\n\n // Select all checkboxes within a filter section.\n var selectAll = (checkboxdiv) => {\n let targetdiv = document.getElementById(checkboxdiv);\n let deselected = targetdiv.querySelectorAll('input[type=\"checkbox\"]:not(:checked)');\n\n deselected.forEach(function(checkbox) {\n checkbox.checked = true;\n });\n };\n\n // Groups filter specific handlers.\n\n // Event to handle select all groups.\n $('#filter-groups-popover .select-all').on('click', function() {\n selectAll('filter-groups-popover');\n });\n\n // Event handler for showing groups filter popover.\n $('#filter-groups-button').on('click', function() {\n // Create popover.\n var referenceElement = document.querySelector('#filter-groups-button'),\n popperContent = document.querySelector('#filter-groups-popover');\n\n new Popper(referenceElement, popperContent, {placement: 'bottom'});\n\n // Show popover.\n $('#filter-groups-popover').removeClass('hidden');\n });\n\n // Event handler to save groups filter.\n $(root).on(\"click\", \"#filter-groups-popover .filter-save\", function() {\n // Close the popover.\n $('#filter-groups-popover').addClass('hidden');\n\n // Submit the filter values and re-generate report.\n generateWithFilters(false);\n });\n};\n"],"file":"filters.min.js"} \ No newline at end of file diff --git a/mod/forum/report/summary/amd/src/filters.js b/mod/forum/report/summary/amd/src/filters.js new file mode 100644 index 00000000000..cac7f920763 --- /dev/null +++ b/mod/forum/report/summary/amd/src/filters.js @@ -0,0 +1,117 @@ +// 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 . + +/** + * Module responsible for handling forum summary report filters. + * + * @module forumreport_summary/filters + * @package forumreport_summary + * @copyright 2019 Michael Hawkins + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import $ from 'jquery'; +import Popper from 'core/popper'; + +export const init = (root) => { + root = $(root); + + // Hide loading spinner and show report once page is ready. + // This ensures filters can be applied when sorting by columns. + $(document).ready(function() { + $('.loading-icon').hide(); + $('#summaryreport').removeClass('hidden'); + }); + + // Generic filter handlers. + + // Event handler to clear filters. + $(root).on("click", ".filter-clear", function(event) { + // Clear checkboxes. + let selected = event.target.parentNode.parentNode.parentElement.querySelectorAll('input[type="checkbox"]:checked'); + + selected.forEach(function(checkbox) { + checkbox.checked = false; + }); + }); + + // Called to override click event to trigger a proper generate request with filtering. + var generateWithFilters = (event) => { + var newLink = $('#filtersform').attr('action'); + + if (event) { + event.preventDefault(); + + let filterParams = event.target.search.substr(1); + newLink += '&' + filterParams; + } + + $('#filtersform').attr('action', newLink); + $('#filtersform').submit(); + }; + + // Override 'reset table preferences' so it generates with filters. + $('.resettable').on("click", "a", function(event) { + generateWithFilters(event); + }); + + // Override table heading sort links so they generate with filters. + $('thead').on("click", "a", function(event) { + generateWithFilters(event); + }); + + // Override pagination page links so they generate with filters. + $('.pagination').on("click", "a", function(event) { + generateWithFilters(event); + }); + + // Select all checkboxes within a filter section. + var selectAll = (checkboxdiv) => { + let targetdiv = document.getElementById(checkboxdiv); + let deselected = targetdiv.querySelectorAll('input[type="checkbox"]:not(:checked)'); + + deselected.forEach(function(checkbox) { + checkbox.checked = true; + }); + }; + + // Groups filter specific handlers. + + // Event to handle select all groups. + $('#filter-groups-popover .select-all').on('click', function() { + selectAll('filter-groups-popover'); + }); + + // Event handler for showing groups filter popover. + $('#filter-groups-button').on('click', function() { + // Create popover. + var referenceElement = document.querySelector('#filter-groups-button'), + popperContent = document.querySelector('#filter-groups-popover'); + + new Popper(referenceElement, popperContent, {placement: 'bottom'}); + + // Show popover. + $('#filter-groups-popover').removeClass('hidden'); + }); + + // Event handler to save groups filter. + $(root).on("click", "#filter-groups-popover .filter-save", function() { + // Close the popover. + $('#filter-groups-popover').addClass('hidden'); + + // Submit the filter values and re-generate report. + generateWithFilters(false); + }); +}; diff --git a/mod/forum/report/summary/classes/output/filters.php b/mod/forum/report/summary/classes/output/filters.php new file mode 100644 index 00000000000..054f55ed912 --- /dev/null +++ b/mod/forum/report/summary/classes/output/filters.php @@ -0,0 +1,169 @@ +. + +/** + * Forum summary report filters renderable. + * + * @package forumreport_summary + * @copyright 2019 Michael Hawkins + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace forumreport_summary\output; + +use moodle_url; +use renderable; +use renderer_base; +use stdClass; +use templatable; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Forum summary report filters renderable. + * + * @copyright 2019 Michael Hawkins + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class filters implements renderable, templatable { + + /** + * Course module the report is being run within. + * + * @var stdClass $cm + */ + protected $cm; + + /** + * Moodle URL used as the form action on the generate button. + * + * @var moodle_url $actionurl + */ + protected $actionurl; + + /** + * Details of groups available for filtering. + * Stored in the format groupid => groupname. + * + * @var array $groupsavailable + */ + protected $groupsavailable = []; + + /** + * IDs of groups selected for filtering. + * + * @var array $groupsselected + */ + protected $groupsselected = []; + + /** + * Builds renderable filter data. + * + * @param stdClass $cm The course module object. + * @param moodle_url $actionurl The form action URL. + * @param array $filterdata (optional) Associative array of data that has been set on available filters, if any, + * in the format filtertype => [values] + */ + public function __construct(stdClass $cm, moodle_url $actionurl, array $filterdata = []) { + $this->cm = $cm; + $this->actionurl = $actionurl; + + // Prepare groups filter data. + $groupsdata = $filterdata['groups'] ?? []; + $this->prepare_groups_data($groupsdata); + } + + /** + * Prepares groups data and sets relevant property values. + * + * @param array $groupsdata Groups selected for filtering. + * @return void. + */ + protected function prepare_groups_data(array $groupsdata): void { + $groupsavailable = []; + $groupsselected = []; + + // Only fetch groups user has access to. + $groups = groups_get_activity_allowed_groups($this->cm); + + // Include a 'no groups' option if groups exist. + if (!empty($groups)) { + $nogroups = new stdClass(); + $nogroups->id = -1; + $nogroups->name = get_string('groupsnone'); + array_push($groups, $nogroups); + } + + foreach ($groups as $group) { + $groupsavailable[$group->id] = $group->name; + + // Select provided groups if they are available. + if (in_array($group->id, $groupsdata)) { + $groupsselected[] = $group->id; + } + } + + // Overwrite groups properties. + $this->groupsavailable = $groupsavailable; + $this->groupsselected = $groupsselected; + } + + + /** + * Export data for use as the context of a mustache template. + * + * @param renderer_base $renderer The renderer to be used to display report filters. + * @return array Data in a format compatible with a mustache template. + */ + public function export_for_template(renderer_base $renderer): stdClass { + $output = new stdClass(); + + // Set formaction URL. + $output->actionurl = $this->actionurl->out(false); + + // Set groups filter data. + if (!empty($this->groupsavailable)) { + $output->hasgroups = true; + + $groupscount = count($this->groupsselected); + + if (count($this->groupsavailable) <= $groupscount) { + $output->filtergroupsname = get_string('filter:groupscountall', 'forumreport_summary'); + } else if (!empty($this->groupsselected)) { + $output->filtergroupsname = get_string('filter:groupscountnumber', 'forumreport_summary', $groupscount); + } else { + $output->filtergroupsname = get_string('filter:groupsname', 'forumreport_summary'); + } + + // Set groups filter. + $groupsdata = []; + + foreach ($this->groupsavailable as $groupid => $groupname) { + $groupsdata[] = [ + 'groupid' => $groupid, + 'groupname' => $groupname, + 'checked' => in_array($groupid, $this->groupsselected), + ]; + } + + $output->filtergroups = $groupsdata; + } else { + $output->hasgroups = false; + } + + return $output; + } +} diff --git a/mod/forum/report/summary/classes/summary_table.php b/mod/forum/report/summary/classes/summary_table.php index c59f3e20eb3..2576bc63b0b 100644 --- a/mod/forum/report/summary/classes/summary_table.php +++ b/mod/forum/report/summary/classes/summary_table.php @@ -41,6 +41,9 @@ class summary_table extends table_sql { /** Forum filter type */ const FILTER_FORUM = 1; + /** Groups filter type */ + const FILTER_GROUPS = 2; + /** @var \stdClass The various SQL segments that will be combined to form queries to fetch various information. */ public $sql; @@ -53,25 +56,36 @@ class summary_table extends table_sql { /** @var int The forum ID being reported on. */ protected $forumid; + /** @var \stdClass The course module object of the forum being reported on. */ + protected $cm; + /** * @var int The user ID if only one user's summary will be generated. * This will apply to users without permission to view others' summaries. */ protected $userid; + /** + * @var bool Whether the table should be overridden to show the 'nothing to display' message. + * False unless checks confirm there will be nothing to display. + */ + protected $nothingtodisplay = false; + /** * Forum report table constructor. * * @param int $courseid The ID of the course the forum(s) exist within. - * @param int $forumid The ID of the forum being summarised. + * @param array $filters Report filters in the format 'type' => [values]. */ - public function __construct(int $courseid, int $forumid) { + public function __construct(int $courseid, array $filters) { global $USER; + $forumid = $filters['forums'][0]; + parent::__construct("summaryreport_{$courseid}_{$forumid}"); - $cm = get_coursemodule_from_instance('forum', $forumid, $courseid); - $context = \context_module::instance($cm->id); + $this->cm = get_coursemodule_from_instance('forum', $forumid, $courseid); + $context = \context_module::instance($this->cm->id); // Only show their own summary unless they have permission to view all. if (!has_capability('forumreport/summary:viewall', $context)) { @@ -98,8 +112,8 @@ class summary_table extends table_sql { // Define the basic SQL data and object format. $this->define_base_sql(); - // Set the forum ID. - $this->add_filter(self::FILTER_FORUM, [$forumid]); + // Apply relevant filters. + $this->apply_filters($filters); } /** @@ -111,6 +125,7 @@ class summary_table extends table_sql { public function get_filter_name(int $filtertype): string { $filternames = [ self::FILTER_FORUM => 'Forum', + self::FILTER_GROUPS => 'Groups', ]; return $filternames[$filtertype]; @@ -232,6 +247,8 @@ class summary_table extends table_sql { * @throws coding_exception */ public function add_filter(int $filtertype, array $values = []): void { + global $DB; + $paramcounterror = false; switch($filtertype) { @@ -248,6 +265,68 @@ class summary_table extends table_sql { break; + case self::FILTER_GROUPS: + // Find total number of options available (groups plus 'no groups'). + $availablegroups = groups_get_activity_allowed_groups($this->cm); + $alloptionscount = 1 + count($availablegroups); + + // Skip adding filter if not applied, or all options are selected. + if (!empty($values) && count($values) < $alloptionscount) { + // Include users without groups if that option (-1) is selected. + $nonekey = array_search(-1, $values, true); + + // Users within selected groups or not in any groups are included. + if ($nonekey !== false && count($values) > 1) { + unset($values[$nonekey]); + list($groupidin, $groupidparams) = $DB->get_in_or_equal($values, SQL_PARAMS_NAMED, 'groupid'); + + // No select fields required. + // No joins required (handled by where to prevent data duplication). + $this->sql->filterwhere .= " + AND (u.id = + (SELECT gm.userid + FROM {groups_members} gm + WHERE gm.userid = u.id + AND gm.groupid {$groupidin} + GROUP BY gm.userid + LIMIT 1) + OR + (SELECT nogm.userid + FROM mdl_groups_members nogm + WHERE nogm.userid = u.id + GROUP BY nogm.userid + LIMIT 1) + IS NULL)"; + $this->sql->params += $groupidparams; + + } else if ($nonekey !== false) { + // Only users within no groups are included. + unset($values[$nonekey]); + + // No select fields required. + $this->sql->filterfromjoins .= " LEFT JOIN {groups_members} nogm ON nogm.userid = u.id"; + $this->sql->filterwhere .= " AND nogm.id IS NULL"; + + } else if (!empty($values)) { + // Only users within selected groups are included. + list($groupidin, $groupidparams) = $DB->get_in_or_equal($values, SQL_PARAMS_NAMED, 'groupid'); + + // No select fields required. + // No joins required (handled by where to prevent data duplication). + $this->sql->filterwhere .= " + AND u.id = ( + SELECT gm.userid + FROM {groups_members} gm + WHERE gm.userid = u.id + AND gm.groupid {$groupidin} + GROUP BY gm.userid + LIMIT 1)"; + $this->sql->params += $groupidparams; + } + } + + break; + default: throw new coding_exception("Report filter type '{$filtertype}' not found."); break; @@ -361,6 +440,12 @@ class summary_table extends table_sql { public function out($pagesize, $useinitialsbar, $downloadhelpbutton = ''): void { global $DB; + // If there is nothing to display, print the relevant string and return, no further action is required. + if ($this->nothingtodisplay) { + $this->print_nothing_to_display(); + return; + } + if (!$this->columns) { $sql = $this->get_full_sql(); @@ -378,6 +463,20 @@ class summary_table extends table_sql { $this->finish_output(); } + /** + * Apply the relevant filters to the report. + * + * @param array $filters Report filters in the format 'type' => [values]. + * @return void. + */ + protected function apply_filters(array $filters): void { + // Apply the forums filter. + $this->add_filter(self::FILTER_FORUM, $filters['forums']); + + // Apply groups filter. + $this->add_filter(self::FILTER_GROUPS, $filters['groups']); + } + /** * Prepares a complete SQL statement from the base query and any filters defined. * diff --git a/mod/forum/report/summary/index.php b/mod/forum/report/summary/index.php index 0a898e92441..010cce90072 100644 --- a/mod/forum/report/summary/index.php +++ b/mod/forum/report/summary/index.php @@ -31,6 +31,12 @@ if (isguestuser()) { $courseid = required_param('courseid', PARAM_INT); $forumid = required_param('forumid', PARAM_INT); $perpage = optional_param('perpage', 25, PARAM_INT); +$filters = []; + +// Establish filter values. +$filters['forums'] = [$forumid]; +$filters['groups'] = optional_param_array('filtergroups', [], PARAM_INT); + $cm = null; $modinfo = get_fast_modinfo($courseid); @@ -47,9 +53,9 @@ if ($forumid > 0) { } require_login($courseid, false, $cm); +$context = \context_module::instance($cm->id); // This capability is required to view any version of the report. -$context = \context_module::instance($cm->id); if (!has_capability("forumreport/summary:view", $context)) { $redirecturl = new moodle_url("/mod/forum/view.php"); $redirecturl->param('id', $forumid); @@ -73,7 +79,11 @@ $PAGE->set_heading($course->fullname); echo $OUTPUT->header(); echo $OUTPUT->heading(get_string('summarytitle', 'forumreport_summary', $forumname), 2, 'p-b-2'); -$table = new \forumreport_summary\summary_table($courseid, $forumid); -$table->baseurl = $url; -$table->out($perpage, false); +// Render the report filters form. +$renderer = $PAGE->get_renderer('forumreport_summary'); +echo $renderer->render_filters_form($cm, $url, $filters); + +// Prepare and display the report. +echo $renderer->render_report($courseid, $url, $filters, $perpage); + echo $OUTPUT->footer(); diff --git a/mod/forum/report/summary/lang/en/forumreport_summary.php b/mod/forum/report/summary/lang/en/forumreport_summary.php index c4e0e34d231..20942bde586 100644 --- a/mod/forum/report/summary/lang/en/forumreport_summary.php +++ b/mod/forum/report/summary/lang/en/forumreport_summary.php @@ -24,6 +24,9 @@ $string['attachmentcount'] = 'Number of attachments'; $string['earliestpost'] = 'Earliest post'; +$string['filter:groupsname'] = 'Groups'; +$string['filter:groupscountall'] = 'Groups (all)'; +$string['filter:groupscountnumber'] = 'Groups ({$a})'; $string['latestpost'] = 'Most recent post'; $string['nodetitle'] = 'Summary report'; $string['pluginname'] = 'Forum summary report'; diff --git a/mod/forum/report/summary/renderer.php b/mod/forum/report/summary/renderer.php new file mode 100644 index 00000000000..a7ccfa7c2dc --- /dev/null +++ b/mod/forum/report/summary/renderer.php @@ -0,0 +1,76 @@ +. + +/** + * Provides rendering functionality for the forum summary report subplugin. + * + * @package forumreport_summary + * @copyright 2019 Michael Hawkins + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Renderer for the forum summary report. + * + * @copyright 2019 Michael Hawkins + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class forumreport_summary_renderer extends plugin_renderer_base { + + /** + * Render the filters available for the forum summary report. + * + * @param stdClass $cm The course module object. + * @param moodle_url $actionurl The form action URL. + * @param array $filters Optional array of currently applied filter values. + * @return string The filter form HTML. + */ + public function render_filters_form(stdClass $cm, moodle_url $actionurl, array $filters = []): string { + $renderable = new \forumreport_summary\output\filters($cm, $actionurl, $filters); + $templatecontext = $renderable->export_for_template($this); + + return $this->render_from_template('forumreport_summary/filters', $templatecontext); + } + + /** + * Render the summary report table. + * + * @param int $courseid ID of the course where the forum is located. + * @param string $url Base URL for the report page. + * @param array $filters Values of filters to be applied. + * @param int $perpage Number of results to render per page. + * @return string The report table HTML. + */ + public function render_report($courseid, $url, $filters, $perpage) { + // Initialise table. + $table = new \forumreport_summary\summary_table($courseid, $filters); + $table->baseurl = $url; + + // Buffer so calling script can output the report as required. + ob_start(); + + // Render table. + $table->out($perpage, false); + + $tablehtml = ob_get_contents(); + + ob_end_clean(); + + return $this->render_from_template('forumreport_summary/report', ['tablehtml' => $tablehtml, 'placeholdertext' => false]); + } +} diff --git a/mod/forum/report/summary/templates/filters.mustache b/mod/forum/report/summary/templates/filters.mustache new file mode 100644 index 00000000000..426b19dc187 --- /dev/null +++ b/mod/forum/report/summary/templates/filters.mustache @@ -0,0 +1,81 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template forumreport_summary/filters + + Summary report filters. + + Example context (json): + { + "actionurl": "https://mymoodlesite.com/mod/forum/report/summary/index.php?courseid=2&forumid=2&perpage=50", + "hasgroups": true, + "filtergroupsname" : "Groups (all)", + "filtergroups": [ + { + "gropuid": "1", + "groupname": "Group A", + "checked": true + }, + { + "gropuid": "3", + "groupname": "Group C", + "checked": false + } + ] + } +}} + +
+
+ +
+ {{#hasgroups}} + + + {{! Start groups filter popover}} + + {{! End groups filter popover}} + {{/hasgroups}} +
+
+
+ +{{#js}} +require(['forumreport_summary/filters'], function(Filters) { + Filters.init(document.querySelector("[data-report-id='{{uniqid}}']")); +}); +{{/js}} diff --git a/mod/forum/report/summary/templates/report.mustache b/mod/forum/report/summary/templates/report.mustache new file mode 100644 index 00000000000..ec1a1934d5f --- /dev/null +++ b/mod/forum/report/summary/templates/report.mustache @@ -0,0 +1,39 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template forumreport_summary/report + + Summary report filters. + + Example context (json): + { + "placeholdertext": "To generate the summary report, set any filter values required, then select \"Generate report\".", + "tablehtml": false + } +}} + +{{! The placeholder text, used before the report has been generated }} +{{^tablehtml}}

{{placeholdertext}}

{{/tablehtml}} + +{{#tablehtml}} +
+ {{> core/loading }} +
+ +{{/tablehtml}} diff --git a/mod/forum/report/summary/version.php b/mod/forum/report/summary/version.php index 7ff17f7e1d1..d695f93e325 100644 --- a/mod/forum/report/summary/version.php +++ b/mod/forum/report/summary/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2019082000; +$plugin->version = 2019090200; $plugin->requires = 2019071900; $plugin->component = 'forumreport_summary';