diff --git a/mod/bigbluebuttonbn/classes/external/meeting_info.php b/mod/bigbluebuttonbn/classes/external/meeting_info.php index ffc18d0d96d..34d89b33f58 100644 --- a/mod/bigbluebuttonbn/classes/external/meeting_info.php +++ b/mod/bigbluebuttonbn/classes/external/meeting_info.php @@ -60,6 +60,8 @@ class meeting_info extends external_api { * @param int $groupid * @param bool $updatecache * @return array + * @throws \moodle_exception + * @throws restricted_context_exception */ public static function execute( int $bigbluebuttonbnid, @@ -91,7 +93,8 @@ class meeting_info extends external_api { // Check if the BBB server is working. $serverversion = bigbluebutton_proxy::get_server_version(); if ($serverversion === null) { - throw new \moodle_exception('general_error_no_answer', + throw new \moodle_exception('general_error_no_answer', 'mod_bigbluebuttonbn', + bigbluebutton_proxy::get_server_not_available_url($instance), bigbluebutton_proxy::get_server_not_available_message($instance)); } return (array) meeting::get_meeting_info_for_instance($instance, $updatecache); diff --git a/mod/bigbluebuttonbn/classes/local/exceptions/bigbluebutton_exception.php b/mod/bigbluebuttonbn/classes/local/exceptions/bigbluebutton_exception.php index 29a05013e3e..e1f1a8fe7a2 100644 --- a/mod/bigbluebuttonbn/classes/local/exceptions/bigbluebutton_exception.php +++ b/mod/bigbluebuttonbn/classes/local/exceptions/bigbluebutton_exception.php @@ -19,7 +19,7 @@ namespace mod_bigbluebuttonbn\local\exceptions; use mod_bigbluebuttonbn\plugin; /** - * Class bigbluebutton_exception generic exception + * Class bigbluebutton_exception generic exception. This is supposed to be recoverable. * * @package mod_bigbluebuttonbn * @copyright 2010 onwards, Blindside Networks Inc diff --git a/mod/bigbluebuttonbn/classes/local/exceptions/server_not_available_exception.php b/mod/bigbluebuttonbn/classes/local/exceptions/server_not_available_exception.php index dcdb0fecb98..84afb519a6a 100644 --- a/mod/bigbluebuttonbn/classes/local/exceptions/server_not_available_exception.php +++ b/mod/bigbluebuttonbn/classes/local/exceptions/server_not_available_exception.php @@ -19,6 +19,9 @@ namespace mod_bigbluebuttonbn\local\exceptions; /** * Class server_not_available_exception * + * This kind of error cannot be recovered and should be displayed to the user + * signaling that there is an error in the configuration. + * * @package mod_bigbluebuttonbn * @copyright 2010 onwards, Blindside Networks Inc * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later diff --git a/mod/bigbluebuttonbn/classes/local/proxy/bigbluebutton_proxy.php b/mod/bigbluebuttonbn/classes/local/proxy/bigbluebutton_proxy.php index c2f1a512474..6f5201bf4ff 100644 --- a/mod/bigbluebuttonbn/classes/local/proxy/bigbluebutton_proxy.php +++ b/mod/bigbluebuttonbn/classes/local/proxy/bigbluebutton_proxy.php @@ -340,11 +340,16 @@ class bigbluebutton_proxy extends proxy_base { * @param instance $instance */ public static function require_working_server(instance $instance): void { + $version = null; try { - self::get_server_version(); + $version = self::get_server_version(); } catch (server_not_available_exception $e) { self::handle_server_not_available($instance); } + + if (empty($version)) { + self::handle_server_not_available($instance); + } } /** @@ -384,7 +389,7 @@ class bigbluebutton_proxy extends proxy_base { */ public static function get_server_not_available_url(instance $instance): string { if ($instance->is_admin()) { - return new moodle_url('/admin/settings.php', ['section' => 'modsettingbigbluebuttonbn']); + return new moodle_url('/admin/settings.php', ['section' => 'mod_bigbluebuttonbn_general']); } else if ($instance->is_moderator()) { return new moodle_url('/course/view.php', ['id' => $instance->get_course_id()]); } else { diff --git a/mod/bigbluebuttonbn/classes/local/proxy/curl.php b/mod/bigbluebuttonbn/classes/local/proxy/curl.php index db8e19b9f5b..11e4494c470 100644 --- a/mod/bigbluebuttonbn/classes/local/proxy/curl.php +++ b/mod/bigbluebuttonbn/classes/local/proxy/curl.php @@ -149,7 +149,9 @@ class curl extends \curl { return $xml; } - debugging('Issue retrieving information from the server: ' . $response, DEBUG_DEVELOPER); + $debugabstract = html_to_text($response); + $debugabstract = substr($debugabstract, 0, 1024); // Limit to small amount of info so we do not overload logs. + debugging('Issue retrieving information from the server: ' . $debugabstract, DEBUG_DEVELOPER); return null; } } diff --git a/mod/bigbluebuttonbn/classes/local/proxy/proxy_base.php b/mod/bigbluebuttonbn/classes/local/proxy/proxy_base.php index 1c941018e84..a734b45e81a 100644 --- a/mod/bigbluebuttonbn/classes/local/proxy/proxy_base.php +++ b/mod/bigbluebuttonbn/classes/local/proxy/proxy_base.php @@ -40,7 +40,7 @@ abstract class proxy_base { * Sometimes the server sends back some error and errorKeys that * can be converted to Moodle error messages */ - const MEETING_ERROR = [ + const BBB_TO_MOODLE_ERROR_CODE = [ 'checksumError' => 'index_error_checksum', 'notFound' => 'general_error_not_found', 'maxConcurrent' => 'view_error_max_concurrent', @@ -101,31 +101,61 @@ abstract class proxy_base { * @throws server_not_available_exception */ protected static function assert_returned_xml($xml, ?array $additionaldetails = null): void { - if (empty($xml)) { + $messagekey = ''; + if (!empty($xml)) { + $messagekey = (string) ($xml->messageKey ?? ''); + } + if (empty($xml) || static::is_known_server_unavailable_errorcode($messagekey)) { + $errorcode = self::get_errorcode_from_xml_messagekey($messagekey); throw new server_not_available_exception( - 'general_error_no_answer', + $errorcode, plugin::COMPONENT, - (new moodle_url('/admin/settings.php?section=modsettingbigbluebuttonbn'))->out() + (new moodle_url('/admin/settings.php?section=modsettingbigbluebuttonbn'))->out(), ); } + // If it is a checksum error, this is equivalent to the server not being available. + // So we treat it the same way as if there is not answer. if (is_bool($xml) && $xml) { // Nothing to do here, this might be a post returning that everything went well. return; } if ((string) $xml->returncode == 'FAILED') { - $messagekey = (string) $xml->messageKey ?? ''; + $errorcode = self::get_errorcode_from_xml_messagekey($messagekey); if (!$additionaldetails) { $additionaldetails = []; } $additionaldetails['xmlmessage'] = (string) $xml->message ?? ''; - if (empty($messagekey) || empty(self::MEETING_ERROR[$messagekey])) { - $messagekey = 'general_error_unable_connect'; - } - throw new bigbluebutton_exception($messagekey, json_encode($additionaldetails)); + throw new bigbluebutton_exception($errorcode, json_encode($additionaldetails)); } } + /** + * Get Moodle error code from returned Message Key + * + * @param string $messagekey + * @return string + */ + private static function get_errorcode_from_xml_messagekey(string $messagekey): string { + $errorcode = 'general_error_no_answer'; + if ($messagekey) { + $errorcode = self::BBB_TO_MOODLE_ERROR_CODE[$messagekey] ?? $errorcode; + } + return $errorcode; + } + + /** + * Get Moodle error code from returned Message Key + * + * @param string $messagekey + * @return string + */ + private static function is_known_server_unavailable_errorcode(string $messagekey): string { + // For now, only checksumError is supposed to mean that the server is unavailable. + // Other errors are recoverable. + return in_array($messagekey, ['checksumError']); + } + /** * Fetch the XML from an endpoint and test for success. * diff --git a/mod/bigbluebuttonbn/lib.php b/mod/bigbluebuttonbn/lib.php index 00c25cd3d26..db8b31c5f73 100644 --- a/mod/bigbluebuttonbn/lib.php +++ b/mod/bigbluebuttonbn/lib.php @@ -30,9 +30,11 @@ use core_calendar\local\event\entities\action_interface; use mod_bigbluebuttonbn\completion\custom_completion; use mod_bigbluebuttonbn\instance; use mod_bigbluebuttonbn\local\bigbluebutton; +use mod_bigbluebuttonbn\local\exceptions\server_not_available_exception; use mod_bigbluebuttonbn\local\helpers\files; use mod_bigbluebuttonbn\local\helpers\mod_helper; use mod_bigbluebuttonbn\local\helpers\reset; +use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy; use mod_bigbluebuttonbn\logger; use mod_bigbluebuttonbn\meeting; use mod_bigbluebuttonbn\recording; @@ -482,8 +484,20 @@ function mod_bigbluebuttonbn_core_calendar_provide_event_action( $instance = instance::get_from_instanceid($bigbluebuttonbn->id); // Get if the room is available. $roomavailable = $instance->is_currently_open(); - // Get if the user can join. - $meetinginfo = meeting::get_meeting_info_for_instance($instance); + + $meetinginfo = null; + // Check first if the server can be contacted. + try { + if (empty(bigbluebutton_proxy::get_server_version())) { + // In this case we should already have debugging message printed. + return null; + } + // Get if the user can join. + $meetinginfo = meeting::get_meeting_info_for_instance($instance); + } catch (moodle_exception $e) { + debugging('Error - Cannot retrieve info from meeting ('.$instance->get_meeting_id().') ' . $e->getMessage()); + return null; + } $usercanjoin = $meetinginfo->canjoin; // Check if the room is closed and the user has already joined this session or played the record. diff --git a/mod/bigbluebuttonbn/view.php b/mod/bigbluebuttonbn/view.php index b345024a528..faa5bc8de43 100644 --- a/mod/bigbluebuttonbn/view.php +++ b/mod/bigbluebuttonbn/view.php @@ -26,6 +26,7 @@ */ use mod_bigbluebuttonbn\instance; +use mod_bigbluebuttonbn\local\exceptions\server_not_available_exception; use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy; use mod_bigbluebuttonbn\logger; use mod_bigbluebuttonbn\output\view_page; @@ -78,6 +79,12 @@ $PAGE->set_heading($course->fullname); // Output starts. $renderer = $PAGE->get_renderer('mod_bigbluebuttonbn'); +try { + $renderedinfo = $renderer->render(new view_page($instance)); +} catch (server_not_available_exception $e) { + bigbluebutton_proxy::handle_server_not_available($instance); +} + echo $OUTPUT->header(); // Validate if the user is in a role allowed to join. @@ -92,7 +99,7 @@ if (!$instance->can_join() && $instance->get_type() != instance::TYPE_RECORDING_ } } -echo $renderer->render(new view_page($instance)); +echo $renderedinfo; // Output finishes. echo $OUTPUT->footer();