Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions classes/external.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,164 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class report_customsql_external extends core_external\external_api {
/**
* Describe the optional category filter for query discovery.
*
* @return external_function_parameters
*/
public static function get_queries_parameters() {
return new external_function_parameters([
'categoryids' => new external_multiple_structure(
new external_value(PARAM_INT, 'Category ID'),
'Category IDs to filter by',
VALUE_DEFAULT,
[]
),
]);
}

/**
* List query names and Graphite paths from explicitly allowed categories.
*
* @param array $categoryids Optional category IDs to filter by.
* @return array Query names and category Graphite paths.
*/
public static function get_queries($categoryids = []) {
global $DB;

$params = self::validate_parameters(self::get_queries_parameters(), ['categoryids' => $categoryids]);
$context = \context_system::instance();
self::validate_context($context);
require_capability('report/customsql:usecustomsql', $context);

$categoryids = self::get_allowed_category_ids();
if (!empty($params['categoryids'])) {
$categoryids = array_intersect($categoryids, $params['categoryids']);
}

$queries = [];
foreach ($categoryids as $categoryid) {
if (!$DB->record_exists('report_customsql_categories', ['id' => $categoryid])) {
continue;
}
$graphitepath = get_config('report_customsql', 'customsqlgraphitepath_' . $categoryid) ?: '';
$categoryqueries = $DB->get_records('report_customsql_queries', ['categoryid' => $categoryid], 'id', 'id, displayname');
foreach ($categoryqueries as $query) {
$queries[] = [
'displayname' => $query->displayname,
'graphitepath' => $graphitepath,
];
}
}
return $queries;
}

/**
* Get the category IDs enabled for the monitoring webservices.
*
* @return int[]
*/
private static function get_allowed_category_ids(): array {
$allowedcategories = get_config('report_customsql', 'customsqlcategories');
if (empty($allowedcategories)) {
return [];
}
return array_unique(array_map('intval', explode(',', $allowedcategories)));
}

/**
* Describe the query discovery response.
*
* @return external_multiple_structure
*/
public static function get_queries_returns() {
return new external_multiple_structure(new external_single_structure([
'displayname' => new external_value(PARAM_TEXT, 'Display name'),
'graphitepath' => new external_value(PARAM_PATH, 'Graphite path for this category'),
]));
}

/**
* Describe the parameters for a complete query result.
*
* @return external_function_parameters
*/
public static function get_query_result_parameters() {
return new external_function_parameters([
'queryname' => new external_value(PARAM_TEXT, 'Name of the query.'),
]);
}

/**
* Execute a predefined query from an allowed category without the report display row limit.
*
* @param string $queryname Name of the saved query.
* @return array Column names and rows containing nullable string values.
*/
public static function get_query_result($queryname) {
global $DB;

$params = self::validate_parameters(self::get_query_result_parameters(), ['queryname' => $queryname]);
$context = \context_system::instance();
self::validate_context($context);
require_capability('report/customsql:view', $context);

$report = $DB->get_record('report_customsql_queries', ['displayname' => $params['queryname']]);
// Same error for disallowed categories so query names outside them are not disclosed.
if (!$report || !in_array((int) $report->categoryid, self::get_allowed_category_ids(), true)) {
throw new \moodle_exception('invalidreportid', 'report_customsql');
}
if (!empty($report->capability)) {
require_capability($report->capability, $context);
}

$queryparams = [];
if (!empty($report->queryparams)) {
$queryparams = json_decode($report->queryparams, true);
if (!is_array($queryparams)) {
$queryparams = unserialize($report->queryparams, ['allowed_classes' => false]);
}
if (!is_array($queryparams)) {
throw new \invalid_parameter_exception('Invalid saved query parameters.');
}
}

$sql = report_customsql_prepare_sql($report, time());
$recordset = report_customsql_execute_query($sql, $queryparams, 0);
$result = ['columns' => [], 'rows' => []];
try {
foreach ($recordset as $record) {
if (!$result['columns']) {
$result['columns'] = array_keys((array) $record);
}
$values = [];
foreach ($record as $value) {
$values[] = $value === null ? null : (string) $value;
}
$result['rows'][] = ['values' => $values];
}
} finally {
$recordset->close();
}
return $result;
}

/**
* Describe the complete query result.
*
* @return external_single_structure
*/
public static function get_query_result_returns() {
return new external_single_structure([
'columns' => new external_multiple_structure(new external_value(PARAM_RAW, 'SQL column name.')),
'rows' => new external_multiple_structure(new external_single_structure([
'values' => new external_multiple_structure(
new external_value(PARAM_RAW, 'Cell value as text, or null for SQL NULL.', VALUE_REQUIRED, null, NULL_ALLOWED)
),
])),
]);
}

/**
* Returns description of method parameters
*
Expand Down
6 changes: 6 additions & 0 deletions db/access.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@

$capabilities = [

'report/customsql:usecustomsql' => [
'captype' => 'read',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => [],
],

// People who can view the reports at all.
'report/customsql:view' => [
'riskbitmask' => RISK_PERSONAL,
Expand Down
24 changes: 24 additions & 0 deletions db/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@
defined('MOODLE_INTERNAL') || die();

$functions = [
'report_customsql_get_queries' => [
'classname' => 'report_customsql_external',
'methodname' => 'get_queries',
'classpath' => 'report/customsql/classes/external.php',
'description' => 'Get CustomSQL queries from allowed categories.',
'type' => 'read',
'capabilities' => 'report/customsql:usecustomsql',
'ajax' => false,
],
'report_customsql_get_query_result' => [
'classname' => 'report_customsql_external',
'methodname' => 'get_query_result',
'classpath' => 'report/customsql/classes/external.php',
'description' => 'Execute a predefined query from an allowed category and return all result rows.',
'capabilities' => 'report/customsql:view',
'type' => 'read',
'ajax' => false,
],
'report_customsql_get_users' => [
'classname' => 'report_customsql\external\get_users',
'methodname' => 'execute',
Expand All @@ -45,6 +63,12 @@
];

$services = [
'Custom SQL Query Results API' => [
'functions' => ['report_customsql_get_query_result', 'report_customsql_get_queries'],
'shortname' => 'report_customsql_query_results',
'restrictedusers' => 1,
'enabled' => 1,
],
'Custom SQL Report API' => [
'functions' => [
'report_customsql_get_simple_value',
Expand Down
9 changes: 9 additions & 0 deletions lang/en/report_customsql.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,17 @@
$string['customsql:definequeries'] = 'Define custom queries';
$string['customsql:executebackground'] = 'Execute queries in background';
$string['customsql:managecategories'] = 'Define custom categories';
$string['customsql:usecustomsql'] = 'Discover queries through the Custom SQL webservice';
$string['customsql:view'] = 'View custom queries report';
$string['customsql:viewallexecutions'] = 'View all query executions';
$string['customsqlcategories'] = 'Available Custom SQL categories';
$string['customsqlcategoriesdesc'] = 'Select the categories whose query names can be retrieved through the webservice.';
$string['customsqlgraphitepath'] = 'Graphite path for {$a}';
$string['customsqlgraphitepathdesc'] = 'The Graphite path returned for queries in category "{$a}".';
$string['customsqlgraphitepaths'] = 'Graphite paths for Custom SQL categories';
$string['customsqlgraphitepathsdesc'] = 'Save the category selection first, then configure the Graphite path for each selected category.';
$string['customsqlwebservice'] = 'Custom SQL query discovery webservice';
$string['customsqlwebservicedesc'] = 'Settings for the webservice that lists query names and their category Graphite paths.';
$string['defaultcategory'] = 'Miscellaneous';
$string['deleteareyousure'] = 'Are you sure you want to delete this query?';
$string['deletecategoryareyousure'] = '<p>Are you sure you want to delete this category? </p><p>It cannot contain any queries.</p>';
Expand Down
40 changes: 40 additions & 0 deletions settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,46 @@
defined('MOODLE_INTERNAL') || die();

if ($ADMIN->fulltree) {
require_once($CFG->dirroot . '/report/customsql/locallib.php');

$settings->add(new admin_setting_heading(
'report_customsql/customsqlwebservice',
get_string('customsqlwebservice', 'report_customsql'),
get_string('customsqlwebservicedesc', 'report_customsql')
));
$customsqlcategories = [];
if (!during_initial_install() && get_config('report_customsql', 'version')) {
$customsqlcategories = report_customsql_category_options();
}
$settings->add(new admin_setting_configmulticheckbox(
'report_customsql/customsqlcategories',
get_string('customsqlcategories', 'report_customsql'),
get_string('customsqlcategoriesdesc', 'report_customsql'),
[],
$customsqlcategories
));
$settings->add(new admin_setting_heading(
'report_customsql/customsqlgraphitepaths',
get_string('customsqlgraphitepaths', 'report_customsql'),
get_string('customsqlgraphitepathsdesc', 'report_customsql')
));
$selectedcategories = get_config('report_customsql', 'customsqlcategories');
if (!empty($selectedcategories)) {
foreach (explode(',', $selectedcategories) as $categoryid) {
$categoryid = (int) $categoryid;
if (isset($customsqlcategories[$categoryid])) {
$categoryname = $customsqlcategories[$categoryid];
$settings->add(new admin_setting_configtext(
'report_customsql/customsqlgraphitepath_' . $categoryid,
get_string('customsqlgraphitepath', 'report_customsql', $categoryname),
get_string('customsqlgraphitepathdesc', 'report_customsql', $categoryname),
'mebis.lern.count.',
PARAM_PATH
));
}
}
}

// Start of week, used for the day to run weekly reports.
$days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
$days = array_map(function ($day) {
Expand Down
Loading
Loading