Commit 22d896e0 authored by David Matamoros's avatar David Matamoros Committed by Paul Holden
Browse files

MDL-70795 reportbuilder: add interface for creating reports.



Implement elements for creating/editing reports, along with
new system report for listing and accompanying JS modules for
user interaction.

Create "Users" datasource as proof-of-concept.

Co-authored-By: Paul Holden's avatarPaul Holden <paulh@moodle.com>
parent 95967d62
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Report builder related settings.
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
use core_admin\local\externalpage\accesscallback;
use core_reportbuilder\permission;
defined('MOODLE_INTERNAL') || die;
/** @var admin_root $ADMIN */
$ADMIN->add('reports', new admin_category('reportbuilder', new lang_string('reportbuilder', 'core_reportbuilder')));
$ADMIN->add(
'reportbuilder', new accesscallback(
'customreports',
get_string('customreports', 'core_reportbuilder'),
(new moodle_url('/reportbuilder/index.php'))->out(),
static function(accesscallback $accesscallback): bool {
return permission::can_view_reports_list();
}
)
);
......@@ -28,6 +28,11 @@ $string['coursefullnamewithlink'] = 'Course full name with link';
$string['courseidnumberewithlink'] = 'Course ID number with link';
$string['courseshortnamewithlink'] = 'Course short name with link';
$string['customfieldcolumn'] = '{$a}';
$string['deletereport'] = 'Delete report';
$string['deletereportconfirm'] = 'Are you sure you want to delete the report \'{$a}\' and all associated data?';
$string['editreportdetails'] = 'Edit report details';
$string['editreportname'] = 'Edit report name';
$string['customreports'] = 'Custom reports';
$string['entitycourse'] = 'Course';
$string['entityuser'] = 'User';
$string['errorreportaccess'] = 'You can not view this report';
......@@ -66,6 +71,9 @@ $string['filtersapplied'] = 'Filters applied';
$string['filtersappliedx'] = 'Filters ({$a})';
$string['filtersreset'] = 'Filters reset';
$string['filterstartswith'] = 'Starts with';
$string['includedefaultsetup'] = 'Include default setup';
$string['includedefaultsetup_help'] = 'Populate report with default layout as defined by the selected source. These include pre-defined columns, filters and conditions.';
$string['newreport'] = 'New report';
$string['privacy:metadata:column'] = 'Report column definitions';
$string['privacy:metadata:column:uniqueidentifier'] = 'Unique identifier of the column';
$string['privacy:metadata:column:usercreated'] = 'The ID of the user who created the column';
......@@ -79,8 +87,18 @@ $string['privacy:metadata:report'] = 'Report definitions';
$string['privacy:metadata:report:name'] = 'The name of the report';
$string['privacy:metadata:report:usercreated'] = 'The ID of the user who created the report';
$string['privacy:metadata:report:usermodified'] = 'The ID of the user who last modified the report';
$string['reportbuilder'] = 'Report builder';
$string['reportcreated'] = 'Report created';
$string['reportdeleted'] = 'Report deleted';
$string['reportsource'] = 'Report source';
$string['reportsource_help'] = 'The report source defines where the data for the report will come from';
$string['reportupdated'] = 'Report updated';
$string['resetall'] = 'Reset all';
$string['selectareportsource'] = 'Select a report source';
$string['selectcourses'] = 'Select courses';
$string['timeadded'] = 'Time added';
$string['timecreated'] = 'Time created';
$string['timemodified'] = 'Time modified';
$string['userfullnamewithlink'] = 'Full name with link';
$string['userfullnamewithpicture'] = 'Full name with picture';
$string['userfullnamewithpicturelink'] = 'Full name with picture and link';
......
......@@ -371,6 +371,9 @@ $string['rating:rate'] = 'Add ratings to items';
$string['rating:view'] = 'View the total rating you received';
$string['rating:viewany'] = 'View total ratings that anyone received';
$string['rating:viewall'] = 'View all raw ratings given by individuals';
$string['reportbuilder:edit'] = 'Create/edit custom reports';
$string['reportbuilder:editall'] = 'Create/edit all custom reports';
$string['reportbuilder:view'] = 'View custom reports';
$string['resetrole'] = 'Reset';
$string['resettingrole'] = 'Resetting role \'{$a}\'';
$string['restore:configure'] = 'Configure restore options';
......
......@@ -2627,4 +2627,31 @@ $capabilities = array(
'coursecreator' => CAP_ALLOW,
]
],
// Allow users to view custom reports.
'moodle/reportbuilder:view' => [
'captype' => 'read',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => [
'user' => CAP_ALLOW,
],
],
// Allow users to create/edit their own custom reports.
'moodle/reportbuilder:edit' => [
'captype' => 'write',
'riskbitmap' => RISK_PERSONAL,
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => [
'manager' => CAP_ALLOW,
],
],
// Allow users to create/edit all custom reports.
'moodle/reportbuilder:editall' => [
'captype' => 'write',
'riskbitmap' => RISK_PERSONAL,
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => [],
],
);
......@@ -2798,6 +2798,12 @@ $functions = array(
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_reports_delete' => [
'classname' => 'core_reportbuilder\external\reports\delete',
'description' => 'Delete report',
'type' => 'write',
'ajax' => true,
],
);
$services = array(
......
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
// 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 to handle modal form requests
*
* @module core_reportbuilder/local/repository/modals
* @package core_reportbuilder
* @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import ModalForm from 'core_form/modalform';
import {get_string as getString} from 'core/str';
/**
* Open the New Report modal form
*
* @param {EventTarget} triggerElement
* @param {Promise} modalTitle
* @param {Number} reportId
* @return {ModalForm}
*/
export const createReportModal = (triggerElement, modalTitle, reportId = 0) => {
return new ModalForm({
modalConfig: {
title: modalTitle,
},
formClass: 'core_reportbuilder\\form\\report',
args: {
id: reportId,
},
saveButtonText: getString('save', 'moodle'),
returnFocus: triggerElement,
});
};
// 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 to handle report AJAX requests
*
* @module core_reportbuilder/local/repository/reports
* @package core_reportbuilder
* @copyright 2021 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Ajax from 'core/ajax';
/**
* Delete given report
*
* @param {Number} reportId
* @return {Promise}
*/
export const deleteReport = reportId => {
const request = {
methodname: 'core_reportbuilder_reports_delete',
args: {reportid: reportId}
};
return Ajax.call([request])[0];
};
......@@ -37,7 +37,10 @@ const SELECTORS = {
},
actions: {
reportActionPopup: '[data-action="report-action-popup"]',
}
reportCreate: '[data-action="report-create"]',
reportEdit: '[data-action="report-edit"]',
reportDelete: '[data-action="report-delete"]',
},
};
/**
......
// 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/>.
/**
* Report builder reports list management
*
* @module core_reportbuilder/reports_list
* @package core_reportbuilder
* @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
"use strict";
import {dispatchEvent} from 'core/event_dispatcher';
import Notification from 'core/notification';
import Pending from 'core/pending';
import {get_string as getString, get_strings as getStrings} from 'core/str';
import {add as addToast} from 'core/toast';
import * as reportEvents from 'core_reportbuilder/local/events';
import * as reportSelectors from 'core_reportbuilder/local/selectors';
import {deleteReport} from 'core_reportbuilder/local/repository/reports';
import {createReportModal} from 'core_reportbuilder/local/repository/modals';
/**
* Initialise module
*/
export const init = () => {
document.addEventListener('click', event => {
const reportCreate = event.target.closest(reportSelectors.actions.reportCreate);
if (reportCreate) {
event.preventDefault();
// Redirect user to editing interface for the report after submission.
const reportModal = createReportModal(event.target, getString('newreport', 'core_reportbuilder'));
reportModal.addEventListener(reportModal.events.FORM_SUBMITTED, event => {
window.location.href = event.detail;
});
reportModal.show();
}
const reportEdit = event.target.closest(reportSelectors.actions.reportEdit);
if (reportEdit) {
event.preventDefault();
// Reload current report page after submission.
const reportModal = createReportModal(event.target, getString('editreportdetails', 'core_reportbuilder'),
reportEdit.dataset.reportId);
reportModal.addEventListener(reportModal.events.FORM_SUBMITTED, () => {
const reportElement = event.target.closest(reportSelectors.regions.report);
getString('reportupdated', 'core_reportbuilder')
.then(addToast)
.then(() => {
dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);
return;
})
.catch(Notification.exception);
});
reportModal.show();
}
const reportDelete = event.target.closest(reportSelectors.actions.reportDelete);
if (reportDelete) {
event.preventDefault();
getStrings([
{key: 'deletereport', component: 'core_reportbuilder'},
{key: 'deletereportconfirm', component: 'core_reportbuilder', param: reportDelete.dataset.reportName},
{key: 'delete', component: 'moodle'},
]).then(([confirmTitle, confirmText, confirmButton]) => {
Notification.confirm(confirmTitle, confirmText, confirmButton, null, () => {
const pendingPromise = new Pending('core_reportbuilder/reports:delete');
const reportElement = event.target.closest(reportSelectors.regions.report);
deleteReport(reportDelete.dataset.reportId)
.then(() => getString('reportdeleted', 'core_reportbuilder'))
.then(addToast)
.then(() => {
pendingPromise.resolve();
dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);
return;
})
.catch(Notification.exception);
});
return;
}).catch(Notification.exception);
}
});
};
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types=1);
namespace core_reportbuilder;
use coding_exception;
use core_reportbuilder\local\helpers\report;
use core_reportbuilder\local\models\filter as filter_model;
use core_reportbuilder\local\report\base;
use core_reportbuilder\local\report\column;
use core_reportbuilder\local\report\filter;
/**
* Class datasource
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class datasource extends base {
/**
* Return user friendly name of the datasource
*
* @return string
*/
abstract public static function get_name(): string;
}
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types=1);
namespace core_reportbuilder\event;
use core\event\base;
use core_reportbuilder\local\models\report;
use moodle_url;
/**
* Report builder custom report created event class.
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*
* @property-read array $other {
* Extra information about the event.
*
* - string name: The name of the report
* - string source: The report source class
* }
*/
class report_created extends base {
/**
* Initialise the event data.
*/
protected function init() {
$this->data['objecttable'] = report::TABLE;
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Creates an instance from a report object
*
* @param report $report
* @return self
*/
public static function create_from_object(report $report): self {
$eventparams = [
'context' => $report->get_context(),
'objectid' => $report->get('id'),
'other' => [
'name' => $report->get('name'),
'source' => $report->get('source'),
]
];
$event = self::create($eventparams);
$event->add_record_snapshot($event->objecttable, $report->to_record());
return $event;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('reportcreated', 'core_reportbuilder');
}
/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' created the custom report with id '$this->objectid'.";
}
/**
* Returns relevant URL.
*
* @return moodle_url
*/
public function get_url(): moodle_url {
return new moodle_url('/reportbuilder/edit.php', ['id' => $this->objectid]);
}
}
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types=1);
namespace core_reportbuilder\event;
use core\event\base;
use core_reportbuilder\local\models\report;
/**
* Report builder custom report deleted event class.
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*
* @property-read array $other {
* Extra information about the event.
*
* - string name: The name of the report
* - string source: The report source class
* }
*/
class report_deleted extends base {
/**
* Initialise the event data.
*/
protected function init() {
$this->data['objecttable'] = report::TABLE;
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Creates an instance from a report object
*
* @param report $report
* @return self
*/
public static function create_from_object(report $report): self {
$eventparams = [
'context' => $report->get_context(),
'objectid' => $report->get('id'),
'other' => [
'name' => $report->get('name'),
'source' => $report->get('source'),
]
];
$event = self::create($eventparams);
$event->add_record_snapshot($event->objecttable, $report->to_record());
return $event;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('reportdeleted', 'core_reportbuilder');
}
/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' deleted the custom report with id '$this->objectid'.";
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment