diff --git a/.github/workflows/pull_request_template.md b/.github/pull_request_template.md similarity index 100% rename from .github/workflows/pull_request_template.md rename to .github/pull_request_template.md diff --git a/.gitignore b/.gitignore index 0b42136..4a88761 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,10 @@ node_modules/ vendor/ -/.vs \ No newline at end of file +/.vs + +# Compiled CSS files +css/*.min.css +css/*.min.css.map + +# Ignore the entire build folder +/amd/build/ diff --git a/ajax/search_suggestions.php b/ajax/search_suggestions.php new file mode 100644 index 0000000..502caad --- /dev/null +++ b/ajax/search_suggestions.php @@ -0,0 +1,111 @@ +libdir . '/filelib.php'); +require_login(); // Ensures full session context, like in core_renderer + +header('Content-Type: application/json'); + +// Function to handle JSON error response and exit. +// This centralizes error handling and ensures consistent output. +function send_json_error($message, $status = 'error', $http_status_code = 500) { + error_log("theme_nhse: send_json_error - Status: " . $status . ", Message: " . $message); + http_response_code($http_status_code); + echo json_encode(['status' => $status, 'message' => $message]); + exit; +} + +try { + global $USER, $DB; + $theme_identifier = 'theme_nhse'; + + // Get the .NET application base URL theme property + $dotnet_base_url = get_config($theme_identifier, 'dotnet_base_url'); + // Ensure the .NET application base URL ends with a slash if it's not empty + if (!empty($dotnet_base_url) && substr($dotnet_base_url, -1) !== '/') { + $dotnet_base_url .= '/'; + } + + // Get the API Base URL theme property + $api_base_url = get_config($theme_identifier, 'api_base_url'); + // Ensure the API base URL ends with a slash if it's not empty + if (empty($api_base_url)) { + send_json_error('API Base URL is not configured in theme settings.', 'error', 400); + } + if (substr($api_base_url, -1) !== '/') { + $api_base_url .= '/'; + } + + // Retrieve the OIDC token + $accesstoken = null; + $token_record = $DB->get_record('auth_oidc_token', ['username' => $USER->username]); + // IMPORTANT: Only access $token->token if $token is not false (i.e., a record was found) + if ($token_record) { + $accesstoken = $token_record->token; // Assuming the actual token is in the 'token' column + } else { + send_json_error( + 'No OIDC token found for user.', // The message for the client and log + 'error', // The status field in the JSON response + 500 // The HTTP status code (e.g., 500 Internal Server Error) + ); + exit; // Exit if no token is found and you want to indicate an error + } + +} catch (Throwable $e) { + send_json_error( + 'An unexpected error occurred: ' . $e->getMessage(), + 'exception_error', + 500 + ); + exit; +} + +$searchterm = optional_param('query', '', PARAM_TEXT); + +$api_endpoint_path = 'Search/GetAutoSuggestionResult/'; +$url_for_search = $api_base_url . $api_endpoint_path . urlencode($searchterm); + + +try + { + $curl = new \curl(); + // Set Authorization header + $options = [ + 'HTTPHEADER' => [ + 'Authorization: Bearer ' . $accesstoken, + 'Accept: application/json' + ] + ]; + $response = $curl->get($url_for_search, null, $options); + $result = json_decode($response, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + $json_error_msg = json_last_error_msg(); + error_log("theme_nhse: JSON decoding error: " . $json_error_msg); + send_json_error( + 'API response could not be decoded: ' . $json_error_msg, + 'json_decode_error', + 500 // Internal Server Error as the server received unparseable data + ); + } + + echo json_encode([ + 'status' => 'ok', + 'test_url' => $url_for_search, + 'accesstoken' => $accesstoken, + 'api_raw_response' => $response, // Add the raw string response + 'api_decoded_result' => $result // Add the decoded array/object response + ]); + exit; + + +} catch (Exception $e) { + error_log("theme_nhse: CURL Exception caught for URL: " . $url_for_search . " Message: " . $e->getMessage()); // Use $url_for_search for clarity + send_json_error( + 'Failed to connect to API: ' . $e->getMessage(), // Message for client and log + 'curl_error', // Specific status for cURL issues + 500 // HTTP 500 Internal Server Error for network/server-side issues + ); +} + + + diff --git a/amd/src/autosuggest.js b/amd/src/autosuggest.js new file mode 100644 index 0000000..ae59ece --- /dev/null +++ b/amd/src/autosuggest.js @@ -0,0 +1,194 @@ +define(['jquery'], function($) { + return { + init: function() { + const searchInput = $('#search-field'); + // Ensure you have a diff --git a/templates/navbar_account.mustache b/templates/navbar_account.mustache new file mode 100644 index 0000000..85527e7 --- /dev/null +++ b/templates/navbar_account.mustache @@ -0,0 +1,27 @@ +{{! + 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 . +}} + +{{#is_user_logged_in}} {{! This block will only be rendered if is_user_logged_in is true }} +
+ {{! Assuming dotnet_base_url is passed and correctly formatted with a trailing slash }} + + +
+{{/is_user_logged_in}} {{! End of the conditional block }} + + + diff --git a/templates/navbar_search.mustache b/templates/navbar_search.mustache new file mode 100644 index 0000000..49223c3 --- /dev/null +++ b/templates/navbar_search.mustache @@ -0,0 +1,80 @@ +{{! + 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 theme_nhse/navbar + + This template renders the top navbar. + + Example context (json): + { + "output": { + "should_display_navbar_logo": true, + "get_compact_logo_url": "http://placekitten.com/50/50", + "custom_menu": "
  • ..
  • ", + "page_heading_menu": "
  • ..
  • ", + "search_box": "
    ", + "navbar_plugin_output": "", + "user_menu": "" + }, + "config": { + "wwwroot": "#", + "homeurl": "/my/" + }, + "sitename": "Moodle Site", + "mobileprimarynav": [ + { + "text": "Dashboard", + "url": "/my", + "isactive": true + }, + { + "text": "Site home", + "url": "/", + "isactive": false + }, + { + "text": "My courses", + "url": "/course", + "isactive": false + } + ] + } +}} +
    + +
    + + +{{#output.page_heading_menu}} + +{{/output.page_heading_menu}} diff --git a/templates/secure.mustache b/templates/secure.mustache index a165532..6354e77 100644 --- a/templates/secure.mustache +++ b/templates/secure.mustache @@ -83,7 +83,9 @@ -{{> theme_nhse/footer }} +{{# output.get_footer_data }} + {{> theme_nhse/footer }} +{{/ output.get_footer_data }} {{#js}} diff --git a/version.php b/version.php index 07c4031..ea05e48 100755 --- a/version.php +++ b/version.php @@ -26,7 +26,7 @@ defined('MOODLE_INTERNAL') || die(); // This is the version of the plugin. -$plugin->version = 2025022005; +$plugin->version = 2025091201; $plugin->release = '404.4.0'; $plugin->maturity = MATURITY_BETA;