Skip to content

Commit 55be38d

Browse files
committed
fix: admin ui: dedicated connection testing tool
1 parent 3277263 commit 55be38d

9 files changed

Lines changed: 420 additions & 44 deletions

File tree

amd/build/test_tool.min.js

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

amd/build/test_tool.min.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

amd/src/test_tool.js

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// This file is part of Moodle - http://moodle.org/
2+
//
3+
// Moodle is free software: you can redistribute it and/or modify
4+
// it under the terms of the GNU General Public License as published by
5+
// the Free Software Foundation, either version 3 of the License, or
6+
// (at your option) any later version.
7+
//
8+
// Moodle is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU General Public License
14+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15+
16+
/**
17+
* Test tool for checking the connection and credentials of a given Etherpad Lite API-URL and API-key.
18+
*
19+
* @module mod_etherpadlite/test_tool
20+
* @copyright 2025 André Menrath <andre.menrath@uni-graz.at>, University of Graz
21+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22+
*/
23+
24+
import Modal from 'core/modal';
25+
import Notification from 'core/notification';
26+
import Templates from 'core/templates';
27+
import {call as fetchMany} from 'core/ajax';
28+
import {getString} from 'core/str';
29+
30+
const SELECTORS = {
31+
SETTING_ETHERPAD_URL: 'url',
32+
SETTING_ETHERPAD_APIKEY: 'apikey',
33+
TEST_TOOL_BUTTON: '[data-action="mod-etherpadlite-test-tool"]',
34+
};
35+
36+
/**
37+
* Entrypoint of the JS.
38+
*
39+
* @method init
40+
*/
41+
export const init = () => {
42+
if (window.location.pathname === '/admin/settings.php') {
43+
registerListenerEvents();
44+
}
45+
};
46+
47+
/**
48+
* Register snippet related event listeners.
49+
*
50+
* @method registerListenerEvents
51+
*/
52+
const registerListenerEvents = () => {
53+
const testButton = document.querySelector(SELECTORS.TEST_TOOL_BUTTON);
54+
55+
// If the button for the Connection Test Tool is not found, no listeners are registered.
56+
if (!testButton) {
57+
return;
58+
}
59+
60+
// Add event listener which will trigger the connection test with the given Etherpad Lite API credentials.
61+
testButton.addEventListener('click', (event) => {
62+
event.preventDefault();
63+
testConnection().catch(Notification.exception);
64+
});
65+
66+
// Make the button clickable after the event listener is added.
67+
if (testButton.hasAttribute('disabled')) {
68+
testButton.removeAttribute('disabled');
69+
}
70+
if (testButton.classList.contains('disabled')) {
71+
testButton.classList.remove('disabled');
72+
}
73+
};
74+
75+
/**
76+
* Execute the tests via webservice.
77+
*
78+
* @param {string} url
79+
* @param {string} apikey
80+
*
81+
* @returns string The HTML of the test.
82+
*/
83+
export const getTestResults = (url, apikey) => fetchMany([{
84+
methodname: 'mod_etherpadlite_test_tool',
85+
args: {
86+
url: url,
87+
apikey: apikey,
88+
}
89+
}])[0];
90+
91+
/**
92+
* Get the value of a Moodle Admin setting on the current page.
93+
*
94+
* @param {string} setting
95+
*
96+
* @returns {string|integer} The settings Value
97+
*/
98+
const getAdminSettingValue = (setting) => {
99+
const settingElementId = 'admin-' + setting;
100+
const settingElement = document.getElementById(settingElementId);
101+
const input = settingElement.querySelector('input');
102+
return input ? input.value : null;
103+
};
104+
105+
/**
106+
* Build the modal with the provided data.
107+
*
108+
* @method buildModal
109+
*/
110+
const testConnection = async() => {
111+
const url = getAdminSettingValue(SELECTORS.SETTING_ETHERPAD_URL);
112+
const apikey = getAdminSettingValue(SELECTORS.SETTING_ETHERPAD_APIKEY);
113+
114+
const testResult = await getTestResults(url, apikey);
115+
116+
const modal = await Modal.create({
117+
title: getString('testmodaltitle', 'mod_etherpadlite'),
118+
body: Templates.render('mod_etherpadlite/test_tool_result', testResult),
119+
large: true,
120+
isVerticallyCentered: true,
121+
buttons: {
122+
'cancel': getString('closebuttontitle', 'moodle'),
123+
},
124+
});
125+
modal.show();
126+
};

classes/api/client.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,9 @@ public function send_clients_message($padid, $msg) {
738738
* @return string|bool The blocked host or false
739739
*/
740740
public static function is_url_blocked($urlstring) {
741+
global $CFG;
742+
require_once($CFG->libdir . '/filelib.php');
743+
741744
$curl = new \curl(['ignoresecurity' => true]);
742745
$url = new \moodle_url($urlstring);
743746
if ($curl->get_security()->url_is_blocked($url)) {
@@ -761,6 +764,8 @@ public static function is_url_blocked($urlstring) {
761764
* @return static
762765
*/
763766
public static function get_instance($apikey, $apiurl = null) {
767+
global $CFG;
768+
require_once($CFG->libdir . '/filelib.php');
764769
static $client;
765770
if (empty($client)) {
766771
if (static::is_testing()) {

classes/external/test_tool.php

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
// This file is part of Moodle - http://moodle.org/
3+
//
4+
// Moodle is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// Moodle is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16+
17+
namespace mod_etherpadlite\external;
18+
19+
use core_external\external_function_parameters;
20+
use core_external\external_single_structure;
21+
use core_external\external_api;
22+
use core_external\external_value;
23+
24+
/**
25+
* Implementation of web service mod_etherpadlite_test_tool
26+
*
27+
* @package mod_etherpadlite
28+
* @copyright 2025 André Menrath <andre.menrath@uni-graz.at>, University of Graz
29+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30+
*/
31+
class test_tool extends external_api {
32+
33+
/**
34+
* Describes the parameters for mod_etherpadlite_test_tool
35+
*
36+
* @return external_function_parameters
37+
*/
38+
public static function execute_parameters(): external_function_parameters {
39+
return new external_function_parameters([
40+
'url' => new external_value(PARAM_URL, 'The Etherpad Lite API URL'),
41+
'apikey' => new external_value(PARAM_TEXT, 'The Etherpad Lite API Key'),
42+
]);
43+
}
44+
45+
/**
46+
* Implementation of web service mod_etherpadlite_test_tool
47+
*
48+
* @param string $url
49+
* @param string $apikey
50+
*/
51+
public static function execute($url, $apikey) {
52+
// Parameter validation.
53+
['url' => $url, 'apikey' => $apikey] = self::validate_parameters(
54+
self::execute_parameters(),
55+
['url' => $url, 'apikey' => $apikey]
56+
);
57+
58+
// Verify context and capability.
59+
$context = \context_system::instance();
60+
self::validate_context($context);
61+
62+
\require_admin();
63+
64+
// Is the current host blocked?
65+
$blockedhost = \mod_etherpadlite\api\client::is_url_blocked($url);
66+
$ignoresecurity = !empty(get_config('mod_etherpadlite', 'ignoresecurity'));
67+
68+
$connected = false;
69+
$infotext = '';
70+
71+
if (!$blockedhost || $ignoresecurity) {
72+
// Try to establish a connection.
73+
try {
74+
$client = \mod_etherpadlite\api\client::get_instance($apikey, $url);
75+
$connected = true;
76+
unset($client);
77+
} catch (\mod_etherpadlite\api\api_exception $e) {
78+
$infotext = $e->getMessage();
79+
}
80+
}
81+
82+
// Prepare blocked host information.
83+
if ($blockedhost) {
84+
$blockedhostinfo = $ignoresecurity
85+
? get_string('urlisblocked_but_ignored', 'etherpadlite', $blockedhost)
86+
: get_string('urlisblocked', 'etherpadlite', $blockedhost);
87+
} else {
88+
$blockedhostinfo = '';
89+
}
90+
91+
$result = [
92+
'connection' => [
93+
'success' => $connected,
94+
'info' => $infotext ?? '',
95+
'blockedinfo' => $blockedhostinfo,
96+
],
97+
];
98+
99+
return $result;
100+
}
101+
102+
/**
103+
* Describe the return structure for mod_etherpadlite_test_tool
104+
*
105+
* @return external_single_structure
106+
*/
107+
public static function execute_returns(): external_single_structure {
108+
return new external_single_structure([
109+
'connection' => new external_single_structure([
110+
'success' => new external_value(PARAM_BOOL, 'The connection result'),
111+
'info' => new external_value(PARAM_RAW, 'Any warning or info message from the connection'),
112+
'blockedinfo' => new external_value(PARAM_RAW, 'Any warning or info message from the blocked check'),
113+
]),
114+
]);
115+
}
116+
}

db/services.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
// This file is part of Moodle - http://moodle.org/
3+
//
4+
// Moodle is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// Moodle is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16+
17+
/**
18+
* External functions and service declaration for Etherpad Lite
19+
*
20+
* Documentation: {@link https://moodledev.io/docs/apis/subsystems/external/description}
21+
*
22+
* @package mod_etherpadlite
23+
* @category webservice
24+
* @copyright 2025 André Menrath <andre.menrath@uni-graz.at>, University of Graz
25+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26+
*/
27+
28+
defined('MOODLE_INTERNAL') || die();
29+
30+
$functions = [
31+
32+
'mod_etherpadlite_test_tool' => [
33+
'classname' => mod_etherpadlite\external\test_tool::class,
34+
'description' => 'Connection and API-key test tool for Etherpad Lite',
35+
'type' => 'read',
36+
'ajax' => true,
37+
],
38+
];
39+
40+
$services = [
41+
];

0 commit comments

Comments
 (0)