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
+ }
+ ]
+ }
+}}
+
+
+
+
+
+{{#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}}