diff --git a/pandora_console/include/javascript/pandora_visual_console.js b/pandora_console/include/javascript/pandora_visual_console.js index 06cc22fc2a..d49f26df28 100755 --- a/pandora_console/include/javascript/pandora_visual_console.js +++ b/pandora_console/include/javascript/pandora_visual_console.js @@ -288,6 +288,56 @@ function createVisualConsole( }) .init(); break; + case "image-console": + asyncTaskManager + .add(identifier + "-" + params.id, function(doneAsyncTask) { + var abortable = getImagesVisualConsole( + baseUrl, + visualConsole.props.id, + function(error, data) { + if (error || !data) { + console.log( + "[ERROR]", + "[VISUAL-CONSOLE-CLIENT]", + "[API]", + error ? error.message : "Invalid response" + ); + + done(error); + doneAsyncTask(); + return; + } + + if (typeof data === "string") { + try { + data = JSON.parse(data); + } catch (error) { + console.log( + "[ERROR]", + "[VISUAL-CONSOLE-CLIENT]", + "[API]", + error ? error.message : "Invalid response" + ); + + done(error); + doneAsyncTask(); + return; // Stop task execution. + } + } + + done(null, data); + doneAsyncTask(); + } + ); + + return { + cancel: function() { + abortable.abort(); + } + }; + }) + .init(); + break; default: done(new Error("identifier not found")); } @@ -1036,6 +1086,69 @@ function getAllVisualConsole(baseUrl, vcId, callback) { }; } +/** + * Fetch groups access user. + * @param {string} baseUrl Base URL to build the API path. + * @param {number} vcId Identifier of the Visual Console. + * @param {function} callback Function to be executed on request success or fail. + * @return {Object} Cancellable. Object which include and .abort([statusText]) function. + */ +// eslint-disable-next-line no-unused-vars +function getImagesVisualConsole(baseUrl, vcId, callback) { + var apiPath = baseUrl + "/ajax.php"; + var jqXHR = null; + + // Cancel the ajax requests. + var abort = function(textStatus) { + if (textStatus == null) textStatus = "abort"; + + // -- XMLHttpRequest.readyState -- + // Value State Description + // 0 UNSENT Client has been created. open() not called yet. + // 4 DONE The operation is complete. + + if (jqXHR.readyState !== 0 && jqXHR.readyState !== 4) + jqXHR.abort(textStatus); + }; + + // Failed request handler. + var handleFail = function(jqXHR, textStatus, errorThrown) { + abort(); + // Manually aborted or not. + if (textStatus === "abort") { + callback(); + } else { + var error = new Error(errorThrown); + error.request = jqXHR; + callback(error); + } + }; + + // Function which handle success case. + var handleSuccess = function(data) { + callback(null, data); + }; + + // Visual Console container request. + jqXHR = jQuery + .get( + apiPath, + { + page: "include/rest-api/index", + getImagesVisualConsole: 1, + visualConsoleId: vcId + }, + "json" + ) + .done(handleSuccess) + .fail(handleFail); + + // Abortable. + return { + abort: abort + }; +} + /** * Copy an item. * @param {string} baseUrl Base URL to build the API path. diff --git a/pandora_console/include/rest-api/index.php b/pandora_console/include/rest-api/index.php index 6269661672..39aa904362 100644 --- a/pandora_console/include/rest-api/index.php +++ b/pandora_console/include/rest-api/index.php @@ -20,6 +20,7 @@ $removeVisualConsoleItem = (bool) get_parameter('removeVisualConsoleItem'); $copyVisualConsoleItem = (bool) get_parameter('copyVisualConsoleItem'); $getGroupsVisualConsoleItem = (bool) get_parameter('getGroupsVisualConsoleItem'); $getAllVisualConsole = (bool) get_parameter('getAllVisualConsole'); +$getImagesVisualConsole = (bool) get_parameter('getImagesVisualConsole'); ob_clean(); @@ -239,6 +240,49 @@ if ($getVisualConsole === true) { } } + echo json_encode(io_safe_output($result)); + return; +} else if ($getImagesVisualConsole) { + $result = []; + + // Extract images. + $all_images = list_files( + $config['homedir'].'/images/console/icons/', + 'png', + 1, + 0 + ); + + if (isset($all_images) === true && is_array($all_images) === true) { + $base_url = ui_get_full_url( + '/images/console/icons/', + false, + false, + false + ); + + foreach ($all_images as $image_file) { + $image_file = substr($image_file, 0, (strlen($image_file) - 4)); + + if (strpos($image_file, '_bad') !== false) { + continue; + } + + if (strpos($image_file, '_ok') !== false) { + continue; + } + + if (strpos($image_file, '_warning') !== false) { + continue; + } + + $result[] = [ + 'name' => $image_file, + 'src' => $base_url.$image_file, + ]; + } + } + echo json_encode(io_safe_output($result)); return; } diff --git a/visual_console_client/src/Item.ts b/visual_console_client/src/Item.ts index 78319a6fcf..1371cbf4bc 100644 --- a/visual_console_client/src/Item.ts +++ b/visual_console_client/src/Item.ts @@ -95,6 +95,10 @@ export interface ItemSelectionChangedEvent { selected: boolean; } +export interface ImageInputGroupProps { + imageSrc: string | null; +} + // TODO: Document class LinkInputGroup extends InputGroup> { protected createContent(): HTMLElement | HTMLElement[] { @@ -755,6 +759,112 @@ export class LinkConsoleInputGroup extends InputGroup< }; } +/** + * Class to add item to the static Graph item form + * This item consists of a label and a color type select. + * Show Last Value is stored in the showLastValueTooltip property + */ +export class ImageInputGroup extends InputGroup> { + protected createContent(): HTMLElement | HTMLElement[] { + const imageLabel = document.createElement("label"); + imageLabel.textContent = t("Image"); + + const divImage = document.createElement("div"); + + const imageTypes = ["", "_bad", "_ok", "_warning"]; + + // Create element Spinner. + const spinner = fontAwesomeIcon(faCircleNotch, t("Spinner"), { + size: "small", + spin: true + }); + imageLabel.appendChild(spinner); + + // Init request + this.requestData("image-console", {}, (error, data) => { + // Remove Spinner. + spinner.remove(); + + // Check errors. + if (error) { + // Add img error. + imageLabel.appendChild( + fontAwesomeIcon(faExclamationCircle, t("Error"), { + size: "small", + color: "#e63c52" + }) + ); + } + + if (data instanceof Array) { + const labelSelect = document.createElement("select"); + labelSelect.required = true; + + data.forEach(option => { + const optionElement = document.createElement("option"); + optionElement.value = option.name; + optionElement.textContent = option.name; + labelSelect.appendChild(optionElement); + }); + + labelSelect.addEventListener("change", event => { + const imageSrc = (event.target as HTMLSelectElement).value; + this.updateData({ imageSrc }); + + if (imageSrc != null) { + const imageItem = data.find(item => item.name === imageSrc); + + if (imageItem) { + const deleteImg = divImage.querySelectorAll(".img-vc-elements"); + deleteImg.forEach(value => { + divImage.removeChild(value); + }); + + imageTypes.forEach(value => { + const imagePaint = document.createElement("img"); + imagePaint.alt = t("Image VC"); + imagePaint.style.width = "40px"; + imagePaint.style.height = "40px"; + imagePaint.className = "img-vc-elements"; + imagePaint.src = `${imageItem.src}${value}.png`; + divImage.appendChild(imagePaint); + }); + } + } + }); + + const valueImage = `${this.currentData.imageSrc || + this.initialData.imageSrc || + null}`; + + labelSelect.value = valueImage; + + imageLabel.appendChild(labelSelect); + + if (valueImage != null) { + const imageItem = data.find(item => item.name === valueImage); + + if (imageItem) { + imageTypes.forEach(value => { + const imagePaint = document.createElement("img"); + imagePaint.alt = t("Image VC"); + imagePaint.style.width = "40px"; + imagePaint.style.height = "40px"; + imagePaint.className = "img-vc-elements"; + imagePaint.src = `${imageItem.src}${value}.png`; + divImage.appendChild(imagePaint); + }); + } + + imageLabel.appendChild(divImage); + } + } + }); + + return imageLabel; + } +} + /** * Extract a valid enum value from a raw label position value. * @param labelPosition Raw value. diff --git a/visual_console_client/src/items/StaticGraph.ts b/visual_console_client/src/items/StaticGraph.ts index 19e49bd581..01c3477c9c 100644 --- a/visual_console_client/src/items/StaticGraph.ts +++ b/visual_console_client/src/items/StaticGraph.ts @@ -14,7 +14,8 @@ import Item, { ItemType, ItemProps, itemBasePropsDecoder, - LinkConsoleInputGroup + LinkConsoleInputGroup, + ImageInputGroup } from "../Item"; import { InputGroup, FormContainer } from "../Form"; @@ -149,6 +150,9 @@ export default class StaticGraph extends Item { */ public getFormContainer(): FormContainer { const formContainer = super.getFormContainer(); + formContainer.addInputGroup( + new ImageInputGroup("image-console", this.props) + ); formContainer.addInputGroup( new ShowLastValueInputGroup("show-last-value", this.props) );