diff --git a/.gitignore b/.gitignore index 52fdd6e5f..fcfe03190 100644 --- a/.gitignore +++ b/.gitignore @@ -25,9 +25,12 @@ css/custom.css # file marking a development version .hrm_devel_version -# Composer's vendor folder +# Composer's vendor folder /vendor/ -# macOS finder files +# macOS finder files *.DS_Store *.phpunit.result.cache + +# symlink to the HRM-OMERO connector +bin/ome_hrm.py diff --git a/add_user.php b/add_user.php index 8333eafde..4f785fee3 100644 --- a/add_user.php +++ b/add_user.php @@ -16,8 +16,6 @@ // Settings global $hrm_url, $image_folder, $image_host, $email_sender, $userManagerScript; -require_once dirname(__FILE__) . '/inc/bootstrap.php'; - session_start(); if (!isset($_SESSION['user']) || !$_SESSION['user']->isLoggedIn()) { @@ -89,7 +87,7 @@ } else if ($clean["email"] == "") { $message = "Please provide a valid email address!"; } else if ($clean['group'] == "") { - $message = "Please a group!"; + $message = "Please provide a group!"; } else { // Make sure that there is no user with same name diff --git a/ajax/json-rpc-server.php b/ajax/json-rpc-server.php index c1995003f..5576986a5 100644 --- a/ajax/json-rpc-server.php +++ b/ajax/json-rpc-server.php @@ -389,7 +389,7 @@ function jsonSendTestEmail() global $email_admin; // Include configuration file - include(dirname(__FILE__) . "/../config/hrm_client_config.inc"); + include(dirname(__FILE__) . "/../config/hrm_config.inc"); // Prepare the output array $json = initJSONArray(); @@ -1069,6 +1069,20 @@ function jsonRemoveFilesFromSelection($fileList) function processFilesAndSelectedFilesLists($allFiles, $selectedFiles, $fileServer, $format) { + // Sanity checks + if ($allFiles === null) { + $allFiles = []; + } + if ($selectedFiles === null) { + $selectedFiles = []; + } + if (gettype($allFiles) == "string") { + $allFiles = [$allFiles]; + } + if (gettype($selectedFiles) == "string") { + $selectedFiles = [$selectedFiles]; + } + // Are the selected files compatible with the current format? if (count($selectedFiles) > 0 && !$fileServer->checkAgainstFormat($selectedFiles[0], $format)) { $fileServer->removeAllFilesFromSelection(); diff --git a/bin/hrm_config.py b/bin/hrm_config.py deleted file mode 100644 index 0165f349f..000000000 --- a/bin/hrm_config.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python - -"""Helper module to parse the HRM (shell style) config file. - -Usually, the config is located at /etc/hrm.conf and written in shell syntax as -this file simply gets sourced by the bash init script and other shell based -tools. - -This module is not meant to be executed directly and doesn't do anything in -this case. -""" - -# NOTE: -# It might be worth checking out the solution described on stackoverflow [1] -# using an approach based on a real shell subprocess like this: -""" ->>> command = ['bash', '-c', 'source init_env && env'] ->>> proc = subprocess.Popen(command, stdout = subprocess.PIPE) ->>> for line in proc.stdout: -... (key, _, value) = line.partition("=") -... os.environ[key] = value ->>> proc.communicate() -""" -# [1]: http://stackoverflow.com/questions/3503719/ - - -import shlex -import sys - - -def parse_hrm_conf(filename): - """Assemble a dict from the HRM config file (shell syntax). - - Parameters - ========== - filename: str - the filename to parse - - Returns - ======= - config: dict - - Example - ======= - { - 'HRM_DATA': '/export/hrm_data', - 'HRM_DEST': 'dst', - 'HRM_HOME': '/var/www/hrm', - 'HRM_LOG': '/var/log/hrm', - 'HRM_SOURCE': 'src', - 'OMERO_HOSTNAME': 'omero.mynetwork.xy', - 'OMERO_PKG': '/opt/OMERO/OMERO.server', - 'OMERO_PORT': '4064', - 'PHP_CLI': '/usr/local/php/bin/php', - 'SUSER': 'hrm' - } - """ - config = dict() - body = file(filename, 'r').read() - lexer = shlex.shlex(body) - lexer.wordchars += '-./' - while True: - token = lexer.get_token() - if token is None or token == '': - break - # it's valid sh syntax to use a semicolon to join lines, so accept it: - if token == ';': - continue - # we assume entries of the following form: - # KEY="some-value" - key = token - try: - equals = lexer.get_token() - assert equals == '=' - except AssertionError: - raise SyntaxError( - "Can't parse %s, invalid syntax in line %s " - "(expected '=', found '%s')." % - (filename, lexer.lineno, equals)) - value = lexer.get_token() - value = value.replace('"', '') # remove double quotes - value = value.replace("'", '') # remove single quotes - config[key] = value - return config - - -def check_hrm_conf(config): - """Check the config dict for required entries.""" - required = ['OMERO_PKG', 'OMERO_HOSTNAME'] - for entry in required: - if entry not in config: - raise SyntaxError('Missing "%s" in the HRM config file.' % entry) - - -if __name__ == "__main__": - print __doc__ - sys.exit(1) - -CONFIG = parse_hrm_conf('/etc/hrm.conf') -check_hrm_conf(CONFIG) diff --git a/bin/hrm_queuemanager b/bin/hrm_queuemanager index b89d22c0f..11a35a133 100755 --- a/bin/hrm_queuemanager +++ b/bin/hrm_queuemanager @@ -54,7 +54,7 @@ test_writable() { # Test if a file or directory is writable, exit otherwise. - if ! [ -w "$1" ] ; then + if ! [ -w "$1" ]; then echo "ERROR: file or directory not writable: '$1'!" exit 1 fi @@ -63,7 +63,7 @@ test_writable() { test_writable_create() { # Test if a file exists and is writable, try to create it otherwise. If # both fails exit with an error message. - if ! [ -e "$1" ] ; then + if ! [ -e "$1" ]; then touch "$1" 2>/dev/null || { echo "ERROR: cannot create file: '$1'!" exit 1 @@ -73,12 +73,11 @@ test_writable_create() { fi } - -lsb_check_for_running_queuemanager() { +ensure_no_qm_is_running_lsb() { # use LSB functions to check if the QM is already running, # if that is true -> exit LSB_INITFUNCTIONS="/lib/lsb/init-functions" - if ! [ -r "$LSB_INITFUNCTIONS" ] ; then + if ! [ -r "$LSB_INITFUNCTIONS" ]; then echo "WARNING: missing the LSB init functions ($LSB_INITFUNCTIONS)!" echo "--> CANNOT check if the Queue Manager is already running!" return 1 @@ -87,16 +86,16 @@ lsb_check_for_running_queuemanager() { RUNNING_PID=$(pidofproc -p "$PID_FILE" "$0") RUNNING_STATUS=$? # NOTE: see the LSB specification for return values of "pidofproc" - if [ -n "$RUNNING_PID" ] ; then + if [ -n "$RUNNING_PID" ]; then echo "The Queue Manager seems to be running already ($RUNNING_PID)." echo "NOT starting a second instance!" echo exit 1 fi - if [ "$RUNNING_STATUS" = "1" ] ; then + if [ "$RUNNING_STATUS" = "1" ]; then echo "NOTE: leftover pid file '$PID_FILE' (no Queue Manager running)." fi - if [ "$RUNNING_STATUS" = "4" ] ; then + if [ "$RUNNING_STATUS" = "4" ]; then echo "ERROR: unable to determine status from pid file '$PID_FILE'." echo " Please manually check if another instance is running" echo " and remove the file if not." @@ -104,6 +103,15 @@ lsb_check_for_running_queuemanager() { fi } +ensure_no_qm_is_running_systemd() { + # use systemd to check if the QM is already running, exit if true + if systemctl is-active --quiet hrmd.service; then + echo "According to systemd the HRM service is already running." + echo "NOT starting a second instance of the Queue Manager!" + echo + exit 1 + fi +} we_are_on_systemd() { # try to find out whether we are run as a systemd service by checking if @@ -111,9 +119,31 @@ we_are_on_systemd() { ps -p $PPID -o command | grep -qs systemd } +ensure_no_qm_is_running() { + # check for a running QM using the proper way for systemd or LSB + if we_are_on_systemd; then + ensure_no_qm_is_running_systemd + else + ensure_no_qm_is_running_lsb + fi +} + +wait_for_pid_file() { + # wait up to 5 seconds for the PID file to show up, check every 0.1s + for _WAIT_COUNT in $(seq 1 50); do + if [ -f "$PID_FILE" ]; then + PID="$(cat "$PID_FILE")" + echo "Background process started, PID is $PID." + return + fi + sleep .1 + done + echo "WARNING: PID file still not present, looks like startup failed!" + cleanup_exit 3 +} warn_daemon_user() { - cat << EOF + cat <> "$LOG_OUT" 2>> "$LOG_ERR" & + $QUEUEMGR 1>>"$LOG_OUT" 2>>"$LOG_ERR" & # NOTE: we cannot check the status of the previous command via "$?" as this # variable is only set for commands executed in *foreground* (see "man # bash" in the "Special Parameters" section), therefore we have to use the @@ -159,7 +191,7 @@ run_qm_daemon() { # pidfile but the PID of *this* bash process as *we* are meant to stay # alive during its runtime to handle signals and clean up after the QM has # stopped (upon request/signal or crashed): - echo $$ > "$PID_FILE" || { + echo $$ >"$PID_FILE" || { echo "ERROR: can't write PID file '$PID_FILE'!" err "[$0] ERROR: can't write PID file '$PID_FILE'!" cleanup_exit @@ -167,13 +199,12 @@ run_qm_daemon() { # loop until we receive SIGINT/SIGTERM or the subprocess exited for some # other reason (e.g. because it crashed or was killed directly): - while : - do + while :; do sleep 1 # "jobs -p" lists the PID's of *all* active jobs (asynchronous # subprocesses of this bash instance), so the if-clause below would # also be true if there was *more* than one subprocess: - if [ "$QUEUEMGR_PID" != "$(jobs -p)" ] ; then + if [ "$QUEUEMGR_PID" != "$(jobs -p)" ]; then echo "WARNING: the HRM Queue Manager is not running any more!" # show everything of the errorlog since our startup-mark (maximum # of 100 lines) to give a hint what could have gone wrong: @@ -186,7 +217,7 @@ run_qm_daemon() { # NOTE: due to some weird bash behaviour (bug?), the joblist doesn't # get updated when "jobs -p" is called but *ONLY* if we call "jobs -l" # or something, so we do this explicitly as a workaround: - jobs -l > /dev/null + jobs -l >/dev/null done } @@ -204,17 +235,15 @@ cleanup_exit() { } err() { - echo "$*" >> "$LOG_ERR" + echo "$*" >>"$LOG_ERR" } log() { - echo "$*" >> "$LOG_OUT" + echo "$*" >>"$LOG_OUT" } ################## end of function definitions ################### - - ######################### main script part ######################### # Make sure to have a known locale environment for the script: @@ -225,31 +254,31 @@ PHP_CLI="php" # Read in hrm.conf (defines variables like HRM_HOME) set -e -if [ -n "$HRM_CONF" ] ; then +if [ -n "$HRM_CONF" ]; then . "$HRM_CONF" else - . /etc/hrm.conf + . "/etc/hrm.conf" fi set +e -if [ -n "$DEBUG_HRMD" ] ; then +if [ -n "$DEBUG_HRMD" ]; then set -o xtrace fi # refuse to start if no SUSER is configured: -if [ -z "$SUSER" ] ; then +if [ -z "$SUSER" ]; then warn_daemon_user exit 1 fi # re-exec with correct user in case we were started as root: -if [ "$(id -u)" = 0 ] ; then - exec su -l $SUSER -c "$0 $@" +if [ "$(id -u)" = 0 ]; then + exec su -l "$SUSER" -c "$0 $@" fi # refuse to start if we were started with the wrong account: WHOAMI=$(id -u -n) -if [ "$WHOAMI" != "$SUSER" ] ; then +if [ "$WHOAMI" != "$SUSER" ]; then echo "User mismatch: '$WHOAMI' <-> '$SUSER'." exit 2 fi @@ -265,21 +294,14 @@ test_writable_create "${LOG_OUT}" test_writable_create "${LOG_ERR}" # detach into background if requested -if [ "$1" = "--detach" ] ; then +if [ "$1" = "--detach" ]; then shift echo "HRM Queue Manager: forking background process." "$0" "$@" & + wait_for_pid_file exit 0 fi -# check for a running QM unless we are operated by systemd (in which case we -# simply assume that we are well taken care of!) -if we_are_on_systemd ; then - echo "HRM Queue Manager: running as a systemd service." -else - lsb_check_for_running_queuemanager -fi - # install the signal handler (runs the function "cleanup_exit" when either of # the given signals SIGINT (2) or SIGTERM (15) is received): trap "cleanup_exit" SIGINT SIGTERM diff --git a/bin/hrm_user_manager b/bin/hrm_user_manager index 65c08d34c..ce64a33e7 100755 --- a/bin/hrm_user_manager +++ b/bin/hrm_user_manager @@ -19,9 +19,9 @@ FILE_MODE=0775 # $HRM_DATA as otherwise these operations will fail! create() { - mkdir -pv "${HRM_DATA}/${1}/${HRM_SOURCE}" - mkdir -pv "${HRM_DATA}/${1}/${HRM_DEST}" - chmod -R $FILE_MODE "${HRM_DATA}/${1}" + mkdir -pv "${HRM_DATA:?}/${1}/${HRM_SOURCE}" + mkdir -pv "${HRM_DATA:?}/${1}/${HRM_DEST}" + chmod -R $FILE_MODE "${HRM_DATA:?}/${1}" # TODO / discuss : We could use this function to make sure the newly # created directory has the correct group (chgrp) and the setgid bit # configured (chmod g+s). However, this requires to know the GROUP that is @@ -30,7 +30,7 @@ create() { } delete() { - rm -rf "${HRM_DATA}/${1}" + rm -rf "${HRM_DATA:?}/${1}" } case "$1" in diff --git a/bin/ome_hrm.py b/bin/ome_hrm.py deleted file mode 100755 index 11ef7e9a1..000000000 --- a/bin/ome_hrm.py +++ /dev/null @@ -1,587 +0,0 @@ -#!/usr/bin/env python - -"""OMERO connector for the Huygens Remote Manager (HRM). - -This wrapper processes all requests from the HRM web interface to communicate -to an OMERO server for listing available images, transferring data, etc. -""" - -# pylint: disable=superfluous-parens - -# TODO: -# - trees for different groups -# - proper logging, separate logfile for the connector -# - redirect logging of CLI -# - offer download of OME-TIFFs? - - -import sys -import hrm_config - -# optionally put EXT_LIB into our PYTHONPATH: -if 'PYTHON_EXTLIB' in hrm_config.CONFIG: - sys.path.insert(0, hrm_config.CONFIG['PYTHON_EXTLIB']) - -try: - import argparse - import os - import json - import re -except ImportError as err: - print "ERROR importing required Python packages:", err - print "Current PYTHONPATH: ", sys.path - sys.exit(1) - -# try to put OMERO into our PYTHONPATH: -if 'OMERO_PKG' in hrm_config.CONFIG: - OMERO_LIB = '%s/lib/python' % hrm_config.CONFIG['OMERO_PKG'] - sys.path.insert(0, OMERO_LIB) -else: - print "Could not find configuration value 'OMERO_PKG', omitting." -try: - from omero.gateway import BlitzGateway -except ImportError as err: - print "ERROR importing the OMERO Python bindings:", err - print "Current PYTHONPATH: ", sys.path - sys.exit(2) - -# the connection values -HOST = hrm_config.CONFIG['OMERO_HOSTNAME'] -if 'OMERO_PORT' in hrm_config.CONFIG: - PORT = hrm_config.CONFIG['OMERO_PORT'] -else: - PORT = 4064 - - -def omero_login(user, passwd, host, port): - """Establish the connection to an OMERO server. - - Parameters - ========== - user : str - OMERO user name (e.g. "demo_user_01") - passwd : str - OMERO user password - host : str - OMERO server hostname to connect to - port : int - OMERO server port number (e.g. 4064) - - Returns - ======= - conn : omero.gateway._BlitzGateway - OMERO connection object - """ - conn = BlitzGateway(user, passwd, host=host, port=port, secure=True, - useragent="HRM-OMERO.connector") - conn.connect() - return conn - - -def tree_to_json(obj_tree): - """Create a JSON object with a given format from a tree.""" - return json.dumps(obj_tree, sort_keys=True, - indent=4, separators=(',', ': ')) - - -def print_children_json(conn, id_str): - """Print the child nodes of the given ID in JSON format. - - Parameters - ========== - conn : omero.gateway._BlitzGateway - id_str : str - OMERO object ID string (e.g. "G:23:Image:42") - - Returns - ======= - bool - True in case printing the nodes was successful, False otherwise. - """ - try: - children = gen_children(conn, id_str) - except: - print "ERROR generating OMERO tree / node!" - return False - print tree_to_json(children) - return True - - -def gen_obj_dict(obj, id_pfx=''): - """Create a dict from an OMERO object. - - Returns - ======= - obj_dict : dict - dictionary with the following structure: - { - 'children': [], - 'id': 'Project:1154', - 'label': 'HRM_TESTDATA', - 'owner': u'demo01', - 'class': 'Project' - } - """ - obj_dict = dict() - obj_dict['label'] = obj.getName() - obj_dict['class'] = obj.OMERO_CLASS - if obj.OMERO_CLASS == 'Experimenter': - obj_dict['owner'] = obj.getId() - obj_dict['label'] = obj.getFullName() - elif obj.OMERO_CLASS == 'ExperimenterGroup': - # for some reason getOwner() et al. return nothing on a group, so we - # simply put it to None for group objects: - obj_dict['owner'] = None - else: - obj_dict['owner'] = obj.getOwnerOmeName() - obj_dict['id'] = id_pfx + "%s:%s" % (obj.OMERO_CLASS, obj.getId()) - obj_dict['children'] = [] - return obj_dict - - -def gen_children(conn, id_str): - """Get the children for a given node. - - Parameters - ========== - conn : omero.gateway._BlitzGateway - id_str : str - OMERO object ID string (e.g. "G:23:Image:42") - - Returns - ======= - list - a list of the child nodes dicts, having the 'load_on_demand' - property set to True required by the jqTree JavaScript library - """ - if id_str == 'ROOT': - return gen_base_tree(conn) - children = [] - _, gid, obj_type, oid = id_str.split(':') - conn.SERVICE_OPTS.setOmeroGroup(gid) - obj = conn.getObject(obj_type, oid) - # we need different child-wrappers, depending on the object type: - if obj_type == 'Experimenter': - children_wrapper = conn.listProjects(oid) - elif obj_type == 'ExperimenterGroup': - children_wrapper = None # FIXME - else: - children_wrapper = obj.listChildren() - # now recurse into children: - for child in children_wrapper: - children.append(gen_obj_dict(child, 'G:' + gid + ':')) - # set the on-demand flag unless the children are the last level: - if not obj_type == 'Dataset': - for child in children: - child['load_on_demand'] = True - return children - - -def gen_base_tree(conn): - """Generate all group trees with their members as the basic tree. - - Parameters - ========== - conn : omero.gateway._BlitzGateway - - Returns - ======= - base : a list of grouptree dicts - """ - tree = [] - for group in conn.getGroupsMemberOf(): - tree.append(gen_group_tree(conn, group)) - return tree - - -def gen_group_tree(conn, group=None): - """Create the tree nodes for a group and its members. - - Parameters - ========== - conn : omero.gateway._BlitzGateway - - Returns - ======= - grouptree : a nested dict of a group (the default unless explicitly - requested) and its members as a list of dicts in the 'children' - item, starting with the current user as the first entry - """ - if group is not None: - conn.SERVICE_OPTS.setOmeroGroup(group.getId()) - else: - group = conn.getGroupFromContext() - gid = str(group.getId()) - group_dict = gen_obj_dict(group) - # add the user's own tree first: - user = conn.getUser() - user_dict = gen_obj_dict(user, 'G:' + gid + ':') - user_dict['load_on_demand'] = True - group_dict['children'].append(user_dict) - # then add the trees for other group members - for user in conn.listColleagues(): - user_dict = gen_obj_dict(user, 'G:' + gid + ':') - user_dict['load_on_demand'] = True - group_dict['children'].append(user_dict) - return group_dict - - -def check_credentials(conn): - """Check if supplied credentials are valid. - - Parameters - ========== - conn : omero.gateway.BlitzGateway - - Returns - ======= - conntected : bool - True if connecting was successful (i.e. credentials are correct), False - otherwise. In addition, a corresponding message is printed. - """ - connected = conn.connect() - if connected: - print('Success logging into OMERO with user ID %s' % conn.getUserId()) - else: - print('ERROR logging into OMERO.') - return connected - - -def omero_to_hrm(conn, id_str, dest): - """Download the corresponding original file(s) from an image ID. - - This works only for image ID's that were created with OMERO 5.0 or later as - previous versions don't have an "original file" linked to an image. - - Note that files will be downloaded with their original name, which is not - necessarily the name shown by OMERO, e.g. if an image name was changed in - OMERO. To keep filesets consistent, we have to preserve the original names! - - In addition to the original file(s), it also downloads a thumbnail of the - requested file from OMERO and puts it into the appropriate place so HRM - will show it as a preview until the user hits "re-generate preview". - - Parameters - ========== - conn : omero.gateway.BlitzGateway - id_str: str - the ID of the OMERO image (e.g. "G:23:Image:42") - dest: str - destination directory - - Returns - ======= - True in case the download was successful, False otherwise. - """ - # FIXME: group switching required!! - _, gid, obj_type, image_id = id_str.split(':') - if not image_id: - print("Could not parse ID string '%s'. Expecting [GID]:[Type]:[Image_ID]" % id_str) - return False - # Provided that the tree displays only groups that the current user has access to, cross-group query (introduced in - # OMERO 4.4) is a generic way to get the image. - if not gid: - gid = '-1' - conn.SERVICE_OPTS.setOmeroGroup(gid) - # check if dest is a directory, rewrite it otherwise: - if not os.path.isdir(dest): - dest = os.path.dirname(dest) - from omero_model_OriginalFileI import OriginalFileI - # use image objects and getFileset() methods to determine original files, - # see the following OME forum thread for some more details: - # https://www.openmicroscopy.org/community/viewtopic.php?f=6&t=7563 - image_obj = conn.getObject("Image", image_id) - if not image_obj: - print("ERROR: can't find image with ID %s!" % image_id) - return False - fset = image_obj.getFileset() - if not fset: - print("ERROR: no original file(s) for image %s found!" % image_id) - return False - # TODO I (issue #438): in case the query fails, this means most likely that - # a file was uploaded in an older version of OMERO and therefore the - # original file is not available. However, it was possible to upload with - # the "archive" option, we should check if such archived files are - # retrieved with the above query. - # TODO II (issue #398): in case no archived file is available, we could - # fall back to downloading the OME-TIFF instead. - downloads = [] - # assemble a list of items to download, check if any files already exist: - for fset_file in fset.listFiles(): - tgt = os.path.join(dest, fset_file.getName()) - if os.path.exists(tgt): - print("ERROR: target file '%s' already existing!" % tgt) - return False - fset_id = fset_file.getId() - downloads.append((fset_id, tgt)) - # now initiate the downloads for all original files: - for (fset_id, tgt) in downloads: - try: - conn.c.download(OriginalFileI(fset_id), tgt) - except: - print("ERROR: downloading %s to '%s' failed!" % (fset_id, tgt)) - return False - print("ID %s downloaded as '%s'" % (fset_id, os.path.basename(tgt))) - # NOTE: for filesets with a single file or e.g. ICS/IDS pairs it makes - # sense to use the target name of the first file to construct the name for - # the thumbnail, but it is unclear whether this is a universal approach: - download_thumb(conn, image_id, downloads[0][1]) - return True - - -def download_thumb(conn, image_id, dest): - """Download the thumbnail of a given image from OMERO. - - In case PIL (Python Imaging Library) is installed, download the thumbnail - of a given OMERO image and place it as preview in the corresponding HRM - directory. - - Parameters - ========== - conn : omero.gateway.BlitzGateway - image_id: str - an OMERO object ID of an image (e.g. '102') - dest: str - destination filename - - Returns - ======= - True in case the download was successful, False otherwise. - """ - try: - import Image - import StringIO - except ImportError: - return False - image_obj = conn.getObject("Image", image_id) - image_data = image_obj.getThumbnail() - thumbnail = Image.open(StringIO.StringIO(image_data)) - base_dir, fname = os.path.split(dest) - target = "/hrm_previews/" + fname + ".preview_xy.jpg" - try: - thumbnail.save(base_dir + target) - # TODO: os.chown() to fix permissions, see #457! - print("Thumbnail downloaded to '%s'." % target) - return True - except: - print("ERROR downloading thumbnail to '%s'." % target) - return False - - -def hrm_to_omero(conn, id_str, image_file): - """Upload an image into a specific dataset in OMERO. - - In case we know from the suffix that a given file format is not supported - by OMERO, the upload will not be initiated at all (e.g. for SVI-HDF5, - having the suffix '.h5'). - - The import itself is done by instantiating the CLI class, assembling the - required arguments, and finally running cli.invoke(). This eventually - triggers the importer() method defined in OMERO's Python bindings in - , respectively (source) - . - - Parameters - ========== - id_str: str - the ID of the target dataset in OMERO (e.g. "G:7:Dataset:23") - image_file: str - the local image file including the full path - - Returns - ======= - True in case of success, False otherwise. - """ - if image_file.lower().endswith(('.h5', '.hdf5')): - print 'ERROR: HDF5 files are not supported by OMERO!' - return False - # TODO I: group switching required!! - _, gid, obj_type, dset_id = id_str.split(':') - # we have to create the annotations *before* we actually upload the image - # data itself and link them to the image during the upload - the other way - # round is not possible right now as the CLI wrapper (see below) doesn't - # expose the ID of the newly created object in OMERO (confirmed by J-M and - # Sebastien on the 2015 OME Meeting): - namespace = 'deconvolved.hrm' - #### mime = 'text/plain' - # extract the image basename without suffix: - # TODO: is it [0-9a-f] or really [0-9a-z] as in the original PHP code? - basename = re.sub(r'(_[0-9a-f]{13}_hrm)\..*', r'\1', image_file) - comment = gen_parameter_summary(basename + '.parameters.txt') - #### annotations = [] - #### # TODO: the list of suffixes should not be hardcoded here! - #### for suffix in ['.hgsb', '.log.txt', '.parameters.txt']: - #### if not os.path.exists(basename + suffix): - #### continue - #### ann = conn.createFileAnnfromLocalFile( - #### basename + suffix, mimetype=mime, ns=namespace, desc=None) - #### annotations.append(ann.getId()) - # currently there is no direct "Python way" to import data into OMERO, so - # we have to use the CLI wrapper for this: - from omero.cli import CLI - cli = CLI() - cli.loadplugins() - # NOTE: cli._client should be replaced with cli.set_client() when switching - # to support for OMERO 5.1 and later only: - cli._client = conn.c - import_args = ["import"] - import_args.extend(['-d', dset_id]) - if comment is not None: - import_args.extend(['--annotation_ns', namespace]) - import_args.extend(['--annotation_text', comment]) - #### for ann_id in annotations: - #### import_args.extend(['--annotation_link', str(ann_id)]) - import_args.append(image_file) - # print("import_args: " + str(import_args)) - try: - cli.invoke(import_args, strict=True) - except: - print('ERROR: uploading "%s" to %s failed!' % (image_file, id_str)) - # print(import_args) - return False - return True - - -def gen_parameter_summary(fname): - """Generate a parameter summary from the HRM-generated HTML file. - - Parse the HTML file generated by the HRM containing the parameter summary - and generate a plain-text version of it. The HTML file is assumed to - contain three items that contain a single column with the - table title in the first row, the second row is ignored (column legend) and - the actual parameters in four columns in each of the subsequent rows, e.g. - something of this form: - - ___________________________________________ - |___________________title_________________| - |_________________(ignored)_______________| - | parameter | channel | (ignored) | value | - ... - | parameter | channel | (ignored) | value | - ------------------------------------------- - - Parameters - ========== - fname : str - the filename of the HTML parameter summary - - Returns - ======= - str - the formatted string containing the parameter summary - """ - try: - from bs4 import BeautifulSoup - except ImportError: - try: - from BeautifulSoup import BeautifulSoup - except ImportError: - return """This file was imported via the HRM-OMERO connector. - For a parameter summary, the 'BeautifulSoup' package for - Python is required at import time on the HRM system. - """ - try: - soup = BeautifulSoup(open(fname, 'r')) - except IOError: - return None - summary = '' - for table in soup.findAll('table'): - rows = table.findAll('tr') - # the table header: - summary += "%s\n" % rows[0].findAll('td')[0].text - summary += "==============================\n" - # and the table body: - for row in rows[2:]: - cols = row.findAll('td') - summary += "%s [Ch: %s]: %s\n" % ( - cols[0].text.replace('μm', 'um').replace(u'\u03bc', 'u'), - cols[1].text, - cols[3].text) - summary += '\n' - return summary - - -def bool_to_exitstatus(value): - """Convert a boolean to a POSIX process exit code. - - As boolean values in Python are a subset of int, True will be converted to - the int value '1', which is the opposite of a successful process return - code on POSIX systems. Therefore, we simply invert the boolean value to - turn it into a proper exit code. - """ - if type(value) is bool: - return not value - else: - return value - - -def parse_arguments(): - """Parse the commandline arguments.""" - argparser = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter - ) - argparser.add_argument( - '-v', '--verbose', dest='verbosity', action='count', default=0, - help='verbose messages (repeat for more details)') - - # required arguments group - req_args = argparser.add_argument_group( - 'required arguments', 'NOTE: MUST be given before any subcommand!') - req_args.add_argument( - '-u', '--user', required=True, help='OMERO username') - req_args.add_argument( - '-w', '--password', required=True, help='OMERO password') - - subparsers = argparser.add_subparsers( - help='.', dest='action', - description='Action to be performed, one of the following:') - - # checkCredentials parser - subparsers.add_parser( - 'checkCredentials', help='check if login credentials are valid') - - # retrieveChildren parser - parser_subtree = subparsers.add_parser( - 'retrieveChildren', - help="get the children of a given node object (JSON)") - parser_subtree.add_argument( - '--id', type=str, required=True, - help='ID string of the object to get the children for, e.g. "User:23"') - - # OMEROtoHRM parser - parser_o2h = subparsers.add_parser( - 'OMEROtoHRM', help='download an image from the OMERO server') - parser_o2h.add_argument( - '-i', '--imageid', required=True, - help='the OMERO ID of the image to download, e.g. "Image:42"') - parser_o2h.add_argument( - '-d', '--dest', type=str, required=True, - help='the destination directory where to put the downloaded file') - - # HRMtoOMERO parser - parser_h2o = subparsers.add_parser( - 'HRMtoOMERO', help='upload an image to the OMERO server') - parser_h2o.add_argument( - '-d', '--dset', required=True, dest='dset', - help='the ID of the target dataset in OMERO, e.g. "Dataset:23"') - parser_h2o.add_argument( - '-f', '--file', type=str, required=True, - help='the image file to upload, including the full path') - parser_h2o.add_argument( - '-n', '--name', type=str, required=False, - help='a label to use for the image in OMERO') - parser_h2o.add_argument( - '-a', '--ann', type=str, required=False, - help='annotation text to be added to the image in OMERO') - - try: - return argparser.parse_args() - except IOError as err: - argparser.error(str(err)) - - -def main(): - """Parse commandline arguments and initiate the requested tasks.""" - args = parse_arguments() - - conn = omero_login(args.user, args.password, HOST, PORT) - - # TODO: implement requesting groups via cmdline option - - if args.action == 'checkCredentials': - return check_credentials(conn) - elif args.action == 'retrieveChildren': - return print_children_json(conn, args.id) - elif args.action == 'OMEROtoHRM': - return omero_to_hrm(conn, args.imageid, args.dest) - elif args.action == 'HRMtoOMERO': - return hrm_to_omero(conn, args.dset, args.file) - else: - raise Exception('Huh, how could this happen?!') - - -if __name__ == "__main__": - sys.exit(bool_to_exitstatus(main())) diff --git a/calculate_bp_pinhole.php b/calculate_bp_pinhole.php index cdc5e6a60..175b10c5f 100644 --- a/calculate_bp_pinhole.php +++ b/calculate_bp_pinhole.php @@ -54,20 +54,37 @@ "Nikon TE2000-E with the C1 scanning head" => array("micro=Nikon_TE2000E_C1¶m=Pinhole+diameter+(microns)&a=1&b=0&na=0&wl=0&msys=1&c=0.5&u=-6&extra1=1.5&txt1=Optional+1.5x+magnification", "https://svi.nl/Nikon_TE2000E_C1"), - "Nikon TiE A1R" => - array("micro=Nikon_TiE_A1R¶m=Pinhole+diameter+(microns)&a=1&b=0&na=0&wl=0&msys=1&c=0.456&u=-6", "https://svi.nl/Nikon_TiE_A1R"), + "Nikon TE2000-E with the C2 scanning head" => + array("micro=Nikon_TE2000E_C2¶m=Pinhole+diameter+(microns)&a=1&b=0&na=0&wl=0&msys=1&c=0.5&u=-6&extra1=1.5&txt1=Optional+1.5x+magnification", + "https://svi.nl/Nikon_TE2000E_C2"), + "Nikon A1, A1 Plus, A1HD25, A1RHD25, AX, AXR" => + array("micro=Nikon_A_Series¶m=Pinhole+diameter+(microns)&a=1&b=0&na=0&wl=0&msys=1&c=0.456&u=-6", "https://svi.nl/Nikon_A_Series"), + "Olympus FV10i" => + array("micro=Olympus_FV10i¶m=Pinhole+side+(microns)&a=1&b=0&na=0&wl=0&msys=3.8&c=0.399&u=-6", "https://svi.nl/Olympus_FV10i"), "Olympus FV300 and FVX" => array("micro=Olympus_FV300¶m=Reported+pinhole+parameter&table=1&a=1&b=0&na=0&wl=0&msys=3.426&c=0.5&u=-6", "https://svi.nl/Olympus_FV300"), "Olympus FV500" => array("micro=Olympus_FV500¶m=Pinhole+side+(microns)&a=1&b=0&na=0&wl=0&msys=3.8&c=0.5641896&u=-6", "https://svi.nl/Olympus_FV500"), "Olympus FV1000" => array("micro=Olympus_FV1000¶m=Pinhole+side+(microns)&a=1&b=0&na=0&wl=0&msys=3.82&c=0.5641896&u=-6", "https://svi.nl/Olympus_FV1000"), - "Yokogawa spinning disk (pinhole radius)" => + "Olympus FV3000" => + array("micro=Olympus_FV3000¶m=Pinhole+side+(microns)&a=1&b=0&na=0&wl=0&msys=7.6&c=0.5641896&u=-6", "https://svi.nl/Olympus_FV3000"), + "Yokogawa spinning disk - pinhole RADIUS - all CSU models" => array("micro=Yokogawa_spinning_disk&d=50&a=1&b=0&na=0&wl=0&msys=1&c=0.5&u=-6", "https://svi.nl/YokogawaDisk"), - "Yokogawa spinning disk (pinhole distance)" => + "Yokogawa spinning disk - pinhole DISTANCE - CSU-10, CSU-22, CSU-X1" => array("micro=Yokogawa_disk_(pinhole_distance)&d=253&a=1&b=0&na=0&wl=0&msys=1&c=1&u=-6&ru=-6&rtag=pinhole+distance", "https://svi.nl/BackProjectedPinholeDistance"), + "Yokogawa spinning disk - pinhole DISTANCE - CSU-W1" => + array("micro=Yokogawa_disk_(pinhole_distance)&d=253&a=1&b=0&na=0&wl=0&msys=0.5065&c=1&u=-6&ru=-6&rtag=pinhole+distance", + "https://svi.nl/BackProjectedPinholeDistance"), + + "Visitech Infinity spinning disk - pinhole RADIUS" => + array("micro=Visitech_spinning_disk&d=50&a=1&b=0&na=0&wl=0&msys=1&c=0.5&u=-6", + "https://svi.nl/Visitech_Infinity"), + "Visitech Infinity spinning disk - pinhole DISTANCE" => + array("micro=Visitech_disk_(pinhole_distance)&d=253&a=1&b=0&na=0&wl=0&msys=1&c=1&u=-6&ru=-6&rtag=pinhole+distance", + "https://svi.nl/BackProjectedPinholeDistance"), "Zeiss LSM410 inverted" => array("micro=Zeiss_LSM410_inverted_P8¶m=Reported+parameter+(P_8)&a=3.92157&b=0&na=0&wl=0&msys=2.23&c=0.56419&u=-6", "https://svi.nl/Zeiss_LSM410_inverted"), @@ -79,6 +96,11 @@ array("micro=Zeiss_LSM710¶m=Pinhole+diameter+(microns)&a=1&b=0&na=0&wl=0&msys=1.9048&c=0.564&u=-6", "https://svi.nl/Zeiss_LSM710"), "Zeiss LSM780" => array("micro=Zeiss_780¶m=Pinhole+diameter+(microns)&a=1&b=0&na=0&wl=0&msys=1.9048&c=0.564&u=-6", "https://svi.nl/Zeiss_LSM780"), + "Zeiss LSM800" => + array("micro=Zeiss_800¶m=Pinhole+diameter+(microns)&a=1&b=0&na=0&wl=0&msys=1.53&c=0.564&u=-6", "https://svi.nl/Zeiss_LSM800"), + "Zeiss LSM880" => + array("micro=Zeiss_800¶m=Pinhole+diameter+(microns)&a=1&b=0&na=0&wl=0&msys=1.9048&c=0.564&u=-6", "https://svi.nl/Zeiss_LSM880"), + "Not listed" => array("micro=Not_listed_microscope¶m=Pinhole+physical+diameter+(microns)&a=1&b=0&na=0&wl=0&u=-6", "https://svi.nl/ReportOtherMicroscope") @@ -484,14 +506,8 @@ function serve() if ($error) { $out .= "

Error!

\n" . $error; } else { - $result = round($phr / $M, 2); - $out .= "

"; - $out .= "Backprojected $rtag $runits: $result"; - $out .= "

"; - $out .= "

 

"; - - $out .= "

This is the parameter list used in this calculation:

"; + $out .= "

Parameters used for the calculation:

"; $out .= $warning; $out .= "

"; @@ -506,6 +522,14 @@ function serve() } } $out .= "

\n"; + + $out .= "

 

"; + + $result = round($phr / $M, 2); + $out .= "
"; + $out .= "Result"; + $out .= "Backprojected $rtag $runits: $result"; + $out .= "
"; } $out .= "
"; diff --git a/composer.json b/composer.json index 5ecced3dc..f2d9e2618 100644 --- a/composer.json +++ b/composer.json @@ -45,14 +45,14 @@ } ], "require": { - "php": ">5.5.0", + "php": ">=7.2.0", "adodb/adodb-php": "v5.20.21", "adldap2/adldap2": "4.0.5", - "aarpon/php-traditional-server_old": "1.0.2", + "aarpon/php-traditional-server_old": "1.0.3", "monolog/monolog": "1.19.0" }, "require-dev": { - "phpunit/phpunit": "9.5.7", + "phpunit/phpunit": "8.5.25", "squizlabs/php_codesniffer": "3.6.0" }, "autoload": { diff --git a/composer.lock b/composer.lock index 7b50e1103..8655314d3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b588929232a140c1e0d689a1a0783ef0", + "content-hash": "36a39fd5ad5de980a4c58351cb01d18c", "packages": [ { "name": "aarpon/php-traditional-server_old", - "version": "1.0.2", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/aarpon/php-traditional-server_old.git", - "reference": "aaaba3c3e5687ecf1e8f9082ece7befdefb4de6d" + "reference": "2853cddcfeceb59831be792fa066bbe91e7c630b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aarpon/php-traditional-server_old/zipball/aaaba3c3e5687ecf1e8f9082ece7befdefb4de6d", - "reference": "aaaba3c3e5687ecf1e8f9082ece7befdefb4de6d", + "url": "https://api.github.com/repos/aarpon/php-traditional-server_old/zipball/2853cddcfeceb59831be792fa066bbe91e7c630b", + "reference": "2853cddcfeceb59831be792fa066bbe91e7c630b", "shasum": "" }, "type": "library", @@ -27,9 +27,9 @@ "description": "Endpoint handler for Fine Uploader's traditional server requests.", "homepage": "http://fineuploader.com", "support": { - "source": "https://github.com/aarpon/php-traditional-server_old/tree/1.0.2" + "source": "https://github.com/aarpon/php-traditional-server_old/tree/1.0.3" }, - "time": "2019-01-15T11:43:58+00:00" + "time": "2022-01-03T11:22:45+00:00" }, { "name": "adldap2/adldap2", @@ -280,29 +280,30 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^8.0", + "doctrine/coding-standard": "^9", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.22" }, "type": "library", "autoload": { @@ -329,7 +330,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + "source": "https://github.com/doctrine/instantiator/tree/1.4.1" }, "funding": [ { @@ -345,38 +346,42 @@ "type": "tidelift" } ], - "time": "2020-11-10T18:47:58+00:00" + "time": "2022-03-03T08:28:38+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.10.2", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, "files": [ "src/DeepCopy/deep_copy.php" - ] + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -392,7 +397,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" }, "funding": [ { @@ -400,63 +405,7 @@ "type": "tidelift" } ], - "time": "2020-11-13T09:40:50+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.13.2", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "210577fe3cf7badcc5814d99455df46564f3c077" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077", - "reference": "210577fe3cf7badcc5814d99455df46564f3c077", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2" - }, - "time": "2021-11-30T19:35:32+00:00" + "time": "2022-03-03T13:19:32+00:00" }, { "name": "phar-io/manifest", @@ -520,16 +469,16 @@ }, { "name": "phar-io/version", - "version": "3.1.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "bae7c545bef187884426f042434e561ab1ddb182" + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", - "reference": "bae7c545bef187884426f042434e561ab1ddb182", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { @@ -565,9 +514,9 @@ "description": "Library for handling version information and constraints", "support": { "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.1.0" + "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "time": "2021-02-23T14:00:09+00:00" + "time": "2022-02-21T01:04:05+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -798,44 +747,40 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.10", + "version": "7.0.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687" + "reference": "819f92bba8b001d4363065928088de22f25a3a48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687", - "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/819f92bba8b001d4363065928088de22f25a3a48", + "reference": "819f92bba8b001d4363065928088de22f25a3a48", "shasum": "" }, "require": { "ext-dom": "*", - "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.13.0", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "php": ">=7.2", + "phpunit/php-file-iterator": "^2.0.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^3.1.3 || ^4.0", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^4.2.2", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^8.2.2" }, "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "ext-xdebug": "^2.7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-master": "7.0-dev" } }, "autoload": { @@ -863,7 +808,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" }, "funding": [ { @@ -871,32 +816,32 @@ "type": "github" } ], - "time": "2021-12-05T09:12:13+00:00" + "time": "2021-07-26T12:20:09+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "2.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", + "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -923,7 +868,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5" }, "funding": [ { @@ -931,38 +876,26 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2021-12-02T12:42:26+00:00" }, { - "name": "phpunit/php-invoker", - "version": "3.1.1", + "name": "phpunit/php-text-template", + "version": "1.2.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" + "php": ">=5.3.3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, "autoload": { "classmap": [ "src/" @@ -979,47 +912,41 @@ "role": "lead" } ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "process" + "template" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2015-06-21T13:50:34+00:00" }, { - "name": "phpunit/php-text-template", - "version": "2.0.4", + "name": "phpunit/php-timer", + "version": "2.1.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -1038,14 +965,14 @@ "role": "lead" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "template" + "timer" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" }, "funding": [ { @@ -1053,32 +980,33 @@ "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2020-11-30T08:20:02+00:00" }, { - "name": "phpunit/php-timer", - "version": "5.0.3", + "name": "phpunit/php-token-stream", + "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/a853a0e183b9db7eed023d7933a858fa1c8d25a3", + "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3", "shasum": "" }, "require": { - "php": ">=7.3" + "ext-tokenizer": "*", + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1093,18 +1021,17 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", "keywords": [ - "timer" + "tokenizer" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", + "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" }, "funding": [ { @@ -1112,20 +1039,21 @@ "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "abandoned": true, + "time": "2020-08-04T08:28:15+00:00" }, { "name": "phpunit/phpunit", - "version": "9.5.7", + "version": "8.5.25", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d0dc8b6999c937616df4fb046792004b33fd31c5" + "reference": "9ff23f4dfde040ccd3b8db876192d1184b934158" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d0dc8b6999c937616df4fb046792004b33fd31c5", - "reference": "d0dc8b6999c937616df4fb046792004b33fd31c5", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ff23f4dfde040ccd3b8db876192d1184b934158", + "reference": "9ff23f4dfde040ccd3b8db876192d1184b934158", "shasum": "" }, "require": { @@ -1136,35 +1064,32 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.1", + "myclabs/deep-copy": "^1.10.0", + "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.3", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^2.3.4", - "sebastian/version": "^3.0.2" + "php": ">=7.2", + "phpspec/prophecy": "^1.10.3", + "phpunit/php-code-coverage": "^7.0.12", + "phpunit/php-file-iterator": "^2.0.4", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^2.1.2", + "sebastian/comparator": "^3.0.2", + "sebastian/diff": "^3.0.2", + "sebastian/environment": "^4.2.3", + "sebastian/exporter": "^3.1.2", + "sebastian/global-state": "^3.0.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^2.0.1", + "sebastian/type": "^1.1.3", + "sebastian/version": "^2.0.1" }, "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" + "ext-pdo": "*" }, "suggest": { "ext-soap": "*", - "ext-xdebug": "*" + "ext-xdebug": "*", + "phpunit/php-invoker": "^2.0.0" }, "bin": [ "phpunit" @@ -1172,15 +1097,12 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.5-dev" + "dev-master": "8.5-dev" } }, "autoload": { "classmap": [ "src/" - ], - "files": [ - "src/Framework/Assert/Functions.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1203,11 +1125,11 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.7" + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.25" }, "funding": [ { - "url": "https://phpunit.de/donate.html", + "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { @@ -1215,144 +1137,32 @@ "type": "github" } ], - "time": "2021-07-19T06:14:47+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2022-03-16T16:24:13+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -1374,7 +1184,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" }, "funding": [ { @@ -1382,34 +1192,34 @@ "type": "github" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2020-11-30T08:15:22+00:00" }, { "name": "sebastian/comparator", - "version": "4.0.6", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "php": ">=7.1", + "sebastian/diff": "^3.0", + "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1448,64 +1258,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:49:45+00:00" - }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" }, "funding": [ { @@ -1513,33 +1266,33 @@ "type": "github" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2020-11-30T08:04:30+00:00" }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1571,7 +1324,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" }, "funding": [ { @@ -1579,27 +1332,27 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2020-11-30T07:59:04+00:00" }, { "name": "sebastian/environment", - "version": "5.1.3", + "version": "4.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac" + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^7.5" }, "suggest": { "ext-posix": "*" @@ -1607,7 +1360,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -1634,7 +1387,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" }, "funding": [ { @@ -1642,34 +1395,34 @@ "type": "github" } ], - "time": "2020-09-28T05:52:38+00:00" + "time": "2020-11-30T07:53:42+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.4", + "version": "3.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" + "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", + "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "php": ">=7.0", + "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "3.1.x-dev" } }, "autoload": { @@ -1704,14 +1457,14 @@ } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", + "homepage": "http://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.4" }, "funding": [ { @@ -1719,30 +1472,30 @@ "type": "github" } ], - "time": "2021-11-11T14:18:36+00:00" + "time": "2021-11-11T13:51:24+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.3", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49" + "reference": "de036ec91d55d2a9e0db2ba975b512cdb1c23921" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49", - "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/de036ec91d55d2a9e0db2ba975b512cdb1c23921", + "reference": "de036ec91d55d2a9e0db2ba975b512cdb1c23921", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=7.2", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^8.0" }, "suggest": { "ext-uopz": "*" @@ -1750,7 +1503,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1775,7 +1528,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.2" }, "funding": [ { @@ -1783,91 +1536,34 @@ "type": "github" } ], - "time": "2021-06-11T13:31:12+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2022-02-10T06:55:38+00:00" }, { "name": "sebastian/object-enumerator", - "version": "4.0.4", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -1889,7 +1585,7 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" }, "funding": [ { @@ -1897,32 +1593,32 @@ "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2020-11-30T07:40:27+00:00" }, { "name": "sebastian/object-reflector", - "version": "2.0.4", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=7.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "1.1-dev" } }, "autoload": { @@ -1944,7 +1640,7 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" }, "funding": [ { @@ -1952,32 +1648,32 @@ "type": "github" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2020-11-30T07:37:18+00:00" }, { "name": "sebastian/recursion-context", - "version": "4.0.4", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=7.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -2007,7 +1703,7 @@ "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" }, "funding": [ { @@ -2015,32 +1711,29 @@ "type": "github" } ], - "time": "2020-10-26T13:17:30+00:00" + "time": "2020-11-30T07:34:24+00:00" }, { "name": "sebastian/resource-operations", - "version": "3.0.3", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2062,7 +1755,7 @@ "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" }, "funding": [ { @@ -2070,32 +1763,32 @@ "type": "github" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2020-11-30T07:30:19+00:00" }, { "name": "sebastian/type", - "version": "2.3.4", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" + "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", - "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", + "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=7.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "1.1-dev" } }, "autoload": { @@ -2118,7 +1811,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/2.3.4" + "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" }, "funding": [ { @@ -2126,29 +1819,29 @@ "type": "github" } ], - "time": "2021-06-15T12:49:02+00:00" + "time": "2020-11-30T07:25:11+00:00" }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=5.6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -2171,15 +1864,9 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "source": "https://github.com/sebastianbergmann/version/tree/master" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2016-10-03T07:35:21+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -2239,7 +1926,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -2271,12 +1958,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2301,7 +1988,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" }, "funding": [ { @@ -2434,7 +2121,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">5.5.0" + "php": ">=7.2.0" }, "platform-dev": [], "plugin-api-version": "2.2.0" diff --git a/composer.phar b/composer.phar index bd447c65b..ab3b2a133 100755 Binary files a/composer.phar and b/composer.phar differ diff --git a/config/.htaccess b/config/.htaccess old mode 100755 new mode 100644 diff --git a/config/samples/hrm.conf.sample b/config/samples/etc/hrm.conf.sample similarity index 77% rename from config/samples/hrm.conf.sample rename to config/samples/etc/hrm.conf.sample index 583ac3e4c..b4df786ef 100644 --- a/config/samples/hrm.conf.sample +++ b/config/samples/etc/hrm.conf.sample @@ -26,14 +26,9 @@ SUSER="hrm" HRM_LOG="/var/log/hrm" # Interaction with OMERO (if switched on in hrm/config). -# OMERO_PKG specifies the path where the "OMERO.server" package is installed -# OMERO_PKG="/opt/OMERO/OMERO.server-5.0.3-ice34-b41.linux" # OMERO_HOSTNAME="localhost" # OMERO_PORT="4064" -# PYTHON_EXTLIB allows adding a directory to the PYTHONPATH -# PYTHON_EXTLIB="/opt/OMERO/python-extlibs" - # Set a custom PHP CLI binary if necessary: # PHP_CLI="/usr/local/php/bin/php" diff --git a/config/samples/hrm_client_config.inc.sample b/config/samples/hrm_client_config.inc.sample deleted file mode 100644 index 081843eef..000000000 --- a/config/samples/hrm_client_config.inc.sample +++ /dev/null @@ -1,260 +0,0 @@ -= 5.6, the mysql driver is deprecated and will -// overflow your log files when used! -$db_type = "mysqli"; - -// The name of the database host machine (this may be localhost if it is the -// machine on which the web server runs) -$db_host = "localhost"; - -// The name of the database used by HRM -$db_name = "hrm"; - -// The name of the user with which the database is accessed -$db_user = "dbuser"; - -// The password of the account with which the database is accessed -$db_password = "dbpasswd"; - -//============================================================================== -// Huygens settings -//============================================================================== - -// Huygens server default user -$huygens_user = "hrm"; -$huygens_group = "hrm"; - -// Path to a local Huygens Core executable, to handle image information and -// thumbnails. This installation of hucore doesn't require a full license -// unless this is also the computation server. For image information and -// thumbnails mostly freeware functions will be used, but support for certain -// proprietary file formats requires a B flag in the license string. (See -// https://svi.nl/FileFormats). -// -// In combination with Huygens 3.5.1 or higher, HRM can also provide an -// estimate of the Signal-To-Noise ratio (SNR) of an image, interactively. At -// least that same simple license is required for this to work. -$local_huygens_core = "/usr/local/bin/hucore"; - -//============================================================================== -// File server settings -//============================================================================== - -// File server host name -$image_host = "localhost"; - -// File server default user -$image_user = "hrm"; -$image_group = "hrm"; - -// File server base folder -$image_folder = "/scratch/hrm_data"; - -// File server image source folder -$image_source = "src"; - -// File server image destination folder -$image_destination = "dst"; - -// File server base folder as seen from the Huygens server machines -$huygens_server_image_folder = "/scratch/hrm_data"; - -// Allow HTTP transfer of the restored (download) images -$allowHttpTransfer = true; - -// Allow HTTP transfer of original (upload) images. -// Limitations in upload size must be configured in the below parameters -// $max_upload_limit AND $max_post_limit. If these are set to 0, they are -// instead read from the PHP settings. If set to higher values, make sure -// that the PHP settings allow for such transfer limits. These PHP settings -// are normally located in php.ini, with the options upload_max_filesize AND -// post_max_size. -// Unzipping large archives may also require high max_execution_time and -// memory_limit variables. -$allowHttpUpload = true; -$max_upload_limit = 0; // Maximum file size for uploads in Mb -$max_post_limit = 0; // Maximum post size for uploads in Mb - -// Archiver for downloading the results via the web browser: -// $compressBin is the archiver, including options. %DEST% will be replaced -// with the user's destination path; can be used as an option or part of the -// command to change directories with 'cd'. Separate commands with "\n". -// $packExcludePath controls whether the archiving command excludes the whole -// file path per file when this is taken into account in the $compressBin -// command itself by using %DEST% properly. - -$compressExt = ".zip"; - -switch ($compressExt) { - case ".tgz": - case ".tar.gz": - $compressBin = "cd %DEST% \n /bin/tar chzf "; - $packExcludePath = true; - $dlMimeType = "application/x-force-download"; - break; - case ".tar": - $compressBin = "cd %DEST% \n /bin/tar cfh "; - $packExcludePath = true; - $dlMimeType = "application/x-force-download"; - break; - case ".zip": - $compressBin = "cd %DEST% \n /usr/bin/zip -0 "; - $packExcludePath =true; - $dlMimeType = "application/x-force-download"; - break; - default: - $compressExt = ".zip"; - $compressBin = "cd %DEST% \n/usr/bin/zip -0 -D"; - $packExcludePath = true; - $dlMimeType = "application/x-force-download"; -} - -// Tools to decompress uploaded archives. This is an array, each key linked to -// an archive extension. %DEST% will be replaced with the user's destination -// path; can be used as an option or part of the command to change directories -// with 'cd'. -// More formats are possible, just add more commands to the array. - -$decompressBin['zip'] = "cd %DEST% \n /usr/bin/unzip -o "; -$decompressBin['tgz'] = "cd %DEST% \n /usr/bin/tar xzf "; -$decompressBin['tar'] = "cd %DEST% \n /usr//bin/tar xf "; -$decompressBin['tar.gz'] = "cd %DEST% \n /usr/bin/tar xzf "; - -// Default output file format. -// One of: 'ics', 'ics2', 'hdf5', 'r3d', 'ome'. -$default_output_format = "ics"; - -//============================================================================== -// HRM configuration parameters -//============================================================================== - -// URL -$hrm_url = "http://localhost/hrm"; - -// Install dir -$hrm_path = "/var/www/html/hrm"; - -// Logging -// Please create a directory for the logging (default and recommended is /var/log/hrm) -// and make sure to grant read/write access to the web server user. -$log_verbosity = 1; // 0=quiet, 1=normal, 2=debug -$logdir = "/var/log/hrm"; -$logfile = "hrm.log"; -$logfile_max_size = 100; // maximum size of the logfile in MB - -// Email -$send_mail = true; -$email_sender = "hrm@localhost"; -$email_admin = "hrm@localhost"; -// Comma (',') is the standard separator for lists of email addresses. -// Please see: http://tools.ietf.org/html/rfc2822#section-3.6.3 -// Unfortunately, Microsoft Outlook uses semicolon (';') by default and -// ',' only optionally. Please see: http://support.microsoft.com/kb/820868 -// on how to configure Outlook to support comma-separated lists. -// If your users are mostly using Outlook, you can tell the HRM to use ';' -// instead of the standard ',' for distribution lists. -$email_list_separator = ","; // Valid options are ',' and ';'. - -// Supported authentication types. Possible values are 'integrated', 'active_dir', 'ldap'. -// These values replace the previous ones (but the old ones are still recognized): -// 'integrated' replaces 'MYSQL'; -// 'active_dir' replaces 'ACTIVE_DIR'; -// 'ldap' replaces 'LDAP'. -// You can use more than one authentication mechanism by listing them all in the -// $authenticateAgainst array below. -// -// e.g. $authenticateAgainst = array("active_dir", "integrated"); -// -// The first entry in the array is the default authentication mode that will be used -// for new users. Administrators can then change the authentication mode for individual -// users in the user management pages. -// -// Make sure to properly configure all authentication modes -//(see sample files in config/samples). -$authenticateAgainst = array("integrated"); - -// If true, the queue manager and the image processing server run on the same -// machine -$imageProcessingIsOnQueueManager = true; - -// If true the images are copied to the huygens server machine -$copy_images_to_huygens_server = false; - -// Minimum amount of free memory required from the processing machine -// to accept deconvolution jobs. Larger means more restrictive. -$min_free_mem_launch_requirement = 0; // In Mb. - -// Thumbnails require Huygens Core 3.3.2 or higher. - -// Notice that despite some script executions are relatively fast (like -// thumbnail generation and SNR estimation), they still may take a long time if -// the images are very large, or if deconvolution jobs are already running in -// the local server. You may need to increase PHP's max_execution_time -// directive in php.ini, and restart the web server. - -// If true, allows generates thumbnails and previews for the originals and the -// results during the deconvolution (i.e. in the computation servers). These -// are maximum intensity projections (MIP) of the stacks along the main axes, -// so they provide xy, xz and zy views. -$useThumbnails = true; - -// If true, allows on-demand generation of a preview of the original images in -// this web server prior to the deconvolution. For this to work the variable -// $local_huygens_core must be set correctly above. Notice that not all file -// formats are readable in a freeware Huygens Core and for full functionality -// you may need a license string. Otherwise just let the original thumbnails -// be generated during the deconvolution itself with the previous option. -// If this is true and the correct version of Huygens is installed, the SNR -// estimator tools is also enabled (see $enable_code_for_huygens above). -// This requires $useThumbnails = true; -$genThumbnails = true; - -// Moreover, if $genThumbnails is true and Huygens 3.5.1 or higher is -// installed (see $enable_code_for_huygens above), the Signal To Noise ratio -// can be estimated visually from raw images, in the restoration parameters -// editor. - -// If larger than zero, generates an AVI movie for 3D stacks that allows -// browsing along the XY slices, and for time series to browse along MIPs in -// time. The movie will have this maximum number of pixels in the largest -// dimension. -// This requires $useThumbnails = true; -$movieMaxSize = 300; - -// If true, (and $useThumbnails is also true), it generates not only the MIP -// previews but also Simulated Fluorescence Process top-view renderings of the -// original and the restored images (https://svi.nl/SFP). -$saveSfpPreviews = true; - -// If non-zero, save stack and time series previews of the original and restored -// data side by side, to allow comparisons. (Requires $useThumbnails and -// Huygens Core 3.5.2). This is the max size in pixels for each of the images's -// slicer. -$maxComparisonSize = 300; - -// A shell command that executes ping four times and stops afterwards -$ping_command = 'ping -w 4'; // use this on linux systems -// $ping_command = 'ping -t 4'; // use this on macosx systems -//$ping_command = 'ping'; // use this on cygwin - -// The parameter for the ping command after the hostname -$ping_parameter = ''; // use this on linux systems -//$ping_parameter = '56 4'; // use this on cygwin - -//============================================================================== -// HRM + Omero -//============================================================================== - -// Switch on/off (true/false) data transfers between HRM and Omero. -$omero_transfers = false; diff --git a/config/samples/hrm_server_config.inc.sample b/config/samples/hrm_config.inc.sample similarity index 99% rename from config/samples/hrm_server_config.inc.sample rename to config/samples/hrm_config.inc.sample index adf7f059e..6e75bb0e3 100644 --- a/config/samples/hrm_server_config.inc.sample +++ b/config/samples/hrm_config.inc.sample @@ -148,7 +148,7 @@ $hrm_path = "/var/www/html/hrm"; // Logging // Please create a directory for the logging (default and recommended is /var/log/hrm) // and make sure to grant read/write access to the web server user. -$log_verbosity = 1; // 0=quiet, 1=normal, 2=debug +$log_verbosity = 1; // 0=error, 1=warning, 2=info, 3=debug $logdir = "/var/log/hrm"; $logfile = "hrm.log"; $logfile_max_size = 100; // maximum size of the logfile in MB diff --git a/create_job.php b/create_job.php index f71e1c48f..5eaed5b73 100644 --- a/create_job.php +++ b/create_job.php @@ -57,6 +57,11 @@ // save preferred output file format if ($_SESSION['task_setting']->save()) { // TODO source/destination folder names should be given to JobDescription + // Call to sanitize files this will replace special characters with + // underscores. A warning is shown in the case this would actually + // change any file names. + $_SESSION['fileserver']->sanitizeFiles(); + $job = new JobDescription(); $job->setParameterSetting($_SESSION['setting']); $job->setTaskSetting($_SESSION['task_setting']); @@ -73,7 +78,7 @@ } } else { $message = "An unknown error has occurred. " . - "Please inform the administrator"; + "Please inform the administrator"; } } elseif (isset($_POST['OK'])) { header("Location: " . "select_parameter_settings.php"); @@ -146,37 +151,37 @@ $timeValue = $timeParameter->value(); // Make sure that if we had TIFF (8 or 16 bit) as output file format and a - // multichannel dataset, we reset the value to ics - if (($value == 'TIFF 18-bit') || ($value == 'TIFF 16-bit')) { + // multichannel dataset, we reset the value to ics2 + if (($value == 'TIFF 8-bit') || ($value == 'TIFF 16-bit')) { /** @var NumberOfChannels $nChannelsParameter */ $nChannelsParameter = $_SESSION['setting']->parameter("NumberOfChannels"); $numberOfChannels = $nChannelsParameter->value(); if ($numberOfChannels > 1) { - $parameter->setValue("ICS (Image Cytometry Standard)"); + $parameter->setValue("ICS2 (Image Cytometry Standard 2)"); $_SESSION['first_visit'] = false; } } // Make sure that if we had RGB-TIFF 8 bit as output file format and a - // single-channel dataset or more than 3 channels, we reset the value to ics + // single-channel dataset or more than 3 channels, we reset the value to ics2 if ($value == 'RGB TIFF 8-bit') { $nChannelsParameter = $_SESSION['setting']->parameter("NumberOfChannels"); $numberOfChannels = $nChannelsParameter->value(); if (($numberOfChannels == 1 || $numberOfChannels > 3)) { - $parameter->setValue("ICS (Image Cytometry Standard)"); + $parameter->setValue("ICS2 (Image Cytometry Standard 2)"); $_SESSION['first_visit'] = false; } } // Make sure that if we had Imaris Classic, TIFF 8, or TIFF 16 // as output file format and a time-series dataset, we reset - // the value to ics + // the value to ics2 if ( ($value == 'IMS (Imaris Classic)') || - ($value == 'TIFF 18-bit') || ($value == 'TIFF 16-bit') + ($value == 'TIFF 8-bit') || ($value == 'TIFF 16-bit') ) { if (($_SESSION['autoseries'] == "TRUE") || ($timeValue > 0)) { - $parameter->setValue("ICS (Image Cytometry Standard)"); + $parameter->setValue("ICS2 (Image Cytometry Standard 2)"); $_SESSION['first_visit'] = false; } } @@ -231,13 +236,24 @@ class="selection" // See config files. if (!isset($_SESSION['first_visit'])) { global $default_output_format; + + /* Notice that unfortunately 'values-translations' are inverted in + parameter "OutputFileFormat" w.r.t "ImageFileFormat". Therefore, a + translation is needed here. */ + $defaultFormatSet = False; + foreach ($possibleValues as $possibleValue) { + if ($parameter->translatedValueFor($possibleValue) == $default_output_format) { + $parameter->setValue($possibleValue); + $defaultFormatSet = True; + break; + } + } - /* Fallback. */ - if (! in_array($default_output_format, $possibleValues)) { - $default_output_format = "ICS (Image Cytometry Standard)"; + /* Fallback */ + if ($defaultFormatSet === False) { + $parameter->setValue("ICS2 (Image Cytometry Standard 2)"); } - $parameter->setValue($default_output_format); $_SESSION['first_visit'] = false; } @@ -368,6 +384,7 @@ class="selection" readonly="readonly"> selectedFiles(); foreach ($files as $file) { echo " " . $file . "\n"; @@ -445,6 +462,13 @@ class="icon launch_start" echo "

$message

"; + if (!$_SESSION['fileserver']->checkSanitization()) { + echo "

Warning: files containing special characters in their " . + "names have been selected. These files will automatically " . + "be renamed on disk if you continue.

" . + "

For example a file called \"B@d img(náme).h5\" " . + "would be renamed to \"B_d_img_n__me_.h5\".

"; + } ?>
diff --git a/css/default.css b/css/default.css index f3d8b9c2b..15c310919 100644 --- a/css/default.css +++ b/css/default.css @@ -620,7 +620,7 @@ input.upload { } input.omero { - background: url("../images/omero_data.png") no-repeat center; + background: url("../images/omero_data_neg.png") no-repeat center; } input.save { @@ -679,6 +679,7 @@ input.apply { #actions td.label { font-weight: bold; width: 50px; + vertical-align: top; } #omeroActions { @@ -960,6 +961,13 @@ table#Servers td { font-size: 90%; } +#HotPixelCorrection p.info { + text-align: center; + margin-bottom: 0; + color: #4988DE; + font-size: 90%; +} + #AberrationCorrectionModeDiv { margin-top: 25px; } @@ -1093,6 +1101,7 @@ table#Servers td { #colocCoefficients table { font-size: 100%; float: left; + color: black; } #colocCoefficients td { @@ -1103,32 +1112,38 @@ table#Servers td { text-align: center; font-weight: bold; background-color: #BFFFB3; -} - -#colocCoefficients td.header { + color: #3F4641; + } + + #colocCoefficients td.header { width: 60px; font-weight: bold; background-color: #CFD7F2; border: solid thin #B6ECFF; -} - -#colocCoefficients td.coefficient { - background-color: #DAFFDA; -} - -#colocCoefficients td.thresh { - background-color: #EEFFEE; -} - -#colocCoefficients td.cell { - background-color: #DAFFDA; -} - -#colocCoefficients td.marked { - background-color: #B6ECFF; -} - -#colocMap table { + color: #3F4641; + } + + #colocCoefficients td.coefficient { + background-color: #A9A9A9; + color: #3F4641; + } + + #colocCoefficients td.thresh { + background-color: #A9A9A9; + color: #3F4641; + } + + #colocCoefficients td.cell { + background-color: #A9A9A9; + color: #3F4641; + } + + #colocCoefficients td.marked { + background-color: #A9A9A9; + color: #3F4641; + } + + #colocMap table { font-size: 100%; float: left; } @@ -1608,6 +1623,12 @@ input.abort { background: url("../images/cancel_small.png") no-repeat center; } +input.clearlist { + width: 60px; + height: 60px; + background: url("../images/clear-list.png") no-repeat center; +} + /* The dimensions are set to values smaller than the image to solve an issue in Chrome where some white padding is added to the image. */ input.noPreview { @@ -1617,7 +1638,7 @@ input.noPreview { } .expandedView { - font-size: 110%; + font-size: 175%; font-weight: bold; color: #4988de; cursor: pointer; @@ -1655,6 +1676,10 @@ select { padding: 5px; } +select:disabled { + color: #777777; +} + select.snrselect { width: 80px; margin-left: 7px; @@ -2314,8 +2339,11 @@ div.StedDeplModeValues { } .theme_div { - margin-left: 35px; - font-size: 0.85em; + float: right; + font-size: 1.5em; + position: relative; + top: -18px; + color: lightslategrey; } .theme_div > form > label { @@ -2331,3 +2359,53 @@ div.StedDeplModeValues { .highlighted { color: darkorange; } + +/* Override color of jqTree items */ +.jqtree-tree .jqtree-title { + color: #73A7C3; +} + +ul.jqtree-tree .jqtree-toggler { + color: #aaa; +} + +ul.jqtree-tree .jqtree-toggler:hover { + color: #fff; +} + +/* Overrides for selections (NOTE: using 'background: unset;' doesn't work) */ +ul.jqtree-tree li.jqtree-selected > .jqtree-element, +ul.jqtree-tree li.jqtree-selected > .jqtree-element:hover { + background-color: #445662; + background: -webkit-gradient(linear, left top, left bottom, from(#445662), to(#445662)); + background: -moz-linear-gradient(top, #445662, #445662); + background: -ms-linear-gradient(top, #445662, #445662); + background: -o-linear-gradient(top, #445662, #445662); + text-shadow: unset; +} + +#OmeroData { + border: 1px solid #335c70; + border-radius: 10px; + padding: 5px; + background: #1e2a30; + margin-left: 15px; + margin-right: 10px; +} + +#legendOmeroData { + width: unset; + margin-left: 20px; + padding-left: 5px; + padding-right: 5px; +} + +#backproj_result { + color: #3f993f; +} + +.result_value { + font-weight: bolder; + font-size: 1.2em; + font-style: normal; +} diff --git a/css/themes/light.css b/css/themes/light.css index f545a13e4..4164e6bc5 100644 --- a/css/themes/light.css +++ b/css/themes/light.css @@ -266,6 +266,10 @@ input.noPreview { background: url("../../images/no_preview_button.png") no-repeat center; } +input.omero { + background: url("../../images/omero_data_pos.png") no-repeat center; +} + select { width: 100%; text-align: center; @@ -348,4 +352,62 @@ select { .highlight { background-color: yellow; padding: 2px; -} \ No newline at end of file +} + +#colocCoefficients td.title { + text-align: center; + font-weight: bold; + background-color: #BFFFB3; +} + +#colocCoefficients td.header { + width: 60px; + font-weight: bold; + background-color: #CFD7F2; + border: solid thin #B6ECFF; +} + +#colocCoefficients td.coefficient { + background-color: #DAFFDA; +} + +#colocCoefficients td.thresh { + background-color: #EEFFEE; +} + +#colocCoefficients td.cell { + background-color: #DAFFDA; +} + +#colocCoefficients td.marked { + background-color: #B6ECFF; +} + +/* Override color of jqTree items */ +.jqtree-tree .jqtree-title { + color: #1C4257; +} + +ul.jqtree-tree .jqtree-toggler { + color: #333; +} + +ul.jqtree-tree .jqtree-toggler:hover { + color: #000; +} + +/* Overrides for selections (NOTE: using 'background: unset;' doesn't work) */ +ul.jqtree-tree li.jqtree-selected > .jqtree-element, +ul.jqtree-tree li.jqtree-selected > .jqtree-element:hover { + background-color: #9ebbcf; + background: -webkit-gradient(linear, left top, left bottom, from(#9ebbcf), to(#9ebbcf)); + background: -moz-linear-gradient(top, #9ebbcf, #9ebbcf); + background: -ms-linear-gradient(top, #9ebbcf, #9ebbcf); + background: -o-linear-gradient(top, #9ebbcf, #9ebbcf); + text-shadow: unset; +} + +#OmeroData { + border: 1px solid #b3d1e0; + background: #f0f5ff; +} diff --git a/footer.inc.php b/footer.inc.php index 784e09bb5..a692f42fe 100644 --- a/footer.inc.php +++ b/footer.inc.php @@ -71,6 +71,7 @@ function addDevelopers(array $name_list, array $email_list) isLoggedIn()); @@ -89,8 +90,10 @@ function addDevelopers(array $name_list, array $email_list) echo "v" . System::getHRMVersionAsString(); } ?> -
+ diff --git a/images/clear-list.png b/images/clear-list.png new file mode 100644 index 000000000..c8e3f73d9 Binary files /dev/null and b/images/clear-list.png differ diff --git a/images/hpc.png b/images/hpc.png new file mode 100644 index 000000000..b78c46c5c Binary files /dev/null and b/images/hpc.png differ diff --git a/images/omero_data_neg.png b/images/omero_data_neg.png new file mode 100644 index 000000000..c4f325f86 Binary files /dev/null and b/images/omero_data_neg.png differ diff --git a/images/omero_data.png b/images/omero_data_pos.png similarity index 100% rename from images/omero_data.png rename to images/omero_data_pos.png diff --git a/inc/DatabaseConnection.php b/inc/DatabaseConnection.php index 34351c5af..6c1b17fe3 100644 --- a/inc/DatabaseConnection.php +++ b/inc/DatabaseConnection.php @@ -22,8 +22,6 @@ use hrm\setting\TaskSetting; use hrm\user\UserV2; -require_once dirname(__FILE__) . "/bootstrap.php"; - /** * Manages the database connection through the ADOdb library. * @@ -518,8 +516,8 @@ public function saveParameterSettings(Setting $settings) } // Accumulate the successes (or failures) of the queries. If a query - // fails, the return of $this->execute() will be === false; otherwise - // it is an ADORecordSet. + // fails, the return of $this->execute() will be === false; + // otherwise it is an ADORecordSet. $result &= ($this->execute($query) !== false); } @@ -591,9 +589,18 @@ public function saveSharedParameterSettings($settings, $targetUserName) // Create hard links and update paths to the PSF files // to point to the hard-links. $fileServer = new Fileserver($original_user); - $parameterValue = $fileServer->createHardLinksToSharedPSFs( - $parameterValue, $targetUserName); + $parameterValue = $fileServer->createHardLinksToSharedAuxFiles( + $parameterValue, "psf", $targetUserName); + } + // Special treatment for the HotPixelCorrection parameter. + if ($parameter->name() == "HotPixelCorrection") { + + // Create hard links and update paths to the HPC files + // to point to the hard-links. + $fileServer = new Fileserver($original_user); + $parameterValue = $fileServer->createHardLinksToSharedAuxFiles( + $parameterValue, "hpc", $targetUserName); } /*! @@ -657,7 +664,7 @@ public function loadParameterSettings($settings) } - if ($newValue{0} == '#') { + if ($newValue[0] == '#') { switch ($parameterName) { case "DeconvolutionAlgorithm": case "ExcitationWavelength": @@ -665,8 +672,14 @@ public function loadParameterSettings($settings) case "PinholeSize": case "PinholeSpacing": case "SignalNoiseRatio": + case "Acuity": case "BackgroundOffsetPercent": - case "ChromaticAberration": + case "ChromaticAberrationCh0": + case "ChromaticAberrationCh1": + case "ChromaticAberrationCh2": + case "ChromaticAberrationCh3": + case "ChromaticAberrationCh4": + case "ChromaticAberrationCh5": case "StedDepletionMode": case "StedWavelength": case "StedSaturationFactor": @@ -682,6 +695,7 @@ public function loadParameterSettings($settings) case "ColocChannel": case "ColocThreshold": case "ColocCoefficient": + case "HotPixelCorrection": case "PSF": /* Extract and continue to explode. */ $newValue = substr($newValue, 1); @@ -689,9 +703,7 @@ public function loadParameterSettings($settings) $newValues = explode("#", $newValue); } - if (strcmp($parameterName, "PSF") != 0 - && strpos($newValue, "/") - ) { + if (!in_array($parameterName, array("PSF", "HotPixelCorrection")) && strpos($newValue, "/")) { $newValue = array(); for ($i = 0; $i < count($newValues); $i++) { if (strpos($newValues[$i], "/")) { @@ -778,21 +790,27 @@ public function loadSharedParameterSettings($id, $type) continue; } } - if ($newValue{0} == '#') { + if ($newValue[0] == '#') { switch ($parameterName) { case "DeconvolutionAlgorithm": case "ExcitationWavelength": case "EmissionWavelength": case "SignalNoiseRatio": + case "Acuity": case "BackgroundOffsetPercent": - case "ChromaticAberration": + case "ChromaticAberrationCh0": + case "ChromaticAberrationCh1": + case "ChromaticAberrationCh2": + case "ChromaticAberrationCh3": + case "ChromaticAberrationCh4": + case "ChromaticAberrationCh5": /* Extract and continue to explode. */ $newValue = substr($newValue, 1); default: $newValues = explode("#", $newValue); } - if (strcmp($parameterName, "PSF") != 0 && strpos($newValue, "/")) { + if (!in_array($parameterName, array("PSF", "HotPixelCorrection")) && strpos($newValue, "/")) { $newValue = array(); for ($i = 0; $i < count($newValues); $i++) { //$val = explode("/", $newValues[$i]); @@ -934,16 +952,33 @@ public function copySharedTemplate($id, $sourceSettingTable, $psfFiles = explode('#', $values); // Create hard-links to the target user folder - $newPSFFiles = $fileserver->createHardLinksFromSharedPSFs( - $psfFiles, $owner, $previous_owner); + $newPSFFiles = $fileserver->createHardLinksFromSharedAuxFiles( + $psfFiles, "psf", $owner, $previous_owner); // Update the entries for the database $record["value"] = "#" . implode('#', $newPSFFiles); + + } elseif ($record["name"] == "HotPixelCorrection") { - } else { + // Instantiate a Fileserver object for the target user + $fileserver = new Fileserver($owner); - $record["value"] = $row["value"]; + // Get the array of HPC names + $values = $row["value"]; + if ($values[0] == "#") { + $values = substr($values, 1); + } + $hpcFiles = explode('#', $values); + // Create hard-links to the target user folder + $newHPCFiles = $fileserver->createHardLinksFromSharedAuxFiles( + $hpcFiles, "hpc", $owner, $previous_owner); + + // Update the entries for the database + $record["value"] = "#" . implode('#', $newHPCFiles); + + } else { + $record["value"] = $row["value"]; } $insertSQL = $this->connection()->GetInsertSQL($destParameterTable, @@ -1040,10 +1075,28 @@ public function deleteSharedTemplate($id, $sourceSettingTable, $psfFiles = explode("#", $psfFiles); // Delete them - Fileserver::deleteSharedFSPFilesFromBuffer($psfFiles); + Fileserver::deleteSharedAuxFilesFromBuffer($psfFiles, "psf"); + } + } + + // Delete shared HPC files if any exist + if ($sourceParameterTable == "shared_parameter") { + $query = "select value from $sourceParameterTable where setting_id=$id and name='HotPixelCorrection'"; + $hpcFiles = $this->queryLastValue($query); + if (null != $hpcFiles && $hpcFiles != "#####") { + if ($hpcFiles[0] == "#") { + $hpcFiles = substr($hpcFiles, 1); + } + + // Extract HPC file paths from the string + $hpcFiles = explode("#", $hpcFiles); + + // Delete them + Fileserver::deleteSharedAuxFilesFromBuffer($hpcFiles, "hpc"); } } + $this->connection()->BeginTrans(); // Delete parameter entries @@ -2282,6 +2335,7 @@ public function getParameterConfidenceLevel($fileFormat, $parameterName) case 'PerformAberrationCorrection': case 'AberrationCorrectionMode': case 'AdvancedCorrectionOptions': + case 'HotPixelCorrection': case 'PSF' : return "provided"; case 'Binning': diff --git a/inc/FileBrowser.php b/inc/FileBrowser.php index ddc107501..020eca959 100644 --- a/inc/FileBrowser.php +++ b/inc/FileBrowser.php @@ -10,8 +10,6 @@ namespace hrm; -require_once dirname(__FILE__) . '/bootstrap.php'; - /** Image file browser @@ -38,14 +36,18 @@ function fileButton($type) { # confirmation before actually posting. $mode = "ajax"; + $buttonId = ""; + switch ($type) { case "download": + $buttonId = "id=\"webTransferButton\""; $onClick = "downloadImages()"; $name = "download"; $tip = 'Compress and download'; break; case "upload": + $buttonId = "id=\"webTransferButton\""; $max = UtilV2::getMaxUploadFileSize() / 1024 / 1024; $maxFile = "$max MB"; $max = UtilV2::getMaxPostSize() / 1024 / 1024; @@ -77,6 +79,7 @@ function fileButton($type) { break; case "omeroImport": + $buttonId = "id=\"omeroButton\""; $name = "getOmeroData"; $value = "OMERO Data"; $mode = "post"; @@ -87,6 +90,7 @@ function fileButton($type) { break; case "omeroExport": + $buttonId = "id=\"omeroButton\""; $name = "getOmeroData"; $value = "OMERO Data"; $mode = "post"; @@ -109,10 +113,12 @@ function fileButton($type) { } $ret = "\n\n"; } else { $ret = "\n\n"; } @@ -527,7 +533,7 @@ class="selection" forceMultipart: true, customHeaders: { "DestinationFolder" : "sourceFolder()); ?>", - "ImageExtensions" : ['dv', 'ims', 'lif', 'lof', 'lsm', 'oif', 'pic', 'r3d', 'stk', + "ImageExtensions" : ['dv', 'ims', 'lif', 'lof', 'lsm', 'vsi', 'oif', 'pic', 'r3d', 'stk', 'zvi', 'czi', 'nd2', 'nd', 'tf2', 'tf8', 'btf', 'h5', 'tif', 'tiff', 'ome.tif', 'ome.tiff', 'ome', 'ics', 'ids'] } @@ -547,9 +553,9 @@ class="selection" validation: { stopOnFirstInvalidFile: false, sizeLimit: totalAllowedSizeOfSingleFile, - acceptFiles: ".dv,.ims,.lif,.lof,.lsm,.oif,.pic,.3rd,.stk,.zvi,.czi,.nd2,.nd,.tf2,.tf8,.btf,.h5," + + acceptFiles: ".dv,.ims,.lif,.lof,.lsm,.vsi,.oif,.pic,.3rd,.stk,.zvi,.czi,.nd2,.nd,.tf2,.tf8,.btf,.h5," + ".tif,.tiff,.ome.tif,.ome.tiff,.ics,.ids,.zip,.tgz,.tar,.tar.gz", - allowedExtensions: ['dv', 'ims', 'lif', 'lof', 'lsm', 'oif', 'pic', 'r3d', 'stk', + allowedExtensions: ['dv', 'ims', 'lif', 'lof', 'lsm', 'vsi', 'oif', 'pic', 'r3d', 'stk', 'zvi', 'czi', 'nd2', 'nd', 'tf2', 'tf8', 'btf', 'h5', 'tif', 'tiff', 'ome.tif', 'ome.tiff', 'ome', 'ics', 'ids', 'zip', 'tgz', 'tar', 'tar.gz'], }, diff --git a/inc/Fileserver.php b/inc/Fileserver.php index 78c680e63..9360facf5 100644 --- a/inc/Fileserver.php +++ b/inc/Fileserver.php @@ -12,8 +12,6 @@ use hrm\job\JobDescription; -require_once dirname(__FILE__) . '/bootstrap.php'; - /** * Takes care of all file handling to and from the image area and * provides commodity functions for creating and displaying previews. @@ -112,7 +110,7 @@ class Fileserver * Fileserver constructor. * @param string $name User name. */ - function __construct($name) + public function __construct($name) { // Replace the array of decompression options from the // (deprecated) configuration files with the only supported @@ -124,6 +122,7 @@ function __construct($name) $this->selectedFiles = NULL; $this->destFiles = NULL; $this->imageExtensions = NULL; + $this->selectionSanitized = false; // Set the valid image extensions $db = DatabaseConnection::get(); @@ -262,6 +261,7 @@ public function checkAgainstFormat($file, $selectedFormat) case 'lof': case 'lsm': case 'oif': + case 'vsi': case 'pic': case 'r3d': case 'stk': @@ -625,6 +625,129 @@ public function expandSubImages($bool) $this->expandSubImages = $bool; } + + /** + * Returns a postfixed version of the given path, so that it is unique. + * It will add "_$inx" as suffix, whith $inx = 0,1,2... until 100. Returns + * false if nothing was found. + */ + private function getPostfixedPath($path) + { + $dir = pathinfo($path, PATHINFO_DIRNAME); + $file = pathinfo($path, PATHINFO_FILENAME); + $ext = pathinfo($path, PATHINFO_EXTENSION); + + for ($inx = 0; $inx <= 100; $inx+=1) { + $suffixed = $dir . "/" . $file . "_" . $inx . "." . $ext; + if (!realpath($suffixed)) { + return $suffixed; + } + } + return false; + } + + /** + * Returns true if sanization of the selection is not needed or already + * done. Otherwise returns false. + */ + public function checkSanitization() + { + if ($this->selectionSanitized) { + return true; + } + if ($this->selectedFiles == NULL) { + $this->selectionSanitized = true; + return true; + } + + // Compare the filename with it's sanitized version. If not the same + // it needs sanitization. + foreach ($this->selectedFiles as $key => $file) { + $fileName = pathinfo($file, PATHINFO_FILENAME); + $sanitized = FileserverV2::sanitizeFileName($fileName); + + if (strcmp($fileName, $sanitized) !== 0) { + $this->selectionSanitized = false; + return false; + } + } + $this->selectionSanitized = true; + return true; + } + + /** + * Returns the list of selected files after sanitizing bad filenames. It + * will rename the files on disk. + * @return array Array of file names after sanitation. + */ + public function sanitizeFiles() + { + if ($this->selectedFiles == null) { + $this->selectedFiles = array(); + } + if ($this->selectionSanitized) { + return $this->selectedFiles; + } + + // Compare the filename and it's sanitized version, enter sanitization + // when they are not the same. + $renamesDone = array(); + foreach ($this->selectedFiles as $key => $file) { + $fileName = pathinfo($file, PATHINFO_FILENAME); + $sanitized = FileserverV2::sanitizeFileName($fileName); + + if (strcmp($fileName, $sanitized) !== 0) { + + // Create the actual paths of the files (the extension might + // have to be modified, so it is processed a little bit). + $extension = pathinfo($file, PATHINFO_EXTENSION); + $extensionProcessed = explode(" ", $extension)[0]; + + $oldPath = $this->sourceFolder() . + "/" . $fileName . "." . $extensionProcessed; + $newPath = $this->sourceFolder() . + "/" . $sanitized . "." . $extensionProcessed; + + // Check if the old path exists. If not it might have already + // been renamed, so check $renamesDone. + if (!realpath($oldPath)) { + + if (array_key_exists($fileName, $renamesDone)) { + $sanitized = $renamesDone[$fileName]; + $this->selectedFiles[$key] = $sanitized . "." . + $extension; + } else { + error_log("Path " . $oldPath . " can't be found " . + "during sanitization."); + } + continue; + } + + // Check the new path. If it exsists already find an + // alternative path by adding a postfix. + if (realpath($newPath)) { + $newPath = $this->getPostfixedPath($newPath); + if (!$newPath) { + error_log("Path ". $newPath . " already exists and " . + "no viable suffixed alternative was found. " . + "Sanitization skipped for " . $oldPath . "."); + continue; + } else { + $sanitized = pathinfo($newPath, PATHINFO_FILENAME); + } + } + rename($oldPath, $newPath); + $renamesDone[$fileName] = $sanitized; + $this->selectedFiles[$key] = $sanitized . "." . $extension; + } + } + + $this->selectionSanitized = true; + $this->getFiles(); + return $this->selectedFiles; + } + + /** * Returns the list of selected files that will be added to a job. * @return array Array of selected file names. @@ -650,6 +773,9 @@ public function addFilesToSelection(array $files) $new = array_diff($files, $selected); $this->selectedFiles = array_merge($new, $this->selectedFiles); sort($this->selectedFiles); + + // After adding any files, set the selectionSanitized to false. + $this->selectionSanitized = false; } /** @@ -816,17 +942,30 @@ public function deleteFiles(array $files, $dir = "dest") // Delete all files name like this one, with all different extensions. $dirname = dirname($pdir . "/" . $file); + Log::debug("dirname: " . $dirname); $basename = basename($pdir . "/" . $file); + Log::debug("basename (initial): " . $basename); + + # some formats (like .vsi) have extra dirs with the actual data: + $extra_dir = ""; if ($dir == "src") { $pattern = "/(\.([^\..]+))*\.([A-Za-z0-9]+)(\s\(.*\))*$/"; preg_match($pattern, $basename, $matches); $pattern = "/$matches[0]$/"; + Log::debug("filename pattern: " . $pattern); $basename = preg_replace($pattern, "\\1.*", $basename); + Log::debug("basename: ". $basename); $path = $dirname . "/" . $basename; + Log::debug("path: ". $path); $path_preview = $dirname . "/hrm_previews/" . $basename; + + if ($pattern == "/.vsi$/") { + Log::info("Detected 'VSI', scanning for related dirs..."); + $extra_dir = $dirname . "/_" . preg_replace("/\.\*/", "_/", $basename); + } } else { $filePattern = $this->getFilePattern($basename); $path = $dirname . "/" . $filePattern; @@ -843,6 +982,12 @@ public function deleteFiles(array $files, $dir = "dest") foreach ($allFiles as $f) { $success &= unlink($f); } + + # deal with formats having extra folders: + if ($extra_dir != "" and is_dir($extra_dir)) { + Log::info("Removing extra folders for '{$file}': [{$extra_dir}]"); + FileserverV2::removeDirAndContent($extra_dir); + } } if ($dir == "src") { @@ -1013,7 +1158,8 @@ public function uploadFiles($files, $dir) $baseName = basename($name); $baseName = str_replace(" ", "_", $baseName); $uploadFile = $uploadDir . "/" . $baseName; - $bareName = reset(explode('.', $baseName)); + $splitBaseName = explode('.', $baseName); + $bareName = reset($splitBaseName); $extension = str_replace($bareName, "", $baseName); // If the php.ini upload variables are overriden in HRM @@ -1218,17 +1364,20 @@ public function isValidImage($filename, $alsoExtras = false) { $filename = strtolower($filename); $ext = $this->getFileNameExtension($filename); + if ($ext === "") { + return false; + } if ($ext === "gz") { // Use two suffixes as extension $filename = basename($filename, ".gz"); $ext = $this->getFileNameExtension($filename) . ".gz"; } - $result = False; + $result = false; if (in_array($ext, $this->validImageExtensions)) { - $result = True; + $result = true; } if ($alsoExtras && (in_array($ext, $this->validImageExtensionsExtras))) { - $result = True; + $result = true; } return $result; } @@ -1586,11 +1735,15 @@ public function getImageAction($file, $index, $dir, $type, $ref = 0, $data = 1) if ($compare > 0) { $compare = 400; } + + // Also supply the sanitized name, to see if renaming is needed and + // handle it properly. + $sanitized = FileserverV2::sanitizeFileName($file); return "imgPrev('" . rawurlencode($file) . "', $mode, " . - (int)$genThumbnails . ", $compare, $index, '$dir', " . - "'$referer', $data)"; + (int)$genThumbnails . ", '" . rawurlencode($sanitized) . + "', $compare, $index, '$dir', '$referer', $data)"; } /** @@ -2221,7 +2374,7 @@ public function compareResult($file, $size = "400", $op = "close", $mode = "MIP" if ($mode == "MIP") { $altMode = "SFP"; } else { - $altMod = "MIP"; + $altMode = "MIP"; } echo ""; @@ -2382,6 +2535,31 @@ public function genPreview($file, $src, $dest, $index, $sizes = "preview", $data if ($src == "src") { $psrc = $this->sourceFolder(); + + // Check "src" files for sanitization, since they might not have + // been santized before. + $fileName = pathinfo($file, PATHINFO_FILENAME); + $sanitized = FileserverV2::sanitizeFileName($fileName); + if (strcmp($fileName, $sanitized) !== 0) { + + // Backup the current selection, then set the current file as + // selection. + $selectedBackup = $this->selectedFiles(); + $this->removeAllFilesFromSelection(); + $fileArray = array(0 => $file); + $this->addFilesToSelection($fileArray); + $result = $this->sanitizeFiles(); + if ($result) { + // Successfull rename. + $updateSelection = array_search($file, $selectedBackup); + if ($updateSelection) { + $selectedBackup[$updateSelection] = $result[0]; + } + $file = $result[0]; + } + $this->removeAllFilesFromSelection(); + $this->addFilesToSelection($selectedBackup); + } } else { $psrc = $this->destinationFolder(); } @@ -2627,173 +2805,188 @@ public function folderContainsNewerFile($folder, $date) return $result; } + /** - * Create hard links into the psf_sharing/buffer folder from the + * Create hard links into the ${auxType}_sharing/buffer folder from the * folder of the sharing user and return an array of full paths * created links. - * @param array $psfFiles Array of psf files paths relatives to current user. + * @param array $auxfiles Array of auxiliary files for running jobs (PSFs, HPCs) with paths + * relative to current user. + * @param string $auxType whether 'psf' or 'hpc'. * @param string $targetUser name of the target user. - * @return array Array of destination PSF paths. + * @return array Array of destination paths. */ - public function createHardLinksToSharedPSFs(array $psfFiles, $targetUser) + public function createHardLinksToSharedAuxFiles(array $auxFiles, $auxType, $targetUser) { - global $image_folder; - // Prepare output - $destPFSPaths = array(); - // Full path to psf_sharing and psf_sharing/buffer - $psf_sharing = $image_folder . "/" . "psf_sharing"; - $buffer = $psf_sharing . "/" . "buffer"; + if (!in_array($auxType, array('psf', 'hpc'))) { + Log::error("Unimplemented file type '$auxType' found in shared hard links infrastructure."); + return null; + } - // Create a timestamp for current hard links + // Prepare output. + $destAuxPaths = array(); + + // Full path to shared folders. + $aux_sharing = $image_folder . "/" . $auxType . "_sharing"; + $buffer = $aux_sharing . "/" . "buffer"; + + // Create a timestamp for current hard links. $mt = microtime(); $mt = explode(" ", $mt); $timestamp = (string)$mt[1] . (string)round(1e6 * $mt[0]); - // Go over all PSF files - for ($i = 0; $i < count($psfFiles); $i++) { + // Go over all files. + for ($i = 0; $i < count($auxFiles); $i++) { // If we have a file, process it - if ($psfFiles[$i] != "") { + if ($auxFiles[$i] != "") { - // Full psf file path - $fullSourcePSFPath = $this->sourceFolder() . "/" . $psfFiles[$i]; + // Full aux file path. + $fullSourceAuxPath = $this->sourceFolder() . "/" . $auxFiles[$i]; - // Destination psf file path - $fullDestPSFPath = $buffer . "/" . $targetUser . "/" . - $this->username . "/" . $timestamp . "/" . $psfFiles[$i]; + // Destination aux file path. + $fullDestAuxPath = $buffer . "/" . $targetUser . "/" . + $this->username . "/" . $timestamp . "/" . $auxFiles[$i]; - // Destination psf containing folder - $contDestPSFFolder = dirname($fullDestPSFPath); + // Destination aux containing folder. + $contDestAuxFolder = dirname($fullDestAuxPath); - // Create the container folder if it does not exist - if (!file_exists($contDestPSFFolder)) { - if (!mkdir($contDestPSFFolder, 0777, true)) { - $destPFSPaths[$i] = ""; + // Create the container folder if it does not exist. + if (!file_exists($contDestAuxFolder)) { + if (!mkdir($contDestAuxFolder, 0777, true)) { + $destAuxPaths[$i] = ""; continue; } } - // Create hard link - $cmd = "ln \"" . $fullSourcePSFPath . "\" \"" . $contDestPSFFolder . "/.\""; + // Create hard link. + $cmd = "ln \"" . $fullSourceAuxPath . "\" \"" . $contDestAuxFolder . "/.\""; $out = shell_exec($cmd); - // If the PSF file is a *.ics/*.ids pair, we make sure to - // hard-link also the companion file - $companion = Fileserver::findCompanionFile($fullSourcePSFPath); + // If the aux file is a *.ics/*.ids pair, we make sure to + // hard-link also the companion file. + $companion = Fileserver::findCompanionFile($fullSourceAuxPath); if (NULL !== $companion) { - $cmd = "ln \"" . $companion . "\" \"" . $contDestPSFFolder . "/.\""; + $cmd = "ln \"" . $companion . "\" \"" . $contDestAuxFolder . "/.\""; $out = shell_exec($cmd); } - // Store the relative path to the destination PSF file to the - // output array - $relPath = substr($fullDestPSFPath, strlen($image_folder) + 1); - $destPFSPaths[$i] = $relPath; + // Store the relative path to the destination aux file to the + // output array. + $relPath = substr($fullDestAuxPath, strlen($image_folder) + 1); + $destAuxPaths[$i] = $relPath; } else { - $destPFSPaths[$i] = ""; + $destAuxPaths[$i] = ""; } } - // Return the aray of full PSF destination paths - return $destPFSPaths; + // Return the aray of full aux destination paths + return $destAuxPaths; } + /** * Create hard links into the folder of the target user from - * the psf_sharing/buffer folder and return an array of full paths + * the aux_sharing/buffer folder and return an array of full paths * created links. - * @param array $psfFiles array of psf files paths relatives to current user. + * @param array $auxFiles Array of auxiliary files for running jobs (PSFs, HPCs) with paths + * relative to current user. + * @param string $auxType whether 'psf' or 'hpc'. * @param string $targetUser Name of the target user. * @param string $previousUser Name of the previous (source) user. - * @return array Array of destination PSF paths. + * @return array Array of destination aux paths. */ - public function createHardLinksFromSharedPSFs(array $psfFiles, $targetUser, $previousUser) + public function createHardLinksFromSharedAuxFiles(array $auxFiles, $auxType, $targetUser, $previousUser) { - global $image_folder; global $image_source; - // Full path to psf_sharing and psf_sharing/buffer - $psf_sharing = $image_folder . "/" . "psf_sharing"; - $buffer = $psf_sharing . "/" . "buffer"; - // Create a timestamp for current hard links + if (!in_array($auxType, array('psf', 'hpc'))) { + Log::error("Unimplemented file type '$auxType' found in shared hard links infrastructure."); + return null; + } + + // Full path to shared folders. + $aux_sharing = $image_folder . "/" . $auxType . "_sharing"; + $buffer = $aux_sharing . "/" . "buffer"; + + // Create a timestamp for current hard links. $mt = microtime(); $mt = explode(" ", $mt); $targetTimestamp = (string)$mt[1] . (string)round(1e6 * $mt[0]); - // Full path with user and time information + // Full path with user and time information. $full_buffer = $buffer . "/" . $targetUser . "/" . $previousUser . "/"; // Prepare output - $destPFSPaths = array(); + $destAuxPaths = array(); - // Go over all PSF files - for ($i = 0; $i < count($psfFiles); $i++) { + // Go over all aux files. + for ($i = 0; $i < count($auxFiles); $i++) { - // If we have a file, process it - if ($psfFiles[$i] != "") { + // If we have a file, process it. + if ($auxFiles[$i] != "") { - // Full psf file path - $fullSourcePSFPath = $image_folder . "/" . $psfFiles[$i]; + // Full aux file path. + $fullSourceAuxPath = $image_folder . "/" . $auxFiles[$i]; - // Get the numeric timestamp - $pos = strpos($fullSourcePSFPath, "/", strlen($full_buffer)); + // Get the numeric timestamp. + $pos = strpos($fullSourceAuxPath, "/", strlen($full_buffer)); if (False === $pos) { - // This should not happen - $destPFSPaths[$i] = ""; + // This should not happen. + $destAuxPaths[$i] = ""; continue; } - // Relative PSF path - $relPSFPath = $targetTimestamp . "/" . substr($fullSourcePSFPath, ($pos + 1)); + // Relative aux path. + $relAuxPath = $targetTimestamp . "/" . substr($fullSourceAuxPath, ($pos + 1)); - // Destination psf file path - $fullDestPSFPath = $image_folder . "/" . $targetUser . "/" . - $image_source . "/" . $relPSFPath; + // Destination aux file path. + $fullDestAuxPath = $image_folder . "/" . $targetUser . "/" . + $image_source . "/" . $relAuxPath; - // Destination psf containing folder - $contDestPSFFolder = dirname($fullDestPSFPath); + // Destination aux containing folder. + $contDestAuxFolder = dirname($fullDestAuxPath); - // Create the container folder if it does not exist - if (!file_exists($contDestPSFFolder)) { - if (!mkdir($contDestPSFFolder, 0777, true)) { - $destPFSPaths[$i] = ""; + // Create the container folder if it does not exist. + if (!file_exists($contDestAuxFolder)) { + if (!mkdir($contDestAuxFolder, 0777, true)) { + $destAuxPaths[$i] = ""; continue; } } - // Create hard link - $cmd = "ln \"" . $fullSourcePSFPath . "\" \"" . $contDestPSFFolder . "/.\""; + // Create hard link. + $cmd = "ln \"" . $fullSourceAuxPath . "\" \"" . $contDestAuxFolder . "/.\""; $out = shell_exec($cmd); - // Now delete the source file - unlink($fullSourcePSFPath); + // Now delete the source file. + unlink($fullSourceAuxPath); - // If the PSF file is a *.ics/*.ids pair, we make sure to - // hard-link also the companion file - $companion = Fileserver::findCompanionFile($fullSourcePSFPath); + // If the aux file is a *.ics/*.ids pair, we make sure to + // hard-link also the companion file. + $companion = Fileserver::findCompanionFile($fullSourceAuxPath); if (NULL !== $companion) { - $cmd = "ln \"" . $companion . "\" \"" . $contDestPSFFolder . "/.\""; + $cmd = "ln \"" . $companion . "\" \"" . $contDestAuxFolder . "/.\""; $out = shell_exec($cmd); // Now delete the companion file unlink($companion); - } - // Store the relative path to the destination PSF file to the - // output array - $destPFSPaths[$i] = $relPSFPath; + // Store the relative path to the destination aux file to the + // output array. + $destAuxPaths[$i] = $relAuxPath; - // Delete the containing folders if they are no longer needed - $contFolder = dirname($fullSourcePSFPath); + // Delete the containing folders if they are no longer needed. + $contFolder = dirname($fullSourceAuxPath); while ($contFolder != $buffer && Fileserver::is_dir_empty($contFolder)) { if (!rmdir($contFolder)) { break; @@ -2802,36 +2995,42 @@ public function createHardLinksFromSharedPSFs(array $psfFiles, $targetUser, $pre } } else { - - $destPFSPaths[$i] = ""; - + $destAuxPaths[$i] = ""; } } - // Return the aray of full PSF destination paths - return $destPFSPaths; + // Return the aray of full aux destination paths. + return $destAuxPaths; } + /** - * Delete PSF files (hard links) with given relative path from - * the psf_sharing/buffer folder. - * @param array $psfFiles Array of PSF files paths relative to the file server root. + * Delete auxiliary files (hard links) with given relative path from + * the ${auxType}_sharing/buffer folder. + * @param array $auxFiles Array of auxiliary files for running jobs (PSFs, HPCs) with paths + * relative to the file server root. + * @param string $auxType whether 'psf' or 'hpc'. */ - public static function deleteSharedFSPFilesFromBuffer(array $psfFiles) + public static function deleteSharedAuxFilesFromBuffer(array $auxFiles, $auxType) { - global $image_folder; - // Full path of the psf_sharing/buffer folder - $buffer = $image_folder . "/psf_sharing/buffer"; - // Process the PSF files - foreach ($psfFiles as $f) { + if (!in_array($auxType, array('psf', 'hpc'))) { + Log::error("Unimplemented file type '$auxType' found in shared hard links infrastructure."); + return null; + } + + // Full path to shared buffer folder. + $buffer = $image_folder . "/" . $auxType . "_sharing/buffer"; + + // Process the aux files. + foreach ($auxFiles as $f) { // Make sure the file points in the the buffer folder! - if (strpos($f, "psf_sharing/buffer") === 0) { + if (strpos($f, $auxType . "_sharing/buffer") === 0) { - // Full path + // Full path. $f = $image_folder . "/" . $f; // Delete the file. If the file does not exist or cannot be @@ -2865,6 +3064,7 @@ public static function deleteSharedFSPFilesFromBuffer(array $psfFiles) } + /** * Given the name of either an ics or and ids file, returns the name of the companion. * @@ -3463,16 +3663,28 @@ public function basename($filename) */ public function getFileNameExtension($filename) { + // Process the path information $info = pathinfo($filename); + if (! array_key_exists("extension", $info)) { + return ""; + } + + $allExtensions = FileserverV2::getAllValidExtensions(); + if (in_array(strtolower($info["extension"]), $allExtensions)) { + return $info["extension"]; + } + + # Process possibly composed extension $info_ext = pathinfo($info["filename"], PATHINFO_EXTENSION); if ($info_ext == "") { return $info["extension"]; } else { - if (strlen($info_ext) > 4) { - // Avoid pathological cases with dots somewhere in the file name. + $composedExt = strtolower($info_ext . "." . $info["extension"]); + if (in_array($composedExt, $allExtensions)) { + return $info_ext . "." . $info["extension"]; + } else { return $info["extension"]; } - return $info_ext . "." . $info["extension"]; } } diff --git a/inc/FileserverV2.php b/inc/FileserverV2.php index c56869576..23335117f 100644 --- a/inc/FileserverV2.php +++ b/inc/FileserverV2.php @@ -10,8 +10,6 @@ namespace hrm; -require_once dirname(__FILE__) . '/bootstrap.php'; - /** * Takes care of all file handling to and from the image area (version 2). * @@ -19,33 +17,14 @@ */ class FileserverV2 { - /** - * Get the compression command (with %DEST% placeholder for the source) - * - * Note: when serving a compressed file for download, remember to set the - * MIME type to "application/x-force-download". - * - * @return array Array with the command for each supported compressor (currently only zip). - */ - private static function getCompressor() - { - // Replace the array of compression options from the - // (deprecated) configuration files with the only supported - // option: zip. - $compressBin['zip'] = "cd %DEST% \n /usr/bin/zip -0 "; - return $compressBin; - } - /** * Get the decompression command (with %DEST% placeholder for the target) * @return array Array with the command for each supported decompressor (currently only zip). */ - private static function getDecompressor() + private static function getDecompressors() { - // Replace the array of decompression options from the - // (deprecated) configuration files with the only supported - // option: zip. - $decompressBin['zip'] = "cd %DEST% \n /usr/bin/unzip -o "; + // Get the archive file extensions from the configuration files. + global $decompressBin; return $decompressBin; } @@ -68,7 +47,7 @@ public static function moveUploadedFile($file, $destDir, &$errorMessage) } // Drop path info and sanitize file name - $destBaseName = str_replace(" ", "_", basename($file)); + $destBaseName = FileserverV2::sanitizeFileName(basename($file)); // Full path of destination file $destFile = $destDir . "/" . $destBaseName; @@ -136,11 +115,8 @@ public static function moveUploadedFile($file, $destDir, &$errorMessage) */ public static function decompressArchive($file, $destDir) { - // Replace the array of decompression options from the - // (deprecated) configuration files with the only supported - // option: zip. - // @TODO: Simplify. - $decompressBin = self::getDecompressor(); + // Get the archive file extensions from the configuration files. + $decompressBin = self::getDecompressors(); // Create the output directory if (!is_dir($destDir)) { @@ -189,11 +165,7 @@ public static function decompressArchive($file, $destDir) public static function isArchiveFile($fileName) { // Get the archive file extensions from the configuration files. - // Replace the array of decompression options from the - // (deprecated) configuration files with the only supported - // option: zip. - // @TODO: Simplify. - $decompressBin = self::getDecompressor(); + $decompressBin = self::getDecompressors(); // Get the file extension $extension = FileserverV2::getFileNameExtension($fileName); @@ -252,11 +224,8 @@ public static function getImageExtensions() */ public static function getArchiveExtensions() { - // Replace the array of decompression options from the - // (deprecated) configuration files with the only supported - // option: zip. - // @TODO: Simplify. - $decompressBin = self::getDecompressor(); + // Get the archive file extensions from the configuration files. + $decompressBin = self::getDecompressors(); // Archive extensions return array_keys($decompressBin); @@ -271,7 +240,7 @@ public static function getArchiveExtensions() public static function getImageExtrasExtensions() { // Return the image extras extensions - return array("ids", 'idx.gz'); + return array("ids", 'ids.gz'); } /** @@ -319,18 +288,19 @@ public static function getFileNameExtension($filename) { // Process the path information $info = pathinfo($filename); + $allExtensions = FileserverV2::getAllValidExtensions(); + if (in_array(strtolower($info["extension"]), $allExtensions)) { + return $info["extension"]; + } + + # Process possibly composed extension $info_ext = pathinfo($info["filename"], PATHINFO_EXTENSION); if ($info_ext == "") { return $info["extension"]; } else { - if (strlen($info_ext) > 4) { - // Avoid pathological cases with dots somewhere in the file name. - return $info["extension"]; - } - $allExtensions = FileserverV2::getAllValidExtensions(); - $composedExt = $info_ext . "." . $info["extension"]; + $composedExt = strtolower($info_ext . "." . $info["extension"]); if (in_array($composedExt, $allExtensions)) { - return $composedExt; + return $info_ext . "." . $info["extension"]; } else { return $info["extension"]; } @@ -468,15 +438,21 @@ public static function createUpDownloadFolderIfMissing() if (isset($allowHttpTransfer) && $allowHttpTransfer == true) { // Does the download directory exist? if (!is_dir($httpDownloadTempFilesDir)) { - // Try creating it + Log::info("Trying to create " . $httpDownloadTempFilesDir); $result &= mkdir($httpDownloadTempFilesDir, 0775); if (! $result) { - // @todo Report! + Log::error("ERROR creating " . $httpDownloadTempFilesDir); return false; } } + // $perms = substr(sprintf('%o', fileperms($httpDownloadTempFilesDir)), -4); + // Log::info("Permissions: " . $perms); + if (! chmod($httpDownloadTempFilesDir, 0775)) { + Log::warning("Adjusting permissions failed on " . $httpDownloadTempFilesDir); + } + // Check that the download directory is writable $fid = @fopen("$httpDownloadTempFilesDir/$fname", "w"); if (false === $fid) { @@ -496,14 +472,18 @@ public static function createUpDownloadFolderIfMissing() // Does the chunk upload directory exist? if (!is_dir($httpUploadTempChunksDir)) { - // Try creating it + Log::info("Trying to create " . $httpUploadTempChunksDir); $result &= mkdir($httpUploadTempChunksDir, 0775); if (! $result) { - // @todo Report! + Log::error("Error creating " . $httpUploadTempChunksDir); return false; } } + if (! chmod($httpUploadTempChunksDir, 0775)) { + Log::warning("Adjusting permissions failed on " . $httpUploadTempChunksDir); + } + // Check that the chunk upload directory is writable $fid = @fopen("$httpUploadTempChunksDir/$fname", "w"); if (false === $fid) { @@ -518,15 +498,19 @@ public static function createUpDownloadFolderIfMissing() // Does the file upload directory exist? if (!is_dir($httpUploadTempFilesDir)) { - // Try creating it + Log::info("Trying to create " . $httpUploadTempFilesDir); $result &= mkdir($httpUploadTempFilesDir, 0775); if (! $result) { - // @todo Report! + Log::error("Error creating " . $httpUploadTempFilesDir); return false; } } + if (! chmod($httpUploadTempFilesDir, 0775)) { + Log::warning("Adjusting permissions failed on " . $httpUploadTempFilesDir); + } + // Check that the file upload directory is writable $fid = @fopen("$httpUploadTempFilesDir/$fname", "w"); if (false === $fid) { @@ -542,4 +526,52 @@ public static function createUpDownloadFolderIfMissing() // Return global result return $result; } + + /** + * Remove any non-ASCII characters and convert known non-ASCII characters + * to their ASCII equivalents, if possible. Also, replaces blank spaces + * with "_". + * + * @param string $string + * @return string $string + * @author Jay Williams + * @license MIT License + * @link http://gist.github.com/119517 + * + * Modified by Aaron Ponti and Kevin Namink for the HRM project. + */ + public static function sanitizeFileName($string) + { + // Replace Single Curly Quotes + $search[] = chr(226).chr(128).chr(152); + $replace[] = "'"; + $search[] = chr(226).chr(128).chr(153); + $replace[] = "'"; + + // Replace En Dash + $search[] = chr(226).chr(128).chr(147); + $replace[] = '--'; + + // Replace Em Dash + $search[] = chr(226).chr(128).chr(148); + $replace[] = '---'; + + // Replace Ellipsis with three consecutive dots + $search[] = chr(226).chr(128).chr(166); + $replace[] = '...'; + + // Replace blank spaces with underscores + $search[] = " "; + $replace[] = "_"; + + // Apply Replacements + $string = str_replace($search, $replace, $string); + + // Replace non-ASCII characters and any characters that are not + // one of the following: "!%^&=',.-_" "0-9" "A-Z" "a-z" with "_". + $string = preg_replace("/[^\!\%\^\&\=\'\,\.\-\_0-9A-Za-z]/","_", + $string); + + return $string; + } } diff --git a/inc/HuygensTemplate.php b/inc/HuygensTemplate.php index 2515351c8..7dfd11db0 100644 --- a/inc/HuygensTemplate.php +++ b/inc/HuygensTemplate.php @@ -206,6 +206,12 @@ class HuygensTemplate */ private $setpConfArray; + /** + * Array with information on the hot pixel correction subtask. + * @var array + */ + private $hpcArray; + /** * Array with information on thumbnail projections. * @var array @@ -218,6 +224,12 @@ class HuygensTemplate */ private $setpList; + /** + * A Tcl list with information for the 'hot pixel correction' subtask. + * @var string + */ + private $hpcList; + /** * Path and name of the source 'raw' image. * @var string @@ -366,7 +378,7 @@ private function initializeJobInfo() $this->jobInfoArray = array('title' => 'Batch Processing template', - 'version' => '2.3', + 'version' => '2.4', 'templateName' => '', 'date' => '', 'listID' => 'info'); @@ -394,7 +406,7 @@ private function initializeEnvironment() 'perJobThreadCnt' => 'auto', 'concurrentJobCnt' => '1', 'OMP_DYNAMIC' => '1', - 'timeOut' => '10000', + 'timeOut' => '100000', 'exportFormat' => '', 'gpuDevice' => '0', 'listID' => 'setEnv'); @@ -442,6 +454,7 @@ private function initializeImgProcessing() $this->imgProcessTasksArray = array('open' => 'imgOpen', 'setParameters' => 'setp', + 'hotPixelCorrection' => 'hotPix', 'autocrop' => 'autocrop', 'adjustBaseline' => 'adjbl', 'ZStabilization' => 'stabilize', @@ -538,6 +551,13 @@ private function initializeImgProcessing() 'spimDir' => 'parState,spimDir', 'listID' => 'setp'); + /* Options for the 'hot pixel correction' action */ + $this->hpcArray = + array('hotPath' => '', + 'timeOut' => '10000', + 'listID' => 'hotPix'); + + /* Options for the 'adjust baseline' action */ $this->adjblArray = array('enabled' => '0', @@ -571,7 +591,9 @@ private function initializeImgProcessing() 'it' => '', 'bgMode' => '', 'bg' => '', - 'sn' => '', + 'snr' => '', + 'acuity' => '', + 'acuityMode' => '', 'blMode' => 'auto', 'pad' => 'auto', 'reduceMode' => 'auto', @@ -837,10 +859,14 @@ private function getImgProcessTaskList() $list = ""; foreach ($this->imgProcessTasksArray as $key => $value) { + if ($key == 'hotPixelCorrection' && !$this->hotPixelMaskExists()) { + continue; + } switch ($key) { case 'open': case 'save': case 'setParameters': + case 'hotPixelCorrection': case 'autocrop': case 'adjustBaseline': case 'ZStabilization': @@ -890,6 +916,10 @@ private function getImgProcessTasksDescr() $this->initializeThumbCounter(); foreach ($this->imgProcessTasksArray as $key => $value) { + if ($key == 'hotPixelCorrection' && !$this->hotPixelMaskExists()) { + continue; + } + $tasksDescr .= " "; switch ($key) { case 'open': @@ -901,6 +931,9 @@ private function getImgProcessTasksDescr() case 'setParameters': $tasksDescr .= $this->getImgTaskDescrSetp(); break; + case 'hotPixelCorrection': + $tasksDescr .= $this->getImgTaskDescrHotPixelCorrection(); + break; case 'autocrop': $tasksDescr .= $this->getImgTaskDescrAutocrop(); break; @@ -1053,6 +1086,43 @@ private function getImgTaskDescrSetp() return $this->setpList; } + /** + * Gets options for the 'hot pixel correction' task. + * @return string Tcl list with the 'Hot Pixel Correction' task and its options. + */ + private function getImgTaskDescrHotPixelCorrection() + { + $taskDescr = ""; + + /* The HPC mask may be located in a different subfolder than the raw + data. Thus, its path must be found independently of the raw images. */ + $userFileArea = $this->jobDescription->sourceFolder(); + $deconSetting = $this->deconSetting; + + foreach ($this->hpcArray as $key => $value) { + if ($key != "listID") { + $taskDescr .= " " . $key . " "; + } + + switch ($key) { + case 'hotPath': + $hpcFile = $deconSetting->parameter("HotPixelCorrection")->value(); + $hpcPath = trim($userFileArea . $hpcFile[0]); + $taskDescr .= $this->string2tcllist($hpcPath); + break; + case 'timeOut': + $taskDescr .= $value; + case 'listID': + $this->hpcList = $value . " " . $this->string2tcllist($taskDescr); + break; + default: + Log::error("Hot pixel correction field $key not yet implemented."); + } + } + + return $this->hpcList; + } + /** * Gets options for the 'adjust baseline' task. * @return string Tcl list with the 'Adjust baseline' task and its options. @@ -1098,13 +1168,14 @@ private function getImgTaskDescrChromatic() if (empty($channelsArray)) { return $allTasksDescr; } - - $chromaticParam = $this->deconSetting->parameter("ChromaticAberration"); + foreach ($channelsArray as $chanKey => $chan) { $taskDescr = ""; - /** @var ChromaticAberration $chromaticParam */ - $chanVector = implode(' ', $chromaticParam->chanValue($chan)); - + /** @var ChromaticAberrationCh$chan $chromaticParam */ + $chromaticParam = + $this->deconSetting->parameter("ChromaticAberrationCh" . $chan); + $chanVector = implode(' ', $chromaticParam->value()); + foreach ($this->chromaticArray as $chromKey => $chromValue) { if ($chromKey != "listID") { $taskDescr .= " " . $chromKey . " "; @@ -1128,7 +1199,7 @@ private function getImgTaskDescrChromatic() $taskDescr .= $this->string2tcllist($chanVector); break; case 'reference': - if ($chanVector == "0 0 0 0 1") { + if (trim($chanVector) == "0 0 0 0 1") { $reference = 1; } else { $reference = 0; @@ -1895,9 +1966,11 @@ private function getTaskDescrAlgorithm($channel) switch ($key) { case 'timeOut': case 'pad': - case 'blMode': $taskDescr .= $value; break; + case 'blMode': + $taskDescr .= $this->getBleachingMode(); + break; case 'q': $taskDescr .= $this->getQualityFactor(); break; @@ -1916,9 +1989,15 @@ private function getTaskDescrAlgorithm($channel) case 'bg': $taskDescr .= $this->getBgValue($channel); break; - case 'sn': + case 'snr': $taskDescr .= $this->getSnrValue($channel); break; + case 'acuity': + $taskDescr .= $this->getAcuityValue($channel); + break; + case 'acuityMode': + $taskDescr .= $this->getAcuityMode(); + break; case 'psfMode': $taskDescr .= $this->getPsfMode(); break; @@ -2083,6 +2162,20 @@ private function getBgValue($channel) } } + /** + * Gets the bleaching mode. + * @return string Bleaching mode. + */ + private function getBleachingMode() + { + /** @var TaskSetting $deconSetting */ + $deconSetting = $this->deconSetting; + + $blMode = $deconSetting->parameter("BleachingMode")->value(); + + return $blMode; + } + /** * Gets the SNR value. One channel. * @param int $channel A channel @@ -2095,15 +2188,40 @@ private function getSnrValue($channel) $snrRate = $deconSetting->parameter("SignalNoiseRatio")->value(); $snrValue = $snrRate[$channel]; - if ($this->getAlgorithm($channel) == "qmle") { - $indexValues = array(1, 2, 3, 4, 5); - $snrArray = array("low", "fair", "good", "inf", "auto"); - $snrValue = str_replace($indexValues, $snrArray, $snrValue); - } - return $snrValue; } + /** + * Gets the acuity value. One channel. + * @param int $channel A channel + * @return int|string The acuity value. + */ + private function getAcuityValue($channel) + { + /** @var TaskSetting $deconSetting */ + $deconSetting = $this->deconSetting; + $acuityRate = $deconSetting->parameter("Acuity")->value(); + $acuityValue = $acuityRate[$channel]; + + if ($acuityValue == "") { + $acuityValue = 0; + } + + return $acuityValue; + } + + /** + * Gets the acuity mode. + * @return string Acuity mode. + */ + private function getAcuityMode() + { + $deconSetting = $this->deconSetting; + $acuityMode = $deconSetting->parameter("AcuityMode")->value(); + + return $acuityMode; + } + /** * Gets the PSF mode. * @return string PSF mode. @@ -2206,11 +2324,12 @@ private function getChansForChromaticCorrection() return $channelsArray; } - /** @var ChromaticAberration $chromaticParam */ - $chromaticParam = $this->deconSetting->parameter("ChromaticAberration"); - + for ($chan = 0; $chan < $chanCnt; $chan++) { - $chromaticChan = $chromaticParam->chanValue($chan); + /** @var ChromaticAberrationCh$chan $chromaticParamCh$chan */ + $chromaticParam = + $this->deconSetting->parameter("ChromaticAberrationCh" . $chan); + $chromaticChan = $chromaticParam->value(); foreach ($chromaticChan as $component => $value) { if (isset($value) && $value > 0) { @@ -3030,6 +3149,24 @@ private function getNumberColocRuns() return $colocRuns; } + /** + * Checks whether a hot pixel correction mask exists. + * @param void + * @return bool true if it exists false otherwise. + */ + private function hotPixelMaskExists() + { + $userFileArea = $this->jobDescription->sourceFolder(); + $deconSetting = $this->deconSetting; + $hpcFile = $deconSetting->parameter("HotPixelCorrection")->value(); + + if ($hpcFile[0] == "") { + return false; + } else { + return true; + } + } + /** * Checks whether a JPEG can be made from the image. * @param string $image The image to be checked. @@ -3170,7 +3307,7 @@ private function setSrcImage() /*If a (string) comes after the file name, the string is interpreted as a subimage. Currently this is for LIF, LOF and CZI files only. */ - if (preg_match("/^(.*\.(lif|czi|lof))\s\((.*)\)/i", + if (preg_match("/^(.*\.(lif|czi|lof|nd))\s\((.*)\)/i", $this->srcImage, $match)) { $this->srcImage = $match[1]; $this->subImage = $match[3]; // @todo Is this correct? diff --git a/inc/HuygensTools.php b/inc/HuygensTools.php index fa905ff20..5d2acf6e4 100644 --- a/inc/HuygensTools.php +++ b/inc/HuygensTools.php @@ -45,7 +45,7 @@ public static function huCoreTools($tool, $options, $server="", $hucorePath="") if (!isset($local_huygens_core)) { Log::error("Huygens tools can only work if you define a variable " . "'local_huygens_core' in the configuration files pointing to a local " . - "hucore. Administrator: see hrm_client_config.inc.sample."); + "hucore. Administrator: see hrm_config.inc.sample."); return null; } $hucorePathToUse = $local_huygens_core; diff --git a/inc/Log.php b/inc/Log.php index bfaf47261..69ff463c9 100644 --- a/inc/Log.php +++ b/inc/Log.php @@ -14,8 +14,6 @@ use Monolog\Logger; use Monolog\Handler\StreamHandler; -require_once dirname(__FILE__) . '/bootstrap.php'; - /** * Singleton class that sets up the logging facility (using Monolog) once. * @@ -63,6 +61,9 @@ private function __construct() case 2: $level = Logger::INFO; break; + case 3: + $level = Logger::DEBUG; + break; default: $level = Logger::WARNING; break; @@ -96,6 +97,18 @@ private static function getMonoLogger() return self::$monologger; } + /** + * Log debug message. + * @param string $message Debug message. + */ + public static function debug($message) + { + if (is_array($message)) { + $message = implode(", ", $message); + } + self::getMonoLogger()->addDebug($message); + } + /** * Log info message. * @param string $message Info message. diff --git a/inc/Mail.php b/inc/Mail.php index 5dc20c3d4..94e18360c 100644 --- a/inc/Mail.php +++ b/inc/Mail.php @@ -10,8 +10,6 @@ namespace hrm; -require_once dirname(__FILE__) . '/bootstrap.php'; - /** * Commodity class for sending e-mails. * diff --git a/inc/Nav.php b/inc/Nav.php index bf509e129..eb8ca3105 100644 --- a/inc/Nav.php +++ b/inc/Nav.php @@ -10,8 +10,6 @@ namespace hrm; -require_once dirname(__FILE__) . '/bootstrap.php'; - /** * Commodity class to manage all links and actions of the navigation bar. * @@ -405,7 +403,6 @@ public static function exitToLogin($wrapInLiElement = true) public static function actionStyleToggle() { $html = <<
@@ -413,7 +410,6 @@ public static function actionStyleToggle()
- EOT; return $html; } diff --git a/inc/OmeroConnection.php b/inc/OmeroConnection.php index 8ed087525..f0a76fb0e 100644 --- a/inc/OmeroConnection.php +++ b/inc/OmeroConnection.php @@ -1,4 +1,5 @@ omelog("No OMERO user name provided, cannot login.", 2); + $this->omelog("No OMERO user name provided, cannot login.", 0); return; } if (empty($omeroPass)) { - $this->omelog("No OMERO password provided, cannot login.", 2); + $this->omelog("No OMERO password provided, cannot login.", 0); return; } @@ -78,13 +78,23 @@ public function __construct($omeroUser, $omeroPass) if ($this->loggedIn == TRUE) { $this->omelog("Successfully connected to OMERO!", 2); } else { - $this->omelog("ERROR connecting to OMERO!", 2); + $this->omelog("ERROR connecting to OMERO!", 0); } } /* -------------------- General OMERO processes -------------------- */ + public static function getConnectorVersion() + { + $cmd = OmeroConnection::$omeroWrapper . " --version"; + $out = array(); // reset variable, otherwise `exec` will append to it + exec($cmd, $out, $retval); + + return join(" ", $out); + } + + /** * Check to authenticate to OMERO using given credentials. * @@ -95,20 +105,14 @@ public function __construct($omeroUser, $omeroPass) */ private function checkOmeroCredentials() { - $this->omelog("attempting to log on to OMERO.", 2); - $cmd = $this->buildCmd("checkCredentials"); + list($retval, $out) = $this->callConnector("checkCredentials"); - /* Authenticate against the OMERO server. */ - exec($cmd, $out, $retval); - - /* $retval is zero in case of success */ if ($retval != 0) { $this->loggedIn = FALSE; - $this->omelog("ERROR: checkCredentials(): " . implode(' ', $out), 1); return; - } else { - $this->loggedIn = TRUE; } + + $this->loggedIn = TRUE; } /** @@ -124,24 +128,20 @@ public function downloadFromOMERO($images, Fileserver $fileServer) $done = ""; foreach ($selected as $img) { $fileAndPath = $fileServer->sourceFolder() . "/" . $img['name']; - $param = array("--imageid", $img{'id'}, "--dest", $fileAndPath); - $cmd = $this->buildCmd("OMEROtoHRM", $param); - - $this->omelog('requesting ' . $img['id'] . ' to ' . $fileAndPath); - // somehow exec() seems to append to $out instead of overwriting - // it, so we create an empty array for it explicitly: - $out = array(); - exec($cmd, $out, $retval); - $this->omelog(implode(' ', $out)); + $this->omelog('requesting [' . $img['id'] . '] to [' . $fileAndPath . ']', 2); + + $param = array("--imageid", $img['id'], "--dest", $fileAndPath); + list($retval, $out) = $this->callConnector("OMEROtoHRM", $param); + if ($retval != 0) { - $this->omelog("failed retrieving " . $img['id'], 1); - $this->omelog("ERROR: downloadFromOMERO(): " . implode(' ', $out), 2); + $this->omelog("failed retrieving [" . $img['id'] . "] from OMERO", 0); $fail .= "
" . $img['id'] . "    "; $fail .= "[" . implode(' ', $out) . "]
"; - } else { - $this->omelog("successfully retrieved " . $img['id'], 1); - $done .= "
" . implode('
', $out) . "
"; + continue; } + + $this->omelog("success retrieving [" . $img['id'] . "] from OMERO", 2); + $done .= "
" . implode('
', $out) . "
"; } // build the return message: $msg = ""; @@ -171,13 +171,13 @@ public function uploadToOMERO(array $postedParams, Fileserver $fileServer) if (sizeof($selectedFiles) < 1) { $msg = "No files selected for upload."; - $this->omelog($msg); + $this->omelog($msg, 0); return $msg; } if (!isset($postedParams['OmeDatasetId'])) { $msg = "No destination dataset selected."; - $this->omelog($msg); + $this->omelog($msg, 0); return $msg; } @@ -189,23 +189,21 @@ public function uploadToOMERO(array $postedParams, Fileserver $fileServer) foreach ($selectedFiles as $file) { // TODO: check if $file may contain relative paths! $fileAndPath = $fileServer->destinationFolder() . "/" . $file; + $this->omelog('uploading [' . $fileAndPath . '] to dataset ' . $datasetId, 2); + $param = array("--file", $fileAndPath, "--dset", $datasetId); - $cmd = $this->buildCmd("HRMtoOMERO", $param); + list($retval, $out) = $this->callConnector("HRMtoOMERO", $param); - $this->omelog('uploading "' . $fileAndPath . '" to dataset ' . $datasetId); - // somehow exec() seems to append to $out instead of overwriting - // it, so we create an empty array for it explicitly: - $out = array(); - exec($cmd, $out, $retval); if ($retval != 0) { - $this->omelog("failed uploading file to OMERO: " . $file, 1); - $this->omelog("ERROR: uploadToOMERO(): " . implode(' ', $out), 2); + $this->omelog("failed uploading file to OMERO: " . $file, 0); + $this->omelog("ERROR: uploadToOMERO(): " . implode(' ', $out), 0); $fail .= "
" . $file . "    "; $fail .= "[" . implode(' ', $out) . "]
"; - } else { - $this->omelog("success uploading file to OMERO: " . $file, 2); - $done .= "
" . $file; + continue; } + + $this->omelog("success uploading [" . $file . "] to OMERO.", 2); + $done .= "
" . $file; } // reload the OMERO tree: $this->resetNodes(); @@ -225,7 +223,7 @@ public function uploadToOMERO(array $postedParams, Fileserver $fileServer) } - /* ---------------------- Command builder --------------------------- */ + /* ------------- Command builder and call wrapper ------------------- */ /** * Generic command builder for the OMERO connector. @@ -248,24 +246,59 @@ private function buildCmd($command, array $parameters = array()) } // build a temporary array with the command elements, starting with the // connector/wrapper itself: - $tmp = array($this->omeroWrapper); + $tmp = array(OmeroConnection::$omeroWrapper); //// $tmp = array("/usr/bin/python"); - //// array_push($tmp, $this->omeroWrapper); + //// array_push($tmp, OmeroConnection::$omeroWrapper); // user/password must be given first: array_push($tmp, "--user", escapeshellarg($this->omeroUser)); - array_push($tmp, "--password", escapeshellarg($this->omeroPass)); // next the *actual* command: array_push($tmp, escapeshellarg($command)); // and finally the parameters (if any): $tmp = array_merge($tmp, $parameters); // now we can assemble the full command string: $cmd = join(" ", $tmp); - // and and intermediate one for logging w/o password: - $tmp[4] = "[********]"; - $this->omelog("> " . join(" ", $tmp), 1); + $this->omelog("> " . join(" ", $tmp), 2); return $cmd; } + /** + * Call the OMERO connector with a given request and evaluate its return status. + * + * This function is preparing the call to the connector by assembling the command + * itself, then setting the `OMERO_PASSWORD` environment variable for the call (to + * be used for authenticating to OMERO) and then running the connector executable. + * + * In case the connector call returns a non-zero status, an error message is logged + * with the output returned by the connector. + * + * Eventually, the return status of the connector call and its output are returned + * as an array. + * + * @param string $request The command to request from the connector executable. + * @param array $parameters (optional) An array of additional parameters. + * required by the wrapper to run the requested command. + * @return array An array with the first item being the return value of the + * connector call and the second item being an array with the output. + */ + private function callConnector($request, array $parameters = array()) + { + $this->omelog("calling OMERO connector with request '{$request}'...", 2); + $cmd = $this->buildCmd($request, $parameters); + putenv("OMERO_PASSWORD=" . $this->omeroPass); + $out = array(); // reset variable, otherwise `exec` will append to it + exec($cmd, $out, $retval); + + /* uncomment the line below to always log the output with severity "info" */ + // $this->omelog(implode(' ', $out), 2); + + /* $retval is zero in case of success */ + if ($retval != 0) { + $this->omelog("ERROR: <{$request}> " . implode(' ', $out), 0); + } + + return array($retval, $out); + } + /* ---------------------- OMERO Tree Assemblers ------------------- */ @@ -278,14 +311,12 @@ public function getChildren($id) { if (!isset($this->nodeChildren[$id])) { $param = array('--id', $id); - $cmd = $this->buildCmd("retrieveChildren", $param); - exec($cmd, $out, $retval); + list($retval, $out) = $this->callConnector("retrieveChildren", $param); if ($retval != 0) { - $this->omelog("ERROR: getChildren(): " . implode(' ', $out), 1); return FALSE; - } else { - $this->nodeChildren[$id] = implode(' ', $out); } + + $this->nodeChildren[$id] = implode(' ', $out); } return $this->nodeChildren[$id]; } @@ -304,26 +335,26 @@ public function resetNodes() /** * Simple wrapper function to unify log messages from this module. * @param string $text Text to be logged - * @param int $level Severity level. + * @param int $level Severity level, 0=error, 1=warning (default), 2=info. * @todo Is this wrapper necessary? */ private function omelog($text, $level = 0) { + $message = "[HRM-OMERO] " . $text; // @todo Use class Log switch ($level) { case 0: - Log::error("OMERO connector: " . $text); + Log::error($message); break; case 1: - Log::warning("OMERO connector: " . $text); + Log::warning($message); break; case 2: - Log::info("OMERO connector: " . $text); + Log::info($message); break; default: - Log::warning("OMERO connector: " . $text); + Log::warning($message); break; } } - } diff --git a/inc/QueueManager.php b/inc/QueueManager.php index cd1fabea5..26f0dabf7 100644 --- a/inc/QueueManager.php +++ b/inc/QueueManager.php @@ -217,6 +217,12 @@ public function copyImagesToServer(Job $job, $server_hostname) $psf = $parameterSetting->parameter('PSF'); $values = $psf->value(); foreach ($values as $value) { + + /* This loop goes over the max number of channels, even if + they are empty. To prevent sftp errors those channels have to + be skipped. */ + if ($value == "") continue; + $path = explode("/", $value); if (sizeof($path) > 0) { for ($i = 0; $i < sizeof($path) - 1; $i++) { @@ -254,7 +260,7 @@ public function copyImagesToServer(Job $job, $server_hostname) foreach ($files as $file) { $counter++; $match = array(); - if (preg_match("/^(.*\.(lif|lof|czi))\s\((.*)\)/i", $file, $match)) { + if (preg_match("/^(.*\.(lif|lof|czi|nd))\s\((.*)\)/i", $file, $match)) { $filteredFiles[$counter] = $match[1]; } else { $filteredFiles[$counter] = $file; @@ -275,7 +281,19 @@ public function copyImagesToServer(Job $job, $server_hostname) $image_source . "/" . $file; if (stristr($filename, ".ics")) { $batch .= "put \"" . $filename . "\"\n"; - $filename = substr($filename, 0, strrpos($filename, '.ics')) . ".ids"; + + // Get the 'c' from 'ics' in order to find its case. + $nameLength = strlen($filename); + $extensionChar = $filename[$nameLength - 2]; + + if (ctype_upper($extensionChar)) { + $extensionChar = "D"; + } else { + $extensionChar = "d"; + } + + $filename[$nameLength - 2] = $extensionChar; + $batch .= "put \"" . $filename . "\"\n"; } elseif (stristr($filename, ".tif") || stristr($filename, ".tiff")) { // TODO: if ImageFileFormat = single TIFF file, do not send @@ -577,13 +595,17 @@ public function cleanUpRemoteServer($job) $remotePath = $huygens_server_image_folder . "/"; $remotePath .= $jobOwner->name() . "/" . $image_source . "/"; + $extension = $fileServer->getFileNameExtension($srcFile); + $srcFile = $fileServer->basename($srcFile); + $srcFile = str_replace($srcFile, $extension, ""); + // If we are dealing with a series, remove all of its files. if ($fileSeries) { - $extension = $fileServer->getFileNameExtension($srcFile); - $srcFile = $fileServer->basename($srcFile); - $srcFile = str_replace($srcFile, $extension, ""); - $srcFile .= "*" . $extension; + $srcFile .= "*" . $extension; + } elseif ($extension == "ics") { + $srcFile .= ".i*s"; } + $remoteFiles = $srcFile; $cmd .= "find " . $remotePath . " -name '" . $remoteFiles . "' -delete;"; diff --git a/inc/System.php b/inc/System.php index 44325992c..c01a6439a 100644 --- a/inc/System.php +++ b/inc/System.php @@ -1,4 +1,5 @@ query( - "SELECT * FROM global_variables WHERE name LIKE 'dbrevision';"); + $rows = $db->query("SELECT * FROM global_variables WHERE name LIKE 'dbrevision';"); if (!$rows) { return 0; } else { @@ -240,11 +239,10 @@ public static function getMinHuCoreVersionAsString() */ public static function getMinHuCoreVersionAsInteger() { - $v = self::MIN_HUCORE_VERSION_MAJOR * 1000000 + + return self::MIN_HUCORE_VERSION_MAJOR * 1000000 + self::MIN_HUCORE_VERSION_MINOR * 10000 + self::MIN_HUCORE_VERSION_MAINTENANCE * 100 + self::MIN_HUCORE_VERSION_PATCH; - return $v; } /** @@ -314,7 +312,7 @@ public static function getHucoreVersionAsString($version = -1) */ public static function getAllLicenses() { - $allLicenses = array( + return array( "microscopes" => array( "confocal" => "Confocal", "multi-photon" => "Multi-photon", @@ -360,8 +358,6 @@ public static function getAllLicenses() "visu" => "Visualization" ) ); - - return $allLicenses; } /** @@ -372,8 +368,7 @@ public static function getAllLicenses() public static function getActiveLicenses() { $db = DatabaseConnection::get(); - $activeLicenses = $db->getActiveLicenses(); - return $activeLicenses; + return $db->getActiveLicenses(); } /** @@ -462,6 +457,14 @@ public static function getKernelRelease() return ($r . " (Yosemite)"); case '15': return ($r . " (El Capitan)"); + case '16': + return ($r . " (Sierra)"); + case '17': + return ($r . " (High Sierra)"); + case '18': + return ($r . " (Mojave)"); + case '19': + return ($r . " (Catalina)"); default: return ($r); } @@ -479,9 +482,23 @@ public static function getKernelRelease() */ public static function getApacheVersion() { - $apver = ""; - if (preg_match('|Apache\/(\d+)\.(\d+)\.(\d+)|', apache_get_version(), $apver)) { - return "${apver[1]}.${apver[2]}.${apver[3]}"; + // Query the Apache version string. + // + // Depending on how PHP and Apache are installed, the `apache_get_version()` + // function may not exist. In that case, we will assume that getApacheVersion() + // is triggered via the browser and hence the $_SERVER variable will be defined. + $version = ""; + if (function_exists('apache_get_version')) { + $version = apache_get_version(); + } else { + if (isset($_SERVER['SERVER_SOFTWARE']) && strlen($_SERVER['SERVER_SOFTWARE']) > 0) { + $version = $_SERVER["SERVER_SOFTWARE"]; + } + } + + // Extract the version number + if (preg_match('|Apache/(\d+)\.(\d+)\.(\d+)|', $version, $apver)) { + return "${apver[1]}.${apver[2]}.${apver[3]}"; } else { return "Unknown"; } @@ -506,7 +523,12 @@ public static function getDatabaseVersion() { $dbver = ""; $db = DatabaseConnection::get(); - if (preg_match('|(\d+)\.(\d+)\.(\d+)|', $db->version(), $dbver)) { + if (preg_match( + '|(\d+)\.(\d+)\.(\d+)|', + $db->version(), + $dbver + ) + ) { return "${dbver[1]}.${dbver[2]}.${dbver[3]}"; } else { return "Unknown"; @@ -531,7 +553,9 @@ public static function getPHPVersion() public static function getMemoryLimit($unit = 'M') { return System::formatMemoryStringByUnit( - UtilV2::let_to_num(ini_get('memory_limit')), $unit); + UtilV2::let_to_num(ini_get('memory_limit')), + $unit + ); } /** @@ -544,7 +568,9 @@ public static function getMemoryLimit($unit = 'M') public static function getPostMaxSizeFromIni($unit = 'M') { return System::formatMemoryStringByUnit( - UtilV2::let_to_num(ini_get('post_max_size')), $unit); + UtilV2::let_to_num(ini_get('post_max_size')), + $unit + ); } /** @@ -561,7 +587,9 @@ public static function getPostMaxSizeFromConfig($unit = 'M') return "limited by php.ini."; } else { return System::formatMemoryStringByUnit( - UtilV2::let_to_num(ini_get('$max_post_limit')), $unit); + UtilV2::let_to_num(ini_get('$max_post_limit')), + $unit + ); } } else { return "Not defined!"; @@ -648,7 +676,9 @@ public static function isUploadEnabled() public static function isUploadMaxFileSizeFromIni($unit = 'M') { return System::formatMemoryStringByUnit( - UtilV2::let_to_num(ini_get('upload_max_filesize')), $unit); + UtilV2::let_to_num(ini_get('upload_max_filesize')), + $unit + ); } /** @@ -665,7 +695,9 @@ public static function isUploadMaxFileSizeFromConfig($unit = 'M') return "limited by php.ini."; } else { return System::formatMemoryStringByUnit( - UtilV2::let_to_num($max_upload_limit), $unit); + UtilV2::let_to_num($max_upload_limit), + $unit + ); } } else { return "Not defined!"; @@ -705,15 +737,15 @@ public static function getMaxExecutionTimeFromIni() * Gigabytes. Default is 'M'. Omit the parameter to use the default. * @return string Memory amount in the requested unit. */ - private function formatMemoryStringByUnit($value, $unit = 'M') + private static function formatMemoryStringByUnit($value, $unit = 'M') { switch ($unit) { - case 'G' : + case 'G': $factor = 1024 * 1024 * 1024; $digits = 3; $unit_string = "GB"; break; - case 'B' : + case 'B': $factor = 1; $digits = 0; $unit_string = " bytes"; @@ -770,5 +802,4 @@ private static function parseHucoreVersionIntegerToString($version) } return $versionString; } - } diff --git a/inc/Util.php b/inc/Util.php index a8085d83c..4be2e9f5b 100644 --- a/inc/Util.php +++ b/inc/Util.php @@ -9,8 +9,6 @@ */ namespace hrm; -require_once dirname(__FILE__) . '/bootstrap.php'; - /** * Static class with some commodity functionality. */ diff --git a/inc/UtilV2.php b/inc/UtilV2.php index 246f5258a..f5ed064d2 100644 --- a/inc/UtilV2.php +++ b/inc/UtilV2.php @@ -9,8 +9,6 @@ */ namespace hrm; -require_once dirname(__FILE__) . '/bootstrap.php'; - /** * Static class with some commodity functionality (version 2). */ diff --git a/inc/Validator.php b/inc/Validator.php index 846cb9f30..2d9b1e811 100644 --- a/inc/Validator.php +++ b/inc/Validator.php @@ -10,8 +10,6 @@ namespace hrm; -require_once dirname(__FILE__) . '/bootstrap.php'; - /** * Validates and in very rare cases sanitizes relevant user input. * diff --git a/inc/hrm_config.inc.php b/inc/hrm_config.inc.php index a43a112e9..7d2483080 100644 --- a/inc/hrm_config.inc.php +++ b/inc/hrm_config.inc.php @@ -15,15 +15,7 @@ // //////////////////////////////////////////////////////////////////////////////// -if (!isset($isServer)) { - $isServer = false; -} - -if ($isServer == true) { - require_once dirname(__FILE__) . '/../config/hrm_server_config.inc'; -} else { - require_once dirname(__FILE__) . '/../config/hrm_client_config.inc'; -} +require_once dirname(__FILE__) . '/../config/hrm_config.inc'; // This is a hidden parameter for advanced users if (!isset($userManagerScript)) { diff --git a/inc/job/Job.php b/inc/job/Job.php index a3495b78f..250dcc2f1 100644 --- a/inc/job/Job.php +++ b/inc/job/Job.php @@ -17,8 +17,6 @@ use hrm\HuygensTemplate; use hrm\Log; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * Stores all information for a deconvolution Job. @@ -169,6 +167,7 @@ private function initialize(JobDescription $jobDescription) 'estimation' => 'Background estimation', 'ratio' => 'Signal/Noise ratio', 'array detector reduction mode' => 'Array detector reduction mode', + 'bleaching' => 'Bleaching Mode', 'autocrop' => 'Autocrop', 'z stabilization' => 'Z Stabilization', 't stabilization' => 'T Stabilization'); diff --git a/inc/job/JobDescription.php b/inc/job/JobDescription.php index 750264a64..49e078154 100644 --- a/inc/job/JobDescription.php +++ b/inc/job/JobDescription.php @@ -376,7 +376,7 @@ public function addJob() $result &= $db->addFileToJob($id, $this->owner, $file, $this->autoseries); // Now add a Job to the queue for this file - $result &= $db->queueJob($id, $settingsId, $ownerName); + $result &= ($db->queueJob($id, $settingsId, $ownerName) !== false); } // Assign priorities @@ -506,7 +506,7 @@ public function sourceImageShortName() //$parameterSetting = $this->parameterSetting; //$parameter = $parameterSetting->parameter('ImageFileFormat'); //$fileFormat = $parameter->value(); - if (preg_match("/^(.*)\.(lif|lof|czi)\s\((.*)\)/i", $inputFile[0], $match)) { + if (preg_match("/^(.*)\.(lif|lof|czi|nd)\s\((.*)\)/i", $inputFile[0], $match)) { $inputFile = $match[1] . '_' . $match[2]; } else { $inputFile = substr(end($inputFile), 0, strrpos(end($inputFile), ".")); diff --git a/inc/job/JobQueue.php b/inc/job/JobQueue.php index e783e4ac2..4ba99eceb 100644 --- a/inc/job/JobQueue.php +++ b/inc/job/JobQueue.php @@ -15,9 +15,6 @@ use hrm\Log; use hrm\shell\ExternalProcessFactory; -require_once dirname(__FILE__) . '/../bootstrap.php'; - - /** * Manages the queue of deconvolution Jobs. * diff --git a/inc/param/Acuity.php b/inc/param/Acuity.php new file mode 100644 index 000000000..04771c67b --- /dev/null +++ b/inc/param/Acuity.php @@ -0,0 +1,67 @@ +message = "Acuity: " . $this->message; + } + return $result; + } + + /** + * Returns the string representation of the Acuity parameter. + * @param int $numberOfChannels Number of channels. + * @return string String representation of the Acuity Parameter. + */ + public function displayString($numberOfChannels = 0) + { + + $value = array_slice($this->value, 0, $numberOfChannels); + $value = implode($value, ", "); + $result = $this->formattedName() . $value . "\n"; + + return $result; + } +} diff --git a/inc/param/AcuityMode.php b/inc/param/AcuityMode.php new file mode 100644 index 000000000..cd2822ffd --- /dev/null +++ b/inc/param/AcuityMode.php @@ -0,0 +1,43 @@ +formattedName(); + $result = $result . $this->value() . "\n"; + return $result; + } +} diff --git a/inc/param/Binning.php b/inc/param/Binning.php index 3d1d1e33b..f3d08f0f3 100644 --- a/inc/param/Binning.php +++ b/inc/param/Binning.php @@ -11,8 +11,6 @@ use hrm\param\base\ChoiceParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A ChoiceParameter to represent the binning. * diff --git a/inc/param/BleachingMode.php b/inc/param/BleachingMode.php new file mode 100644 index 000000000..a6cd810d8 --- /dev/null +++ b/inc/param/BleachingMode.php @@ -0,0 +1,41 @@ +formattedName(); + $result = $result . $this->value . "\n"; + return $result; + } +} diff --git a/inc/param/ChromaticAberration.php b/inc/param/ChromaticAberration.php index 2610518ad..d36dc88dd 100644 --- a/inc/param/ChromaticAberration.php +++ b/inc/param/ChromaticAberration.php @@ -14,11 +14,8 @@ use hrm\param\base\Parameter; /** - * A multi-channel, vector parameter to characterize the chromatic aberration. - * - * @todo This did not inherit from any base class. Now it inherits from Parameter. - * Make sure that it still works as expected! - * + * A single-channel, vector parameter to characterize the chromatic aberration. + * Has one instance per channel. * * @package hrm */ @@ -32,13 +29,22 @@ class ChromaticAberration extends Parameter public $value; /** - * The number of channels for which a vector is needed. + * The channel this vector describes. + * @var int + */ + public $channel; + + + /** + * The maximum number of vector components used to describe the CA. * @var int */ - public $chanCnt; + public $maxComponentCnt; + /** - * The numer of vector components used to describe the CA. Currently 5. + * The numer of vector components used to describe the CA. Currently 5 or + * 14. * @var int */ public $componentCnt; @@ -48,27 +54,35 @@ class ChromaticAberration extends Parameter * * This method does NOT call the parent constructor! * - * @todo: Provide an input argument $chanCnt with the number of channels of - * the data set. */ - public function __construct() + public function __construct($ch) { - $this->name = "ChromaticAberration"; - - /* 5 components for shift x, y, z, rotation and scale. */ + $this->name = "ChromaticAberrationCh" . $ch; + $this->channel = $ch; + + /* 14 components for shift x, y, z, rotation, scaleX, scaleY, scaleZ, + angleX, angleY, barrelPincushion1, barrelPincushion2, + barrelPincushion3, barrelPincushionXcenter, + barrelPincushionYcenter. Return either the first 5 or all 14 + parameters, depending on the input. Default to componentCnt 5.*/ + $this->maxComponentCnt = 14; $this->componentCnt = 5; - $db = DatabaseConnection::get(); - $this->chanCnt = $db->getMaxChanCnt(); - - // Add a NumericalVectorParameter per channel - for ($chan = 0; $chan < $this->chanCnt; $chan++) { - $this->value[$chan] = new NumericalVectorParameter( - $this->name() . "Ch" . $chan, $this->componentCnt()); - } + // Add a NumericalVectorParameter per channel with maxComponentCnt size. + $this->value = new NumericalVectorParameter( + $this->name(), $this->maxComponentCnt()); } + /** + * A function for retrieving the maximum number of elements of the vector. + * @return int The number of vector elements. + */ + public function maxComponentCnt() + { + return $this->maxComponentCnt; + } + /** * A function for retrieving the number of elements of the vector. * @return int The number of vector elements. @@ -78,6 +92,15 @@ public function componentCnt() return $this->componentCnt; } + /** + * A function for retrieving the number of elements to show of the vector. + * @return int The number of vector elements. + */ + public function shownComponentCnt() + { + return $this->componentCnt(); + } + /** * Checks whether the Parameter is a Task Parameter. * @return bool Always true. @@ -99,23 +122,15 @@ public function isVariableChannel() /** * The string representation of the Parameter. - * @param int $chanCnt The number of channels. * @return string String representation of the Parameter. */ public function displayString($chanCnt = 0) { - $result = ""; - - if (!is_numeric($chanCnt)) { - $db = DatabaseConnection::get(); - $chanCnt = $db->getMaxChanCnt(); - } - - for ($i = 0; $i < $chanCnt; $i++) { - $result .= $this->value[$i]->displayString(); + // Don't show anything when the channel is irrelevant. + if ($this->channel > $chanCnt) { + return ""; } - - return $result; + return rtrim($this->value->displayString(), ", \n\r\t\v\x00"). "\n"; } /** @@ -126,11 +141,12 @@ public function displayString($chanCnt = 0) */ public function setValue($values) { - + // If it comes from the database explode it into an array. if (!is_array($values)) { /* The first element of the array will be empty due to the explode. */ $valuesArray = explode('#', $values); unset($valuesArray[0]); + $valuesArray = array_values($valuesArray); } else { $valuesArray = $values; } @@ -138,12 +154,23 @@ public function setValue($values) if (empty($valuesArray) || is_null($valuesArray)) { return; } - - for ($chan = 0; $chan < $this->chanCnt; $chan++) { - $offset = $chan * $this->componentCnt; - $chanArray = array_slice($valuesArray, $offset, $this->componentCnt); - $this->value[$chan]->setValue($chanArray); + + // If all the values are 0 it is the 14 parameter reference, set it + // like the 5 parameter reference so it is properly recognized. + $isReference = 1; + foreach ($valuesArray as $val) { + if (floatval($val) != 0.0) { + $isReference = 0; + break; + } } + if ($isReference) { + $valuesArray = array("0","0","0","0","1",null,null,null, + null,null,null,null,null,null); + } + + //error_log(implode('_', $valuesArray)); + $this->value->setValue($valuesArray); } /** @@ -156,28 +183,17 @@ public function value() /* The first element of the array will be empty due to the explode. */ unset($valuesArray[0]); - + /* Re-index with array_values. */ return array_values($valuesArray); } - /** - * A function for retrieving the parameter value for a specific channel - * @param int $chan The requested channel. - * @return array An array with one component per vector element. - */ - public function chanValue($chan) - { - $valuesArray = $this->value(); - $offset = $chan * $this->componentCnt; - $chanArray = array_slice($valuesArray, $offset, $this->componentCnt); - - return $chanArray; - } - + /** * A function to set the number of channels for the correction. * @param int $chanCnt The number of channels. + * + * TODO: not necessary? */ public function setNumberOfChannels($chanCnt) { @@ -203,13 +219,7 @@ public function defaultValue() */ public function internalValue() { - $result = ""; - - for ($i = 0; $i < $this->chanCnt; $i++) { - $result .= $this->value[$i]->value(); - } - - return $result; + return $this->value->value(); } /** diff --git a/inc/param/DeconvolutionAlgorithm.php b/inc/param/DeconvolutionAlgorithm.php index 662fe507b..452f3a077 100644 --- a/inc/param/DeconvolutionAlgorithm.php +++ b/inc/param/DeconvolutionAlgorithm.php @@ -57,7 +57,7 @@ public function isTaskParameter() */ public function translatedValue() { - $db = new DatabaseConnection(); + $db = DatabaseConnection::get(); $result = $db->translationFor($this->name, $this->value); return $result; } diff --git a/inc/param/EmissionWavelength.php b/inc/param/EmissionWavelength.php index 19519036e..040cdc9e6 100644 --- a/inc/param/EmissionWavelength.php +++ b/inc/param/EmissionWavelength.php @@ -11,8 +11,6 @@ use hrm\param\base\NumericalArrayParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A NumericalArrayParameter to represent the excitation wavelength. * diff --git a/inc/param/ExcitationWavelength.php b/inc/param/ExcitationWavelength.php index b0620eeff..a96e95366 100644 --- a/inc/param/ExcitationWavelength.php +++ b/inc/param/ExcitationWavelength.php @@ -11,8 +11,6 @@ use hrm\param\base\NumericalArrayParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A NumericalArrayParameter to represent the excitation wavelength. * diff --git a/inc/param/HotPixelCorrection.php b/inc/param/HotPixelCorrection.php new file mode 100644 index 000000000..91a35d52c --- /dev/null +++ b/inc/param/HotPixelCorrection.php @@ -0,0 +1,60 @@ +formattedName("hot pixel correction - mask file"); + + if ($this->notSet()) { + $result = $result . "*not set*" . "\n"; + } else { + $result = $result . $this->value[0] . "\n"; + } + + return $result; + } + +} diff --git a/inc/param/ImageFileFormat.php b/inc/param/ImageFileFormat.php index 71d742283..831e4dc16 100644 --- a/inc/param/ImageFileFormat.php +++ b/inc/param/ImageFileFormat.php @@ -12,8 +12,6 @@ use hrm\DatabaseConnection; use hrm\param\base\SingleOrMultiChannelParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A SingleOrMultiChannelParameter to represent the image file format. * diff --git a/inc/param/IsMultiChannel.php b/inc/param/IsMultiChannel.php index 156a26f07..59efcd6b2 100644 --- a/inc/param/IsMultiChannel.php +++ b/inc/param/IsMultiChannel.php @@ -11,8 +11,6 @@ use hrm\param\base\BooleanParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A BooleanParameter that distinguishes between single- and multi-channel * images. diff --git a/inc/param/MicroscopeType.php b/inc/param/MicroscopeType.php index 626c186b7..72a6150a8 100644 --- a/inc/param/MicroscopeType.php +++ b/inc/param/MicroscopeType.php @@ -12,8 +12,6 @@ use hrm\DatabaseConnection; use hrm\param\base\ChoiceParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A ChoiceParameter to represent the microscope type. * diff --git a/inc/param/NumberOfChannels.php b/inc/param/NumberOfChannels.php index f18cfdce6..bfd095781 100644 --- a/inc/param/NumberOfChannels.php +++ b/inc/param/NumberOfChannels.php @@ -11,8 +11,6 @@ use hrm\param\base\ChoiceParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A ChoiceParameter to represent the number of channels. * diff --git a/inc/param/NumericalAperture.php b/inc/param/NumericalAperture.php index be6cc94c2..45a3f531d 100644 --- a/inc/param/NumericalAperture.php +++ b/inc/param/NumericalAperture.php @@ -11,8 +11,6 @@ use hrm\param\base\NumericalParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A NumericalParameter to represent the numerical aperture of the objective. * diff --git a/inc/param/ObjectiveMagnification.php b/inc/param/ObjectiveMagnification.php index c1840464f..12a58d948 100644 --- a/inc/param/ObjectiveMagnification.php +++ b/inc/param/ObjectiveMagnification.php @@ -11,8 +11,6 @@ use hrm\param\base\ChoiceParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A ChoiceParameter to represent the objective magnification. * diff --git a/inc/param/ObjectiveType.php b/inc/param/ObjectiveType.php index eb13f396d..f6060da66 100644 --- a/inc/param/ObjectiveType.php +++ b/inc/param/ObjectiveType.php @@ -12,8 +12,6 @@ use hrm\DatabaseConnection; use hrm\param\base\ChoiceParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A ChoiceParameter to represent the objective type. * diff --git a/inc/param/PSF.php b/inc/param/PSF.php index 087b48467..5f9f5f02c 100644 --- a/inc/param/PSF.php +++ b/inc/param/PSF.php @@ -11,8 +11,6 @@ use hrm\param\base\AnyTypeArrayParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * An AnyTypeArrayParameter that handles the file names of the PSF files per * channel. diff --git a/inc/param/PointSpreadFunction.php b/inc/param/PointSpreadFunction.php index 4ff0bce2c..7822a64c6 100644 --- a/inc/param/PointSpreadFunction.php +++ b/inc/param/PointSpreadFunction.php @@ -11,8 +11,6 @@ use hrm\param\base\ChoiceParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A ChoiceParameter to handle the type of PointSpreadFunction to be used, * theoretical or measured. diff --git a/inc/param/SampleMedium.php b/inc/param/SampleMedium.php index 0f65b5d22..6d91130c7 100644 --- a/inc/param/SampleMedium.php +++ b/inc/param/SampleMedium.php @@ -12,8 +12,6 @@ use hrm\DatabaseConnection; use hrm\param\base\ChoiceParameter; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A ChoiceParameter to represent the sample medium. * diff --git a/inc/param/SignalNoiseRatio.php b/inc/param/SignalNoiseRatio.php index 8e2e874d9..5739a403a 100644 --- a/inc/param/SignalNoiseRatio.php +++ b/inc/param/SignalNoiseRatio.php @@ -9,6 +9,7 @@ */ namespace hrm\param; +use hrm\DatabaseConnection; use hrm\param\base\NumericalArrayParameter; /** @@ -77,24 +78,23 @@ public function setAlgorithm($algorithm) */ public function displayString($numberOfChannels = 0) { - $snrQMLEArray = array("1" => "low", "2" => "fair", "3" => "good", "4" => "inf"); - $result = $this->formattedName(); - - for ($ch = 0; $ch < $numberOfChannels; $ch++) { + + if (!is_numeric($numberOfChannels)) { + $db = DatabaseConnection::get(); + $numberOfChannels = $db->getMaxChanCnt(); + } + + for ($ch = 0; $ch < $numberOfChannels; $ch++) { $snrChan = "*not set*"; switch ($this->algorithm[$ch]) { case "skip": $snrChan = "-"; - break; - case "qmle": - if (array_key_exists($this->value[$ch], $snrQMLEArray)) { - $snrChan = $snrQMLEArray[$this->value[$ch]]; - } - break; + break; + case "qmle": case "gmle": - case "cmle" : + case "cmle": default: if (isset($this->value[$ch]) && $this->value[$ch] != "") { $snrChan = $this->value[$ch]; diff --git a/inc/param/base/AnyTypeArrayParameter.php b/inc/param/base/AnyTypeArrayParameter.php index 2282ce9ca..5aea9c53a 100644 --- a/inc/param/base/AnyTypeArrayParameter.php +++ b/inc/param/base/AnyTypeArrayParameter.php @@ -11,9 +11,6 @@ use hrm\DatabaseConnection; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - - /** * Class for a Parameter that has an array of variable of any type * as possible value. diff --git a/inc/param/base/BooleanParameter.php b/inc/param/base/BooleanParameter.php index df33ced16..86fe90d22 100644 --- a/inc/param/base/BooleanParameter.php +++ b/inc/param/base/BooleanParameter.php @@ -9,8 +9,6 @@ */ namespace hrm\param\base; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * Class for a Parameter that has only true and false as possible value. * diff --git a/inc/param/base/ChoiceParameter.php b/inc/param/base/ChoiceParameter.php index 3915efec4..2be6e332d 100644 --- a/inc/param/base/ChoiceParameter.php +++ b/inc/param/base/ChoiceParameter.php @@ -11,8 +11,6 @@ use hrm\DatabaseConnection; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * The ChoiceParameter can assume a limited number of possible values. * diff --git a/inc/param/base/NumericalArrayParameter.php b/inc/param/base/NumericalArrayParameter.php index 794cda9ff..564bcf358 100644 --- a/inc/param/base/NumericalArrayParameter.php +++ b/inc/param/base/NumericalArrayParameter.php @@ -11,8 +11,6 @@ use hrm\DatabaseConnection; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * Class for a Parameter that has an array of numbers as possible value, * where each entry represents a channel. @@ -109,6 +107,7 @@ public function numberOfChannels() */ public function check() { + $this->message = ''; $result = True; // First check that all values are set @@ -141,24 +140,23 @@ public function check() * be an array, since this requires a large refactoring that will be * done in a later stage. * - * @param array $value Array of values for the NumericalArrayParameter. + * @param array $values Array of values for the NumericalArrayParameter. */ - public function setValue($value) + public function setValue($values) { $db = DatabaseConnection::get(); $maxChanCnt = $db->getMaxChanCnt(); - if (is_array($value)) { - $n = count($value); - for ($i = 0; $i < $maxChanCnt; $i++) { - if ($i < $n) { - $this->value[$i] = $value[$i]; + if (is_array($values)) { + foreach ($values as $i => $value) { + if ($i < $maxChanCnt) { + $this->value[$i] = $value; } else { $this->value[$i] = null; } } } else { - $this->value = array($value); + $this->value = array($values); } } diff --git a/inc/param/base/NumericalParameter.php b/inc/param/base/NumericalParameter.php index 351127af8..c538146b6 100644 --- a/inc/param/base/NumericalParameter.php +++ b/inc/param/base/NumericalParameter.php @@ -11,8 +11,6 @@ use hrm\DatabaseConnection; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * Class for a Parameter that has a scalar number as possible value. * diff --git a/inc/param/base/NumericalVectorParameter.php b/inc/param/base/NumericalVectorParameter.php index c1c956494..d37e59a55 100644 --- a/inc/param/base/NumericalVectorParameter.php +++ b/inc/param/base/NumericalVectorParameter.php @@ -9,8 +9,6 @@ */ namespace hrm\param\base; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * Class for a channel Parameter consisting of N components. * @@ -91,12 +89,11 @@ public function check() * The value must be an array with as many components as $componentCnt * @param array $value Array of values for the NumericalVectorParameter. */ - public function setValue($value) + public function setValue($values) { - $n = count($value); - for ($i = 0; $i < $this->componentCnt; $i++) { - if ($i < $n) { - $this->value[$i] = $value[$i]; + foreach ($values as $i => $value) { + if ($i < $this->componentCnt) { + $this->value[$i] = $value; } else { $this->value[$i] = null; } diff --git a/inc/param/base/Parameter.php b/inc/param/base/Parameter.php index 9d5520b3c..d1d1c71bc 100644 --- a/inc/param/base/Parameter.php +++ b/inc/param/base/Parameter.php @@ -12,9 +12,6 @@ use hrm\DatabaseConnection; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - - /** * (Abstract) base class for all Parameter types in HRM. */ diff --git a/inc/param/base/SingleOrMultiChannelParameter.php b/inc/param/base/SingleOrMultiChannelParameter.php index 2b67e932c..0fc93ae41 100644 --- a/inc/param/base/SingleOrMultiChannelParameter.php +++ b/inc/param/base/SingleOrMultiChannelParameter.php @@ -9,8 +9,6 @@ */ namespace hrm\param\base; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * A ChoiceParameter that handles single- and multi-channel Parameters * with prefixing. diff --git a/inc/rpc/SessionManager.php b/inc/rpc/SessionManager.php new file mode 100644 index 000000000..4d3634030 --- /dev/null +++ b/inc/rpc/SessionManager.php @@ -0,0 +1,152 @@ +lastMessage = "Invalid client session ID."; + return false; + } + + $userID = $this->get("UserID"); + if ($userID == null) + { + $this->lastMessage = "No user in session."; + return false; + } + + $this->lastMessage = ""; + return true; + } + + /** + * Get current session ID. + * + * Return the session ID or "" if no session exists. + * + * @return string Session ID. + */ + public function getSessionID(): string + { + return session_id(); + } + + /** + * Return the last message. + * + * @return string The last message. + */ + public function getLastMessage(): string + { + return $this->lastMessage; + } +} diff --git a/inc/setting/AnalysisSetting.php b/inc/setting/AnalysisSetting.php index 46f0d1544..89ced8703 100644 --- a/inc/setting/AnalysisSetting.php +++ b/inc/setting/AnalysisSetting.php @@ -14,8 +14,6 @@ use hrm\param\ColocAnalysis; use hrm\setting\base\Setting; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * An AnalysisSetting is a complete set of analysis parameters. * diff --git a/inc/setting/AnalysisSettingEditor.php b/inc/setting/AnalysisSettingEditor.php index 8d2bfec13..390af8423 100644 --- a/inc/setting/AnalysisSettingEditor.php +++ b/inc/setting/AnalysisSettingEditor.php @@ -12,9 +12,6 @@ use hrm\setting\base\SettingEditor; use hrm\user\UserV2; -require_once dirname(__FILE__) . '/../bootstrap.php'; - - /** * Implements an Editor for AnalysisSetting. * diff --git a/inc/setting/JobAnalysisSetting.php b/inc/setting/JobAnalysisSetting.php index 23055da08..2590126f0 100644 --- a/inc/setting/JobAnalysisSetting.php +++ b/inc/setting/JobAnalysisSetting.php @@ -9,10 +9,6 @@ */ namespace hrm\setting; - -require_once dirname(__FILE__) . '/../bootstrap.php'; - - /** * A JobAnalysisSetting is an AnalysisSetting that is used when a Job is * executed by the queue manager. It uses different database tables and knows diff --git a/inc/setting/JobParameterSetting.php b/inc/setting/JobParameterSetting.php index 03f236ac2..e7653ddce 100644 --- a/inc/setting/JobParameterSetting.php +++ b/inc/setting/JobParameterSetting.php @@ -9,10 +9,6 @@ */ namespace hrm\setting; - -require_once dirname(__FILE__) . '/../bootstrap.php'; - - /** * A JobParameterSetting is a ParameterSetting that is used when a Job is * executed by the queue manager. It uses different database tables and knows diff --git a/inc/setting/JobTaskSetting.php b/inc/setting/JobTaskSetting.php index 4cf2efaa9..fb4785dca 100644 --- a/inc/setting/JobTaskSetting.php +++ b/inc/setting/JobTaskSetting.php @@ -9,9 +9,6 @@ */ namespace hrm\setting; -require_once dirname(__FILE__) . '/../bootstrap.php'; - - /** * A JobTaskSetting is a TaskSetting that is used when a Job is executed by * the queue manager. It uses different database tables and knows how to put diff --git a/inc/setting/ParameterSetting.php b/inc/setting/ParameterSetting.php index eedff6c67..d7180b3a9 100644 --- a/inc/setting/ParameterSetting.php +++ b/inc/setting/ParameterSetting.php @@ -19,8 +19,6 @@ use hrm\param\PSF; use hrm\setting\base\Setting; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * A ParameterSetting is a complete set of microscope, image, SPIM, STED, * aberration correction, pixel size calculation and capture parameters. diff --git a/inc/setting/ParameterSettingEditor.php b/inc/setting/ParameterSettingEditor.php index 77191c8cb..1ea07b444 100644 --- a/inc/setting/ParameterSettingEditor.php +++ b/inc/setting/ParameterSettingEditor.php @@ -13,9 +13,6 @@ use hrm\setting\base\SettingEditor; use hrm\user\UserV2; -require_once dirname(__FILE__) . '/../bootstrap.php'; - - /** * Implements an Editor for ParameterSetting. * diff --git a/inc/setting/TaskSetting.php b/inc/setting/TaskSetting.php index 16074282e..a202a2819 100644 --- a/inc/setting/TaskSetting.php +++ b/inc/setting/TaskSetting.php @@ -16,9 +16,6 @@ use hrm\setting\base\Setting; use hrm\System; -require_once dirname(__FILE__) . '/../bootstrap.php'; - - /** * A TaskSetting is a complete set of restoration parameters. * @@ -37,6 +34,8 @@ public function __construct() // @todo Retrieve this information from the database. $parameterClasses = array( + 'Acuity', + 'AcuityMode', 'Autocrop', 'SignalNoiseRatio', 'BackgroundOffsetPercent', @@ -46,15 +45,32 @@ public function __construct() 'QualityChangeStoppingCriterion', 'DeconvolutionAlgorithm', 'ArrayDetectorReductionMode', + 'BleachingMode', 'ZStabilization', - 'ChromaticAberration', + 'ChromaticAberrationCh0', + 'ChromaticAberrationCh1', + 'ChromaticAberrationCh2', + 'ChromaticAberrationCh3', + 'ChromaticAberrationCh4', + 'ChromaticAberrationCh5', 'TStabilization', 'TStabilizationMethod', 'TStabilizationRotation', - 'TStabilizationCropping'); + 'TStabilizationCropping', + 'HotPixelCorrection'); // Instantiate the Parameter objects foreach ($parameterClasses as $class) { + if (strpos($class, 'ChromaticAberration') !== false) { + $ch = substr($class, -1, 1); + $className = 'hrm\\param\\ChromaticAberration'; + $param = new $className($ch); + /** @var Parameter $param */ + $name = $param->name(); + $this->parameter[$name] = $param; + $this->numberOfChannels = null; + continue; + } $className = 'hrm\\param\\' . $class; $param = new $className; /** @var Parameter $param */ @@ -212,6 +228,44 @@ public function checkPostedTaskParameters(array $postedParameters) $noErrorsFound = false; } + + // Acuity mode + if (isset($postedParameters["AcuityMode"]) || $postedParameters["AcuityMode"] == '') { + $parameter = $this->parameter("AcuityMode"); + $parameter->setValue($postedParameters["AcuityMode"]); + $this->set($parameter); + if (!$parameter->check()) { + $this->message = $parameter->message(); + $noErrorsFound = false; + } + } + + // Acuity + for ($i = 0; $i < $maxChanCnt; $i++) { + $value[$i] = null; + $name = "Acuity$i"; + if (isset($postedParameters[$name])) { + // We need to set a default value in case of skipped channels + // so that the parameter can get processed. Unfortunately, + // there's no default for this parameter in the DB. We also + // default to this value if acuity mode is disabled. + if ($deconAlgorithms[$i] == "skip" || $parameter->value() == "off") { + $value[$i] = "0"; + } else { + $value[$i] = $postedParameters[$name]; + } + } + } + + /** @var Acuity $parameter */ + $parameter = $this->parameter("Acuity"); + $parameter->setValue($value); + $this->set($parameter); + if (!$skipDeconAll && !$parameter->check()) { + $this->message = $parameter->message(); + $noErrorsFound = false; + } + // Background estimation if (!isset($postedParameters["BackgroundEstimationMode"]) || $postedParameters["BackgroundEstimationMode"] == '') { $this->message = 'Please choose a background estimation mode!'; @@ -294,6 +348,17 @@ public function checkPostedTaskParameters(array $postedParameters) } } + // Bleaching Mode + if (isset($postedParameters["BleachingMode"]) || $postedParameters["BleachingMode"] == '') { + $parameter = $this->parameter("BleachingMode"); + $parameter->setValue($postedParameters["BleachingMode"]); + $this->set($parameter); + if (!$parameter->check()) { + $this->message = $parameter->message(); + $noErrorsFound = false; + } + } + // ArrayDetectorReductionMode if (isset($postedParameters["ArrayDetectorReductionMode"]) || $postedParameters["ArrayDetectorReductionMode"] == '') { $parameter = $this->parameter("ArrayDetectorReductionMode"); @@ -339,23 +404,32 @@ public function checkPostedChromaticAberrationParameters(array $postedParameters if (!$noErrorsFound) { return $noErrorsFound; } - - $parameter = $this->parameter("ChromaticAberration"); - - /* The posted parameters are received in increasing 'chan component' - order. */ - $i = 0; + + // Set the values. Overwrite only when the value has changed, + // otherwise the higher order corrections (which when present are not + // editable) can be overwritten. + // Every 5 indexes are a channel. + $ch = 0; + $newVal = array(); foreach ($postedParameters as $name => $param) { if (strpos($name, 'ChromaticAberration') === false) { continue; } - - $valuesArray[$i] = $param; - $i++; + + $newVal[] = $param; + if (count($newVal) == 5) { + $parameter = $this->parameter("ChromaticAberrationCh".$ch); + $oldVal = $parameter->value(); + if (array_splice($oldVal, 0, 5) == $newVal) { + // The relevant values have not changed; no work needed. + } else { + $parameter->setValue($newVal); + } + $ch++; + $newVal = array(); + } } - $parameter->setValue($valuesArray); - return $noErrorsFound; } @@ -424,6 +498,28 @@ public function checkPostedTStabilizationParameters(array $postedParameters) } + /** + * Checks that the posted Hot Pixel Correction Parameters are defined. + * This correction is optional. + * @param array $postedParameters The array of posted parameters. + * @return bool True if all Parameters are defined and valid, false + * otherwise. + */ + // For now this is a dummy function as any hot pixel choice should be accepted. + public function checkPostedHotPixelCorrectionParameters(array $postedParameters) + { + if (count($postedParameters) == 0) { + $this->message = ''; + return false; + } + + $this->message = ''; + $noErrorsFound = true; + + return $noErrorsFound; + } + + /** * Returns all Task Parameter names. * @return array Array of Task Parameter names. @@ -465,7 +561,7 @@ public function displayString($numberOfChannels = 0, $micrType = null, $timeInte if ($numberOfChannels == 0) { $numberOfChannels = $this->numberOfChannels(); } - + // These parameters are important to properly display other parameters. $algorithm = $this->parameter('DeconvolutionAlgorithm')->value(); $TStabilization = $this->parameter('TStabilization')->value(); @@ -495,7 +591,8 @@ public function displayString($numberOfChannels = 0, $micrType = null, $timeInte if ($parameter->name() == 'TStabilizationCropping' && ($TStabilization == 0 || $timeInterval == 0)) { continue; } - if ($parameter->name() == 'ChromaticAberration' && $numberOfChannels == 1) { + if (strpos($parameter->name(), 'ChromaticAberration') !== false + && $numberOfChannels == 1) { continue; } if ($parameter->name() == 'ArrayDetectorReductionMode' && !strstr($micrType, "array detector confocal")) { @@ -645,27 +742,73 @@ public function parseParamsFromHuCore(array $huArray) $algorithm->setValue($algArray); // SNR. - $snrQMLEArray = array("low" => "1", "fair" => "2", "good" => "3", "inf" => "4"); for ($chan = 0; $chan < $maxChanCnt; $chan++) { if ($algArray[$chan] == "cmle") { - $key = "cmle:" . $chan . " sn"; + $key = "cmle:" . $chan . " snr"; } elseif ($algArray[$chan] == "gmle") { - $key = "gmle:" . $chan . " sn"; + $key = "gmle:" . $chan . " snr"; } elseif ($algArray[$chan] == "qmle") { - $key = "qmle:" . $chan . " sn"; + $key = "qmle:" . $chan . " snr"; } - + if (isset($huArray[$key])) { - if ($algArray[$chan] == "qmle" && array_key_exists($huArray[$key], $snrQMLEArray)) { - $snr[$chan] = $snrQMLEArray[$huArray[$key]]; - } else { - $snr[$chan] = $huArray[$key]; - } + $snr[$chan] = $huArray[$key]; } } + if (isset($snr)) { $this->parameter['SignalNoiseRatio']->setValue($snr); } + + // Acuity. + + for ($chan = 0; $chan < $maxChanCnt; $chan++) { + if ($algArray[$chan] == "cmle") { + $key = "cmle:" . $chan . " acuity"; + } elseif ($algArray[$chan] == "gmle") { + $key = "gmle:" . $chan . " acuity"; + } elseif ($algArray[$chan] == "qmle") { + $key = "qmle:" . $chan . " acuity"; + } + + if (isset($huArray[$key])) { + $acuity[$chan] = $huArray[$key]; + $this->parameter['Acuity']->setValue($acuity); + } + } + + // Acuity mode. Turn it off unless there is at least one channel for + // which a value of on has been provided. + + $globalAcuityMode = 'off'; + for ($chan = 0; $chan < $maxChanCnt; $chan++) { + if ($algArray[$chan] == "cmle") { + $key = "cmle:" . $chan . " acuityMode"; + } elseif ($algArray[$chan] == "gmle") { + $key = "gmle:" . $chan . " acuityMode"; + } elseif ($algArray[$chan] == "qmle") { + $key = "qmle:" . $chan . " acuityMode"; + } + + if (isset($huArray[$key])) { + $acuityMode = $huArray[$key]; + + switch ($acuityMode) { + case 'auto': + break; + case 'on': + $globalAcuityMode = 'on'; + break; + case 'off': + if ($globalAcuityMode != "on") $globalAcuityMode = 'off'; + break; + default: + $this->message = 'Unknown acuity mode!'; + $noErrorsFound = false; + } + } + } + $this->parameter['AcuityMode']->setValue($globalAcuityMode); // Autocrop. if (isset($huArray['autocrop enabled'])) { @@ -673,6 +816,16 @@ public function parseParamsFromHuCore(array $huArray) $this->parameter['Autocrop']->setValue($autocrop); } + // Bleaching mode. + for ($chan = 0; $chan < $maxChanCnt; $chan++) { + $key = $algArray[$chan] . ":" . $chan . " blMode"; + if (isset($huArray[$key]) && $huArray[$key] != "off") { + $blMode = $huArray[$key]; + $this->parameter('BleachingMode')->setValue($blMode); + break; + } + } + // Background. // Set it to manual only if all channels are specified. // Otherwise set it to the first other mode encountered. @@ -771,7 +924,6 @@ public function parseParamsFromHuCore(array $huArray) } $this->parameter["QualityChangeStoppingCriterion"]->setValue($qMin); - // Stabilization in Z. if (isset($huArray['stabilize enabled'])) { $stabilize = $huArray['stabilize enabled']; @@ -779,32 +931,37 @@ public function parseParamsFromHuCore(array $huArray) } // Chromatic Aberration. - $compCnt = 5; + $maxCmp = $this->parameter['ChromaticAberrationCh0']->maxComponentCnt(); for ($chan = 0; $chan < $maxChanCnt; $chan++) { $key = "shift:" . $chan . " vector"; - + unset($vector); if (isset($huArray[$key])) { - $vector = explode(" ", $huArray[$key], $compCnt); + $vector = explode(' ', $huArray[$key], $maxCmp); } - - for ($comp = 0; $comp < $compCnt; $comp++) { - $compKey = $chan * $compCnt + $comp; - + + unset($aberration); + for ($comp = 0; $comp < $maxCmp; $comp++) { if (isset($vector[$comp])) { - $aberration[$compKey] = $vector[$comp]; + $aberration[$comp] = $vector[$comp]; } else { - if ($comp < $compCnt - 1) { - $aberration[$compKey] = 0.; - } else { + // A CAC correction template should have one of 3, 4, 5 or + // 14 components. The first 5 are editable and have to + // exist. Components 6 to 14 are either present or should + // not be present. + if ($comp < 4) { + $aberration[$comp] = 0.; + } elseif ($comp < 5) { // Scale component. - $aberration[$compKey] = 1.; + $aberration[$comp] = 1.; + } else { + $aberration[$comp] = null; } } } - } - if (isset($aberration)) { - $this->parameter['ChromaticAberration']->setValue($aberration); + if (isset($aberration)) { + $this->parameter['ChromaticAberrationCh' . $chan]->setValue($aberration); + } } // Stabilization in T. diff --git a/inc/setting/TaskSettingEditor.php b/inc/setting/TaskSettingEditor.php index 861519b11..9ba3b56e9 100644 --- a/inc/setting/TaskSettingEditor.php +++ b/inc/setting/TaskSettingEditor.php @@ -9,14 +9,10 @@ */ namespace hrm\setting; - use hrm\HuygensTools; use hrm\setting\base\SettingEditor; use hrm\user\UserV2; -require_once dirname(__FILE__) . '/../bootstrap.php'; - - /** * Implements an Editor for TaskSetting. * diff --git a/inc/setting/base/Setting.php b/inc/setting/base/Setting.php index ccec293f6..07cab7ef2 100644 --- a/inc/setting/base/Setting.php +++ b/inc/setting/base/Setting.php @@ -13,8 +13,6 @@ use hrm\param\base\Parameter; use hrm\user\UserV2; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * (Abstract) base class for all specific Setting classes. * diff --git a/inc/setting/base/SettingEditor.php b/inc/setting/base/SettingEditor.php index f333ba914..14d89197d 100644 --- a/inc/setting/base/SettingEditor.php +++ b/inc/setting/base/SettingEditor.php @@ -13,9 +13,6 @@ use hrm\HuygensTools; use hrm\user\UserV2; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - - /** * Abstract class for a SettingEditor. * diff --git a/inc/shell/ExternalProcess.php b/inc/shell/ExternalProcess.php index e2fc9b381..13c176c7d 100644 --- a/inc/shell/ExternalProcess.php +++ b/inc/shell/ExternalProcess.php @@ -12,8 +12,6 @@ use hrm\Log; -require_once dirname(__FILE__) . "/../bootstrap.php"; - global $hucore, $hutask; /** @todo Why is $hucore forced here? */ diff --git a/inc/shell/ExternalProcessFactory.php b/inc/shell/ExternalProcessFactory.php index 65d1b7d4a..60c11956e 100644 --- a/inc/shell/ExternalProcessFactory.php +++ b/inc/shell/ExternalProcessFactory.php @@ -12,8 +12,6 @@ use hrm\DatabaseConnection; -require_once dirname(__FILE__) . "/../bootstrap.php"; - /** * Factory that returns an external process (Shell) that works either locally or on a remote server. * diff --git a/inc/shell/LocalExternalProcess.php b/inc/shell/LocalExternalProcess.php index 5b024daf5..b148e813e 100644 --- a/inc/shell/LocalExternalProcess.php +++ b/inc/shell/LocalExternalProcess.php @@ -10,8 +10,6 @@ namespace hrm\shell; -require_once dirname(__FILE__) . "/../bootstrap.php"; - global $hucore, $hutask; /** @todo Why is $hucore forced here? */ @@ -118,6 +116,8 @@ public function rewakeHuygensProcess($pid) // therefore nothing to do } + // TODO: a rename function? + /** * Executes a command. * @param string $command Command to be executed on the host. diff --git a/inc/stats/Stats.php b/inc/stats/Stats.php index 7371b8a9e..db4e7c7a1 100644 --- a/inc/stats/Stats.php +++ b/inc/stats/Stats.php @@ -14,8 +14,6 @@ use hrm\Util; use hrm\user\UserV2; -require_once dirname(__FILE__) . '/../bootstrap.php'; - /** * Commodity class to generate statistics of HRM usage. * diff --git a/inc/user/UserManager.php b/inc/user/UserManager.php index 8b2630c60..538ab44dd 100644 --- a/inc/user/UserManager.php +++ b/inc/user/UserManager.php @@ -16,9 +16,6 @@ use hrm\System; use hrm\user\proxy\ProxyFactory; -require_once dirname(__FILE__) . '/../bootstrap.php'; - - /** * Manages Users. * diff --git a/inc/user/UserV2.php b/inc/user/UserV2.php index 01b1474ba..896fdbfd5 100644 --- a/inc/user/UserV2.php +++ b/inc/user/UserV2.php @@ -1,4 +1,5 @@ setLastAccessDate(); - } return $this->isLoggedIn; @@ -409,7 +406,7 @@ public function logIn($password) */ public function logOut() { - $this->isLoggedIn = False; + $this->isLoggedIn = false; } /** @@ -573,8 +570,7 @@ private function load() // Load all information for current user $result = $db->connection()->Execute("SELECT * FROM username WHERE name = ?;", array($this->name)); $rows = $result->GetRows(); - if (count($rows) == 0) - { + if (count($rows) == 0) { // A user with current name does not yet exist: we create it. $row = array(); $row["name"] = $this->name(); @@ -587,7 +583,6 @@ private function load() $row["last_access_date"] = $this->lastAccessDate(); $row["status"] = $this->status(); $row["id"] = -1; - } else { $row = $rows[0]; } diff --git a/inc/user/proxy/AbstractProxy.php b/inc/user/proxy/AbstractProxy.php index f317ac19c..0076a7921 100644 --- a/inc/user/proxy/AbstractProxy.php +++ b/inc/user/proxy/AbstractProxy.php @@ -14,8 +14,6 @@ use hrm\user\UserManager; use hrm\user\UserConstants; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * Abstract base proxy class that provides an interface for concrete * classes to establish communication with external user management systems diff --git a/inc/user/proxy/ActiveDirectoryProxy.php b/inc/user/proxy/ActiveDirectoryProxy.php index ecbd8928f..a57d562a2 100644 --- a/inc/user/proxy/ActiveDirectoryProxy.php +++ b/inc/user/proxy/ActiveDirectoryProxy.php @@ -15,8 +15,6 @@ use Exception; use hrm\Log; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * Manages Active Directory connections through the adLDAP library. * diff --git a/inc/user/proxy/Auth0Proxy.php b/inc/user/proxy/Auth0Proxy.php index 1009e8e25..adb5bffe1 100644 --- a/inc/user/proxy/Auth0Proxy.php +++ b/inc/user/proxy/Auth0Proxy.php @@ -13,8 +13,6 @@ use hrm\Log; use Auth0\SDK\Auth0; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * Manages authentication against the internal HRM user database. * diff --git a/inc/user/proxy/DatabaseProxy.php b/inc/user/proxy/DatabaseProxy.php index e3f92bd12..c59f38e17 100644 --- a/inc/user/proxy/DatabaseProxy.php +++ b/inc/user/proxy/DatabaseProxy.php @@ -16,8 +16,6 @@ use hrm\user\UserManager; use hrm\user\UserConstants; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * Manages authentication against the internal HRM user database. * diff --git a/inc/user/proxy/LDAPProxy.php b/inc/user/proxy/LDAPProxy.php index 09a4bd359..bbf96042d 100644 --- a/inc/user/proxy/LDAPProxy.php +++ b/inc/user/proxy/LDAPProxy.php @@ -13,8 +13,6 @@ use Exception; use hrm\Log; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * Manages LDAP connections through built-in PHP LDAP support * diff --git a/inc/user/proxy/ProxyFactory.php b/inc/user/proxy/ProxyFactory.php index 82bab5116..c9abd4b23 100644 --- a/inc/user/proxy/ProxyFactory.php +++ b/inc/user/proxy/ProxyFactory.php @@ -10,13 +10,10 @@ namespace hrm\user\proxy; -// Include the HRM configuration files. use hrm\DatabaseConnection; use hrm\Log; use hrm\System; -require_once dirname(__FILE__) . '/../../bootstrap.php'; - /** * Returns the Proxy object to be used to manage the user based on the * value of $authenticateAgainst from the configuration files. diff --git a/login.php b/login.php index 62c67c8ac..4c0312b6b 100644 --- a/login.php +++ b/login.php @@ -154,7 +154,6 @@ @@ -191,7 +190,7 @@ if (System::getHuCoreVersionAsInteger() == 0) { echo "
Warning: unknown HuCore version!\n"; echo "

Please ask the administrator to start the queue manager.

" . - "

You are now allowed to login until this issue has been " . + "

You are not allowed to log in until this issue has been " . "fixed.

"; echo "\n"; include("footer.inc.php"); diff --git a/omero_UI.php b/omero_UI.php index f406bf5ec..b0148b4bb 100644 --- a/omero_UI.php +++ b/omero_UI.php @@ -3,6 +3,13 @@ // This file is part of the Huygens Remote Manager // Copyright and license notice: see license.txt +// clear OMERO connection related variables if "disconnectOmero" was requested: +if (isset($_POST['disconnectOmero']) && isset($omeroConnection)) { + unset($omeroConnection); + unset($_SESSION['omeroConnection']); +} + + // Dialog to ask for the OMERO credentials. if (isset($_POST['getOmeroData']) && !isset($omeroConnection)) { ?> @@ -67,9 +74,16 @@ // if we are connected to an OMERO server, always show the tree by default: -if (isset($omeroConnection)) { +if (isset($omeroConnection) && !isset($_POST['disconnectOmero'])) { ?> + + +

An OMERO transfer is currently running, please wait until it has finished.

@@ -91,6 +105,9 @@ function activeTransferDialog() { +
+ Your OMERO data +
- - -

Disclaimer: - HRM cannot guarantee that OME-TIFFs - provided by OMERO contain the original metadata.

- + + @@ -150,7 +164,6 @@ function activeTransferDialog() {
- Your OMERO data


@@ -201,8 +214,20 @@ function activeTransferDialog() {
+ +

Disclaimer: + OME-TIFFs provided by OMERO might be missing the + original metadata.

+ +
+
+ parameter("ChromaticAberration"); $chanCnt = $_SESSION['task_setting']->numberOfChannels(); -$componentCnt = $chromaticParam->componentCnt(); -$chromaticArray = $chromaticParam->value(); -ksort($chromaticArray); +$chromaticArray = array(); +for ($ch=0; $ch<$chanCnt; $ch++) { + $chromaticParam[$ch] = + $_SESSION['task_setting']->parameter("ChromaticAberrationCh" . $ch); +} +$maxComponentCnt = $chromaticParam[0]->maxComponentCnt(); +$shownComponentCnt = $chromaticParam[0]->shownComponentCnt(); /* ***************************************************************************** @@ -52,11 +55,14 @@ $postErrors++; } } +if (!$_SESSION['task_setting']->checkPostedHotPixelCorrectionParameters($_POST)) { + $postErrors++; +} if ($postErrors == 0) { $saved = $_SESSION['task_setting']->save(); if ($saved) { - header("Location: " . "select_task_settings.php"); + header("Location: " . "select_hpc.php"); exit(); } else { $message = $_SESSION['task_setting']->message(); @@ -169,14 +175,13 @@ class="selection"
+ onmouseout="UnTip()" onclick="deleteValuesAndRedirect( 'select_task_settings.php' );"/> - diff --git a/resources/checkConfig.php b/resources/checkConfig.php index 84763590b..d4cbde032 100644 --- a/resources/checkConfig.php +++ b/resources/checkConfig.php @@ -4,7 +4,7 @@ // $ php checkConfig.php /path/to/config/file // -// Example: php checkConfig.php /var/www/html/hrm/config/hrm_server_config.inc +// Example: php checkConfig.php /var/www/html/hrm/config/hrm_config.inc require_once dirname(__FILE__) . '/../inc/bootstrap.php'; @@ -28,7 +28,7 @@ function displayUsage() { echo PHP_EOL . "Usage: php checkConfig.php /path/to/config/file" . PHP_EOL . PHP_EOL . - "Example: php checkConfig.php /var/www/html/hrm/config/hrm_server_config.inc" . + "Example: php checkConfig.php /var/www/html/hrm/config/hrm_config.inc" . PHP_EOL . PHP_EOL; } diff --git a/resources/munin/README.md b/resources/munin/README.md new file mode 100644 index 000000000..7397a5ffe --- /dev/null +++ b/resources/munin/README.md @@ -0,0 +1,51 @@ +# HRM Munin Plugin + +This is an absolut minimalistic plugin for the [Munin resource monitoring +tool][1] that will monitor the HRM Job Queue length. + +## Prerequisites + +As a basic requirement you will need to set up Munin itself. Please refer to +the [Munin documentation][1] or to one of the various distribution-specific +guides found on the web. + +In addition, the plugin requires the `mysql` command line interface to be +available on the system. + +## Installation + +The plugin itself needs to be placed (or symlinked) into Munin's `plugins` +directory. In a default installation this can be done via + +```bash +cp -v plugins/hrm_jobqueue /etc/munin/plugins +chmod +x /etc/munin/plugins/hrm_jobqueue +``` + +The plugin requires three configuration parameters to be set, so it can +connect to the database and check the HRM queue. This is done through a +configuration file located in `/etc/munin/plugin-conf.d/`, an example is +provided in the corresponding directory here and can just be copied there: + +```bash +cp -v plugin-conf.d/90-hrm /etc/munin/plugin-conf.d/ +``` + +Make sure to **adjust the values** therein according to your setup! + +Using a separate read-only database user (i.e. not the one used for the HRM +itself) that has access to the `job_queue` table is **strongly recommended**! + +**FINALLY**, please make sure to re-start the `munin-node` service on the +host, otherwise the new plugin will be ignored. + +```bash +service munin-node restart +``` + +## Results + +Graphs of the HRM Queue length will be placed in the `processes` section of +the generated Munin pages. + +[1]: http://munin-monitoring.org/ \ No newline at end of file diff --git a/resources/munin/plugin-conf.d/90-hrm b/resources/munin/plugin-conf.d/90-hrm new file mode 100644 index 000000000..4a1db5269 --- /dev/null +++ b/resources/munin/plugin-conf.d/90-hrm @@ -0,0 +1,5 @@ +[hrm_jobqueue] +user root +env.db_name DB_NAME +env.db_user DB_READONLY_USER +env.db_pass DB_READONY_PASSWORD diff --git a/resources/munin/plugins/hrm_jobqueue b/resources/munin/plugins/hrm_jobqueue new file mode 100755 index 000000000..0d5065921 --- /dev/null +++ b/resources/munin/plugins/hrm_jobqueue @@ -0,0 +1,31 @@ +#!/bin/sh +# +# Minimal plugin for MUNIN to monitor HRM's job queue length +# +# Written 2020-10 by Niko Ehrenfeuchter +# + +JOBCOLOUR='4169e1' # Royal blue + +case "$1" in + "config") + cat << EOM +graph_category processes +graph_title HRM Job Queue +graph_info The number of jobs in the HRM (Huygens Remote Manager) queue. +graph_vlabel number of queued jobs +jobs.label jobs in queue +jobs.colour $JOBCOLOUR +jobs.warning 200 +EOM + exit 0;; +esac + +printf "jobs.value " +mysql \ + --user "$db_user" \ + --password="$db_pass" \ + --execute="SELECT COUNT(id) FROM job_queue;" \ + --silent \ + --skip-column-names \ + "$db_name" diff --git a/resources/ome_hrm_check_imports.py b/resources/ome_hrm_check_imports.py deleted file mode 100644 index c3ebaee1a..000000000 --- a/resources/ome_hrm_check_imports.py +++ /dev/null @@ -1,32 +0,0 @@ -import sys - -print "sys.version:", sys.version -print "sys.executable:", sys.executable - -import argparse -print "argparse:", argparse.__file__, "version:", argparse.__version__ - -import os -print "os:", os.__file__ - -import json -print "json:", json.__file__, "version:", json.__version__ - -import re -print "re:", re.__file__, "version:", re.__version__ - -OMERO_LIB='/opt/OMERO/OMERO.server-5.1.2-ice34-b45/lib/python' -sys.path.insert(0, OMERO_LIB) -print "sys.path: [" -for path in sys.path: - print " '%s'," % path -print "]" - -try: - import omero - print "OMERO Python bindings:", omero.__file__ - from omero.gateway import BlitzGateway - print "Successfully loaded OMERO's BlitzGateway." -except ImportError as err: - print "ERROR importing the OMERO Python bindings! Message:", err - sys.exit(1) diff --git a/resources/ome_hrm_debug_wrapper.sh b/resources/ome_hrm_debug_wrapper.sh deleted file mode 100644 index 5c05aa2ce..000000000 --- a/resources/ome_hrm_debug_wrapper.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -BASEDIR=$(dirname $0) - -# export PYTHONPATH="/opt/OMERO/python-extlibs":$PYTHONPATH -{ - echo $BASEDIR - echo "ome_hrm wrapper" - echo '$PYTHONPATH: '$PYTHONPATH - $BASEDIR/ome_hrm.py.real $@ -} >> /tmp/foooo 2>&1 diff --git a/resources/systemd/hrmd.service b/resources/systemd/hrmd.service index 4a293984b..f69866c80 100644 --- a/resources/systemd/hrmd.service +++ b/resources/systemd/hrmd.service @@ -11,6 +11,9 @@ After=mysql.service network.target network-online.target # If needed, change 'User=' and 'Group=' to point to the correct values. User=hrm Group=hrm +# Setting UMask is required to allow the Queue Manager for relaxing permissions +# of the upload / download directories so Apache is allowed to write there: +UMask=0002 ExecStart=/var/www/html/hrm/bin/hrm_queuemanager --detach Type=forking PIDFile=/var/log/hrm/hrmd.pid diff --git a/run/runHuygensRemoteManager.php b/run/runHuygensRemoteManager.php index 11cb86844..b76522761 100644 --- a/run/runHuygensRemoteManager.php +++ b/run/runHuygensRemoteManager.php @@ -2,10 +2,8 @@ // This file is part of the Huygens Remote Manager // Copyright and license notice: see license.txt -global $isServer; use hrm\QueueManager; -$isServer = true; require_once dirname(__FILE__) . '/../inc/bootstrap.php'; $manager = new QueueManager(); $manager->run(); diff --git a/scripts/common.js b/scripts/common.js index f6cbeb0bb..c3cd976e4 100644 --- a/scripts/common.js +++ b/scripts/common.js @@ -192,6 +192,7 @@ function initChromaticChannelReference() { chan = searchChromaticChannelReference(); } + clearChromaticChannelReference(); setChromaticChannelReference( chan ); } @@ -246,7 +247,7 @@ function setChromaticChannelReference( chan ) { id = id.concat(chan); id += "_"; id = id.concat(component); - + inputElement = document.getElementById(id); inputElement.readOnly = true; inputElement.style.backgroundColor="#888"; @@ -257,13 +258,19 @@ function setChromaticChannelReference( chan ) { inputElement.value = 0; } } - + + var id = channelTag + "DiscardOtherCh" + chan; + buttonElement = document.getElementById(id); + buttonElement.setAttribute('hidden', true); + + // todo: make sure it is "reset" in the case of 14 parameters. + var tag = "ReferenceChannel"; inputElement = document.getElementById(tag); inputElement.value = chan; } -function removeChromaticChannelReference( ) { +function clearChromaticChannelReference( ) { var tableTag = "ChromaticAberrationTable"; var channelTag = "ChromaticAberration"; @@ -273,24 +280,64 @@ function removeChromaticChannelReference( ) { componentCnt = table.rows[0].cells.length - 1; for (var chan = 0; chan < channelCnt; chan++) { + + // If there are 14 parameters known for this chanenl don't make + // it editable. + var id = channelTag + "DiscardOtherCh"; + id = id.concat(chan); + inputElement = document.getElementById(id); + var nonEditable14params = !inputElement.getAttribute('hidden'); + for (var component = 0; component < componentCnt; component++) { var id = channelTag + "Ch"; id = id.concat(chan); id += "_"; id = id.concat(component); - inputElement = document.getElementById(id); - inputElement.readOnly = false; - inputElement.style.backgroundColor=""; + + if (nonEditable14params) { + inputElement.readOnly = true; + inputElement.style.backgroundColor="#666"; + } else { + inputElement.readOnly = false; + inputElement.style.backgroundColor=""; + } } } } function changeChromaticChannelReference(selectObj) { - removeChromaticChannelReference( ); + clearChromaticChannelReference( ); setChromaticChannelReference( selectObj.value ); } +function editChromaticChannelWith14Params(channel) { + // Hide the discard button. + var tag = "ChromaticAberrationDiscardOtherCh" + channel; + butElement = document.getElementById(tag); + butElement.setAttribute('hidden', true); + clearChromaticChannelReference( ); + + // Round the values to 5 decimals. This both makes it easier to edit and + // ensures the new values are saved properly. + var tableTag = "ChromaticAberrationTable"; + var channelTag = "ChromaticAberration"; + table = document.getElementById(tableTag); + channelCnt = table.rows.length - 1; + componentCnt = table.rows[0].cells.length - 1; + for (var component = 0; component < componentCnt; component++) { + var id = channelTag + "Ch" + channel + "_" + component; + inputElement = document.getElementById(id); + var rounded = Math.round(inputElement.value * 100000) / 100000; + inputElement.value = rounded; + } + + // Call setChromaticChannelReference to properly redo the layout. + var tag = "ReferenceChannel"; + refElement = document.getElementById(tag); + setChromaticChannelReference( refElement.value ); +} + // Grey out selected input fields when the decon algorithm is set to 'skip'. function updateDeconEntryProperties( ) { @@ -303,6 +350,7 @@ function updateDeconEntryProperties( ) { "SignalNoiseRatioQMLE", "SignalNoiseRatioGMLE", "SignalNoiseRatioSKIP", + "Acuity", "BackgroundOffsetPercent"]; var skipAllChannels = true; @@ -317,7 +365,7 @@ function updateDeconEntryProperties( ) { if (deconAlgorithm === undefined) continue; if (deconAlgorithm.value !== "skip") skipAllChannels = false; - // QMLE vs CMLE/GMLE. + // Show the input box relevant for the current algorithm. switchSnrMode(deconAlgorithm, chan); for (var tagIdx = 0; tagIdx < paramChanTagArray.length; tagIdx++) { @@ -357,9 +405,9 @@ function updateDeconEntryProperties( ) { function copySnrToOtherAlgorithms(channel, inputObj) { - // QMLE is an exception here. var tagArray = ["SignalNoiseRatioCMLE", "SignalNoiseRatioGMLE", + "SignalNoiseRatioQMLE", "SignalNoiseRatioSKIP"]; for (var i = 0; i < tagArray.length; i++) { @@ -547,6 +595,7 @@ function checkAgainstFormat(file, selectedFormat) { case 'lof': case 'lsm': case 'oif': + case 'vsi': case 'pic': case 'r3d': case 'stk': @@ -886,11 +935,20 @@ function cancelSelection() { changeDiv('selection', control); } -function imgPrev(infile, mode, gen, compare, index, dir, referer, data) { +function changeFileSelectionValueAt(index, newName) { + document.getElementById('fileSelection')[index].value = newName; + document.getElementById('fileSelection')[index].text = newName; + setActionToUpdate(); + document.getElementById('file_browser').submit(); + +} +function imgPrev(infile, mode, gen, sanitized, compare, index, + dir, referer, data) { var file = unescape(infile); var tip, html, link, onClick = ""; - + var regexpRes; + if (mode == 0 && gen == 1) { try { @@ -906,7 +964,7 @@ function imgPrev(infile, mode, gen, compare, index, dir, referer, data) { mode = 0; } } - + switch (mode) { case 0: @@ -919,30 +977,45 @@ function imgPrev(infile, mode, gen, compare, index, dir, referer, data) { // Preview doesn't exist, but you can create it now. link = "file_management.php?genPreview=" + infile + "&src=" + dir - + "&data=" + data + '&index=' + index; - + + "&data=" + data + '&index=' + index; + onClick = '

' + 'Generating preview in another window.
' + 'Please wait...
'; - html = '' - + '
' - + '
' - + ' ' - + 'Click to generate preview' - + '
'; + html = '' + + '
' + + '
' + + ' ' + + 'Click to generate preview'; + + // Check if the (base)names of the sanitized and original name + // are the same, otherwise mention that it will rename the file. + if ( infile != sanitized ) { + regexpRes = /^(.+)\ \(.+\)$/g.exec(file); + if (regexpRes && sanitized.startsWith(regexpRes[1])) { + // It is not a subimage or the basename is the same. + } else { + html = html + ' (the file will be renamed)'; + } + } + html = html + '
'; } break; diff --git a/scripts/hucore.tcl b/scripts/hucore.tcl index 568d7bbce..dc94f0a93 100644 --- a/scripts/hucore.tcl +++ b/scripts/hucore.tcl @@ -85,7 +85,7 @@ proc reportImageDimensions { } { # Return 1 if the image is of a type that supports sub-images. Currently, only # LIF, LOF and CZI. proc isMultiImgFile { filename } { - set multiImgExtensions { ".lif" ".lof" ".czi"} + set multiImgExtensions { ".lif" ".lof" ".czi" ".nd"} set ext [file extension $filename] set isMulti 0 @@ -161,6 +161,8 @@ proc reportSubImages {} { set extension [file extension $path] if { [string equal -nocase $extension ".czi"] } { set subImages [lindex $contents 1] + } elseif { [string equal -nocase $extension ".nd"] } { + set subImages [lindex $contents 1] } elseif { [string equal -nocase $extension ".lif"] || [string equal -nocase $extension ".lof"]} { set resDict [dict create {*}[lindex $contents 1]] @@ -518,6 +520,7 @@ proc getDeconDataFromHuTemplate {} { proc estimateSnrFromImage {} { + if { [info proc ::WebTools::estimateSnrFromImage] == "" } { reportError "This tool requires Huygens Core 3.5.1 or higher" return @@ -527,6 +530,7 @@ proc estimateSnrFromImage {} { set error [ getInputVariables { basename src series dest snrVersion returnImages } ] + if { $error } { exit 1 } # Optional arguments: @@ -535,7 +539,15 @@ proc estimateSnrFromImage {} { # Opening images set srcImg [ hrmImgOpen $src $basename -series $series ] + + array set params [$srcImg setp -tclReturn] + if {"generic" in $params(mType)} { + reportError "The image meta data specifies no microscope type.\ + Impossible to continue." + return + } + if { $s != -1 } { reportMsg "Setting voxel size $s" if { [ catch { eval $srcImg setp -s {$s} } err ] } { diff --git a/scripts/quickhelp/postProcessingHelp.js b/scripts/quickhelp/postProcessingHelp.js index 493da0a2c..6789db1d3 100644 --- a/scripts/quickhelp/postProcessingHelp.js +++ b/scripts/quickhelp/postProcessingHelp.js @@ -15,7 +15,11 @@ window.helpText[ "chromatic" ] = 'Chromatic Aberration Corrector in Huygens Professional ' + 'or Huygens Essential. The values estimated by Huygens can ' + 'be used in this table to correct the images for chromatic aberration ' + - 'in batch mode.

To skip this step just leave the table ' + + 'in batch mode.

Chromatic aberration parameters from a Huygens ' + + 'template can contain more parameters than listed here. While these ' + + 'other parameters exist the shown components cannot be edited. A button ' + + 'will appear which can be used to discard them and enable editing. ' + + '

To skip this step just leave the table ' + 'fields empty.'; window.helpText[ "default" ] = '

On this page you specify the parameters of those ' + diff --git a/scripts/quickhelp/taskParameterHelp.js b/scripts/quickhelp/taskParameterHelp.js index 2ccf6584c..944fb4eff 100644 --- a/scripts/quickhelp/taskParameterHelp.js +++ b/scripts/quickhelp/taskParameterHelp.js @@ -29,16 +29,26 @@ window.helpText[ "method" ] = 'colocalization tasks.

'; window.helpText[ "snr" ] = - '

The SNR controls the sharpness of the result: only with noise-free ' + - 'images you can safely demand very sharp results (high SNR values) without ' + - 'amplifying noise.

' + + '

The signal-to-noise ratio is a constant signifying the relative amount of ' + + 'useful data as opposed to noise that is present in the image.' + + '

Though it is a constant, in legacy SNR mode it may be adjusted to control ' + + 'the sharpness of the result at the risk of amplifying noise. ' + + 'In acuity mode, it is best to provide or estimate this parameter as accurately ' + + 'as possible and use the acuity parameter to adjust sharpness instead.

' + '

The different deconvolution algorithms have different requirements on ' + 'the SNR parameter.

' + - '

For the CMLE and GMLE algorithms, you are asked to give a ' + - 'numericalestimation of the SNR of your images. The SNR estimator can help you ' + - 'calculate the SNR for your images.

' + - '

For the QMLE algorithm, only a coarser classification ' + - 'of the SNR is required.

'; + '

For each of the GMLE, CMLE and GMLE algorithms, you ' + + 'are asked to give a numerical estimation of the SNR of your images. The SNR ' + + 'estimator can help you calculate the SNR for your images.

'; + +window.helpText[ "acuity" ] = + '

The acuity controls the sharpness of the result. If you want to suppress noise ' + + 'use a negative value; if you want to increase sharpness and can tolerate the ' + + 'potential amplification of noise use a positive value.

' + + '

This abstracted parameter is included for completeness and compatibility ' + + 'with other Huygens products. If you are unfamiliar with the effects of this ' + + 'setting it is advised to use the legacy SNR mode.

' + + '

Accepted values range between -100 and 100.

'; window.helpText[ "background" ] = '

The background is any additive and approximately constant signal in ' + @@ -72,6 +82,10 @@ window.helpText[ "autocrop" ] = 'Microscopic Parameters are taken into account, making sure that ' + 'cropping will not have a negative impact on the deconvolution result.

'; +window.helpText[ "bleaching" ] = + '

If possible, apply bleaching correction. This will be the case' + + 'for 3D widefield-based volumes as well as for all time series.

'; + window.helpText[ "arrayDetectorReductionMode" ] = '

The array detector reduction mode specifies which pixel reassignment method ' + 'to use in order to combine the data from all the detectors in the array. ' + diff --git a/scripts/settings.js b/scripts/settings.js index 92dac3b15..cbf7e0d32 100644 --- a/scripts/settings.js +++ b/scripts/settings.js @@ -48,8 +48,8 @@ function release() { element.style.color = 'black'; } -function seek(channel) { - var url = "select_psf_popup.php?channel=" + channel; +function seek(channel,type) { + var url = "select_" + type + "_popup.php?channel=" + channel; var name = "snitch"; var options = "directories = no, menubar = no, status = no, width = 560, height = 400"; snitch = window.open(url, name, options); @@ -93,6 +93,11 @@ function seek(channel) { snitch.focus(); } +function hpcReset() { + var select = document.getElementById("select"); + select.elements["hpc"].value = ""; +} + window.onunload = function() {if (snitch != null) snitch.close()}; function switchSnrMode(algorithm, channel) { @@ -146,6 +151,18 @@ function switchTStabilizationMode() { } } +function switchAcuityMode() { + if ($('#AcuityMode').val() == 'on') { + for (var chan = 0; chan < 6; chan++) { + $('#acuity-' + chan).show(); + } + } else { + for (var chan = 0; chan < 6; chan++) { + $('#acuity-' + chan).hide(); + } + } +} + function switchAdvancedCorrection() { var element = document.getElementById('AberrationCorrectionMode'); var chosenoption = element.options[element.selectedIndex]; diff --git a/select_hpc.php b/select_hpc.php new file mode 100644 index 000000000..eedd8452a --- /dev/null +++ b/select_hpc.php @@ -0,0 +1,225 @@ +isLoggedIn()) { + header("Location: " . "login.php"); + exit(); +} + +if ($_SESSION['user']->isAdmin() + || $_SESSION['task_setting']->isEligibleForCAC() + || $_SESSION['task_setting']->isEligibleForTStabilization($_SESSION['setting'])) { + $back = "post_processing.php"; +} else { + $back = "task_parameter.php"; +} + +// fileserver related code +if (!isset($_SESSION['fileserver'])) { + $name = $_SESSION['user']->name(); + $_SESSION['fileserver'] = new Fileserver($name); +} + +$message = ""; + +/* ***************************************************************************** + * + * MANAGE HPC FILE NAMES + * + **************************************************************************** */ + +/** @var HPC $hpcParam */ +$hpcParam = $_SESSION['task_setting']->parameter("HotPixelCorrection"); +$hpc = $hpcParam->value(); + +if (isset($_POST["hpc"])) { + $hpc = $_POST["hpc"]; +} + +$hpcParam->setValue($hpc); +$_SESSION['task_setting']->set($hpcParam); + +/* ***************************************************************************** + * + * PROCESS THE POSTED PARAMETERS + * * + **************************************************************************** */ + +if (count($_POST) > 0) { + if ($hpcParam->check()) { + $saved = $_SESSION['task_setting']->save(); + $message = $_SESSION['task_setting']->message(); + if ($saved) { + header("Location: select_task_settings.php"); + exit(); + } + } else { + $message = $hpcParam->message(); + } +} + +$script = "settings.js"; + +include("header.inc.php"); + +?> + + + Go back to previous page. + + + Abort editing and go back to the image parameters selection page. + + + Save and return to the image parameters selection page. + + +

+ +
+ +

SelectHPC Hot Pixel Correction - mask file selection

+ +
+ +
+ parameter("HotPixelCorrection"); + $value = $parameter->value(); + $missing = False; + $_SESSION['fileserver']->imageExtensions(); + $files = $_SESSION['fileserver']->allFiles(); + if ($files != null) { + if (!in_array($value[0], $files)) { + $missing = True; + } + + ?> +

+ " + readonly="readonly"/> + + +

+ sourceFolder())) { + + ?> +

Source image folder not found! Make sure + the + folder sourceFolder() ?> + exists.

+ +

No images found on the server!

+ +

The correction is optional: leave empty for skipping.

+
+ +
+ +
+ +
+ + + +
+ + + +
+ +
+ +
+ +

Quick help

+

Select a Hot Pixel Correction mask. This correction will be applied to the target images before any other image processing is executed. For multiple channels, please select a single file with different masks for the channels.

+

Since the images of a batch may or may not have all the same dimensions please notice that the correction will be skipped on runtime for images whose dimensions don't match the selected mask.

+
+ +
+ $message

"; + + ?> +
+ +
+ + diff --git a/select_hpc_popup.php b/select_hpc_popup.php new file mode 100644 index 000000000..861d7f710 --- /dev/null +++ b/select_hpc_popup.php @@ -0,0 +1,107 @@ +isLoggedIn()) { + header("Location: " . "login.php"); + exit(); +} + +// fileserver related code +if (!isset($_SESSION['fileserver'])) { + $name = $_SESSION['user']->name(); + $_SESSION['fileserver'] = new Fileserver($name); +} + +?> + + + + + + + Huygens Remote Manager + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + Available files + listFiles(true); + + ?> + +
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+ + + + diff --git a/select_images.php b/select_images.php index 5485d050b..1211be4ae 100644 --- a/select_images.php +++ b/select_images.php @@ -44,6 +44,7 @@ // If there is no other action on this path, we assume it's entry on the page and initialize the Fileserver object. $_SESSION['fileserver'] = new Fileserver($name); } + if (!isset($_SESSION['parametersetting'])) { $_SESSION['parametersetting'] = new ParameterSetting(); } @@ -68,7 +69,13 @@ $info = "

Quick help

" . "

Here you can select the files to be restored from the list " . "of available images. The file names are filtered by the selected " . - "file format. Use SHIFT- and CTRL-click to select multiple files.

" . + "file format." . + "

Use SHIFT- and CTRL-click to select multiple files from any of the boxes.

" . + "

From the Images available on server box, select the images you " . + "want to process and use the \"Help\" " . + "button to add them to the Selected Images box.

". + "

To remove images, select them in the Selected Images box " . + "and hit the \"Help\" button.

" . "

Where applicable, the files belonging to a series can be condensed " . "into one file name by checking the 'autoseries' option. These files " . "will be loaded and deconvolved as one large dataset. Unchecking " . @@ -386,7 +393,7 @@ function removeFromSelection() { sortOptions("filesPerFormat"); } - // Update bot files and selected files elements + // Update both files and selected files elements function updateFilesAndSelectedFiles(response) { if (response["success"] === "false") { diff --git a/select_parameter_settings.php b/select_parameter_settings.php index 0570a5247..706430282 100644 --- a/select_parameter_settings.php +++ b/select_parameter_settings.php @@ -245,33 +245,34 @@ --> Create a new image template set with the specified name. - + Edit the selected image template. - + Generate template from image file. - + - Import a Huygens template. - + Import a Huygens microscopy template (extension "hgsm"). + - Copy the selected image template to a new one with the - specified name. + Copy the selected image template to a new one with the specified name. + - Share the selected image template with one or more HRM users. + Share the selected image template with one or more HRM users. + Delete the selected image template. - + Accept the template. - + Reject the template. - + Preview the template. - + isAdmin()) { ?> @@ -597,10 +598,10 @@ class="icon delete" Duplicate

isAdmin()) { @@ -737,7 +738,6 @@ class="icon apply" HelpHelp link in the navigation bar.

-

 

isAdmin()) { diff --git a/select_psf.php b/select_psf.php index 1b3b4bed4..fc51c616b 100644 --- a/select_psf.php +++ b/select_psf.php @@ -154,7 +154,7 @@ class=" } ?>" readonly="readonly"/>

parameter( + 'Acuity' )->check() || $_SESSION['task_setting']->parameter( + 'AcuityMode' )->value() == 'off'); $ok = $ok && $_SESSION['task_setting']->parameter( 'SignalNoiseRatio' )->check(); $ok = $ok && $_SESSION['task_setting']->parameter( @@ -191,37 +194,37 @@ include("header.inc.php"); ?> - - - Create a new restoration template with the specified name. - - - Import a Huygens template. - - + + + Create a new restoration
template with the specified name. +
+ + Import a Huygens restoration template (extension "hgsd"). + + Edit the selected restoration template. - - + + Copy the selected restoration template to a new one with the specified name. - - + + Share the selected restoration template with one or more HRM users. - - + + Delete the selected restoration template. - - + + Accept the template. - - + + Reject the template. - - + + Preview the template. - +
isAdmin()) { ?> @@ -539,7 +542,7 @@ class="icon delete" Duplicate isAdmin()) { diff --git a/setup/dbupdate.php b/setup/dbupdate.php index a7ae3b232..ea6383152 100644 --- a/setup/dbupdate.php +++ b/setup/dbupdate.php @@ -442,7 +442,7 @@ function in_array_multi($needle,$haystack) { // Check if the variable dbrevision exists $rs = $db->Execute("SELECT * FROM global_variables WHERE name = 'dbrevision'"); -if ($rs->EOF) { // If the variable dbrevision does not exist, create it and set its value to 0 +if ($rs->EOF ) { // If the variable dbrevision does not exist, create it and set its value to 0 $record = array(); $record["name"] = "dbrevision"; $record["value"] = "0"; @@ -6046,6 +6046,548 @@ function in_array_multi($needle,$haystack) { write_to_log($msg); } +// ----------------------------------------------------------------------------- +// Update to revision 19 +// Description: +// * Update translation for Imaris format +// * Update display name of the Imaris format +// * Add Olympus VSI file format +// * Set ND as multifile format +// * Increase maximum number of iterations to 1000 +// * Add bleaching mode option +// * Extend ChromaticAberration parameter to support 14 values (instead of 5) +// ----------------------------------------------------------------------------- +$n = 19; +if ($current_revision < $n) { + + // Correct Imaris' default. + $tabname = 'possible_values'; + $record = array(); + $record["parameter"] = 'OutputFileFormat'; + $record["value"] = 'IMS (Imaris Classic)'; + $record["translation"] = 'imaris'; + $record["isDefault"] = 'f'; + if (!$db->AutoExecute($tabname, $record, 'UPDATE', + "parameter='OutputFileFormat' and translation='Imaris'")) { + $msg = "Could not correct entry for OutputFileFormat Imaris in possible_values."; + write_message($msg); + write_to_error($msg); + return false; + } + + // Correct Imaris' displayed name to reflect support for Imaris 5.5 files. + $tabname = 'possible_values'; + $record = array(); + $record["parameter"] = 'ImageFileFormat'; + $record["value"] = 'ims'; + $record["translation"] = 'Imaris Classic/Imaris 5.5 (*.ims)'; + $record["isDefault"] = 'f'; + if (!$db->AutoExecute($tabname, $record, 'UPDATE', + "parameter='ImageFileFormat' and value='ims'")) { + $msg = "Could not correct entry for ImageFileFormat ims in possible_values."; + write_message($msg); + write_to_error($msg); + return false; + } + + // Add Olympus VSI file to possible_values + $tabname = 'possible_values'; + $record = array(); + $record["parameter"] = 'ImageFileFormat'; + $record["value"] = 'vsi'; + $record["translation"] = 'Olympus VSI (*.vsi)'; + $record["isDefault"] = 'f'; + + // Skip it if the row is already there. + $query = "SELECT * FROM " . $tabname . " WHERE parameter='" . + $record['parameter'] . "' AND value='" . $record['value'] . "' " . + " AND translation='" . $record["translation"] . "' AND isDefault='" . + $record["isDefault"] . "'"; + if ($db->Execute($query)->RecordCount() == 0) { + if (!$db->AutoExecute($tabname, $record, 'INSERT')) { + $msg = "Could not add entry for Olympus VSI in table 'possible_values'."; + write_message($msg); + write_to_error($msg); + return false; + } + } + + $tabname = "file_extension"; + $record = array(); + $record["file_format"] = "vsi"; + $record["extension"] = "vsi"; + + // Skip it if the row is already there. + $query = "SELECT * FROM " . $tabname . + " WHERE file_format='" . $record['file_format'] . + "' AND extension='" . $record['extension'] . "'"; + if ($db->Execute($query)->RecordCount() == 0) { + $insertSQL = $db->GetInsertSQL($tabname, $record); + if (!$db->Execute($insertSQL)) { + $msg = "Could not add entry for Olympus VSI in table 'file_extension'."; + write_message($msg); + write_to_error($msg); + return; + } + } + + $tabname = "file_format"; + $record = array(); + $record["name"] = "vsi"; + $record["isFixedGeometry"] = "f"; + $record["isSingleChannel"] = "f"; + $record["isVariableChannel"] = "t"; + $record["hucoreName"] = "vsi"; + + // Skip it if the row is already there. + $query = "SELECT * FROM " . $tabname . + " WHERE name='" . $record['name'] . "'"; + if ($db->Execute($query)->RecordCount() == 0) { + $insertSQL = $db->GetInsertSQL($tabname, $record); + if (!$db->Execute($insertSQL)) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_error($msg); + return; + } + } + + // ------- Set ND as multifile format. ------ + $tabname = "file_format"; + $record = array(); + $record["ismultifile"] = 't'; + if (!$db->AutoExecute('file_format', $record, 'UPDATE', "name like 'nd'")) { + $msg = error_message($tabname); + write_message($msg); + write_to_error($msg); + return false; + } + + // Increase the maximum number of iterations to 1000. + $tabname = 'boundary_values'; + $record = array(); + $record["parameter"] = 'NumberOfIterations'; + $record["min"] = '1'; + $record["max"] = '1000'; + $record["min_included"] = 't'; + $record["max_included"] = 't'; + $record["standard"] = null; + if (!$db->AutoExecute($tabname, $record, 'UPDATE', + "parameter='NumberOfIterations' and max='100'")) { + $msg = "Could not update the max value of NumberOfIterations to '1000'."; + write_message($msg); + write_to_error($msg); + return false; + } + + // -------------------- Add bleaching mode option ------------------------ + $tabname = "possible_values"; + $record = array(); + $record["parameter"] = "BleachingMode"; + $record["value"] = "auto"; + $record["translation"] = "Apply a correction for bleaching, if possible"; + $record["isDefault"] = "f"; + + // Skip it if the row is already there. + $query = "SELECT * FROM " . $tabname . + " WHERE parameter='" . $record['parameter'] . + "' AND value='" . $record['value'] . "'"; + if ($db->Execute($query)->RecordCount() == 0) { + $insertSQL = $db->GetInsertSQL($tabname, $record); + if (!$db->Execute($insertSQL)) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_error($msg); + return; + } + } + + $tabname = "possible_values"; + $record = array(); + $record["parameter"] = "BleachingMode"; + $record["value"] = "off"; + $record["translation"] = "Do not apply a bleaching correction"; + $record["isDefault"] = "t"; + + // Skip it if the row is already there. + $query = "SELECT * FROM " . $tabname . + " WHERE parameter='" . $record['parameter'] . + "' AND value='" . $record['value'] . "'"; + if ($db->Execute($query)->RecordCount() == 0) { + $insertSQL = $db->GetInsertSQL($tabname, $record); + if (!$db->Execute($insertSQL)) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_error($msg); + return; + } + } + + // In task_parameter all ChromaticAberration values have to be changed to + // sets of 14 instead of 5. Because this will leave little room (3 + // characters) for each chromatic aberration component it makes more + // sense to split the chromatic aberration components in separate entries + // per channel. + unset($temp); + $tabname = "task_parameter"; + $fields_set = array('owner', 'setting', 'name', 'value'); + $name = "ChromaticAberration"; + $insertArray = array(null, null, null, null, null, null, null, null, null); + $rs = $db->execute("SELECT * FROM " . $tabname . + " WHERE name = '" . $name . "'"); + if ($rs) { + while ($row = $rs->FetchRow()) { + $array = explode('#', $row[3]); + $maxCh = intdiv(count($array), 5); + + // Get sets of 5 components, add null value entries after and 1 in + // front, then implode and add to the database with the channel in + // the value. + for ($ch = 0; $ch < $maxCh; $ch++) { + $inx = $ch * 5 + 1; + $currCh = array_slice($array, $inx, 5); + $currCh = array_merge(array(null), $currCh, $insertArray); + $row[3] = implode($currCh, '#'); + $row[2] = $name . "Ch" . $ch; + for ($i = 0; $i < count($fields_set); $i++) { + $temp[$fields_set[$i]] = $row[$i]; + } + + $insertSQL = $db->GetInsertSQL($tabname, $temp); + if (!$db->Execute($insertSQL)) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_error($msg); + return; + } + } + + // Delete the old entry. + if (!$db->Execute("DELETE FROM " . $tabname . + " WHERE owner='" . $row[0] . + "' AND setting='" . $row[1] . + "' AND name='" . $name . "'")) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_log($msg); + write_to_error($msg); + return; + } + } + } + + // The same for the shared_templates: + $tabname = "shared_task_parameter"; + $fields_set = array('id', 'setting_id', 'owner', 'setting', 'name', 'value'); + $rs = $db->execute("SELECT * FROM " . $tabname . + " WHERE name = '" . $name . "'"); + if ($rs) { + while ($row = $rs->FetchRow()) { + $array = explode('#', $row[5]); + $maxCh = intdiv(count($array), 5); + + // Get sets of 5 components, add null value entries after and 1 in + // front, then implode and add to the database with the channel in + // the value. + for ($ch = 0; $ch < $maxCh; $ch++) { + $inx = $ch * 5 + 1; + $currCh = array_slice($array, $inx, 5); + $currCh = array_merge(array(null), $currCh, $insertArray); + $row[5] = implode($currCh, '#'); + $row[4] = $name . "Ch" . $ch; + for ($i = 0; $i < count($fields_set); $i++) { + $temp[$fields_set[$i]] = $row[$i]; + } + $temp["id"] = null; // An id will be automatically assigned. + + $insertSQL = $db->GetInsertSQL($tabname, $temp); + if (!$db->Execute($insertSQL)) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_error($msg); + return; + } + } + + // Delete the old entry. + if (!$db->Execute("DELETE FROM " . $tabname . + " WHERE id='" . $row[0] . + "' AND setting_id='" . $row[1] . + "' AND owner='" . $row[2] . + "' AND setting='" . $row[3] . + "' AND name='" . $name . "'")) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_log($msg); + write_to_error($msg); + return; + } + } + } + + // Also in possible_values, the old default was actually wrong as well. It + // had a 6'th hashtag. + $tabname = "possible_values"; + $fields_set = array('parameter', 'value', 'translation', 'isDefault'); + $rs = $db->execute("SELECT * FROM " . $tabname . + " WHERE parameter = '" . $name . "'"); + $maxCh = 6; + $defaultCh0 = "#0#0#0#0#1#########"; + $default = "##############"; + if ($rs) { + while ($row = $rs->FetchRow()) { + + // Set new defaults for each channel. + for ($ch = 0; $ch < $maxCh; $ch++) { + $temp["parameter"] = $name . "Ch" . $ch; + if ($ch == 0) { + $temp["value"] = $defaultCh0; + } else { + $temp["value"] = $default; + } + $temp["translation"] = $row[2]; + $temp["isDefault"] = $row[3]; + + $insertSQL = $db->GetInsertSQL($tabname, $temp); + if (!$db->Execute($insertSQL)) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_error($msg); + return; + } + } + + // Delete the old entry. There should only be one. + if (!$db->Execute("DELETE FROM " . $tabname . + " WHERE parameter='" . $row[0] . + "' AND value='" . $row[1] . + "' AND translation='" . $row[2] . + "' AND isDefault='" . $row[3] . "'")) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_log($msg); + write_to_error($msg); + return; + } + } + } + + // -------------------- Add acuity mode option ------------------------ + $tabname = 'possible_values'; + $record = array(); + $record["parameter"] = 'AcuityMode'; + $record["value"] = 'on'; + $record["translation"] = 'Enable acuity mode'; + $record["isDefault"] = 'f'; + + // Skip it if the row is already there. + $query = "SELECT * FROM " . $tabname . + " WHERE parameter='" . $record['parameter'] . + "' AND value='" . $record['value'] . "'"; + if ($db->Execute($query)->RecordCount() == 0) { + $insertSQL = $db->GetInsertSQL($tabname, $record); + if (!$db->Execute($insertSQL)) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_error($msg); + return; + } + } + $tabname = 'possible_values'; + $record = array(); + $record["parameter"] = 'AcuityMode'; + $record["value"] = 'off'; + $record["translation"] = 'Use legacy SNR'; + $record["isDefault"] = 't'; + + // Skip it if the row is already there. + $query = "SELECT * FROM " . $tabname . + " WHERE parameter='" . $record['parameter'] . + "' AND value='" . $record['value'] . "'"; + if ($db->Execute($query)->RecordCount() == 0) { + $insertSQL = $db->GetInsertSQL($tabname, $record); + if (!$db->Execute($insertSQL)) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_error($msg); + return; + } + } + + // -------------------- Add acuity boundary values ------------------------ + $tabname = 'boundary_values'; + $record = array(); + $record["parameter"] = 'Acuity'; + $record["min"] = '-100'; + $record["max"] = '100'; + $record["min_included"] = 't'; + $record["max_included"] = 't'; + $record["standard"] = null; + if ($db->Execute("SELECT * FROM " . $tabname . " WHERE parameter='" . $record["parameter"] . "' AND min='" . $record["min"] . "' AND max='" . $record["max"] . "' AND min_included='" . $record["min_included"] . "' AND max_included='" . $record["max_included"] . "'")->RecordCount() == 0) { + $insertSQL = $db->GetInsertSQL($tabname, $record); + if (!$db->Execute($insertSQL)) { + $msg = "An error occurred while updating the database to revision " . $n . "."; + write_message($msg); + write_to_error($msg); + return; + } + } + + // In HRM 3.8.0 the SNR values are always numeric in order to match the + // current behaviour in the Huygens software. However, the database may + // still be storing qualitative SNR values for restoration templates if the + // deconvolution algorithm was set to QMLE. The qualitative values would be + // saved as integers and mapped according to "1" => "low", "2" => "fair", + // "3" => "good", "4" => "inf". Search through the database and replace + // these instances with appropriate numeric values. + unset($temp); + $tabname = "task_parameter"; + $fields_set = array('owner','setting','name','value'); + $algName = "DeconvolutionAlgorithm"; + $snrName = "SignalNoiseRatio"; + $snrQMLEArray = array("1" => "5.6", "2" => "16.0", + "3" => "33.3", "4" => "1000.0"); + + // Select all decon algorithm entries. + $rs = $db->execute("SELECT * FROM " . $tabname . + " WHERE name = '" . $algName . "'"); + if ($rs) { + while ($row = $rs->FetchRow()) { + $array = explode('#', $row[3]); + $setting = $row[1]; + + // Use the setting to find the corresponding SNR values. + $snrVals = $db->execute("SELECT * FROM " . $tabname . + " WHERE setting = '" . $setting . + "' AND name ='" . $snrName . "'"); + if ($snrVals) { + // Should be just a single row. + $snrRow = $snrVals->FetchRow(); + + // If the decon algorithm entries contain a 'qmle' value, modify + // them according to the conversion array. + for ($ch = 0; $ch < count($array); $ch++) { + if ($array[$ch] == 'qmle') { + $snrArray = explode('#', $snrRow[3]); + if (in_array($snrArray[$ch], array("1","2","3","4"))) { + $snrArray[$ch] = $snrQMLEArray[$snrArray[$ch]]; + $snrRow[3] = implode('#', $snrArray); + } + } + } + for ($i = 0; $i < count($fields_set); $i++) { + $temp[$fields_set[$i]] = $snrRow[$i]; + } + + // Delete the entry. + if (!$db->Execute("DELETE FROM " . $tabname . + " WHERE owner='" . $snrRow[0] . + "' AND setting='" . $snrRow[1] . + "' AND name='" . $snrName . "'")) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_log($msg); + write_to_error($msg); + return; + } + + // Reinsert the entry in the database. + $insertSQL = $db->GetInsertSQL($tabname, $temp); + if (!$db->Execute($insertSQL)) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_error($msg); + return; + } + } + } + } + + // Re-do all of the above for the shared templates. + $tabname = "shared_task_parameter"; + $fields_set = array('id','setting_id','owner','setting','name','value'); + + // Select all decon algorithm entries. + $rs = $db->execute("SELECT * FROM " . $tabname . + " WHERE name = '" . $algName . "'"); + if ($rs) { + while ($row = $rs->FetchRow()) { + $array = explode('#', $row[5]); + $setting = $row[3]; + + // Use the setting to find the corresponding SNR values. + $snrVals = $db->execute("SELECT * FROM " . $tabname . + " WHERE setting = '" . $setting . + "' AND name ='" . $snrName . "'"); + if ($snrVals) { + // Should be just a single row. + $snrRow = $snrVals->FetchRow(); + + // If the decon algorithm entries contain a 'qmle' value, modify + // them according to the conversion array. + for ($ch = 0; $ch < count($array); $ch++) { + if ($array[$ch] == 'qmle') { + $snrArray = explode('#', $snrRow[5]); + if (in_array($snrArray[$ch], array("1","2","3","4"))) { + $snrArray[$ch] = $snrQMLEArray[$snrArray[$ch]]; + $snrRow[5] = implode('#', $snrArray); + } + } + } + for ($i = 0; $i Execute("DELETE FROM " . $tabname . + " WHERE id='" . $snrRow[0] . + "' AND setting_id='" . $snrRow[1] . + "' AND owner='" . $snrRow[2] . + "' AND setting='" . $snrRow[3] . + "' AND name='" . $snrName . "'")) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_log($msg); + write_to_error($msg); + return; + } + + // Reinsert the entry in the database. + $insertSQL = $db->GetInsertSQL($tabname, $temp); + if (!$db->Execute($insertSQL)) { + $msg = "An error occurred while updating " . + "the database to revision " . $n . "."; + write_message($msg); + write_to_error($msg); + return; + } + } + } + } + + // Update revision + if (!update_dbrevision($n)) + return; + + $current_revision = $n; + $msg = "Database successfully updated to revision " . $current_revision . "."; + write_message($msg); + write_to_log($msg); +} fclose($fh); diff --git a/setup/setup_devel.sh b/setup/setup_devel.sh index b0d2c923a..8fa9dc055 100755 --- a/setup/setup_devel.sh +++ b/setup/setup_devel.sh @@ -35,3 +35,7 @@ fi # Make sure to add our source to the autoloader path ${PROJECT_DIR}/composer.phar dump-autoload --optimize --working-dir=${PROJECT_DIR} + +# Set the default coding standard for phpcs +${PROJECT_DIR}/vendor/bin/phpcs --config-set default_standard PSR12 + diff --git a/system.php b/system.php index e937aaa90..3d66dc653 100644 --- a/system.php +++ b/system.php @@ -3,6 +3,7 @@ // Copyright and license notice: see license.txt use hrm\Nav; +use hrm\OmeroConnection; use hrm\System; use hrm\Util; @@ -137,6 +138,14 @@ + + + + + + + + + + + + + + + + + + + + + + + +
value()[13] == null) { echo 'hidden=true'; }?> + onclick="editChromaticChannelWith14Params()"/>
- From
image + Extract
from
image
- Huygens
template + Import
Huygens
template
- Huygens
template + Import
Huygens
template
+ HuCore server type + + +
Licenses in use @@ -169,6 +178,29 @@ ?>
+ HRM-OMERO Connector + +   +
+ Version + + +
System diff --git a/task_parameter.php b/task_parameter.php index c2f189afa..51176864e 100644 --- a/task_parameter.php +++ b/task_parameter.php @@ -71,14 +71,8 @@ header("Location: " . "post_processing.php"); exit(); } else { - - $saved = $_SESSION['task_setting']->save(); - if ($saved) { - header("Location: " . "select_task_settings.php"); - exit(); - } else { - $message = $_SESSION['task_setting']->message(); - } + header("Location: " . "select_hpc.php"); + exit(); } } else { $message = $_SESSION['task_setting']->message(); @@ -260,7 +254,6 @@ $_SESSION['task_setting']->parameter("SignalNoiseRatio"); $signalNoiseRatioValue = $signalNoiseRatioParam->value(); - /* Loop over the channels. */ for ($ch = 0; $ch < $chanCnt; $ch++) { @@ -340,45 +333,24 @@ class="multichannelinput" $value = $signalNoiseRatioValue[$ch]; ?> +
> Ch: - - = 1 - && $signalNoiseRatioValue[$ch] <= 4) { - if ($optionIdx == $signalNoiseRatioValue[$ch]) - $option .= "selected=\"selected\" "; - } else { - if ($optionIdx == 2) - $option .= "selected=\"selected\" "; - } - } else { - if ($optionIdx == 2) - $option .= "selected=\"selected\" "; - } - $option .= "value=\"" . $optionIdx . "\">"; - if ($optionIdx == 1) - $option .= "low"; - else if ($optionIdx == 2) - $option .= "fair"; - else if ($optionIdx == 3) - $option .= "good"; - else if ($optionIdx == 4) - $option .= "inf"; - echo $option; - } - - ?> - + type="text" + size="8" + value="" + class="multichannelinput" + onchange="copySnrToOtherAlgorithms(,this)"/> +   +
@@ -448,6 +420,121 @@ class="multichannelinput" + +
+ + + + ? + Acuity Mode + + +
+ +
Acuity mode: + +
+ + parameter("Acuity"); + $acuityValue = $acuityParam->value(); + + /* Loop over the channels. */ + for ($ch = 0; $ch < $chanCnt; $ch++) { + + // All acuity divs need to be initialized with the value from the saved parameter. + $value = $acuityValue[$ch]; + ?> + + + + "; + } + ?> + + + +
+
> + + Ch: +     + + +   + + +
+
+ + + + + + +
@@ -494,6 +581,54 @@ class="selection">
+ +
+
+ + + + ? + + Bleaching Correction + + + +
+ +
isAdmin() @@ -826,29 +961,10 @@ class="selection"> onmouseover="TagToTip('ttSpanCancel' )" onmouseout="UnTip()" onclick="deleteValuesAndRedirect('select_task_settings.php' );"/> - - isAdmin() - || $_SESSION['task_setting']->isEligibleForCAC() - || $_SESSION['task_setting']->isEligibleForTStabilization($_SESSION['setting'])) { - ?> - - - -
@@ -932,4 +1048,4 @@ class="icon save" \ No newline at end of file +