diff --git a/css/app.css b/css/app.css index 7e0757a10..a3abe43ab 100644 --- a/css/app.css +++ b/css/app.css @@ -1365,6 +1365,10 @@ thumbnail-slider img { overflow-y: hidden; } +.modal-dialog pre { + max-height: 200px; +} + .text-weight-bold { font-weight: 700; } diff --git a/plugin/omero_iviewer/views.py b/plugin/omero_iviewer/views.py index 439b96375..1cfd4f2e6 100644 --- a/plugin/omero_iviewer/views.py +++ b/plugin/omero_iviewer/views.py @@ -23,6 +23,7 @@ from os.path import splitext from collections import defaultdict from struct import unpack +import traceback from omeroweb.api.api_settings import API_MAX_LIMIT from omeroweb.decorators import login_required @@ -106,7 +107,14 @@ def index(request, iid=None, conn=None, **kwargs): if MAX_PROJECTION_BYTES > 0: max_bytes = MAX_PROJECTION_BYTES + try: + nodedescriptors = c.getConfigValue("omero.server.nodedescriptors") + except omero.SecurityViolation: + # nodedescriptors not supported in OMERO before 5.6.6 (Dec 2022) + nodedescriptors = None + params['MAX_PROJECTION_BYTES'] = max_bytes + params['NODEDESCRIPTORS'] = nodedescriptors params['ROI_COLOR_PALETTE'] = ROI_COLOR_PALETTE params['SHOW_PALETTE_ONLY'] = SHOW_PALETTE_ONLY params['ENABLE_MIRROR'] = ENABLE_MIRROR @@ -498,19 +506,19 @@ def image_data(request, image_id, conn=None, **kwargs): # Add extra parameters with units data # Note ['pixel_size']['x'] will have size in MICROMETER px = image.getPrimaryPixels().getPhysicalSizeX() - if (px is not None): + if (px is not None and 'pixel_size' in rv): size = image.getPixelSizeX(True) value = format_pixel_size_with_units(size) rv['pixel_size']['unit_x'] = value[0] rv['pixel_size']['symbol_x'] = value[1] py = image.getPrimaryPixels().getPhysicalSizeY() - if (py is not None): + if (py is not None and 'pixel_size' in rv): size = image.getPixelSizeY(True) value = format_pixel_size_with_units(size) rv['pixel_size']['unit_y'] = value[0] rv['pixel_size']['symbol_y'] = value[1] pz = image.getPrimaryPixels().getPhysicalSizeZ() - if (pz is not None): + if (pz is not None and 'pixel_size' in rv): size = image.getPixelSizeZ(True) value = format_pixel_size_with_units(size) rv['pixel_size']['unit_z'] = value[0] @@ -530,8 +538,8 @@ def image_data(request, image_id, conn=None, **kwargs): rv['families'].append(fam.getValue()) return JsonResponse(rv) - except Exception as image_data_retrieval_exception: - return JsonResponse({'error': repr(image_data_retrieval_exception)}) + except Exception: + return JsonResponse({'error': traceback.format_exc()}) @login_required() diff --git a/src/app/context.js b/src/app/context.js index 710f59039..8d542396f 100644 --- a/src/app/context.js +++ b/src/app/context.js @@ -464,6 +464,9 @@ export default class Context { } this.show_palette_only = (this.initParams[REQUEST_PARAMS.SHOW_PALETTE_ONLY] != 'False') || false this.enable_mirror = (this.initParams[REQUEST_PARAMS.ENABLE_MIRROR] != 'False') || false + // nodedescriptors can be empty string or "None" (undefined) + let nds = this.initParams[REQUEST_PARAMS.NODEDESCRIPTORS]; + this.nodedescriptors = nds == 'None' ? undefined : nds } /** diff --git a/src/model/image_info.js b/src/model/image_info.js index bdfec907b..36670eaf8 100644 --- a/src/model/image_info.js +++ b/src/model/image_info.js @@ -19,6 +19,7 @@ import {noView} from 'aurelia-framework'; import Misc from '../utils/misc'; import Ui from '../utils/ui'; +var escapeHtml = require('escape-html') import { APP_TITLE, CHANNEL_SETTINGS_MODE, INITIAL_TYPES, IVIEWER, PROJECTION, REQUEST_PARAMS, WEBCLIENT, WEBGATEWAY @@ -318,6 +319,13 @@ export default class ImageInfo { this.image_id = response.id; } + // validate response + // check for Exceptions and show error dialog. + const valid = this.validateImageInfo(response); + if (!valid) { + return; + } + // read initial request params this.initializeImageInfo(response, refresh); // check for a parent id (if not well) @@ -360,6 +368,48 @@ export default class ImageInfo { }); } + /** + * Checks that the imgData JSON response is valid, no exceptions etc. + * + * @private + * @param {Object} response the response object + * @memberof ImageInfo + * @return {Boolean} True if data is valid. Also shows dialogs with errors + */ + validateImageInfo(response) { + + if (response.ConcurrencyException) { + let nds = this.context.nodedescriptors; + const pyramidsDisabled = nds != undefined && nds.length > 0 && !nds.includes("PixelData"); + Ui.showModalMessage(`
Image is ${pyramidsDisabled ? "not" : "not currently"} viewable
+ConcurrencyException+ + A pyramid of zoom levels is not available.
Error loading Image data
+${escapeHtml(msg)}`, "OK"); + return false; + } + + if (response.channels == undefined || response.channels.length === 0) { + Ui.showModalMessage(`
No channel data loaded
+${escapeHtml(JSON.stringify(response, null, 4))}`, "OK"); + return false; + } + + return true; + } + /** * Takes the response object and assigns the bits and pieces needed * to the members diff --git a/src/utils/constants.js b/src/utils/constants.js index 4f2eb22cf..208ab6c2d 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -123,6 +123,7 @@ export const REQUEST_PARAMS = { SHAPE: 'SHAPE', MAPS: 'MAPS', MODEL: 'M', + NODEDESCRIPTORS: 'NODEDESCRIPTORS', OMERO_VERSION: 'OMERO_VERSION', PLANE: 'Z', PROJECTION: 'P',