diff --git a/docs/latest/README.md b/docs/latest/README.md deleted file mode 100644 index e4cd6d152..000000000 --- a/docs/latest/README.md +++ /dev/null @@ -1,95 +0,0 @@ -
-
- Looking for a Live Demo? - Preview The OHIF Viewer -
-
- - - -
-
- -> ATTENTION! You are looking at the docs for the `OHIF-v3` Viewer (third time is always a charm). If you're looking for the our `OHIF-v2` (React stable version) or OHIF-v1 (deprecated `Meteor` version) select it's version from the dropdown box in the top left corner -> of this page. - -  -# Introduction - -The [Open Health Imaging Foundation][ohif-org] (OHIF) Viewer is an open source, -web-based, medical imaging viewer. It can be configured to connect to Image -Archives that support [DicomWeb][dicom-web], and offers support for mapping to -proprietary API formats. OHIF maintained extensions add support for viewing, -annotating, and reporting on DICOM images in 2D (slices) and 3D (volumes). - - - -![OHIF Viewer Screenshot](./assets/img/OHIF-viewer.png) - -
The OHIF Viewer v3: A general purpose DICOM Viewer (Live Demo)
- - -  - -## What's new in `OHIF-v3` -`OHIF-v3` is our second try for a React-based viewer, and is the third version of -our medical image web viewers from the start. The summary of changes include: - -- Addition of workflow modes - - - Often, medical imaging use cases involves lots of specific workflows that re-use - functionalities. We have added the capability of workflow modes, that enable people - to customize user interface and configure application for specific workflow. - - The idea is to re-use the functionalities that extensions provide and create a workflow. - Brain segmentation workflow is different from prostate segmentation in UI for sure; however, they share the segmentation tools that can be re-used. - - Our vision is that technical people focus of developing extensions which provides - core functionalities, and experts to build modes by picking the appropriate functionalities - from each extension. - - -- Redux store has been removed from the viewer, and a cleaner, more powerful -- tailwind CSS -- End-to-end test suite - - -Below, you can find the gap analysis between the `OHIF-v2` and `OHIF-v3`: - - -{% include "./_gap_analysis.md" %} - - - -## Where to next? - - -The Open Health Imaging Foundation intends to provide a simple general purpose -DICOM Viewer which can be easily extended for specific uses. If you find -yourself unable to extend the viewer for your purposes, please reach out via our -[GitHub issues][gh-issues]. We are actively seeking feedback on ways to improve -our integration and extension points. - - -Check out these helpful links: - -- Ready to dive into some code? Check out our - [Getting Started Guide](./development/getting-started.md). -- We're an active, vibrant community. - [Learn how you can be more involved.](./development/contributing.md) -- Feeling lost? Read our [help page](./help.md). - - - - - - - - - - -[ohif-org]: http://www.ohif.org -[ohif-demo]: http://viewer.ohif.org/ -[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb -[gh-issues]: https://github.com/OHIF/Viewers/issues - diff --git a/docs/latest/assets/designs/architecture-diagram b/docs/latest/assets/designs/architecture-diagram deleted file mode 100644 index bbf6cf58b..000000000 Binary files a/docs/latest/assets/designs/architecture-diagram and /dev/null differ diff --git a/docs/latest/assets/designs/canny-full.fig b/docs/latest/assets/designs/canny-full.fig deleted file mode 100644 index 8756e9f79..000000000 Binary files a/docs/latest/assets/designs/canny-full.fig and /dev/null differ diff --git a/docs/latest/assets/designs/cloud.svg b/docs/latest/assets/designs/cloud.svg deleted file mode 100644 index ad04389c6..000000000 --- a/docs/latest/assets/designs/cloud.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - diff --git a/docs/latest/assets/designs/embedded-viewer-diagram b/docs/latest/assets/designs/embedded-viewer-diagram deleted file mode 100644 index 182ad2323..000000000 Binary files a/docs/latest/assets/designs/embedded-viewer-diagram and /dev/null differ diff --git a/docs/latest/assets/designs/nginx-image-archive.fig b/docs/latest/assets/designs/nginx-image-archive.fig deleted file mode 100644 index 460ae95dd..000000000 Binary files a/docs/latest/assets/designs/nginx-image-archive.fig and /dev/null differ diff --git a/docs/latest/assets/designs/npm-logo-red.svg b/docs/latest/assets/designs/npm-logo-red.svg deleted file mode 100644 index 8e4aac5d2..000000000 --- a/docs/latest/assets/designs/npm-logo-red.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/docs/latest/assets/designs/scope-of-project.fig b/docs/latest/assets/designs/scope-of-project.fig deleted file mode 100644 index 5eb82e561..000000000 Binary files a/docs/latest/assets/designs/scope-of-project.fig and /dev/null differ diff --git a/docs/latest/assets/designs/user-access-control-request-flow.fig b/docs/latest/assets/designs/user-access-control-request-flow.fig deleted file mode 100644 index 8982a8fed..000000000 Binary files a/docs/latest/assets/designs/user-access-control-request-flow.fig and /dev/null differ diff --git a/docs/latest/assets/img/WORKFLOW_DEPLOY.png b/docs/latest/assets/img/WORKFLOW_DEPLOY.png deleted file mode 100644 index 3e562a797..000000000 Binary files a/docs/latest/assets/img/WORKFLOW_DEPLOY.png and /dev/null differ diff --git a/docs/latest/assets/img/WORKFLOW_PR_CHECKS.png b/docs/latest/assets/img/WORKFLOW_PR_CHECKS.png deleted file mode 100644 index f9c4a568b..000000000 Binary files a/docs/latest/assets/img/WORKFLOW_PR_CHECKS.png and /dev/null differ diff --git a/docs/latest/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png b/docs/latest/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png deleted file mode 100644 index 54b0aa39f..000000000 Binary files a/docs/latest/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png and /dev/null differ diff --git a/docs/latest/assets/img/WORKFLOW_RELEASE.png b/docs/latest/assets/img/WORKFLOW_RELEASE.png deleted file mode 100644 index f3c2a8069..000000000 Binary files a/docs/latest/assets/img/WORKFLOW_RELEASE.png and /dev/null differ diff --git a/docs/latest/assets/img/cornerstone-tools-link.gif b/docs/latest/assets/img/cornerstone-tools-link.gif deleted file mode 100644 index 22fde7a7f..000000000 Binary files a/docs/latest/assets/img/cornerstone-tools-link.gif and /dev/null differ diff --git a/docs/latest/assets/img/embedded-viewer-diagram.png b/docs/latest/assets/img/embedded-viewer-diagram.png deleted file mode 100644 index 426cb7ab8..000000000 Binary files a/docs/latest/assets/img/embedded-viewer-diagram.png and /dev/null differ diff --git a/docs/latest/assets/img/jwt-explained.png b/docs/latest/assets/img/jwt-explained.png deleted file mode 100644 index f26509a16..000000000 Binary files a/docs/latest/assets/img/jwt-explained.png and /dev/null differ diff --git a/docs/latest/assets/img/keycloak-default-theme.png b/docs/latest/assets/img/keycloak-default-theme.png deleted file mode 100644 index 0ea77f965..000000000 Binary files a/docs/latest/assets/img/keycloak-default-theme.png and /dev/null differ diff --git a/docs/latest/assets/img/keycloak-ohif-theme.png b/docs/latest/assets/img/keycloak-ohif-theme.png deleted file mode 100644 index ad060f262..000000000 Binary files a/docs/latest/assets/img/keycloak-ohif-theme.png and /dev/null differ diff --git a/docs/latest/assets/img/locizeSponsor.svg b/docs/latest/assets/img/locizeSponsor.svg deleted file mode 100644 index 1139aa2c7..000000000 --- a/docs/latest/assets/img/locizeSponsor.svg +++ /dev/null @@ -1,187 +0,0 @@ - - - - Custom Preset 2 Copy - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/latest/assets/img/nginx-image-archive.png b/docs/latest/assets/img/nginx-image-archive.png deleted file mode 100644 index bd7547965..000000000 Binary files a/docs/latest/assets/img/nginx-image-archive.png and /dev/null differ diff --git a/docs/latest/assets/img/open-graph.png b/docs/latest/assets/img/open-graph.png deleted file mode 100644 index 5b881abdf..000000000 Binary files a/docs/latest/assets/img/open-graph.png and /dev/null differ diff --git a/docs/latest/assets/img/scope-of-project.png b/docs/latest/assets/img/scope-of-project.png deleted file mode 100644 index 6daac8bee..000000000 Binary files a/docs/latest/assets/img/scope-of-project.png and /dev/null differ diff --git a/docs/latest/assets/img/services.png b/docs/latest/assets/img/services.png deleted file mode 100644 index 569c046c0..000000000 Binary files a/docs/latest/assets/img/services.png and /dev/null differ diff --git a/docs/latest/assets/img/surge-deploy.gif b/docs/latest/assets/img/surge-deploy.gif deleted file mode 100644 index 545f06863..000000000 Binary files a/docs/latest/assets/img/surge-deploy.gif and /dev/null differ diff --git a/docs/latest/assets/img/ui-services.png b/docs/latest/assets/img/ui-services.png deleted file mode 100644 index dd5306377..000000000 Binary files a/docs/latest/assets/img/ui-services.png and /dev/null differ diff --git a/docs/latest/assets/img/user-access-control-request-flow.png b/docs/latest/assets/img/user-access-control-request-flow.png deleted file mode 100644 index 573c83503..000000000 Binary files a/docs/latest/assets/img/user-access-control-request-flow.png and /dev/null differ diff --git a/docs/latest/configuring/data-source.md b/docs/latest/configuring/data-source.md deleted file mode 100644 index f47b12241..000000000 --- a/docs/latest/configuring/data-source.md +++ /dev/null @@ -1,194 +0,0 @@ -## Set up a local DICOM server - -- [Set up a local DICOM server](#set-up-a-local-dicom-server) - - [Requirements](#requirements) -- [Open Source DICOM Image Archives](#open-source-dicom-image-archives) - - [Running Orthanc](#running-orthanc) - - [Orthanc: Learn More](#orthanc-learn-more) - - [Connecting to Orthanc](#connecting-to-orthanc) - - [Configuration: Learn More](#configuration-learn-more) - - [Running DCM4CHEE](#running-dcm4chee) - - -ATTENTION! Already have a remote or local server? Skip to the -[configuration section](#configuration-learn-more) below. - -While the OHIF Viewer can work with any data source, the easiest to configure -are the ones that follow the [DICOMWeb][dicom-web] spec. - -1. Choose and install an Image Archive -2. Upload data to your archive (e.g. with DCMTK's [storescu][storescu] or your - archive's web interface) -3. Keep the server running - -For our purposes, we will be using `Orthanc`, but you can see a list of -[other Open Source options](#open-source-dicom-image-archives) below. - -### Requirements - -- Docker - - [Docker for Mac](https://docs.docker.com/docker-for-mac/) - - [Docker for Windows (recommended)](https://docs.docker.com/docker-for-windows/) - - [Docker Toolbox for Windows](https://docs.docker.com/toolbox/toolbox_install_windows/) - -_Not sure if you have `docker` installed already? Try running `docker --version` -in command prompt or terminal_ - -> If you are using `Docker Toolbox` you need to change the _PROXY_DOMAIN_ -> parameter in _platform/viewer/package.json_ to http://192.168.99.100:8042 or -> the ip docker-machine ip throws. This is the value [`WebPack`][webpack-proxy] -> uses to proxy requests - - - -## Open Source DICOM Image Archives - -There are a lot of options available to you to use as a local DICOM server. Here are some of the more popular ones: - -| Archive | Installation | -| --------------------------------------------- | ---------------------------------- | -| [DCM4CHEE Archive 5.x][dcm4chee] | [W/ Docker][dcm4chee-docker] | -| [Orthanc][orthanc] | [W/ Docker][orthanc-docker] | -| [DICOMcloud][dicomcloud] (**DICOM Web only**) | [Installation][dicomcloud-install] | -| [OsiriX][osirix] (**Mac OSX only**) | Desktop Client | -| [Horos][horos] (**Mac OSX only**) | Desktop Client | - -_Feel free to make a Pull Request if you want to add to this list._ - - -Below, we will focus on `DCM4CHEE` and `Orthanc` usage: - - - -### Running Orthanc - -_Start Orthanc:_ - -```bash -# Runs orthanc so long as window remains open -yarn run orthanc:up -``` - -_Upload your first Study:_ - -1. Navigate to - [Orthanc's web interface](http://localhost:8042/app/explorer.html) at - `http://localhost:8042/app/explorer.html` in a web browser. -2. In the top right corner, click "Upload" -3. Click "Select files to upload..." and select one or more DICOM files -4. Click "Start the upload" - -#### Orthanc: Learn More - -You can see the `docker-compose.yml` file this command runs at -[`/.docker/Nginx-Orthanc/`][orthanc-docker-compose], and more on -Orthanc for Docker in [Orthanc's documentation][orthanc-docker]. - -#### Connecting to Orthanc - -Now that we have a local Orthanc instance up and running, we need to configure -our web application to connect to it. Open a new terminal window, navigate to -this repository's root directory, and run: - -```bash -# If you haven't already, enable yarn workspaces -yarn config set workspaces-experimental true - -# Restore dependencies -yarn install - -# Run our dev command, but with the local orthanc config -yarn run dev:orthanc -``` - -#### Configuration: Learn More - -> For more configuration fun, check out the -> [Essentials Configuration](./index.md) guide. - -Let's take a look at what's going on under the hood here. `yarn run dev:orthanc` -is running the `dev:orthanc` script in our project's `package.json` (inside `platform/viewer`). That script -is: - -```js -cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker_nginx-orthanc.js webpack-dev-server --config .webpack/webpack.pwa.js -w -``` - -- `cross-env` sets three environment variables - - PROXY_TARGET: `/dicom-web` - - PROXY_DOMAIN: `http://localhost:8042` - - APP_CONFIG: `config/docker_nginx-orthanc.js` -- `webpack-dev-server` runs using the `.webpack/webpack.pwa.js` configuration - file. It will watch for changes and update as we develop. - -`PROXY_TARGET` and `PROXY_DOMAIN` tell our development server to proxy requests -to `Orthanc`. This allows us to bypass CORS issues that normally occur when -requesting resources that live at a different domain. - -The `APP_CONFIG` value tells our app which file to load on to `window.config`. -By default, our app uses the file at -`/platform/viewer/public/config/default.js`. Here is what that -configuration looks like: - -```js -window.config = { - routerBasename: '/', - extensions: [], - modes: [], - showStudyList: true, - dataSources: [ - { - friendlyName: 'dcmjs DICOMWeb Server', - namespace: 'org.ohif.default.dataSourcesModule.dicomweb', - sourceName: 'dicomweb', - configuration: { - name: 'DCM4CHEE', - wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado', - qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', - wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', - qidoSupportsIncludeField: true, - supportsReject: true, - imageRendering: 'wadors', - thumbnailRendering: 'wadors', - enableStudyLazyLoad: true, - supportsFuzzyMatching: true, - supportsWildcard: true, - }, - }, - ], - defaultDataSourceName: 'dicomweb', -}; -``` - -To learn more about how you can configure the OHIF Viewer, check out our -[Configuration Guide](./index.md). - - - -### Running DCM4CHEE - -dcm4che is a collection of open source applications for healthcare enterprise written in Java programming language -which implements DICOM standard. dcm4chee (extra 'e' at the end) is dcm4che project -for an Image Manager/Image Archive which provides storage, retrieval and other functionalities. -You can read more about dcm4chee in their website [here](https://www.dcm4che.org/) - -DCM4chee installation is out of scope for these tutorials and can be found [here](https://github.com/dcm4che/dcm4chee-arc-light/wiki/Run-minimum-set-of-archive-services-on-a-single-host) - -An overview of steps for running OHIF Viewer using a local DCM4CHEE is shown below: - -
- - - - -[dcm4chee]: https://github.com/dcm4che/dcm4chee-arc-light -[dcm4chee-docker]: https://github.com/dcm4che/dcm4chee-arc-light/wiki/Running-on-Docker -[orthanc]: https://www.orthanc-server.com/ -[orthanc-docker]: http://book.orthanc-server.com/users/docker.html -[dicomcloud]: https://github.com/DICOMcloud/DICOMcloud -[dicomcloud-install]: https://github.com/DICOMcloud/DICOMcloud#running-the-code -[osirix]: http://www.osirix-viewer.com/ -[horos]: https://www.horosproject.org/ -[default-config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/public/config/default.js -[html-templates]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/html-templates -[config-files]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/config diff --git a/docs/latest/configuring/index.md b/docs/latest/configuring/index.md deleted file mode 100644 index fc0b8b62d..000000000 --- a/docs/latest/configuring/index.md +++ /dev/null @@ -1,190 +0,0 @@ -# Configuring Data Source - - - -- [Configuring Data Source](#configuring-data-source) - - [Overview](#overview) - - [Configuration Files](#configuration-files) - - [Environment Variables](#environment-variables) - - -## Overview - -After following the steps outlined in -[Getting Started](./../development/getting-started.md), you'll notice that the -OHIF Viewer has data for several studies and their images. You didn't add this -data, so where is it coming from? - -By default, the viewer is configured to connect to a remote server hosted by the -nice folks over at [dcmjs.org][dcmjs-org]. While convenient for getting started, -the time may come when you want to develop using your own data either locally or -remotely. - - -## Configuration Files - -The configuration for our viewer is in the `platform/viewer/public/config` -directory. Our build process knows which configuration file to use based on the -`APP_CONFIG` environment variable. By default, its value is -[`config/default.js`][default-config]. The majority of the viewer's features, -and registered extension's features, are configured using this file. - -The simplest way is to update the existing default config: - -> _/platform/viewer/public/config/default.js_ - -```js -window.config = { - routerBasename: '/', - extensions: [], - modes: [], - showStudyList: true, - dataSources: [ - { - friendlyName: 'dcmjs DICOMWeb Server', - namespace: 'org.ohif.default.dataSourcesModule.dicomweb', - sourceName: 'dicomweb', - configuration: { - name: 'DCM4CHEE', - wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado', - qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', - wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', - qidoSupportsIncludeField: true, - supportsReject: true, - imageRendering: 'wadors', - thumbnailRendering: 'wadors', - enableStudyLazyLoad: true, - supportsFuzzyMatching: true, - supportsWildcard: true, - }, - }, - ], - defaultDataSourceName: 'dicomweb', -}; - -``` - - -> As you can see a new change in `OHIF-v3` is the addition of `dataSources`. -> You can build your own datasource and map it to the internal data structure of OHIF’s > metadata and enjoy using other peoples developed mode on your own data! -> -> You can read more about data sources at [Data Source section in Modes](../modes/index.md) -> - - - - - - - -The configuration can also be written as a JS Function in case you need to inject dependencies like external services: - - -```js -window.config = ({ servicesManager } = {}) => { - const { UIDialogService } = servicesManager.services; - return { - cornerstoneExtensionConfig: { - tools: { - ArrowAnnotate: { - configuration: { - getTextCallback: (callback, eventDetails) => UIDialogService.create({... - } - } - }, - }, - routerBasename: '/', - dataSources: [ - { - friendlyName: 'dcmjs DICOMWeb Server', - namespace: 'org.ohif.default.dataSourcesModule.dicomweb', - sourceName: 'dicomweb', - configuration: { - name: 'DCM4CHEE', - wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado', - qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', - wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', - qidoSupportsIncludeField: true, - supportsReject: true, - imageRendering: 'wadors', - thumbnailRendering: 'wadors', - enableStudyLazyLoad: true, - supportsFuzzyMatching: true, - supportsWildcard: true, - }, - }, - ], - defaultDataSourceName: 'dicomweb', - }; -}; -``` - - - -## Environment Variables - -We use environment variables at build and dev time to change the Viewer's -behavior. We can update the `HTML_TEMPLATE` to easily change which extensions -are registered, and specify a different `APP_CONFIG` to connect to an -alternative data source (or even specify different default hotkeys). - -| Environment Variable | Description | Default | -| -------------------- | -------------------------------------------------------------------------------------------------- | ------------------- | -| `HTML_TEMPLATE` | Which [HTML template][html-templates] to use as our web app's entry point. Specific to PWA builds. | `index.html` | -| `PUBLIC_URL` | The route relative to the host that the app will be served from. Specific to PWA builds. | `/` | -| `APP_CONFIG` | Which [configuration file][config-file] to copy to output as `app-config.js` | `config/default.js` | -| `PROXY_TARGET` | When developing, proxy requests that match this pattern to `PROXY_DOMAIN` | `undefined` | -| `PROXY_DOMAIN` | When developing, proxy requests from `PROXY_TARGET` to `PROXY_DOMAIN` | `undefined` | - - - - - - - -You can also create a new config file and specify its path relative to the build -output's root by setting the `APP_CONFIG` environment variable. You can set the -value of this environment variable a few different ways: - -- ~[Add a temporary environment variable in your shell](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-temporary-environment-variables-in-your-shell)~ - - Previous `react-scripts` functionality that we need to duplicate with - `dotenv-webpack` -- ~[Add environment specific variables in `.env` file(s)](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-development-environment-variables-in-env)~ - - Previous `react-scripts` functionality that we need to duplicate with - `dotenv-webpack` -- Using the `cross-env` package in an npm script: - - `"build": "cross-env APP_CONFIG=config/my-config.js react-scripts build"` - -After updating the configuration, `yarn run build` to generate updated build -output. - - - - - - -[dcmjs-org]: https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado -[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb -[storescu]: http://support.dcmtk.org/docs/storescu.html -[webpack-proxy]: https://webpack.js.org/configuration/dev-server/#devserverproxy -[orthanc-docker-compose]: https://github.com/OHIF/Viewers/tree/master/.docker/Nginx-Orthanc - -[dcm4chee]: https://github.com/dcm4che/dcm4chee-arc-light -[dcm4chee-docker]: https://github.com/dcm4che/dcm4chee-arc-light/wiki/Running-on-Docker -[orthanc]: https://www.orthanc-server.com/ -[orthanc-docker]: http://book.orthanc-server.com/users/docker.html -[dicomcloud]: https://github.com/DICOMcloud/DICOMcloud -[dicomcloud-install]: https://github.com/DICOMcloud/DICOMcloud#running-the-code -[osirix]: http://www.osirix-viewer.com/ -[horos]: https://www.horosproject.org/ -[default-config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/public/config/default.js -[html-templates]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/html-templates -[config-files]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/config - diff --git a/docs/latest/deployment/index.md b/docs/latest/deployment/index.md deleted file mode 100644 index e60e4ed39..000000000 --- a/docs/latest/deployment/index.md +++ /dev/null @@ -1,302 +0,0 @@ -# Deployment - -The OHIF Viewer can be embedded in other web applications via it's [packaged -script source][viewer-npm], or served up as a stand-alone PWA ([progressive web -application][pwa-url]) by building and hosting a collection of static assets. In -either case, you will need to configure your instance of the Viewer so that it -can connect to your data source (the database or PACS that provides the data -your Viewer will display). - -## Overview - -Our goal is to make deployment as simple and painless as possible; however, -there is an inherent amount of complexity in configuring and deploying web -applications. If you find yourself a little lost, please don't hesitate to -[reach out for help](/help.md) - -## Deployment Scenarios - -### Embedded Viewer (deprecated) -`OHIF-v3` has deprecated deploying the viewer as an embedded viewer the number of underlying -libraries that run web workers are increasing for OHIF. An example of these libraries is -OHIF's 3D rendering functionality that is provided by `vtk-js`. - -### Stand-alone Viewer - -Deploying the OHIF Viewer as a stand-alone web application provides many -benefits, but comes at the cost of time and complexity. Some benefits include: - -_Today:_ - -- Leverage [extensions](/extensions/index.md) and [modes](/modes/index.md) to drop-in powerful new features -- Add routes and customize the viewer's workflow -- Finer control over styling and whitelabeling - -_In the future:_ - -- The ability to package the viewer for [App Store distribution][app-store] -- Leverage `service-workers` for offline support and speed benefits from caching - -#### Hosted Static Assets - -At the end of the day, a production OHIF Viewer instance is a collection of -HTML, CSS, JS, Font Files, and Images. We "build" those files from our -`source code` with configuration specific to our project. We then make those -files publicly accessible by hosting them on a Web Server. - -If you have not deployed a web application before, this may be a good time to -[reach out for help](/help.md), as these steps assume prior web development and -deployment experience. - -##### Part 1 - Build Production Assets - -"Building", or creating, the files you will need is the same regardless of the -web host you choose. You can find detailed instructions on how to configure and -build the OHIF Viewer in our -["Build for Production" guide](./recipes/build-for-production.md). - -##### Part 2 - Host Your App - -There are a lot of [benefits to hosting static assets][host-static-assets] over -dynamic content. You can find instructions on how to host your build's output -via one of these guides: - -_Drag-n-drop_ - -- [Netlify: Drop](/deployment/recipes/static-assets.md#netlify-drop) - -_Easy_ - -- [Surge.sh](/deployment/recipes/static-assets.md#surgesh) -- [GitHub Pages](/deployment/recipes/static-assets.md#github-pages) - -_Advanced_ - -- [AWS S3 + Cloudfront](/deployment/recipes/static-assets.md#aws-s3--cloudfront) -- [GCP + Cloudflare](/deployment/recipes/static-assets.md#gcp--cloudflare) -- [Azure](/deployment/recipes/static-assets.md#azure) - -## Data - -The OHIF Viewer is able to connect to any data source that implements the [DICOM -Web Standard][dicom-web-standard]. [DICOM Web][dicom-web] refers to RESTful -DICOM Services -- a recently standardized set of guidelines for exchanging -medical images and imaging metadata over the internet. Not all archives fully -support it yet, but it is gaining wider adoption. - -### Configure Connection - -If you have an existing archive and intend to host the OHIF Viewer at the same -domain name as your archive, then connecting the two is as simple as following -the steps layed out in our -[Configuration Essentials Guide](./../configuring/index.md). - -#### What if I don't have an imaging archive? - -We provide some guidance on configuring a local image archive in our -[Data Source Essentials](./../configuring/index.md#set-up-a-local-DICOM-server) guide. Hosting an -archive remotely is a little trickier. You can check out some of our -[advanced recipes](#recipes) for modeled setups that may work for you. - -#### What if I intend to host the OHIF Viewer at a different domain? - -There are two important steps to making sure this setup works: - -1. Your Image Archive needs to be exposed, in some way, to the open web. This - can be directly, or through a `reverse proxy`, but the Viewer needs _some - way_ to request it's data. -2. \* Your Image Archive needs to have appropriate CORS (Cross-Origin Resource - Sharing) Headers - -> \* Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional -> HTTP headers to tell a browser to let a web application running at one origin -> (domain) have permission to access selected resources from a server at a -> different origin. - [MDN Web Docs: Web - Http - CORS][cors] - -Most image archives do not provide either of these features "out of the box". -It's common to use IIS, Nginx, or Apache to route incoming requests and append -appropriate headers. You can find an example of this setup in our -[Nginx + Image Archive Deployment Recipe](./recipes/nginx--image-archive.md). - -#### What if my archive doesn't support DicomWeb? - -It's possible to supply all Study data via JSON format, in the event you do not have a DicomWeb endpoint. -You can host all of the relevant files on any web accessible server (Amazon S3, Azure Blob Storage, Local file server etc.) - -This JSON is supplied via the '?url=' query parameter. -It should reference an endpoint that returns **application/json** formatted text. - -If you do not have an API, you can simply return a text file containing the JSON from any web server. - - -You tell the OHIF viewer to use JSON by using the `dicomjson` datasource and appending `'?url='` query to your mode's route: - -eg. `https://my-test-ohif-server/myMode/dicomjson?url=https://my-json-server/study-uid.json` - - -The returned JSON object must contain a single root object with a 'studies' array. - -You can read more about using different data sources for mode's routes [here](../modes/routes.md#route-path) - -*Sample JSON format:* -```JSON -{ - "studies": [ - { - "StudyInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.78", - "StudyDescription": "BRAIN SELLA", - "StudyDate": "20010108", - "StudyTime": "120022", - "PatientName": "MISTER^MR", - "PatientId": "832040", - "series": [ - { - "SeriesDescription": "SAG T-1", - "SeriesInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.121", - "SeriesNumber": 2, - "SeriesDate": "20010108", - "SeriesTime": "120318", - "Modality": "MR", - "instances": [ - { - "metadata": { - "Columns": 512, - "Rows": 512, - "InstanceNumber": 3, - "AcquisitionNumber": 0, - "PhotometricInterpretation": "MONOCHROME2", - "BitsAllocated": 16, - "BitsStored": 16, - "PixelRepresentation": 1, - "SamplesPerPixel": 1, - "PixelSpacing": [0.390625, 0.390625], - "HighBit": 15, - "ImageOrientationPatient": [0,1,0,0,0,-1], - "ImagePositionPatient": [11.600000,-92.500000, 98.099998], - "FrameOfReferenceUID": "1.2.840.113619.2.5.1762583153.223134.978956938.470", - "ImageType": ["ORIGINAL","PRIMARY","OTHER"], - "Modality": "MR", - "SOPInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.124", - "SeriesInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.121", - "StudyInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.78" - }, - "url": "dicomweb://s3.amazonaws.com/lury/MRStudy/1.2.840.113619.2.5.1762583153.215519.978957063.124.dcm" - } - ] - } - ] - } - ] -} -``` -More info on this JSON format can be found here [Issue #1500](https://github.com/OHIF/Viewers/issues/1500) - - -**Implementation Notes:** - - -1. For each instance url (dicom object) in the returned JSON, you must prefix the `url` with `dicomjson:` in order for the cornerstone image loader to retrieve it correctly. - eg. `https://image-server/my-image.dcm` ---> `dicomjson:https://image-server/my-image.dcm` -2. The JSON format above is compatible with >= v3.7.8 of the application in `V2` version. Older versions of the viewer used a different JSON format. As of 20/04/20 the public [https://viewer.ohif.org/] is a pre 3.0 version that does not support this format yet. -3. The JSON format is case-sensitive. Please ensure you have matched casing with the naturalised Dicom format referenced in [Issue #1500](https://github.com/OHIF/Viewers/issues/1500). - -*CORS Issues (Cross-Origin Resource Sharing)* - -If you host a JSON API or Images on a different domain from the the app itself, you will likely have CORS issues. This will also happen when testing from Localhost and reaching out to remote servers. -Even if the domain is the same, different ports, subdomains or protocols (https vs http) will also cause CORS errors. -You will to need add a configuration on each server hosting these assets to allow your App server origin. - -For example: - -Lets assume your application is hosted on `https://my-ohif-server.com`. - -Your JSON API is hosted on `https://my-json-api.aws.com` - -And your images are stored on Amazon S3 at `https://my-s3-bucket.aws.com` - -When you first start your application, browsing to `https://my-ohif-server.com/myMode/dicomjson?url=https://my-json-api.aws.com/api/my-json-study-info.json`, you will likely get a CORS error in the browser console as it tries to connect to `https://my-json-api.aws.com`. - -Adding a setting on the JSON server to allow the CORS origin = `https://my-ohif-server.com` should solve this. - -Next, you will likely get a similar CORS error, as the browser tries to go to `https://my-s3-bucket.aws.com`. -You will need to go to the S3 bucket configuration, and add a CORS setting to allow origin = `https://my-ohif-server.com`. - -Essentially, whenever the application connects to a remote resource, you will need to add the applications url to the allowed CORS Origins on that resource. Adding an origin similar to https://localhost:3000 will also allow for local testing. - - -### Securing Your Data -Coming soon - - - -### Recipes - -We've included a few recipes for common deployment scenarios. There are many, -many possible configurations, so please don't feel limited to these setups. -Please feel free to suggest or contribute your own recipes. - - -- [Build for Production](./recipes/build-for-production.md) -- [Static](./recipes/static-assets.md) -- [Nginx + Image Archive](./recipes/nginx--image-archive.md) -- [User Account Control](./recipes/user-account-control.md) - - - - -[viewer-npm]: https://www.npmjs.com/package/@ohif/viewer -[pwa-url]: https://developers.google.com/web/progressive-web-apps/ -[static-assets-url]: https://www.maxcdn.com/one/visual-glossary/static-content/ -[app-store]: https://medium.freecodecamp.org/i-built-a-pwa-and-published-it-in-3-app-stores-heres-what-i-learned-7cb3f56daf9b -[dicom-web-standard]: https://www.dicomstandard.org/dicomweb/ -[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb -[host-static-assets]: https://www.netlify.com/blog/2016/05/18/9-reasons-your-site-should-be-static/ -[cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS -[code-flows]: https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660 -[code-sandbox]: https://codesandbox.io/s/viewer-script-tag-tprch - diff --git a/docs/latest/deployment/recipes/build-for-production.md b/docs/latest/deployment/recipes/build-for-production.md deleted file mode 100644 index a77ab21cb..000000000 --- a/docs/latest/deployment/recipes/build-for-production.md +++ /dev/null @@ -1,135 +0,0 @@ -# Build for Production - -> If you've already followed the -> ["Getting Started" Guide](/development/getting-started.md), you can skip ahead -> to [Configuration](#configuration) - -## Overview - -### Build Machine Requirements - -- [Node.js & NPM](https://nodejs.org/en/download/) -- [Yarn](https://yarnpkg.com/lang/en/docs/install/) -- [Git](https://www.atlassian.com/git/tutorials/install-git) - -### Getting the Code - -_With Git:_ - -```bash -# Clone the remote repository to your local machine -git clone https://github.com/OHIF/Viewers.git -``` - -More on: _[`git clone`](https://git-scm.com/docs/git-clone), -[`git checkout`](https://git-scm.com/docs/git-checkout)_ - -_From .zip:_ - -[OHIF/Viewers: react.zip](https://github.com/OHIF/Viewers/archive/master.zip) - -### Restore Dependencies & Build - -Open your terminal, and navigate to the directory containing the source files. -Next run these commands: - -```js -// If you haven't already, enable yarn workspaces -yarn config set workspaces-experimental true - -// Restore dependencies -yarn install - -// Build source code for production -yarn run build -``` - -If everything worked as expected, you should have a new `dist/` directory in the -project's folder. It should roughly resemble the following: - -```bash -platform/viewer/dist/ -├── app-config.js -├── app.bundle.js -├── app.css -├── index.html -├── manifest.json -├── service-worker.js -└── ... -``` - -By default, the build output will connect to OHIF's publicly accessible PACS. If -this is your first time setting up the OHIF Viewer, it is recommended that you -test with these default settings. After testing, you can find instructions on -how to configure the project for your own imaging archive below. - -### Configuration - -The configuration for our viewer is in the `platform/viewer/public/config` -directory. Our build process knows which configuration file to use based on the -`APP_CONFIG` environment variable. By default, its value is -[`config/default.js`][default-config]. The majority of the viewer's features, -and registered extension's features, are configured using this file. - -The easiest way to apply your own configuration is to modify the `default.js` -file. For more advanced cofiguration options, check out our -[configuration essentials guide](/configuring/index.md). - -## Next Steps - -### Deploying Build Output - -_Drag-n-drop_ - -- [Netlify: Drop](/deployment/recipes/static-assets.md#netlify-drop) - -_Easy_ - -- [Surge.sh](/deployment/recipes/static-assets.md#surgesh) -- [GitHub Pages](/deployment/recipes/static-assets.md#github-pages) - -_Advanced_ - -- [AWS S3 + Cloudfront](/deployment/recipes/static-assets.md#aws-s3--cloudfront) -- [GCP + Cloudflare](/deployment/recipes/static-assets.md#gcp--cloudflare) -- [Azure](/deployment/recipes/static-assets.md#azure) - -### Testing Build Output Locally - -A quick way to test your build output locally is to spin up a small webserver. -You can do this by running the following commands in the `dist/` output -directory: - -```js -// Install http-server as a globally available package -yarn global add http-server - -// Change the directory to the platform/viewer - -// Serve the files in our current directory -// Accessible at: `http://localhost:8080` -npx http-server ./dist -``` - -
- -### Automating Builds and Deployments - -If you found setting up your environment and running all of these steps to be a -bit tedious, then you are in good company. Thankfully, there are a large number -of tools available to assist with automating tasks like building and deploying -web application. For a starting point, check out this repository's own use of: - -- [CircleCI][circleci]: [config.yaml][circleci-config] -- [Netlify][netlify]: [netlify.toml][netlify.toml] | - [build-deploy-preview.sh][build-deploy-preview.sh] - - - - -[circleci]: https://circleci.com/gh/OHIF/Viewers -[circleci-config]: https://github.com/OHIF/Viewers/blob/master/.circleci/config.yml -[netlify]: https://app.netlify.com/sites/ohif/deploys -[netlify.toml]: https://github.com/OHIF/Viewers/blob/master/netlify.toml -[build-deploy-preview.sh]: https://github.com/OHIF/Viewers/blob/master/.netlify/build-deploy-preview.sh - diff --git a/docs/latest/deployment/recipes/nginx--image-archive.md b/docs/latest/deployment/recipes/nginx--image-archive.md deleted file mode 100644 index 869988b48..000000000 --- a/docs/latest/deployment/recipes/nginx--image-archive.md +++ /dev/null @@ -1,264 +0,0 @@ -# Nginx + Image Archive - -> DISCLAIMER! We make no claims or guarantees of this approach's security. If in -> doubt, enlist the help of an expert and conduct proper audits. - -At a certain point, you may want others to have access to your instance of the -OHIF Viewer and its medical imaging data. This post covers one of many potential -setups that accomplish that. Please note, noticably absent is user account -control. - -Do not use this recipe to host sensitive medical data on the open web. Depending -on your company's policies, this may be an appropriate setup on an internal -network when protected with a server's basic authentication. For a more robust -setup, check out our [user account control recipe](./user-account-control.md) -that builds on the lessons learned here. - -## Overview - -Our two biggest hurdles when hosting our image archive and web client are: - -- Risks related to exposing our PACS to the netowrk -- Cross-Origin Resource Sharing (CORS) requests - -### Handling Web Requests - -We mittigate our first issue by allowing [Nginx][nginx] to handle incoming web -requests. Nginx is open source software for web serving, reverse proxying, -caching, and more. It's designed for maximum performance and stability -- -allowing us to more reliably serve content than Orthanc's built-in server can. - -More specifically, we accomplish this by using a -[`reverse proxy`](https://en.wikipedia.org/wiki/Reverse_proxy) to retrieve -resources from our image archive (Orthanc), and when accessing its web admin. - -> A reverse proxy is a type of proxy server that retrieves resources on behalf -> of a client from one or more servers. These resources are then returned to the -> client, appearing as if they originated from the proxy server itself. - -### CORS Issues - -Cross-Origin Resource Sharing (CORS) is a mechanism that uses HTTP headers to -tell a browser which web applications have permission to access selected -resources from a server at a different origin (domain, protocol, port). IE. By -default, a Web App located at `http://my-website.com` can't access resources -hosted at `http://not-my-website.com` - -We can solve this one of two ways: - -1. Have our Image Archive located at the same domain as our Web App -2. Add appropriate `Access-Control-Allow-*` HTTP headers - -**This solution uses the first approach.** - -You can read more about CORS in this Medium article: [Understanding -CORS][understanding-cors] - -### Diagram - -This setup allows us to create a setup similar to the one pictured below: - -{% include "./../_nginx-image-archive-diagram.md" %} - -- All web requests are routed through `nginx` on our `OpenResty` image -- `/pacs` is a reverse proxy for `orthanc`'s `DICOM Web` endpoints -- `/pacs-admin` is a reverse proxy for `orthanc`'s Web Admin -- All static resources for OHIF Viewer are served up by `nginx` when a matching - route for that resource is requested - -## Getting Started - -### Requirements - -- Docker - - [Docker for Mac](https://docs.docker.com/docker-for-mac/) - - [Docker for Windows](https://docs.docker.com/docker-for-windows/) - -_Not sure if you have `docker` installed already? Try running `docker --version` -in command prompt or terminal_ - -### Setup - -- Navigate to `viewer` folder inside `platform` -- then: `cd .recipes/OpenResty-Orthanc` -- run: `docker-compose up --build` -- Navigate to `127.0.0.1` for the viewer -- Navigate to `127.0.0.1/pacs-admin` for uploading studies - - -You can see the overview of the mentioned steps: - - -
- - - -### Troubleshooting - -_Exit code 137_ - -This means Docker ran out of memory. Open Docker Desktop, go to the `advanced` -tab, and increase the amount of Memory available. - -_Cannot create container for service X_ - -Use this one with caution: `docker system prune` - -_X is already running_ - -Stop running all containers: - -- Win: `docker ps -a -q | ForEach { docker stop $_ }` -- Linux: `docker stop $(docker ps -a -q)` - - -_Traceback (most recent call last):_ - _File "urllib3/connectionpool.py", line 670, in urlopen_ - _...._ - -Are you sure your docker is running? see explanation [here](https://github.com/docker/compose/issues/7896) - - -### Configuration - -After verifying that everything runs with default configuration values, you will -likely want to update: - -- The domain: `http://127.0.0.1` - -#### OHIF Viewer - -The OHIF Viewer's configuration is imported from a static `.js` file. The -configuration we use is set to a specific file when we build the viewer, and -determined by the env variable: `APP_CONFIG`. You can see where we set its value -in the `dockerfile` for this solution: - -`ENV APP_CONFIG=config/docker_openresty-orthanc.js` - -You can find the configuration we're using here: -`/public/config/docker_openresty-orthanc.js` - -To rebuild the `webapp` image created by our `dockerfile` after updating the -Viewer's configuration, you can run: - -- `docker-compose build` OR -- `docker-compose up --build` - -#### Other - -All other files are found in: `/docker/OpenResty-Orthanc/` - -| Service | Configuration | Docs | -| ----------------- | --------------------------------- | ------------------------------------------- | -| OHIF Viewer | [dockerfile][dockerfile] | You're reading them now! | -| OpenResty (Nginx) | [`/nginx.conf`][config-nginx] | [lua-resty-openidc][lua-resty-openidc-docs] | -| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] | - -## Next Steps - -### Deploying to Production - -While these configuration and docker-compose files model an environment suitable -for production, they are not easy to deploy "as is". You can either: - -- Manually recreate this environment and deploy built application files **OR** -- Deploy to a cloud kubernetes provider like - [Digital Ocean](https://www.digitalocean.com/products/kubernetes/) **OR** - - [See a full list of cloud providers here](https://landscape.cncf.io/category=cloud&format=card-mode&grouping=category) -- Find and follow your preferred provider's guide on setting up - [swarms and stacks](https://docs.docker.com/get-started/) - -### Adding SSL - -Adding SSL registration and renewal for your domain with Let's Encrypt that -terminates at Nginx is an incredibly important step toward securing your data. -Here are some resources, specific to this setup, that may be helpful: - -- [lua-resty-auto-ssl](https://github.com/GUI/lua-resty-auto-ssl) -- [Let's Encrypt + Nginx](https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/) - -While we terminate SSL at Nginx, it may be worth using self signed certificates -for communication between services. - -- [SSL Termination for TCP Upstream Servers](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-tcp/) - -### Use PostgresSQL w/ Orthanc - -Orthanc can handle a large amount of data and requests, but if you find that -requests start to slow as you add more and more studies, you may want to -configure your Orthanc instance to use PostgresSQL. Instructions on how to do -that can be found in the -[`Orthanc Server Book`](http://book.orthanc-server.com/users/docker.html), under -"PostgreSQL and Orthanc inside Docker" - -### Improving This Guide - -Here are some improvements this guide would benefit from, and that we would be -more than happy to accept Pull Requests for: - -- SSL Support -- Complete configuration with `.env` file (or something similar) -- Any security issues -- One-click deploy to a cloud provider - -## Resources - -### Misc. Helpful Commands - -_Check if `nginx.conf` is valid:_ - -```bash -docker run --rm -t -a stdout --name my-openresty -v $PWD/config/:/usr/local/openresty/nginx/conf/:ro openresty/openresty:alpine-fat openresty -c /usr/local/openresty/nginx/conf/nginx.conf -t -``` - -_Interact w/ running container:_ - -`docker exec -it CONTAINER_NAME bash` - -_List running containers:_ - -`docker ps` - -### Referenced Articles - -For more documentation on the software we've chosen to use, you may find the -following resources helpful: - -- [Orthanc for Docker](http://book.orthanc-server.com/users/docker.html) -- [OpenResty Guide](http://www.staticshin.com/programming/definitely-an-open-resty-guide/) -- [Lua Ngx API](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/) - -For a different take on this setup, check out the repositories our community -members put together: - -- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker) -- [trypag/ohif-orthanc-postgres-docker](https://github.com/trypag/ohif-orthanc-postgres-docker) - - - - - -[nginx]: https://www.nginx.com/resources/glossary/nginx/ -[understanding-cors]: https://medium.com/@baphemot/understanding-cors-18ad6b478e2b -[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration -[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc - -[dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/dockerfile -[config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/config/nginx.conf -[config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/config/orthanc.json - diff --git a/docs/latest/deployment/recipes/static-assets.md b/docs/latest/deployment/recipes/static-assets.md deleted file mode 100644 index ebcf9a100..000000000 --- a/docs/latest/deployment/recipes/static-assets.md +++ /dev/null @@ -1,153 +0,0 @@ -# Deploy Static Assets - -> WARNING! All of these solutions stand-up a publicly accessible web viewer. Do -> not hook your hosted viewer up to a sensitive source of data without -> implementing authentication. - -There are a lot of options for deploying static assets. Some services, like -`netlify` and `surge.sh`, specialize in static websites. You'll notice that -deploying with them requires much less time and effort, but comes at the cost of -less product offerings. - -While not required, it can simplify things to host your Web Viewer alongside -your image archive. Services with more robust product offerings, like -`Google Cloud`, `Microsoft's Azure`, and `Amazon Web Services (AWS)`, are able -to accomodate this setup. - -_Drag-n-drop_ - -- [Netlify: Drop](#netlify-drop) - -_Easy_ - -- [Surge.sh](#surgesh) -- [GitHub Pages](#github-pages) - -_Advanced_ - -- [Deploy Static Assets](#deploy-static-assets) - - [Drag-n-drop](#drag-n-drop) - - [Netlify: Drop](#netlify-drop) - - [Easy](#easy) - - [Surge.sh](#surgesh) - - [GitHub Pages](#github-pages) - - [Advanced](#advanced) - - [AWS S3 + Cloudfront](#aws-s3--cloudfront) - - [GCP + Cloudflare](#gcp--cloudflare) - - [Azure](#azure) - -## Drag-n-drop - -### Netlify: Drop - -
- - -_GIF demonstrating deployment with Netlify Drop_ - -1. https://app.netlify.com/drop -2. Drag your `build/` folder on to the drop target -3. ... -4. _annnd you're done_ - -**Features:** - -- Custom domains & HTTPS -- Instant Git integration -- Continuous deployment -- Deploy previews -- Access to add-ons - -(Non-free tiers include identity, FaaS, Forms, etc.) - -Learn more about [Netlify on their website](https://www.netlify.com/) - -## Easy - -### Surge.sh - -> Static web publishing for Front-End Developers. Simple, single-command web -> publishing. Publish HTML, CSS, and JS for free, without leaving the command -> line. - -![surge.sh deploy example](../../assets/img/surge-deploy.gif) - -_GIF demonstrating deployment with surge_ - -```shell -# Add surge command -yarn global add surge - -# In the build directory -surge -``` - -**Features:** - -- Free custom domain support -- Free SSL for surge.sh subdomains -- pushState support for single page apps -- Custom 404.html pages -- Barrier-free deployment through the CLI -- Easy integration into your Grunt toolchain -- Cross-origin resource support -- And more… - -Learn more about [surge.sh on their website](https://surge.sh/) - -### GitHub Pages - -> WARNING! While great for project sites and light use, it is not advised to use -> GitHub Pages for production workloads. Please consider using a different -> service for mission critical applications. - -> Websites for you and your projects. Hosted directly from your GitHub -> repository. Just edit, push, and your changes are live. - -This deploy strategy makes more sense if you intend to maintain your project in -a GitHub repository. It allows you to specify a `branch` or `folder` as the -target for a GitHub Page's website. As you push code changes, the hosted content -updates to reflect those changes. - -1. Head over to GitHub.com and create a new repository, or go to an existing - one. Click on the Settings tab. -2. Scroll down to the GitHub Pages section. Choose the `branch` or `folder` you - would like as the "root" of your website. -3. Fire up a browser and go to `http://username.github.io/repository` - -Configuring Your Site: - -- [Setting up a custom domain](https://help.github.com/en/articles/using-a-custom-domain-with-github-pages) -- [Setting up SSL](https://help.github.com/en/articles/securing-your-github-pages-site-with-https) - -Learn more about [GitHub Pages on its website](https://pages.github.com/) - -## Advanced - -All of these options, while using providers with more service offerings, -demonstrate how to host the viewer with their respective file storage and CDN -offerings. While you can serve your static assets this way, if you're going -through the trouble of using AWS/GCP/Azure, it's more likely you're doing so to -avoid using a proxy or to simplify authentication. - -If that is the case, check out some of our more advanced `docker` deployments -that target these providers from the left-hand sidepanel. - -These guides can be a bit longer and a update more frequently. To provide -accurate documentation, we will link to each provider's own recommended steps: - -### AWS S3 + Cloudfront - -- [Host a Static Website](https://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html) -- [Speed Up Your Website with Cloudfront](https://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-cloudfront-walkthrough.html) - -### GCP + Cloudflare - -- [Things to Know Before Getting Started](https://code.luasoftware.com/tutorials/google-cloud-storage/things-to-know-before-hosting-static-website-on-google-cloud-storage/) -- [Hosting a Static Website on GCP](https://cloud.google.com/storage/docs/hosting-static-website) - -### Azure - -- [Host a Static Website](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website) -- [Add SSL Support](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-https-custom-domain-cdn) -- [Configure a Custom Domain](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-custom-domain-name) diff --git a/docs/latest/deployment/recipes/user-account-control.md b/docs/latest/deployment/recipes/user-account-control.md deleted file mode 100644 index 9535682aa..000000000 --- a/docs/latest/deployment/recipes/user-account-control.md +++ /dev/null @@ -1,3 +0,0 @@ -# User Account Control - -> Coming soon - We are working on bringing the User Account Control to OHIF-v3 diff --git a/docs/latest/development/continous-integration.md b/docs/latest/development/continous-integration.md deleted file mode 100644 index cfc9a09a0..000000000 --- a/docs/latest/development/continous-integration.md +++ /dev/null @@ -1,109 +0,0 @@ -# Continous Integration (CI) - -This repository uses `CircleCI` and `Netlify` for continous integration. - -## Deploy Previews - -[Netlify Deploy previews][deploy-previews] are generated for every pull request. -They allow pull request authors and reviewers to "Preview" the OHIF Viewer as if -the changes had been merged. - -Deploy previews can be configured by modifying the `netlify.toml` file in the -root of the repository. Some additional scripts/assets for netlify are included -in the root `.netlify` directory. - -## Workflows - -[CircleCI Workflows][circleci-workflows] are a set of rules for defining a -collection of jobs and their run order. They are self-documenting and their -configuration can be found in our CircleCI configuration file: -`.circleci/config.yml`. - -### Workflow: PR_CHECKS - -The PR_CHECKS workflow (Pull Request Checks) runs our automated unit and -end-to-end tests for every code check-in. These tests must all pass before code -can be merged to our `master` branch. - -
- - workflow diagram - -
Workflow diagram for PR_CHECKS
-
- -### Workflow: PR_OPTIONAL_DOCKER_PUBLISH - -The PR_OPTIONAL_DOCKER_PUBLISH workflow allows for "manual approval" to publish -the pull request as a tagged docker image. This is helpful when changes need to -be tested with the Google Adapter before merging to `master`. - -
- - workflow diagram - -
Workflow diagram for PR_WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH
-
- -> NOTE: This workflow will fail unless it's for a branch on our `upstream` -> repository. If you need this functionality, but the branch is from a fork, -> merge the changes to a short-lived `feature/` branch on `upstream` - -### Workflow: DEPLOY - -The DEPLOY workflow deploys the OHIF Viewer when changes are merged to master. -It uses the Netlify CLI to deploy assets created as part of the repository's PWA -Build process (`yarn run build`). The workflow allows for "Manual Approval" to -promote the build to `STAGING` and `PRODUCTION` environments. - -
- - workflow diagram - -
Workflow diagram for WORKFLOW_DEPLOY
-
- -| Environment | Description | URL | -| ----------- | ---------------------------------------------------------------------------------- | --------------------------------------------- | -| Development | Always reflects latest changes on `master` branch. | [Netlify][netlify-dev] / [OHIF][ohif-dev] | -| Staging | For manual testing before promotion to prod. Keeps development workflow unblocked. | [Netlify][netlify-stage] / [OHIF][ohif-stage] | -| Production | Stable, tested, updated less frequently. | [Netlify][netlify-prod] / [OHIF][ohif-prod] | - -### Workflow: RELEASE - -The RELEASE workflow publishes our `npm` packages, updated documentation, and -`docker` image when changes are merged to master. `Lerna` and "Semantic Commit -Syntax" are used to independently version and publish the many packages in our -monorepository. If a new version is cut/released, a Docker image is created. -Documentation is generated with `gitbook` and pushed to our `gh-pages` branch. -GitHub hosts the `gh-pages` branch with GitHub Pages. - -- Platform Packages: https://github.com/ohif/viewers/#platform -- Extension Packages: https://github.com/ohif/viewers/#extensions -- Documentation: https://docs.ohif.org/ - -
- - workflow diagram - -
Workflow diagram for WORKFLOW_RELEASE
-
- -### HOTFIX - -_Not yet implemented_ - - - - -[deploy-previews]: https://www.netlify.com/blog/2016/07/20/introducing-deploy-previews-in-netlify/ -[circleci-workflows]: https://circleci.com/docs/2.0/workflows/ -[netlify-dev]: https://ohif-dev.netlify.com -[netlify-stage]: https://ohif-stage.netlify.com -[netlify-prod]: https://ohif-prod.netlify.com -[ohif-dev]: https://viewer-dev.ohif.org -[ohif-stage]: https://viewer-stage.ohif.org -[ohif-prod]: https://viewer-prod.ohif.org - diff --git a/docs/latest/development/contributing.md b/docs/latest/development/contributing.md deleted file mode 100644 index 9f602a617..000000000 --- a/docs/latest/development/contributing.md +++ /dev/null @@ -1,154 +0,0 @@ -# Contributing - -## How can I help? - -Fork the repository, make your change and submit a pull request. If you would -like to discuss the changes you intend to make to clarify where or how they -should be implemented, please don't hesitate to create a new issue. At a -minimum, you may want to read the following documentation: - -- [Getting Started](/development/getting-started.md) -- [Architecture](/architecture/index.md) - -Pull requests that are: - -- Small -- [Well tested](./testing.md) -- Decoupled - -Are much more likely to get reviewed and merged in a timely manner. - -## When changes impact multiple repositories - -While this can be tricky, we've tried to reduce how often this situation crops -up this with our [recent switch to a monorepo][monorepo]. Our maintained -extensions, ui components, internationalization library, and business logic can -all be developed by simply running `yarn run dev` from the repository root. - -Testing the viewer with locally developed, unpublished package changes from a -package outside of the monorepo is most common with extension development. Let's -demonstrate how to accomplish this with two commonly forked extension -dependencies: - -### `cornerstone-tools` - -On your local file system: - -```bash -# code/my-projects/ -. -├── cornerstonejs/cornerstone-tools -└── ohif/viewers -``` - -- Open a terminal/shell -- Navigate to `cornerstonejs/cornerstone-tools` - - `yarn install` - - [`yarn link`](https://yarnpkg.com/en/docs/cli/link) - - `yarn run dev` - - -- Open a new terminal/shell -- Navigate to `ohif/viewers` (the root of ohif project) - - `yarn install` - - [`yarn link cornerstone-tools`](https://yarnpkg.com/en/docs/cli/link) - - `yarn run dev` - -As you make changed to `cornerstone-tools`, and it's output is rebuilt, you -should see the following behavior: - -
- - Example of linked cornerstone-tools package - -
example of linked cornerstone-tools package
-
- -If you wish to stop using your local package, run the following commands in the -`ohif/viewers` repository root: - -- `yarn unlink cornerstone-tools` -- `yarn install --force` - - - -#### Other linkage notes - -We're still working out some of the kinks with local package development as -there are a lot of factors that can influence the behavior of our development -server and bundler. If you encounter issues not addressed here, please don't -hesitate to reach out on GitHub. - -Sometimes you might encounter a situation where the linking doesn't work as expected. -This might happen when there are multiple linked packages with the same name. -You can [remove][unlink] the linked packages inside yarn and try again. - -## Any guidance on submitting changes? - -While we do appreciate code contributions, triaging and integrating contributed -code changes can be very time consuming. Please consider the following tips when -working on your pull requests: - -- Functionality is appropriate for the repository. Consider creating a GitHub - issue to discuss your suggested changes. -- The scope of the pull request is not too large. Please consider separate pull - requests for each feature as big pull requests are very time consuming to - understand. - -We will provide feedback on your pull requests as soon as possible. Following -the tips above will help ensure your changes are reviewed. - - - - - - -[example-url]: https://deploy-preview-237--ohif.netlify.com/viewer/?url=https://s3.eu-central-1.amazonaws.com/ohif-viewer/sampleDICOM.json -[pr-237]: https://github.com/OHIF/Viewers/pull/237 -[monorepo]: https://github.com/OHIF/Viewers/issues/768 -[unlink]: https://stackoverflow.com/questions/58459698/is-there-a-command-to-unlink-all-yarn-packages-yarn-unlink-all - diff --git a/docs/latest/development/getting-started.md b/docs/latest/development/getting-started.md deleted file mode 100644 index f60ecb826..000000000 --- a/docs/latest/development/getting-started.md +++ /dev/null @@ -1,107 +0,0 @@ -# Getting Started - -## Setup - -### Fork & Clone - -If you intend to contribute back changes, or if you would like to pull updates -we make to the OHIF Viewer, then follow these steps: - -- [Fork][fork-a-repo] the [OHIF/Viewers][ohif-viewers-repo] repository -- [Create a local clone][clone-a-repo] of your fork - - `git clone https://github.com/YOUR-USERNAME/Viewers` -- Add OHIF/Viewers as a [remote repository][add-remote-repo] labled `upstream` - - Navigate to the cloned project's directory - - `git remote add upstream https://github.com/OHIF/Viewers.git` - -With this setup, you can now [sync your fork][sync-changes] to keep it -up-to-date with the upstream (original) repository. This is called a "Triangular -Workflow" and is common for Open Source projects. The GitHub blog has a [good -graphic that illustrates this setup][triangular-workflow]. - -### Private - -Alternatively, if you intend to use the OHIF Viewer as a starting point, and you -aren't as concerned with syncing updates, then follow these steps: - -1. Navigate to the [OHIF/Viewers][ohif-viewers] repository -2. Click `Clone or download`, and then `Download ZIP` -3. Use the contents of the `.zip` file as a starting point for your viewer - -> NOTE: It is still possible to sync changes using this approach. However, -> submitting pull requests for fixes and features are best done with the -> separate, forked repository setup described in "Fork & Clone" - -## Developing - -### Requirements - -- [Node.js & NPM](https://nodejs.org/en/) -- [Yarn](https://yarnpkg.com/en/) -- Yarn workspaces should be enabled: - - `yarn config set workspaces-experimental true` - -### Kick the tires - -Navigate to the root of the project's directory in your terminal and run the -following commands: - -```bash -# Switch to the v3 branch -git switch feat/v2-main - -# Restore dependencies -yarn install - -# Start local development server -yarn run dev -``` - -You should see the following output: - -```bash -@ohif/viewer: i 「wds」: Project is running at http://localhost:3000/ -@ohif/viewer: i 「wds」: webpack output is served from / -@ohif/viewer: i 「wds」: Content not from webpack is served from D:\code\ohif\Viewers\platform\viewer -@ohif/viewer: i 「wds」: 404s will fallback to /index.html - -# And a list of all generated files -``` - -### 🎉 Celebrate 🎉 - -
- -### Building for Production - -> More comprehensive guides for building and publishing can be found in our -> [deployment docs](./../deployment/index.md) - -```bash -# Build static assets to host a PWA -yarn run build - -# Build packaged output (script-tag use) -yarn run build:package -``` - -## Troubleshooting - -- If you receive a _"No Studies Found"_ message and do not see your studies, try - changing the Study Date filters to a wider range. -- If you see a 'Loading' message which never resolves, check your browser - JavaScript console inside the Developer Tools to identify any errors. - - - - -[fork-a-repo]: https://help.github.com/en/articles/fork-a-repo -[clone-a-repo]: https://help.github.com/en/articles/fork-a-repo#step-2-create-a-local-clone-of-your-fork -[add-remote-repo]: https://help.github.com/en/articles/fork-a-repo#step-3-configure-git-to-sync-your-fork-with-the-original-spoon-knife-repository -[sync-changes]: https://help.github.com/en/articles/syncing-a-fork -[triangular-workflow]: https://github.blog/2015-07-29-git-2-5-including-multiple-worktrees-and-triangular-workflows/#improved-support-for-triangular-workflows -[ohif-viewers-repo]: https://github.com/OHIF/Viewers -[ohif-viewers]: https://github.com/OHIF/Viewers - diff --git a/docs/latest/development/testing.md b/docs/latest/development/testing.md deleted file mode 100644 index 06a46f317..000000000 --- a/docs/latest/development/testing.md +++ /dev/null @@ -1,211 +0,0 @@ -# Running Tests for OHIF - -We introduce here various test types that is available -for OIHF, and how to run each test in order to make sure your contribution -hasn't broken any existing functionalities. Idea and philosophy of each testing category is discussed in the second -part of this page. - -## Unit test -To run the unit test: -``` -yarn run test:unit:ci -``` - -Note: You should have already installed all the packages with `yarn install`. - - -Running unit test will generate a report at the end showing the successful and -unsuccessful tests with detailed explanations. - -## End-to-end test -For running the OHIF e2e test you need to run the following steps: - -- Create a mini-pacs for OHIF to access the images for testing. We download -and run our lightweight implementation which provides a collection of DICOM studies ([source code][mini-pacs]). - - ``` - docker run -p 5985:5985 -p 5984:5984 -e USE_POUCHDB=true -e DB_SERVER=http://0.0.0.0 ohif/viewer-testdata:0.1-test - ``` - - Successful execution should be - - ![](../assets/img/docker-pacs.png) - -- Open a new terminal, navigate to the OHIF project, and run OHIF with the dicom-server config - - ``` - APP_CONFIG=config/dicomweb-server.js yarn start - ``` - - You should be able to see test studies in the study list - - ![OHIF-e2e-test-studies](../assets/img/OHIF-e2e-test-studies.png) - -- Open a new terminal inside the OIHF project, and run the e2e cypress test - - ``` - yarn run test:e2e - ``` - - You should be able to see the cypress window open - - ![e2e-cypress](../assets/img/e2e-cypress.png) - - Run the tests by clicking on the `Run #number integration tests` . - - A new window will open and you will see e2e tests being executed one after - each other. - - ![e2e-cypress-final](../assets/img/e2e-cypress-final.png) - - - - - - -# Testing Philosiphy - -> Testing is an opinionated topic. Here is a rough overview of our testing -> philosophy. See something you want to discuss or think should be changed? Open -> a PR and let's discuss. - -You're an engineer. You know how to write code, and writing tests isn't all that -different. But do you know why we write tests? Do you know when to write one, or -what kind of test to write? How do you know if a test is a _"good"_ test? This -document's goal is to give you the tools you need to make those determinations. - -Okay. So why do we write tests? To increase our... **CONFIDENCE** - -- If I do a large refactor, does everything still work? -- If I changed some critical piece of code, is it safe to push to production? - -Gaining the confidence we need to answer these questions after every change is -costly. Good tests allow us to answer them without manual regression testing. -What and how we choose to test to increase that confidence is nuanced. - -## Kinds of Tests - -Test's buy us confidence, but not all tests are created equal. Each kind of test -has a different cost to write and maintain. An expensive test is worth it if it -gives us confidence that a payment is processed, but it may not be the best -choice for asserting an element's border color. - -| Test Type | Example | Speed | Cost | -| ----------- | ------------------------------------------------------------------------ | ---------------- | ------------------------------------------------------------------------ | -| Static | `addNums(1, '2')` called with `string`, expected `int`. | :rocket: Instant | :money_with_wings: | -| Unit | `addNums(1, 2)` returns expected result `3` | :airplane: Fast | :money_with_wings::money_with_wings: | -| Integration | Clicking "Sign In", navigates to the dashboard (mocked network requests) | :running: Okay | :money_with_wings::money_with_wings::money_with_wings: | -| End-to-end | Clicking "Sign In", navigates to the dashboard (no mocks) | :turtle: Slow | :money_with_wings::money_with_wings::money_with_wings::money_with_wings: | - -- :rocket: Speed: How quickly tests run -- :money_with_wings: Cost: Time to write, and to debug when broken (more points - of failure) - -### Static Code Analysis - -Modern tooling gives us this "for free". It can catch invalid regular -expressions, unused variables, and guarantee we're calling methods/functions -with the expected paramater types. - -Example Tooling: - -- [ESLint][eslint-rules] -- [TypeScript][typescript-docs] or [Flow][flow-org] - -### Unit Tests - -The building blocks of our libraries and applications. For these, you'll often -be testing a single function or method. Conceptually, this equates to: - -_Pure Function Test:_ - -- If I call `sum(2, 2)`, I expect the output to be `4` - -_Side Effect Test:_ - -- If I call `resetViewport(viewport)`, I expect `cornerstone.reset` to be called - with `viewport` - -#### When to use - -Anything that is exposed as public API should have unit tests. - -#### When to avoid - -You're actually testing implementation details. You're testing implementation -details if: - -- Your test does something that the consumer of your code would never do. - - IE. Using a private function -- A refactor can break your tests - -### Integration Tests - -We write integration tests to gain confidence that several units work together. -Generally, we want to mock as little as possible for these tests. In practice, -this means only mocking network requests. - -#### When to use - -... - -### End-to-End Tests - -These are the most expensive tests to write and maintain. Largely because, when -they fail, they have the largest number of potential points of failure. So why -do we write them? Because they also buy us the most confidence. - -#### When to use - -Mission critical features and functionality, or to cover a large breadth of -functionality until unit tests catch up. Unsure if we should have a test for -feature `X` or scenario `Y`? Open an issue and let's discuss. - -## Summary - -- Does your test increase confidence? -- Does the test type chosen balance the cost-to-confidence ratio? - -## Further Reading - -### General - -- [Assert(js) Conf 2018 Talks][assert-js-talks] - - [Write tests. Not too many. Mostly integration.][kent-talk] - Kent C. Dodds - - [I see your point, but…][gleb-talk] - Gleb Bahmutov -- [Static vs Unit vs Integration vs E2E Testing][kent-blog] - Kent C. Dodds - (Blog) - -### End-to-end Testing w/ Cypress - -- [Getting Started](https://docs.cypress.io/guides/overview/why-cypress.html) - - Be sure to check out `Getting Started` and `Core Concepts` -- [Best Practices](https://docs.cypress.io/guides/references/best-practices.html) -- [Example Recipes](https://docs.cypress.io/examples/examples/recipes.html) - -## Testing Dorito - -[![testing dorito][testing-dorito-img]][testing-dorito] - - - - -[eslint-rules]: https://eslint.org/docs/rules/ -[mini-pacs]: https://github.com/OHIF/viewer-testdata -[typescript-docs]: https://www.typescriptlang.org/docs/home.html -[flow-org]: https://flow.org/ - -[assert-js-talks]: https://www.youtube.com/playlist?list=PLZ66c9_z3umNSrKSb5cmpxdXZcIPNvKGw -[kent-talk]: https://www.youtube.com/watch?v=Fha2bVoC8SE -[gleb-talk]: https://www.youtube.com/watch?v=5FnalKRjpZk -[kent-blog]: https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests - -[testing-trophy]: https://twitter.com/kentcdodds/status/960723172591992832?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E960723172591992832&ref_url=https%3A%2F%2Fkentcdodds.com%2Fblog%2Fwrite-tests -[aaron-square]: https://twitter.com/Carofine247/status/966727489274961920 -[gleb-pyramid]: https://twitter.com/Carofine247/status/966764532046684160/photo/3 -[testing-pyramid]: https://dojo.ministryoftesting.com/dojo/lessons/the-mobile-test-pyramid -[testing-dorito]: https://twitter.com/denvercoder/status/960752578198843392 -[testing-dorito-img]: https://pbs.twimg.com/media/DVVHXycUMAAcN-F?format=jpg&name=4096x4096 - diff --git a/docs/latest/extensions/index.md b/docs/latest/extensions/index.md deleted file mode 100644 index c88e05d56..000000000 --- a/docs/latest/extensions/index.md +++ /dev/null @@ -1,263 +0,0 @@ -# Extensions - -- [Extensions](#extensions) - - [Overview](#overview) - - [Extension Skeleton](#extension-skeleton) - - [OHIF-Maintained Extensions](#ohif-maintained-extensions) - - [Registering an Extension](#registering-an-extension) - - [Registering at Runtime](#registering-at-runtime) - - [Registering at Build Time](#registering-at-build-time) - - [Lifecycle Hooks](#lifecycle-hooks) - - [Modules](#modules) - - [Contexts](#contexts) - -## Overview -We have re-designed the architecture of the `OHIF-v3` to enable building applications -that are easily extensible to various use cases (modes) that behind the scene would utilize desired functionalities (extensions) to reach the goal of the use case. - -Previously, extensions were “additive” and could not easily be mixed and matched within the same viewer for different use cases. Previous `OHIF-v2` architecture meant that -any minor extension alteration usually would require the user to hard fork. E.g. removing some of the tools from the toolbar of the cornerstone extension meant you had to hard fork it, which was frustrating if the implementation was otherwise the same as master. - - -> - Developers should make packages of *reusable* functionality as extensions, and can consume -> publicly available extensions. -> - Any conceivable radiological workflow or viewer setup will be able to be built with the platform through *modes*. - - - -Practical examples of extensions include: - -- A set of segmentation tools that build on top of the `cornerstone` viewport -- A set of rendering functionalities to volume render the data -- [See our maintained extensions for more examples of what's possible](#maintained-extensions) - - - -Diagram showing how extensions are configured and accessed. - - - - -## Extension Skeleton - -An extension is a plain JavaScript object that has an `id` property, and one or -more [modules](#modules) and/or [lifecycle hooks](#lifecycle-hooks). - -```js -// prettier-ignore -export default { - /** - * Only required property. Should be a unique value across all extensions. - */ - id: 'example-extension', - - // Lifecyle - preRegistration() { /* */ }, - onModeEnter() { /* */ }, - onModeExit() { /* */ }, - // Modules - getLayoutTemplateModule() { /* */ }, - getDataSourcesModule() { /* */ }, - getSopClassHandlerModule() { /* */ }, - getPanelModule() { /* */ }, - getViewportModule() { /* */ }, - getCommandsModule() { /* */ }, - getContextModule() { /* */ }, - getToolbarModule() { /* */ }, - getHangingProtocolModule() { /* */ }, -} -``` - -## OHIF-Maintained Extensions -A small number of powerful extensions for popular use cases are maintained by -OHIF. They're co-located in the [`OHIF/Viewers`][viewers-repo] repository, in -the top level [`extensions/`][ext-source] directory. - -{% include "./_maintained-extensions-table.md" %} - - -## Registering an Extension - -Extensions are building blocks that need to be registered. There are two different ways to register and configure extensions: At -[runtime](#registering-at-runtime) and at -[build time](#registering-at-build-time). -You can leverage one or both strategies. Which one(s) you choose depend on your -application's requirements. - -Each [module](#modules) defined by the extension -becomes available to the modes via the `ExtensionManager` by requesting it via -its id. [Read more about Extension Manager](#extension-manager) - - - -### Registering at Runtime - -The `@ohif/viewer` uses a [configuration file](../viewer/configuration.md) at -startup. The schema for that file includes an `extensions` key that supports an -array of extensions to register. - -```js -import MyFirstExtension from '@ohif/extension-first' -import MySecondExtension from '@ohif/extension-second' - -const extensionConfig = {/* extension configuration */} - -// prettier-ignore -const config = { - routerBasename: '/', - extensions: [ - MyFirstExtension, - [ - MySecondExtension, - extensionConfig - ], - ], - modes: [/* modes */], - showStudyList: true, - dataSources: [ /* data source config */] -} -``` - -Then, behind the scene, the runtime-added extensions will get merged with the -default app extensions (note: default app extensions include: `OHIFDefaultExtension`, - `OHIFCornerstoneExtension`, `OHIFDICOMSRExtension`, - `OHIFMeasurementTrackingExtension`) - -### Registering at Build Time - -The `@ohif/viewer` works best when built as a "Progressive Web Application" -(PWA). If you know the extensions your application will need, you can specify -them at "build time" to leverage advantages afforded to us by modern tooling: - -- Code Splitting (dynamic imports) -- Tree Shaking -- Dependency deduplication - -You can update the list of bundled extensions by: - -1. Having your `@ohif/viewer` project depend on the extension -2. Importing and adding it to the list of extensions in the - `/platform/src/index.js` entrypoint: - - ```js - import OHIFDefaultExtension from '@ohif/extension-default'; - import OHIFCornerstoneExtension from '@ohif/extension-cornerstone'; - import OHIFMeasurementTrackingExtension from '@ohif/extension-measurement-tracking'; - import OHIFDICOMSRExtension from '@ohif/extension-dicom-sr'; - import MyFirstExtension from '@ohif/extension-first' - - /** Combine our appConfiguration and "baked-in" extensions */ - const appProps = { - config: window ? window.config : {}, - defaultExtensions: [ - OHIFDefaultExtension, - OHIFCornerstoneExtension, - OHIFMeasurementTrackingExtension, - OHIFDICOMSRExtension, - MyFirstExtension - ], - }; - ``` - -## Lifecycle Hooks - -Currently, there are three lifecycle hook for extensions: - - -[`preRegistration`](./lifecycle/pre-registration.md) -This hook is called once on initialization of the entire viewer application, used to initialize the extensions state, and consume user defined extension configuration. If an extension defines the [`preRegistration`](./lifecycle/pre-registration.md) -lifecycle hook, it is called before any modules are registered in the -`ExtensionManager`. It's most commonly used to wire up extensions to -[services](./../services/index.md) and [commands](./modules/commands.md), and to -bootstrap 3rd party libraries. - - -[`onModeEnter`](./lifecycle/on-mode-enter.md): This hook is called whenever a new mode is entered, or a mode’s data or datasource is switched. This hook can be used to initialize data. - -[`onModeExit`](./lifecycle/on-mode-exit.md): Similarly to onModeEnter, this hook is called when navigating away from a mode, or before a mode’s data or datasource is changed. This can be used to clean up data (e.g. remove annotations that do not need to be persisted) - - - -## Modules -Modules are the meat of extensions, the `blocks` that we have been talking about a lot. -They provide "definitions", components, and filtering/mapping logic that are then made available to modes and services. - -Each module type has a special purpose, and is consumed by our viewer -differently. - - -{% include "./_modules.md" %} - - -
Tbl. Module types with abridged descriptions and examples. Each module links to a dedicated documentation page.
- - - - -### Contexts - -The `@ohif/viewer` tracks "active contexts" that extensions can use to scope -their functionality. Some example contexts being: - -- Route: `ROUTE:VIEWER`, `ROUTE:STUDY_LIST` -- Active Viewport: `ACTIVE_VIEWPORT:CORNERSTONE`, `ACTIVE_VIEWPORT:VTK` - -An extension module can use these to say "Only show this Toolbar Button if the -active viewport is a Cornerstone viewport." This helps us use the appropriate UI -and behaviors depending on the current contexts. - -For example, if we have hotkey that "rotates the active viewport", each Viewport -module that supports this behavior can add a command with the same name, scoped -to the appropriate context. When the `command` is fired, the "active contexts" -are used to determine the appropriate implementation of the rotate behavior. - - - - - - - - - - - - -[viewers-repo]: https://github.com/OHIF/Viewers -[ext-source]: https://github.com/OHIF/Viewers/tree/master/extensions -[module-types]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/extensions/MODULE_TYPES.js - diff --git a/docs/latest/extensions/lifecycle/pre-registration.md b/docs/latest/extensions/lifecycle/pre-registration.md deleted file mode 100644 index 0cb256a54..000000000 --- a/docs/latest/extensions/lifecycle/pre-registration.md +++ /dev/null @@ -1,65 +0,0 @@ -# Lifecylce Hook: preRegistration - -If an extension defines the `preRegistration` lifecycle hook, it is called -before any modules are registered in the `ExtensionManager`. This hook can be -used to: - -- initialize 3rd party libraries -- register event listeners -- add or call services -- add or call commands - -The `preRegistration` hook receives an object containing the -`ExtensionManager`'s associated `ServicesManager`, `CommandsManager`, and any -`configuration` that was provided with the extension at time of registration. - -Example `preRegistration` implementation that register a new service and make it -available in the app. We will talk more in details for creating a new service for -`OHIF-v3`. - -```js - -// new service inside new extension -import MyNewService from './MyNewService'; - -export default function MyNewServiceWithServices(serviceManager) { - return { - name: 'MyNewService', - create: ({ configuration = {} }) => { - return new MyNewService(serviceManager); - }, - }; -} -``` - -and - -```js -import MyNewService from './MyNewService' - -export default { - id: 'MyExampleExtension', - - /** - * @param {object} params - * @param {object} params.configuration - * @param {ServicesManager} params.servicesManager - * @param {CommandsManager} params.commandsManager - * @returns void - */ - preRegistration({ servicesManager, commandsManager, configuration }) { - console.log('Wiring up important stuff.'); - - window.importantStuff = () => { - console.log(configuration); - }; - - console.log('Important stuff has been wired.'); - window.importantStuff(); - - // Registering new services - servicesManager.registerService(MyNewService(servicesManager)); - }, - }, -}; -``` diff --git a/docs/latest/extensions/modules/commands.md b/docs/latest/extensions/modules/commands.md deleted file mode 100644 index b90040c98..000000000 --- a/docs/latest/extensions/modules/commands.md +++ /dev/null @@ -1,125 +0,0 @@ -# Module: Commands - - - -- [Module: Commands](#module-commands) - - [Overview](#overview) - - [Command Definitions](#command-definitions) - - [Command Behavior](#command-behavior) - - [`CommandsManager` Public API](#commandsmanager-public-api) - - [Contexts](#contexts) - -## Overview -`CommandsModule` includes list of arbitrary functions. These may activate tools, communicate with a server, open a modal, etc. -The significant difference between `OHIF-v3` and `OHIF-v2` is that in `v3` a `mode` defines -its toolbar, and which commands each tool call is inside in its toolDefinition - -An extension can register a Commands Module by defining a `getCommandsModule` -method. The Commands Module allows us to register one or more commands scoped to -specific [contexts](./../index.md#contexts). Commands have several unique -characteristics that make them tremendously powerful: - -- Multiple implementations for the same command can be defined -- Only the correct command's implementation will be run, dependent on the - application's "context" -- Commands are used by hotkeys, toolbar buttons and render settings - -Here is a simple example commands module: - -```js -const getCommandsModule = () => ({ - definitions: { - exampleActionDef: { - commandFn: ({ param1 }) => { - console.log(`param1's value is: ${param1}`); - }, - // storeContexts: ['viewports'], - options: { param1: 'param1' }, - context: 'VIEWER', // optional - }, - }, - defaultContext: 'ACTIVE_VIEWPORT::DICOMSR', -}); -``` - - -Each definition returned by the Commands Module is registered to the -`ExtensionManager`'s `CommandsManager`. - -> `storeContexts` has been removed in `OHIF-v3` and now modules have access to all commands and services. This change enables support for user-registered services. - -## Command Definitions - -The command definition consists of a named command (`exampleActionDef` below) and a -`commandFn`. The command name is used to call the command, and the `commandFn` -is the "command" that is actioned. T - -```js -exampleActionDef: { - commandFn: ({ param1, options }) => { }, - options: { param1: 'measurement' }, - context: 'DEFAULT', -} -``` - -| Property | Type | Description | -| --------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | -| `commandFn` | func | The function to call when command is run. Receives `options` and `storeContexts`. | -| `options` | object | (optional) Arguments to pass at the time of calling to the `commandFn` | -| `context` | string[] or string | (optional) Overrides the `defaultContext`. Let's us know if command is currently "available" to be run. | - -## Command Behavior - - - -**If there are multiple valid commands for the application's active contexts** - -- What happens: all commands are run -- When to use: A `clearData` command that cleans up state for multiple - extensions - -**If no commands are valid for the application's active contexts** - -- What happens: a warning is printed to the console -- When to use: a `hotkey` (like "invert") that doesn't make sense for the - current viewport (PDF or HTML) - -## `CommandsManager` Public API - -If you would like to run a command in the consuming app or an extension, you can -use `CommandsManager.runCommand(commandName, options = {}, contextName)` - - -```js -// Returns all commands for a given context -commandsManager.getContext('string'); - -// Run a command, it will run all the `speak` commands in all contexts -commandsManager.runCommand('speak', { command: 'hello' }); - -// Run command, from Default context -commandsManager.runCommand('speak', { command: 'hello' }, ['DEFAULT']); -``` - -The `ExtensionManager` handles registering commands and creating contexts, so -most consumer's won't need these methods. If you find yourself using these, ask -yourself "why can't I register these commands via an extension?" - -```js -// Used by the `ExtensionManager` to register new commands -commandsManager.registerCommand('context', 'name', commandDefinition); - -// Creates a new context; clears the context if it already exists -commandsManager.createContext('string'); -``` - -### Contexts - -It is up to the consuming application to define what contexts are possible, and -which ones are currently active. As extensions depend heavily on these, we will -likely publish guidance around creating contexts, and ways to override extension -defined contexts in the near future. If you would like to discuss potential -changes to how contexts work, please don't hesitate to create a new GitHub -issue. - -[Some additional information on Contexts can be found here.](./../index.md#contexts) diff --git a/docs/latest/extensions/modules/panel.md b/docs/latest/extensions/modules/panel.md deleted file mode 100644 index b23b6a8f0..000000000 --- a/docs/latest/extensions/modules/panel.md +++ /dev/null @@ -1,101 +0,0 @@ -# Module: Panel - -## Overview -UI that is intended to be displayed within a panel. The default LayoutTemplate has panels on the left and right sides, however one could make a template with panels at the top or bottom and make extensions with panels intended for such slots. - - -An extension can register a Panel Module by defining a `getPanelModule` method. -The panel module provides the ability to define `menuOptions` and `components` -that can be used by the consuming application. `components` are React Components -that can be displayed in the consuming application's "Panel" Component. - - - - -The `menuOptions`'s `target` key points to a registered `components`'s `id`. A -`defaultContext` is applied to all `menuOption`s; however, each `menuOption` can -optional provide it's own `context` value. - -The `getPanelModule` receives an object containing the `ExtensionManager`'s -associated `ServicesManager` and `CommandsManager`. - -![panel-module-v3](../../assets/img/panel-module-v3.png) - -```js -import PanelMeasurementTable from './PanelMeasurementTable.js'; - - -function getPanelModule({ - commandsManager, - extensionManager, - servicesManager, -}) { - const wrappedMeasurementPanel = () => { - return ( - - ); - }; - - return [ - { - name: 'measure', - iconName: 'list-bullets', - iconLabel: 'Measure', - label: 'Measurements', - isDisabled: studies => {}, // optional - component: wrappedMeasurementPanel, - }, - ]; -} -``` - - - - -## Consuming Panels Inside Modes -As explained earlier, extensions make the functionalities and components available and -`modes` utilize them to build an app. So, as seen above, we are not actually defining -which side the panel should be opened. Our extension is providing the component with -its. - -New: You can easily add multiple panels to the left/right side of the viewer using the mode -configuration. As seen below, the `leftPanels` and `rightPanels` accept an `Array` of -the `IDs`. - -```js -export default function mode({ modeConfiguration }) { - return { - id: "viewer", - routes: [ - { - path: "longitudinal", - layoutTemplate: ({ location, servicesManager }) => { - return { - id, - props: { - leftPanels: [ - "org.ohif.measurement-tracking.panelModule.seriesList", - ], - rightPanels: [ - "org.ohif.measurement-tracking.panelModule.trackedMeasurements", - ], - viewports, - }, - }; - }, - }, - ], - extensions: [ - "org.ohif.default", - "org.ohif.cornerstone", - "org.ohif.measurement-tracking", - "org.ohif.dicom-sr", - ], - }; -} - - -``` diff --git a/docs/latest/extensions/modules/sop-class-handler.md b/docs/latest/extensions/modules/sop-class-handler.md deleted file mode 100644 index 57f2f487a..000000000 --- a/docs/latest/extensions/modules/sop-class-handler.md +++ /dev/null @@ -1,105 +0,0 @@ -# Module: SOP Class Handler - - -## Overview -This module defines how a specific DICOM SOP class should be processed to make a displaySet, something that can be hung in a viewport. An extension can register a [SOP Class][sop-class-link] Handler Module by defining a `getSopClassHandlerModule` method. The [SOP Class][sop-class-link]. - -The mode chooses what SOPClassHandlers to use, so you could process a series in a different way depending on mode within the same application. - - -SOPClassHandler is a bit different from the other modules, as it doesn't provide a `1:1` -schema for UI or provide it's own components. It instead defines: - -- `sopClassUIDs`: an array of string SOP Class UIDs that the - `getDisplaySetFromSeries` method should be applied to. -- `getDisplaySetFromSeries`: a method that maps series and study metadata to a - display set - -A `displaySet` has the following shape: - -```js -return { - Modality: 'MR', - displaySetInstanceUIDD - SeriesDate, - SeriesTime, - SeriesInstanceUID, - StudyInstanceUID, - SeriesNumber, - FrameRate, - SeriesDescription, - isMultiFrame, - numImageFrames, - SOPClassHandlerId, -} -``` - -## Example SOP Class Handler Module - -```js -import ImageSet from '@ohif/core/src/classes/ImageSet'; - - -const sopClassDictionary = { - CTImageStorage: "1.2.840.10008.5.1.4.1.1.2", - MRImageStorage: "1.2.840.10008.5.1.4.1.1.4", -}; - - -// It is important to note that the used SOPClassUIDs in the modes are in the order that is specified in the array. -const sopClassUids = [ - sopClassDictionary.CTImageStorage, - sopClassDictionary.MRImageStorage, -; - -const makeDisplaySet = (instances) => { - const instance = instances[0]; - const imageSet = new ImageSet(instances); - - imageSet.setAttributes({ - displaySetInstanceUID: imageSet.uid, - SeriesDate: instance.SeriesDate, - SeriesTime: instance.SeriesTime, - SeriesInstanceUID: instance.SeriesInstanceUID, - StudyInstanceUID: instance.StudyInstanceUID, - SeriesNumber: instance.SeriesNumber, - FrameRate: instance.FrameTime, - SeriesDescription: instance.SeriesDescription, - Modality: instance.Modality, - isMultiFrame: isMultiFrame(instance), - numImageFrames: instances.length, - SOPClassHandlerId: `${id}.sopClassHandlerModule.${sopClassHandlerName}`, - }); - - return imageSet; -}; - -getSopClassHandlerModule = () => { - return [ - { - name: 'stack, - sopClassUids, - getDisplaySetsFromSeries: makeDisplaySet, - }, - ]; -}; - -``` - -### More examples : -You can find another example for this mapping between raw metadata and displaySet for -`DICOM-SR` extension. - -## `@ohif/viewer` usage - -We use the `sopClassHandlerModule`s in `DisplaySetService` where we -transform instances from the raw metadata format to a OHIF displaySet format. -You can read more about DisplaySetService here. - - -[sop-class-link]: http://dicom.nema.org/dicom/2013/output/chtml/part04/sect_B.5.html -[dicom-html-sop]: https://github.com/OHIF/Viewers/blob/master/extensions/dicom-html/src/OHIFDicomHtmlSopClassHandler.js#L4-L12 -[dicom-pdf-sop]: https://github.com/OHIF/Viewers/blob/master/extensions/dicom-pdf/src/OHIFDicomPDFSopClassHandler.js#L4-L6 -[dicom-micro-sop]: https://github.com/OHIF/Viewers/blob/master/extensions/dicom-microscopy/src/DicomMicroscopySopClassHandler.js#L5-L7 -[dicom-seg-sop]: https://github.com/OHIF/Viewers/blob/master/extensions/dicom-segmentation/src/OHIFDicomSegSopClassHandler.js#L5-L7 - diff --git a/docs/latest/extensions/modules/toolbar.md b/docs/latest/extensions/modules/toolbar.md deleted file mode 100644 index 95da9acb6..000000000 --- a/docs/latest/extensions/modules/toolbar.md +++ /dev/null @@ -1,257 +0,0 @@ -# Module: Toolbar - -An extension can register a Toolbar Module by defining a `getToolbarModule` -method. `OHIF-v3`'s `default` extension (`"ohif.org.default"`) provides 5 main toolbar button types: - -- [Module: Toolbar](#module-toolbar) - - [Example Toolbar Module](#example-toolbar-module) - - [Toolbar buttons consumed in modes](#toolbar-buttons-consumed-in-modes) - - [Button Definitions](#button-definitions) - - [Nested Buttons](#nested-buttons) - - [Layout Template](#layout-template) - - [Custom Button](#custom-button) - - [Custom tool](#custom-tool) - - - -![toolbarModule](../../assets/img/toolbarModule.png) - - - -## Example Toolbar Module -The Toolbar Module should return an array of `objects`. There are currently a few different variations of definitions, -each one is detailed further down. - -```js -export default function getToolbarModule({ commandsManager, servicesManager }) { - - return [ - { - name: 'ohif.divider', - defaultComponent: ToolbarDivider, - clickHandler: () => {}, - }, - { - name: 'ohif.action', - defaultComponent: ToolbarButton, - clickHandler: () => {}, - }, - { - name: 'ohif.radioGroup', - defaultComponent: ToolbarButton, - clickHandler: () => {}, - }, - { - name: 'ohif.splitButton', - defaultComponent: ToolbarSplitButton, - clickHandler: () => {}, - }, - { - name: 'ohif.layoutSelector', - defaultComponent: ToolbarLayoutSelector, - clickHandler: (evt, clickedBtn, btnSectionName) => {}, - } - ] -} -``` - - -## Toolbar buttons consumed in modes -Below we can see a simplified version of the `longitudinal` mode that shows how -a mode can add buttons to the toolbar by calling `ToolBarService.addButtons(toolbarButtons)`. -`toolbarButtons` is an array of `toolDefinitions` which we will learn next. - - -```js -export default function mode({ modeConfiguration }) { - return { - id: 'viewer', - displayName: 'Basic Viewer', - - onModeEnter: ({ servicesManager, extensionManager }) => { - const { ToolBarService } = servicesManager.services; - - ToolBarService.init(extensionManager); - ToolBarService.addButtons(toolbarButtons); - }, - routes: [ - { - path: 'longitudinal', - layoutTemplate: ({ location, servicesManager }) => { - return {/* */}; - }, - }, - ], - extensions: [ - 'org.ohif.default', - 'org.ohif.cornerstone', - 'org.ohif.measurement-tracking', - 'org.ohif.dicom-sr', - ], - }; -} - -``` - - - - -## Button Definitions - -The simplest toolbarButtons definition has the following properties: - -![toolbarModule-zoom](../../assets/img/toolbarModule-zoom.png) - -```js -{ - id: 'Zoom', - type: 'ohif.radioGroup', - props: { - type: 'tool', - icon: 'tool-zoom', - label: 'Zoom', - commandOptions: { toolName: 'Zoom' }, - }, -}, -``` - - - -| property | description | values | -| ---------------- | ----------------------------------------------------------------- | ----------------------------------------- | -| `id` | Unique string identifier for the definition | \* | -| `label` | User/display friendly to show in UI | \* | -| `icon` | A string name for an icon supported by the consuming application. | \* | -| `type` | Used to determine the button's behaviour | "tool", "toggle", "action" | -| `commandName` | (optional) The command to run when the button is used. | Any command registered by a `CommandModule` | -| `commandOptions` | (optional) Options to pass the target `commandName` | \* | - - -There are three main types of toolbar buttons: - -- `tool`: buttons that enable a tool by running the `setToolActive` command with the `commandOptions` -- `toggle`: buttons that acts as a toggle: e.g., linking viewports -- `action`: buttons that executes an action: e.g., capture button to save screenshot - - - - - - -## Nested Buttons -You can use the `ohif.splitButton` type to build a button with extra tools in the dropdown. - -- First you need to give your `primary` tool definition to the split button -- the `secondary` properties can be a simple arrow down (`chevron-down` icon) -- For adding the extra tools add them to the `items` list. - -You can see below how `longitudinal` mode is using the available toolbarModule to create -`MeasurementTools` nested button - -![toolbarModule-nested-buttons](../../assets/img/toolbarModule-nested-buttons.png) - -```js -// modes/longitudinal/src/toolbarButtons.js - -{ - id: 'MeasurementTools', - type: 'ohif.splitButton', - props: { - groupId: 'MeasurementTools', - isRadio: true, - primary: { - id: 'Length', - icon: 'tool-length', - label: 'Length', - type: 'tool', - commandOptions: { - toolName: 'Length', - } - }, - secondary: { - icon: 'chevron-down', - label: '', - isActive: true, - tooltip: 'More Measure Tools', - }, - items: [ - // Length tool - { - id: 'Length', - icon: 'tool-length', - label: 'Length', - type: 'tool', - commandOptions: { - toolName: 'Length', - } - }, - // Bidirectional tool - { - id: 'Bidirectional', - icon: 'tool-bidirectional', - label: 'Length', - type: 'tool', - commandOptions: { - toolName: 'Bidirectional', - } - }, - // Ellipse tool - { - id: 'EllipticalRoi', - icon: 'tool-elipse', - label: 'Ellipse', - type: 'tool', - commandOptions: { - toolName: 'EllipticalRoi', - } - }, - ], - }, -} -``` - -
- -## Layout Template -Layout selector button and logic is also provided by the OHIF-v3 `default` extension. -To use it, you can just add the following definition to the list of `toolDefinitions` - -![toolbarModule-layout](../../assets/img/toolbarModule-layout.png) -```js -{ - id: 'Layout', - type: 'ohif.layoutSelector', -} -``` - -
- - -## Custom Button -You can also create your own extension, and add your new custom tool appearance (e.g., split horizantlly instead of vertically for split tool). -Simply add `getToolbarModule` to your extension, and pass your tool react component to its -`defaultComponent` property in the returned object. -You can use `@ohif/ui` components such as `IconButton, Icon, Tooltip, ToolbarButton` to -build your own component. - - -```js -import myToolComponent from './myToolComponent' - - -export default function getToolbarModule({ commandsManager, servicesManager }) { - - return [ - { - name: 'new-tool-type', - defaultComponent: myToolComponent, - clickHandler: () => {}, - }, - ] -} -``` - - - -## Custom tool - I want to create a new tool diff --git a/docs/latest/extensions/modules/viewport.md b/docs/latest/extensions/modules/viewport.md deleted file mode 100644 index a6d7c9d82..000000000 --- a/docs/latest/extensions/modules/viewport.md +++ /dev/null @@ -1,95 +0,0 @@ -# Module: Viewport - - -## Overview -Viewports consume a displaySet and display/allow the user to interact with data. An extension can register a Viewport Module by defining a `getViewportModule` -method that returns a React component. Currently, we use viewport components to -add support for: - -- 2D Medical Image Viewing (cornerstone ext.) -- Structured Reports as SR (DICOM SR ext.) -- Structured Reports as HTML (DICOM html ext.) -- Encapsulated PDFs as PDFs (DICOM pdf ext.) -- Whole Slide Microscopy Viewing (whole slide ext.) -- etc. - - - -The general pattern is that a mode can define which `Viewport` to use for which specific -`SOPClassHandlerUID`, so if you want to fork just a single Viewport component for a specialized mode, this is possible. - -```JSX -// displaySet, viewportIndex, dataSource -const getViewportModule = () => { - const wrappedViewport = props => { - return ( - { - commandsManager.runCommand('commandName', data); - }} - /> - ); - }; - - return [{ name: 'example', component: wrappedViewport }]; -}; -``` - - -## Example Viewport Component -A simplified version of the tracked CornerstoneViewport is shown below, which creates -a cornerstone viewport and and action bar on top of it. - - - - -```JSX -function TrackedCornerstoneViewport({ - children, - dataSource, - displaySet, - viewportIndex, - servicesManager, - extensionManager, - commandsManager, -}) { - const renderViewport = () => { - const { component: Component } = extensionManager.getModuleEntry( - 'org.ohif.cornerstone.viewportModule.cornerstone' - ) - return ( - - ) - } - - return ( - <> - -
- {renderViewport()} -
- - ) -} - -``` - - -![viewportModule](../../assets/img/viewportModule.png) -### `@ohif/viewer` - -Viewport components are managed by the `ViewportGrid` Component. Which Viewport -component is used depends on: - -- Hanging Protocols -- The Layout Configuration -- Registered SopClassHandlers - - -![viewportModule-layout](../../assets/img/viewportModule-layout.png) -
An example of three cornerstone Viewports
diff --git a/docs/latest/faq/browser-support.md b/docs/latest/faq/browser-support.md deleted file mode 100644 index d5f1a1cf3..000000000 --- a/docs/latest/faq/browser-support.md +++ /dev/null @@ -1,47 +0,0 @@ -# Browser Support - -The browsers that we support are specified in the `.browserlistrc` file located -in the `platform/viewer` project. While we leverage the latest language features -when writing code, we rely on `babel` to _transpile_ our code so that it can run -in the browsers that we support. - -## In Practice - -The OHIF Viewer is capable of _running_ on: - -- IE 11 -- FireFox -- Chrome -- Safari -- Edge - -However, we do not have the resources to adequately test and maintain bug free -functionality across all of these. In order to push web based medical imaging -forward, we focus our development efforts on recent version of modern evergreen -browsers. - -Our support of older browsers equates to our willingness to review PRs for bug -fixes, and target their minimum JS support whenever possible. - -### Polyfills - -> A polyfill, or polyfiller, is a piece of code (or plugin) that provides the -> technology that you, the developer, expect the browser to provide natively. - -An example of a polyfill is that you expect `Array.prototype.filter` to exist, -but for some reason, the browser that's being used has not implemented that -language feature yet. Our earlier transpilation will rectify _syntax_ -discrepencies, but unimplemented features require a "temporary" implementation. -That's where polyfills step in. - -You can utilize a service like [polyfill.io](https://polyfill.io/v3/) to -auto-detect and apply polyfills as needed, or you can update the PWA build to -include polyfill's in your bundle by incorporating [core-js][core-js] - - - - -[core-js]: https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md - diff --git a/docs/latest/faq/index.md b/docs/latest/faq/index.md deleted file mode 100644 index 7f8c9e670..000000000 --- a/docs/latest/faq/index.md +++ /dev/null @@ -1,81 +0,0 @@ -# Frequently Asked Questions - -## Index - -- [Report a bug][report-bug] -- [Request a feature][new-feature] -- [Commercial Support & Consulting][commercial-support] -- [Academic collaborations][academic] -- [FDA Clearance or CE Marking][fda-clearance] -- [HIPAA Compliance][hipaa] - -### How do I report a bug? - -Navigate to our [GitHub Repository][new-issue], and submit a new bug report. -Follow the steps outlined in the [Bug Report Template][bug-report-template]. - -### How can I request a new feature? - -At the moment we are in the process of defining our roadmap and will do our best -to communicate this to the community. If your requested feature is on the -roadmap, then it will most likely be built at some point. If it is not, you are -welcome to build it yourself and [contribute it](development/contributing.md). -If you have resources and would like to fund the development of a feature, -please [contact us](http://www.ohif.org) or work with community members that -offer [consulting services][commercial-support]. - -### Who should I contact about Academic Collaborations? - -[Gordon J. Harris](http://www.dfhcc.harvard.edu/insider/member-detail/member/gordon-j-harris-phd/) -at Massachusetts General Hospital is the primary contact for any academic -collaborators. We are always happy to hear about new groups interested in using -the OHIF framework, and may be able to provide development support if the -proposed collaboration has an impact on cancer research. - -### Does OHIF offer commercial support? - -The Open Health Imaging Foundation does not offer commercial support, however, -some community members do offer consulting services. The following contacts may -be useful: - -- Rob Lewis ([Radical Imaging](http://radicalimaging.com/)) - -**Please file a Pull Request if you wish to add your name or organization to -this list.** - -### Does The OHIF Viewer have [510(k) Clearance][501k-clearance] from the U.S. F.D.A or [CE Marking][ce-marking] from the European Commission? - -**NO.** The OHIF Viewer is **NOT** F.D.A. cleared or CE Marked. It is the users -responsibility to ensure compliance with applicable rules and regulations. The -[License](https://github.com/OHIF/Viewers/blob/master/LICENSE) for the OHIF -Platform does not prevent your company or group from seeking F.D.A. clearance -for a product built using the platform. - -If you have gone this route (or are going there), please let us know because we -would be interested to hear about your experience. - -### Is The OHIF Viewer [HIPAA][hipaa-def] Compliant? - -**NO.** The OHIF Viewer **DOES NOT** fulfill all of the criteria to become HIPAA -Compliant. It is the users responsibility to ensure compliance with applicable -rules and regulations. - - - - - -[report-bug]: #how-do-i-report-a-bug -[new-feature]: #how-can-i-request-a-new-feature -[commercial-support]: #does-ohif-offer-commercial-support -[academic]: #who-should-i-contact-about-academic-collaborations -[fda-clearance]: #does-the-ohif-viewer-have-510k-clearance-from-the-us-fda-or-ce-marking-from-the-european-commission -[hipaa]: #is-the-ohif-viewer-hipaa-compliant - -[501k-clearance]: https://www.fda.gov/MedicalDevices/DeviceRegulationandGuidance/HowtoMarketYourDevice/PremarketSubmissions/PremarketNotification510k/ -[ce-marking]: https://ec.europa.eu/growth/single-market/ce-marking_en -[hipaa-def]: https://en.wikipedia.org/wiki/Health_Insurance_Portability_and_Accountability_Act -[new-issue]: https://github.com/OHIF/Viewers/issues/new/choose -[bug-report-template]: https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Bug+Report+%3Abug%3A&template=---bug-report.md&title= - diff --git a/docs/latest/faq/pwa-vs-packaged.md b/docs/latest/faq/pwa-vs-packaged.md deleted file mode 100644 index a6f001abc..000000000 --- a/docs/latest/faq/pwa-vs-packaged.md +++ /dev/null @@ -1,33 +0,0 @@ -# PWA vs Packaged - -It's important to know that the OHIF Viewer project provides two different build -processes: - -```bash -# Static Asset output: For deploying PWAs -yarn run build - -# Single `.js` script, for embedding viewer into existing apps -yarn run build:package -``` - -## Progressive Web Application (PWA) - -> [Progressive Web Apps][pwa] are a new breed of web applications that meet the -> [following requirements][pwa-checklist]. Notably, targeting a PWA allows us -> provide a reliable, fast, and engaging experience across different devices and -> network conditions. - -The OHIF Viewer is maintained as a [monorepo][monorepo]. We use WebPack to build -the many small static assets that comprise our application. Also generated is an -`index.html` that will serve as an entry point for loading configuration and the -application, as well as a `service-worker` that can intelligently cache files so -that subsequent requests are from the local file system instead of over the -network. - -You can read more about this particular strategy in our -[Build for Production Deployment Guide](./../deployment/recipes/build-for-production.md) - -## Commonjs Bundle (Packaged Script) - -We are not supporting `Commonjs` bundling inside `OHIF-v3`. diff --git a/docs/latest/faq/scope-of-project.md b/docs/latest/faq/scope-of-project.md deleted file mode 100644 index 4229bb38d..000000000 --- a/docs/latest/faq/scope-of-project.md +++ /dev/null @@ -1,66 +0,0 @@ -# Scope of Project - -The OHIF Viewer is a web based medical imaging viewer. This allows it to be used -on almost any device, anywhere. The OHIF Viewer is what is commonly reffered to -as a ["Dumb Client"][simplicable] - -> A dumb client is software that fully depends on a connection to a server or -> cloud service for its functionality. Without a network connection, the -> software offers nothing useful. - [simplicable.com][simplicable] - -While the Viewer persists some data, it's scope is limited to caching things -like user preferences and previous query paramaters. Because of this, the Viewer -has been built to be highly configurable to work with almost any web accessible -data source. - -![scope-of-project diagram](./../assets/img/scope-of-project.png) - -To be more specific, the OHIF Viewer is a collection of HTML, JS, and CSS files. -These can be delivered to your end users however you would like: - -- From the local networok -- From a remote web server -- From a CDN (content delivery network) -- From a service-worker's cache -- etc. - -These "static asset" files are referred to collectively as a "Progressive Web -Application" (PWA), and have the same capabilities and limitations that all PWAs -have. - -All studies, series, images, imageframes, metadata, and the images themselves -must come from an external source. There are many, many ways to provide this -information, the OHIF Viewer's scope **DOES NOT** encompass providing _any_ -data; only the configuration necessary to interface with one or more of these -many data sources. The OHIF Viewer's scope **DOES** include configuration and -support for services that are protected with OpenID-Connect. - -In an effort to aide our users and contributors, we attempt to provide several -[deployment and hosting recipes](./deployment/index.md) as potential starting -points. These are not meant to be rock solid, production ready, solutions; like -most recipes, they should be augmented to best fit you and your organization's -taste, preferences, etc. - -## FAQ - -_Am I able to cache studies for offline viewing?_ - -Not currently. A web page's offline cache capabilities are limited and somewhat -volatile (mostly imposed at the browser vendor level). For more robust offline -caching, you may want to consider a server on the local network, or packaging -the OHIF Viewer as a desktop application. - -_Does the OHIF Viewer work with the local filesystem?_ - -It is possible to accomplish this through extensions; however, for an user -experience that accomodates a large number of studies, you would likely need to -package the OHIF Viewer as an [Electron app][electron]. - - - - -[simplicable]: https://simplicable.com/new/dumb-client -[electron]: https://electronjs.org/ - diff --git a/docs/latest/help.md b/docs/latest/help.md deleted file mode 100644 index a6c36d676..000000000 --- a/docs/latest/help.md +++ /dev/null @@ -1,39 +0,0 @@ -# Help - -We all need a little help sometimes. Don't let a few roadblocks stand in the way -of you building something awesome. - -## Community Support - -If you're a developer looking to contribute code, documentation, or discussion; -we are more than happy to help provide clarification and answer questions via -[GitHub issues][gh-issues] or our [community forum][google-group]. Regular -contributors may also be invited to join our Slack Group to streamline -discussion. - -For bug reports and feature requests (including incomplete or confusing -documentation), [GitHub issues][gh-issues] continue to be your best avenue of -communication. - -Complex issues specific to your organization/situation are still okay to post, -but they're less likely to receive a response. Unfortunately, we have limited -resources and must be judicious with how we allocate them. If you find yourself -in this situation and in need of assistance, it may be in your best interest to -persue paid support. - -## Commercial Support - -The Open Health Imaging Foundation does not offer commercial support, however, -some community members do offer consulting services: - -- Rob Lewis ([Radical Imaging](http://radicalimaging.com/)) - -**Please file a Pull Request if you wish to add your name or organization to -this list.** - - - -[gh-issues]: https://github.com/OHIF/Viewers/issues/ -[google-group]: https://groups.google.com/forum/#!forum/cornerstone-platform diff --git a/docs/latest/our-process.md b/docs/latest/our-process.md deleted file mode 100644 index c17947be3..000000000 --- a/docs/latest/our-process.md +++ /dev/null @@ -1,161 +0,0 @@ -# Our Process - -Our process is a living, breathing thing. We strive to have regular -[retrospectives][retrospective] that help us shape and adapt our process to our -team's current needs. This document attempts to capture the broad strokes of -that process in an effort to: - -- Strengthen community member involvement and understanding -- Welcome feedback and helpful suggestions - -## Overview - -- [Issue Triage](#issue-triage) -- [Issue Curation ("backlog grooming")](#issue-curation-backlog-grooming) -- [Contributions (Pull Requests)](#contributions-pull-requests) -- [Releases](#releases) - - -## Issue Triage - -[GitHub issues][gh-issues] are the best way to provide feedback, ask questions, -and suggest changes to the OHIF Viewer's core team. Community issues generally -fall into one of three categories, and are marked with a `triage` label when -created. - - - -| Issue Template Name | Description | -| ---------------------- | ---------------------------------------------------------------------------------------- | -| Community: Report 🐛 | Describe a new issue; Provide steps to reproduce; Expected versus actual result? | -| Community: Request ✋ | Describe a proposed new feature. Why should it be implemented? What is the impact/value? | -| Community: Question ❓ | Seek clarification or assistance relevant to the repository. | - -_table 1. issue template names and descriptions_ - -Issues that require `triage` are akin to support tickets. As this is often our -first contact with would-be adopters and contributors, it's important that we -strive for timely responses and satisfactory resolutions. We attempt to -accomplish this by: - -1. Responding to issues requiring `triage` at least once a week -2. Create new "official issues" from "community issues" -3. Provide clear guidance and next steps (when applicable) -4. Regularly clean up old (stale) issues - -> 🖋 Less obviously, patterns in the issues being reported can highlight -> areas that need improvement. For example, users often have difficulty -> navigating CORS issues when deploying the OHIF Viewer -- how do we best reduce -> our ticket volume for this issue? - -### Backlogged Issues - -Community issues serve as vehicles of discussion that lead us to "backlogged -issues". Backlogged issues are the distilled and actionable information -extracted from community issues. They contain the scope and requirements -necessary for hand-off to a core-team (or community) contributor ^\_^ - -| Category | Description | Labels | -| -------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -| Bugs | An issue with steps that produce a bug (an unexpected result). | [Bug: Verified 🐛][label-bug] | -| Stories | A feature/enhancement with a clear benefit, boundaries, and requirements. | [Story 🙌][label-story] | -| Tasks | Changes that improve [UX], [DX], or test coverage; but don't impact application behavior | [Task: CI/Tooling 🤖][label-tooling], [Task: Docs 📖][label-docs], [Task: Refactor 🛠][label-refactor], [Task: Tests 🔬][label-tests] | - -_table 2. backlogged issue types ([full list of labels][gh-labels])_ - -## Issue Curation (["backlog grooming"][groom-backlog]) - -If a [GitHub issue][gh-issues] has a `bug`, `story`, or `task` label; it's on -our backlog. If an issue is on our backlog, it means we are, at the very least, -committed to reviewing any community drafted Pull Requests to complete the -issue. If you're interested in seeing an issue completed but don't know where to -start, please don't hesitate to leave a comment! - -While we don't yet have a long-term or quarterly road map, we do regularly add -items to our ["Active Development" GitHub Project Board][gh-board]. Items on -this project board are either in active development by Core Team members, or -queued up for development as in-progress items are completed. - -> 🖋 Want to contribute but not sure where to start? Check out [Up for -> grabs][label-grabs] issues and our [Contributing -> documentation][contributing-docs] - -## Contributions (Pull Requests) - -Incoming Pull Requests (PRs) are triaged using the following labels. Code review -is performed on all PRs where the bug fix or added functionality is deemed -appropriate: - -| Labels | Description | -| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -| **Classification** | | -| [PR: Bug Fix][label-bug] | Filed to address a Bug. | -| [PR: Draft][draft] | Filed to gather early feedback from the core team, but which is not intended for merging in the short term. | -| **Review Workflow** | | -| [PR: Awaiting Response 💬][awaiting-response] | The core team is waiting for additional information from the author. | -| [PR: Awaiting Review 👀][awaiting-review] | The core team has not yet performed a code review. | -| [PR: Awaiting Revisions 🖊][awaiting-revisions] | Following code review, this label is applied until the author has made sufficient changes. | -| **QA** | | -| [PR: Awaiting User Cases 💃][awaiting-stories] | The PR code changes need common language descriptions of impact to end users before the review can start | -| [PR: No UX Impact 🙃][no-ux-impact] | The PR code changes do not impact the user's experience | - -We rely on GitHub Checks and integrations with third party services to evaluate -changes in code quality and test coverage. Tests must pass and User cases must -be present (when applicable) before a PR can be merged to master, and code -quality and test coverage must not changed by a significant margin. For some -repositories, visual screenshot-based tests are also included, and video -recordings of end-to-end tests are stored for later review. - -[You can read more about our continous integration efforts here](/development/continous-integration.md) - -## Releases - -Releases are made automatically based on the type of commits which have been -merged (major.minor.patch). Releases are automatically pushed to NPM. Release -notes are automatically generated. Users can subscribe to GitHub and NPM -releases. - -We host development, staging, and production environments for the Progressive -Web Application version of the OHIF Viewer. [Development][ohif-dev] always -reflects the latest changes on our master branch. [Staging][ohif-stage] is used -to regression test a release before a bi-weekly deploy to our [Production -environment][ohif-prod]. - -Important announcements are made on GitHub, tagged as Announcement, and pinned -so that they remain at the top of the Issue page. - -The Core team occasionally performs full manual testing to begin the process of -releasing a Stable version. Once testing is complete, the known issues are -addressed and a Stable version is released. - - - - -[groom-backlog]: https://www.agilealliance.org/glossary/backlog-grooming -[retrospective]: https://www.atlassian.com/team-playbook/plays/retrospective -[gh-issues]: https://github.com/OHIF/Viewers/issues/new/choose -[gh-labels]: https://github.com/OHIF/Viewers/labels - -[label-story]: https://github.com/OHIF/Viewers/labels/Story%20%3Araised_hands%3A -[label-tooling]: https://github.com/OHIF/Viewers/labels/Task%3A%20CI%2FTooling%20%3Arobot%3A -[label-docs]: https://github.com/OHIF/Viewers/labels/Task%3A%20Docs%20%3Abook%3A -[label-refactor]: https://github.com/OHIF/Viewers/labels/Task%3A%20Refactor%20%3Ahammer_and_wrench%3A -[label-tests]: https://github.com/OHIF/Viewers/labels/Task%3A%20Tests%20%3Amicroscope%3A -[label-bug]: https://github.com/OHIF/Viewers/labels/Bug%3A%20Verified%20%3Abug%3A - -[draft]: https://github.com/OHIF/Viewers/labels/PR%3A%20Draft -[awaiting-response]: https://github.com/OHIF/Viewers/labels/PR%3A%20Awaiting%20Response%20%3Aspeech_balloon%3A -[awaiting-review]: https://github.com/OHIF/Viewers/labels/PR%3A%20Awaiting%20Review%20%3Aeyes%3A -[awaiting-stories]: https://github.com/OHIF/Viewers/labels/PR%3A%20Awaiting%20UX%20Stories%20%3Adancer%3A -[awaiting-revisions]: https://github.com/OHIF/Viewers/labels/PR%3A%20Awaiting%20Revisions%20%3Apen%3A -[no-ux-impact]: https://github.com/OHIF/Viewers/labels/PR%3A%20No%20UX%20Impact%20%3Aupside_down_face%3A - -[ohif-dev]: https://viewer-dev.ohif.org -[ohif-stage]: https://viewer-stage.ohif.org -[ohif-prod]: https://viewer.ohif.org -[gh-board]: https://github.com/OHIF/Viewers/projects/4 -[label-grabs]: https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs+%3Araising_hand_woman%3A%22 -[contributing-docs]: ./development/contributing.md - diff --git a/docs/latest/services/index.md b/docs/latest/services/index.md deleted file mode 100644 index 01fe35d4d..000000000 --- a/docs/latest/services/index.md +++ /dev/null @@ -1,40 +0,0 @@ -# Services And Managers - -- [Services And Managers](#services-and-managers) - - [Overview](#overview) - - [Services](#services) - -## Overview -Services are "concern-specific" code modules that can be consumed across layers. Services provide -a set of operations, often tied to some shared state, and are made available to -through out the app via the `ServicesManager`. Services are particularly well suited to -address [cross-cutting concerns][cross-cutting-concerns]. - -Each service should be: - -- self-contained -- able to fail and/or be removed without breaking the application -- completely interchangeable with another module implementing the same interface - - -> In `OHIF-v3` we have added multiple non-UI services and have introduced **pub/sub** pattern to reduce coupling between layers. -> -> [Read more about Pub/Sub](./pubsub.md) - - -## Services -The following services is available in the `OHIF-v3`. - -{% include "./_services.md" %} - - - - - - -[core-services]: https://github.com/OHIF/Viewers/tree/master/platform/core/src/services -[services-manager]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/services/ServicesManager.js -[cross-cutting-concerns]: https://en.wikipedia.org/wiki/Cross-cutting_concern - diff --git a/docs/latest/services/ui/index.md b/docs/latest/services/ui/index.md deleted file mode 100644 index 13bbe4e6a..000000000 --- a/docs/latest/services/ui/index.md +++ /dev/null @@ -1,315 +0,0 @@ -# UI Services - - -## Overview - - - - - - -A typical web application will have components and state for common UI like -modals, notifications, dialogs, etc. A UI service makes it possible to leverage -these components from an extension. - -We maintain the following UI Services: - -- [UI Notification Service](ui-notification-service.md) -- [UI Modal Service](ui-modal-service.md) -- [UI Dialog Service](ui-dialog-service.md) -- [UI Viewport Dialog Service](ui-viewport-dialog-service.md) -- [Cine Service](cine.md) -- [Viewport Grid Service](ViewportGridService.md) - - - -
- - UI Services Diagram - -
Diagram showing relationship between React Context and UI Service
-
- - - - -## Providers for UI services -There are several context providers that wraps the application routes. This -makes the context values exposed in the app, and service's `setImplementation` can get run to override the implementation of the service. - -```js -// platform/viewer/src/App.jsx - -function App({ config, defaultExtensions }) { - /**...**/ - /**...**/ - return ( - /**...**/ - - - - - - - {appRoutes} - - - - - - - /**...**/ - ) -} -``` - -## Example - -For instance `UIModalService` has the following Public API: - -```js -// platform/core/src/services/UIModalService/index.js - -const publicAPI = { - name, - hide: _hide, - show: _show, - setServiceImplementation, -} - -function setServiceImplementation({ - hide: hideImplementation, - show: showImplementation, -}) { - /** ... **/ - serviceImplementation._hide = hideImplementation - serviceImplementation._show = showImplementation - /** ... **/ -} - -export default { - name: 'UIModalService', - create: ({ configuration = {} }) => { - return publicAPI - }, -} -``` - -`UIModalService` implementation can be set (override) in its context provider. For instance in `ModalProvider` we have: - -```js -// platform/ui/src/contextProviders/ModalProvider.jsx - -import { Modal } from '@ohif/ui' - -const ModalContext = createContext(null) -const { Provider } = ModalContext - -export const useModal = () => useContext(ModalContext) - -const ModalProvider = ({ children, modal: Modal, service }) => { - const DEFAULT_OPTIONS = { - content: null, - contentProps: null, - shouldCloseOnEsc: true, - isOpen: true, - closeButton: true, - title: null, - customClassName: '', - } - - const show = useCallback( - (props) => setOptions({ ...options, ...props }), - [options] - ) - - const hide = useCallback(() => setOptions(DEFAULT_OPTIONS), [DEFAULT_OPTIONS]) - - useEffect(() => { - if (service) { - service.setServiceImplementation({ hide, show }) - } - }, [hide, service, show]) - - const { - content: ModalContent, - contentProps, - isOpen, - title, - customClassName, - shouldCloseOnEsc, - closeButton, - } = options - - return ( - - {ModalContent && ( - - - - )} - {children} - - ) -} - -export default ModalProvider - -export const ModalConsumer = ModalContext.Consumer -``` - -Therefore, anywhere in the app that we have access to react context we can use it by calling the `useModal` from `@ohif/ui`. As a matter of fact, we are utilizing the modal for the preference window which shows the hotkeys after clicking on the gear button on the right side of the header. - -A `simplified` code for our worklist is: - - -```js -// platform/viewer/src/routes/WorkList/WorkList.jsx - -import { useModal, Header } from '@ohif/ui' - -function WorkList({ - history, - data: studies, - dataTotal: studiesTotal, - isLoadingData, - dataSource, - hotkeysManager, -}) { - const { show, hide } = useModal() - - /** ... **/ - - const menuOptions = [ - { - title: t('Header:About'), - icon: 'info', - onClick: () => show({ content: AboutModal, title: 'About OHIF Viewer' }), - }, - { - title: t('Header:Preferences'), - icon: 'settings', - onClick: () => - show({ - title: t('UserPreferencesModal:User Preferences'), - content: UserPreferences, - contentProps: { - hotkeyDefaults: - hotkeysManager.getValidHotkeyDefinitions(hotkeyDefaults), - hotkeyDefinitions, - onCancel: hide, - currentLanguage: currentLanguage(), - availableLanguages, - defaultLanguage, - onSubmit: (state) => { - i18n.changeLanguage(state.language.value) - hotkeysManager.setHotkeys(state.hotkeyDefinitions) - hide() - }, - onReset: () => hotkeysManager.restoreDefaultBindings(), - }, - }), - }, - ] - /** ... **/ - return ( -
- /** ... **/ -
- /** ... **/ -
- ) -} -``` - - - - - - - - - -## Tips & Tricks - -It's important to remember that all we're doing is making it possible to control -bits of the application's UI from an extension. Here are a few non-obvious -takeaways worth mentioning: - -- Your application code should continue to use React context - (consumers/providers) as it normally would -- You can substitute our "out of the box" UI implementations with your own -- You can create and register your own UI services -- You can choose not to register a service or provide a service implementation -- In extensions, you can provide fallback/alternative behavior if an expected - service is not registered - - No `UIModalService`? Use the `UINotificationService` to notify users. -- You can technically register a service in an extension and expose it to the core application - -> Note: These are recommended patterns, not hard and fast rules. Following them -> will help reduce confusion and interoperability with the larger OHIF -> community, but they're not silver bullets. Please speak up, create an issue, -> if you would like to discuss new services or improvements to this pattern. - - diff --git a/docs/latest/services/ui/ui-dialog-service.md b/docs/latest/services/ui/ui-dialog-service.md deleted file mode 100644 index 2cc000e2c..000000000 --- a/docs/latest/services/ui/ui-dialog-service.md +++ /dev/null @@ -1,44 +0,0 @@ -# UI Dialog Service - -Dialogs have similar characteristics to that of Modals, but often with a -streamlined focus. They can be helpful when: - -- We need to grab the user's attention -- We need user input -- We need to show additional information - -If you're curious about the DOs and DON'Ts of dialogs and modals, check out this -article: ["Best Practices for Modals / Overlays / Dialog Windows"][ux-article] - - - -## Interface - -For a more detailed look on the options and return values each of these methods -is expected to support, [check out it's interface in `@ohif/core`][interface] - -| API Member | Description | -| -------------- | ------------------------------------------------------ | -| `create()` | Creates a new Dialog that is displayed until dismissed | -| `dismiss()` | Dismisses the specified dialog | -| `dismissAll()` | Dismisses all dialogs | - -## Implementations - -| Implementation | Consumer | -| ------------------------------------ | -------------------------- | -| [Dialog Provider][dialog-provider]\* | Baked into Dialog Provider | - -`*` - Denotes maintained by OHIF - -> 3rd Party implementers may be added to this table via pull requests. - - - - -[interface]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/services/UIDialogService/index.js -[dialog-provider]: https://github.com/OHIF/Viewers/blob/master/platform/ui/src/contextProviders/DialogProvider.js -[ux-article]: https://uxplanet.org/best-practices-for-modals-overlays-dialog-windows-c00c66cddd8c - diff --git a/docs/latest/services/ui/ui-modal-service.md b/docs/latest/services/ui/ui-modal-service.md deleted file mode 100644 index e6c208d3e..000000000 --- a/docs/latest/services/ui/ui-modal-service.md +++ /dev/null @@ -1,49 +0,0 @@ -# UI Modal Service - -Modals have similar characteristics to that of Dialogs, but are often larger, -and only allow for a single instance to be viewable at once. They also tend to -be centered, and not draggable. They're commonly used when: - -- We need to grab the user's attention -- We need user input -- We need to show additional information - -If you're curious about the DOs and DON'Ts of dialogs and modals, check out this -article: ["Best Practices for Modals / Overlays / Dialog Windows"][ux-article] - -
- -## Interface - -For a more detailed look on the options and return values each of these methods -is expected to support, [check out it's interface in `@ohif/core`][interface] - -| API Member | Description | -| ---------- | ------------------------------------- | -| `hide()` | Hides the open modal | -| `show()` | Shows the provided content in a modal | - -## Implementations - -| Implementation | Consumer | -| ---------------------------------- | --------- | -| [Modal Provider][modal-provider]\* | Modal.jsx | - -`*` - Denotes maintained by OHIF - - - - - -> 3rd Party implementers may be added to this table via pull requests. - - - - -[interface]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/services/UIModalService/index.js -[modal-provider]: https://github.com/OHIF/Viewers/blob/master/platform/ui/src/contextProviders/ModalProvider.js -[modal-consumer]: https://github.com/OHIF/Viewers/tree/master/platform/ui/src/components/ohifModal -[ux-article]: https://uxplanet.org/best-practices-for-modals-overlays-dialog-windows-c00c66cddd8c - diff --git a/docs/latest/services/ui/ui-notification-service.md b/docs/latest/services/ui/ui-notification-service.md deleted file mode 100644 index a806e3c7c..000000000 --- a/docs/latest/services/ui/ui-notification-service.md +++ /dev/null @@ -1,45 +0,0 @@ -# UI Notification Service - -Notifications can be annoying and disruptive. They can also deliver timely -helpful information, or expedite the user's workflow. Here is some high level -guidance on when and how to use them: - -- Notifications should be non-interfering (timely, relevant, important) -- We should only show small/brief notifications -- Notifications should be contextual to current behavior/actions -- Notifications can serve warnings (acting as a confirmation) - -If you're curious about the DOs and DON'Ts of notifications, check out this -article: ["How To Design Notifications For Better UX"][ux-article] - -
-## Interface - -For a more detailed look on the options and return values each of these methods -is expected to support, [check out it's interface in `@ohif/core`][interface] - -| API Member | Description | -| ---------- | --------------------------------------- | -| `hide()` | Hides the specified notification | -| `show()` | Creates and displays a new notification | - -## Implementations - -| Implementation | Consumer | -| ---------------------------------------- | ----------------------------------------- | -| [Snackbar Provider][snackbar-provider]\* | [SnackbarContainer][snackbar-container]\* | - -`*` - Denotes maintained by OHIF - -> 3rd Party implementers may be added to this table via pull requests. - - - - -[interface]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/services/UINotificationService/index.js -[snackbar-provider]: https://github.com/OHIF/Viewers/blob/master/platform/ui/src/contextProviders/SnackbarProvider.js -[snackbar-container]: https://github.com/OHIF/Viewers/blob/master/platform/ui/src/components/snackbar/SnackbarContainer.js -[ux-article]: https://uxplanet.org/how-to-design-notifications-for-better-ux-6fb0711be54d - diff --git a/docs/latest/viewer/configuration.md b/docs/latest/viewer/configuration.md deleted file mode 100644 index 1f0314347..000000000 --- a/docs/latest/viewer/configuration.md +++ /dev/null @@ -1,58 +0,0 @@ -# Viewer: Configuration - -We maintain a number of common viewer application configurations at -[`/platform/viewer/public/configs`][config-dir]. - -You can take a look at how to use different configs in the [Environment Variables](../configuring/index.md#environment-variables) - - -```js -window.config = { - routerBasename: '/', - /** - * "White Labeling" is used to change the branding, look, and feel of the OHIF - * Viewer. These settings, and the color variables that are used by our components, - * are the easiest way to rebrand the application. - * - * More extensive changes are made possible through swapping out the UI library, - * Viewer project, or extensions. - */ - whiteLabeling: { - /** ... **/ - }, - httpErrorHandler: { - /** coming soon **/ - }, - extensions: [], - showStudyList: true, - filterQueryParam: false, - dataSources: [ - { - friendlyName: 'dcmjs DICOMWeb Server', - namespace: 'org.ohif.default.dataSourcesModule.dicomweb', - sourceName: 'dicomweb', - configuration: { - name: 'DCM4CHEE', - wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado', - qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', - wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', - qidoSupportsIncludeField: true, - supportsReject: true, - imageRendering: 'wadors', - thumbnailRendering: 'wadors', - enableStudyLazyLoad: true, - supportsFuzzyMatching: true, - supportsWildcard: true, - }, - }, - ], -}; -``` - - - - -[config-dir]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/config - diff --git a/docs/latest/viewer/environment-variables.md b/docs/latest/viewer/environment-variables.md deleted file mode 100644 index ce18f9849..000000000 --- a/docs/latest/viewer/environment-variables.md +++ /dev/null @@ -1,23 +0,0 @@ -# Environment Variables - -There are a number of environment variables we use at build time to influence the output application's behavior. - -```bash -# Application -NODE_ENV=< production | development > -DEBUG=< true | false > -APP_CONFIG=< relative path to application configuration file > -PUBLIC_URL=<> -VERSION_NUMBER= -BUILD_NUM= -# i18n -USE_LOCIZE= -LOCIZE_PROJECTID= -LOCIZE_API_KEY= -``` - -## Setting Environment Variables - -- `npx cross-env` -- `.env` files -- env variables on build machine, or for terminal session diff --git a/docs/latest/viewer/internationalization.md b/docs/latest/viewer/internationalization.md deleted file mode 100644 index 9c41713b5..000000000 --- a/docs/latest/viewer/internationalization.md +++ /dev/null @@ -1,332 +0,0 @@ -# Viewer: Internationalization - -OHIF supports internationalization using [i18next](https://www.i18next.com/) -through the npm package [@ohif/i18n](https://www.npmjs.com/package/@ohif/i18n), -where is the main instance of i18n containing several languages and tools. - -
-
-

Our translation management is powered by Locize through their generous support of open source.

-
-
- - Locize Translation Management Logo - -
-
- - -## How to change language for the viewer? -You can take a look into user manuals to see how to change the viewer's language. -In summary you can change the language: - -- In the preference modals -- Using the language query in the URL: `lng=Test-LNG` - - - -## Installing - -```bash -yarn add @ohif/i18n - -# OR - -npm install --save @ohif/i18n -``` - -## How it works - -After installing `@ohif/i18n` npm package, the translation function -[t](https://www.i18next.com/overview/api#t) can be used [with](#with-react) or -[without](#without-react) React. - -A translation will occur every time a text match happens in a -[t](https://www.i18next.com/overview/api#t) function. - -The [t](https://www.i18next.com/overview/api#t) function is responsible for -getting translations using all the power of i18next. - -E.g. - -Before: - -```html -
my translated text
-``` - -After: - -```html -
{t('my translated text')}
-``` - -If the translation.json file contains a key that matches the HTML content e.g. -`my translated text`, it will be replaced automatically by the -[t](https://www.i18next.com/overview/api#t) function. - ---- - -### With React - -This section will introduce you to [react-i18next](https://react.i18next.com/) -basics and show how to implement the [t](https://www.i18next.com/overview/api#t) -function easily. - - -#### Using Hooks -You can use `useTranslation` hooks that is provided by `react-i18next` - - -You can read more about this [here](https://react.i18next.com/latest/usetranslation-hook). - - -```js -import React from 'react'; -import { useTranslation } from 'react-i18next'; - - -function MyComponent() { - const { t } = useTranslation(); - - return

{t('my translated text')}

; -} - -``` - -### Using outside of OHIF viewer - -OHIF Viewer already sets a main -[I18nextProvider](https://react.i18next.com/latest/i18nextprovider) connected to -the shared i18n instance from `@ohif/i18n`, all extensions inside OHIF Viewer -will share this same provider at the end, you don't need to set new providers at -all. - -But, if you need to use it completely outside of OHIF viewer, you can set the -I18nextProvider this way: - -```js -import i18n from '@ohif/i18n'; -import { I18nextProvider } from 'react-i18next'; -import App from './App'; - - - -; -``` - -After setting `I18nextProvider` in your React App, all translations from -`@ohif/i18n` should be available following the basic [With React](#with-react) -usage. - ---- - -### Without React - -When needed, you can also use available translations _without React_. - -E.g. - -```js -import { T } from '@ohif/i18n'; -console.log(T('my translated text')); -console.log(T('$t(Common:Play) my translated text')); -``` - ---- - -# Main Concepts While Translating - -## Namespaces - -Namespaces are being used to organize translations in smaller portions, combined -semantically or by use. Each `.json` file inside `@ohif/i18n` npm package -becomes a new namespace automatically. - -- Buttons: All buttons translations -- CineDialog: Translations for the toll tips inside the Cine Player Dialog -- Common: all common jargons that can be reused like `t('$t(common:image)')` -- Header: translations related to OHIF's Header Top Bar -- MeasurementTable - Translations for the `@ohif/ui` Measurement Table -- UserPreferencesModal - Translations for the `@ohif/ui` Preferences Modal -- Modals - Translations available for other modals -- PatientInfo - Translations for patients info hover -- SidePanel - Translations for side panels -- ToolTip - Translations for tool tips - -### How to use another NameSpace inside the current NameSpace? - -i18next provides a parsing feature able to get translations strings from any -NameSpace, like this following example getting data from `Common` NameSpace: - -``` -$t('Common:Reset') -``` - -## Extending Languages in @ohif/i18n - -Sometimes, even using the same language, some nouns or jargons can change -according to the country, states or even from Hospital to Hospital. - -In this cases, you don't need to set an entire language again, you can extend -languages creating a new folder inside a pre existent language folder and -@ohif/i18n will do the hard work. - -This new folder must to be called with a double character name, like the `UK` in -the following file tree: - -```bash - |-- src - |-- locales - index.js - |-- en - |-- Buttons.json - index.js - | UK - |-- Buttons.js - indes.js - | US - |-- Buttons.js - index.js - ... -``` - -All properties inside a Namespace will be merged in the new sub language, e.g -`en-US` and `en-UK` will merge the props with `en`, using i18next's fallback -languages tool. - -You will need to export all Json files in your `index.js` file, mounting an -object like this: - -```js - { - en: { - NameSpace: { - keyWord1: 'keyWord1Translation', - keyWord2: 'keyWord2Translation', - keyWord3: 'keyWord3Translation', - } - }, - 'en-UK': { - NameSpace: { - keyWord1: 'keyWord1DifferentTranslation', - } - } - } -``` - -Please check the `index.js` files inside locales folder for an example of this -exporting structure. - -### Extending languages dynamically - -You have access to the i18next instance, so you can use the -[addResourceBundle](https://www.i18next.com/how-to/add-or-load-translations#add-after-init) -method to add and change language resources as needed. - -E.g. - -```js -import { i18n } from '@ohif/i18n'; -i18next.addResourceBundle('pt-BR', 'Buttons', { - Angle: 'Ângulo', -}); -``` - ---- - -### How to set a whole new language - -To set a brand new language you can do it in two different ways: - -- Opening a pull request for `@ohif/i18n` and sharing the translation with the - community. 😍 Please see [Contributing](#contributing-with-new-languages) - section for further information. - -- Setting it only in your project or extension: - -You'll need a final object like the following, what is setting French as -language, and send it to `addLocales` method. - -```js -const newLanguage = - { - fr: { - Commons: { - "Reset": "Réinitialiser", - "Previous": "Précédent", - }, - Buttons: { - "Rectangle": "Rectangle", - "Circle": "Cercle", - } - } -``` - -To make it easier to translate, you can copy the .json files in the /locales -folder and theirs index.js exporters, keeping same keys and NameSpaces. -Importing the main index.js file, will provide you an Object as expected by the -method `addlocales`; - -E.g. of `addLocales` usage - -```js -import { addLocales } from '@ohif/i18n'; -import locales from './locales/index.js'; -addLocales(locales); -``` - -You can also set them manually, one by one, using this -[method](#extending-languages-dynamically). - ---- - - - -## Test Language -We have created a test language that its translations can be seen in the locales folder. You can copy paste the folder and its `.json` namespaces and add your custom -language translations. - -> If you apply the test-LNG you can see all the elements get appended with 'Test {}'. -> For instance `Study list` becomes `Test Study list`. - -## Language Detections - -@ohif/i18n uses -[i18next-browser-languageDetector](https://github.com/i18next/i18next-browser-languageDetector) -to manage detections, also exports a method called initI18n that accepts a new -detector config as parameter. - -### Changing the language - -OHIF Viewer accepts a query param called `lng` in the url to change the -language. - -E.g. - -``` -https://docs.ohif.org/demo/?lng=es-MX -``` - -### Language Persistence - -The user's language preference is kept automatically by the detector and stored -at a cookie called 'i18next', and in a localstorage key called 'i18nextLng'. -These names can be changed with a new -[Detector Config](https://github.com/i18next/i18next-browser-languageDetector). - -## Debugging translations - -There is an environment variable responsible for debugging the translations, -called `REACT_APP_I18N_DEBUG`. - -Run the project as following to get full debug information: - -```bash -REACT_APP_I18N_DEBUG=true yarn run dev -``` - -### Contributing with new languages - -Contributions of any kind are welcome! Please check the -[instructions](../development/contributing.md). diff --git a/docs/latest/viewer/themeing.md b/docs/latest/viewer/themeing.md deleted file mode 100644 index abe20ac4e..000000000 --- a/docs/latest/viewer/themeing.md +++ /dev/null @@ -1,167 +0,0 @@ -# Viewer: Theming - - -`OHIF-v3` has introduced the [`LayoutTemplateModule`](../extensions/modules/layout-template.md) which enables addition of custom layouts. You can easily design your custom components inside an extension and consume it via the layoutTemplate module you write. - - - - -## Tailwind CSS -[Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework for creating custom user interfaces. - - -Below you can see a compiled version of the tailwind configs. -Each section can be edited accordingly. For instance screen size break points, primary -and secondary colors, etc. - - - -```js -module.exports = { - prefix: '', - important: false, - separator: ':', - theme: { - screens: { - sm: '640px', - md: '768px', - lg: '1024px', - xl: '1280px', - }, - colors: { - overlay: 'rgba(0, 0, 0, 0.8)', - transparent: 'transparent', - black: '#000', - white: '#fff', - initial: 'initial', - inherit: 'inherit', - - indigo: { - dark: '#0b1a42', - }, - aqua: { - pale: '#7bb2ce', - }, - - primary: { - light: '#5acce6', - main: '#0944b3', - dark: '#090c29', - active: '#348cfd', - }, - - secondary: { - light: '#3a3f99', - main: '#2b166b', - dark: '#041c4a', - active: '#1f1f27', - }, - - common: { - bright: '#e1e1e1', - light: '#a19fad', - main: '#fff', - dark: '#726f7e', - active: '#2c3074', - }, - - customgreen: { - 100: '#05D97C', - }, - - customblue: { - 100: '#c4fdff', - 200: '#38daff', - }, - }, - }, -} -``` - - -You can also use the color variable like before. For instance: - - -```js -primary: { - default: ‘var(--default-color)‘, - light: ‘#5ACCE6’, - main: ‘#0944B3’, - dark: ‘#090C29’, - active: ‘#348CFD’, -} -``` - - -## White Labeling - -A white-label product is a product or service produced by one company (the producer) that other companies (the marketers) rebrand to make it appear as if they had made it - [Wikipedia: White-Label Product](https://en.wikipedia.org/wiki/White-label_product) - -Current white-labeling options are limited. -We expose the ability to replace the "Logo" section of the application with a custom "Logo" component. You can do this by adding a whiteLabeling key to your configuration file. - -```js -window.config = { - /** .. **/ - whiteLabeling: { - createLogoComponentFn: function (React) { - return React.createElement( - 'a', - { - target: '_blank', - rel: 'noopener noreferrer', - className: 'text-white underline', - href: 'http://radicalimaging.com', - }, - React.createElement('h5', {}, 'RADICAL IMAGING') - ) - }, - }, - /** .. **/ -} -``` - -> You can simply use the stylings from tailwind CSS in the whiteLabeling - - -In addition to text, you can also add your custom logo - - -```js -window.config = { - /** .. **/ - whiteLabeling: { - createLogoComponentFn: function (React) { - return React.createElement( - 'a', - { - target: '_self', - rel: 'noopener noreferrer', - className: 'text-purple-600 line-through', - href: '/', - }, - React.createElement('img', { - src: './customLogo.svg', - // className: 'w-8 h-8', - }) - ) - }, - }, - /** .. **/ -} -``` - -The output will look like - - -![custom-logo](../assets/img/custom-logo.png) - - - - - - -[wikipedia]: https://en.wikipedia.org/wiki/White-label_product - diff --git a/platform/docs/docs/README.md b/platform/docs/docs/README.md index 9cb513ec2..e4cd6d152 100644 --- a/platform/docs/docs/README.md +++ b/platform/docs/docs/README.md @@ -1,11 +1,4 @@ ---- -id: Introduction -slug: / -sidebar_position: 1 ---- - - - - -
- Looking for a Live Demo?
- - - - - - - - -> ATTENTION! You are looking at the docs for the `React` version of the OHIF -> Viewer. If you're looking for the `Meteor` version's documentation (now -> deprecated), select it's version from the dropdown box in the top right corner +> ATTENTION! You are looking at the docs for the `OHIF-v3` Viewer (third time is always a charm). If you're looking for the our `OHIF-v2` (React stable version) or OHIF-v1 (deprecated `Meteor` version) select it's version from the dropdown box in the top left corner > of this page. +  +# Introduction + The [Open Health Imaging Foundation][ohif-org] (OHIF) Viewer is an open source, web-based, medical imaging viewer. It can be configured to connect to Image Archives that support [DicomWeb][dicom-web], and offers support for mapping to proprietary API formats. OHIF maintained extensions add support for viewing, annotating, and reporting on DICOM images in 2D (slices) and 3D (volumes). -![OHIF Viewer Screenshot](./assets/img/viewer.png) -
The OHIF Viewer: A general purpose DICOM Viewer (Live Demo)
+ +![OHIF Viewer Screenshot](./assets/img/OHIF-viewer.png) + +
The OHIF Viewer v3: A general purpose DICOM Viewer (Live Demo)
+ + +  + +## What's new in `OHIF-v3` +`OHIF-v3` is our second try for a React-based viewer, and is the third version of +our medical image web viewers from the start. The summary of changes include: + +- Addition of workflow modes + + - Often, medical imaging use cases involves lots of specific workflows that re-use + functionalities. We have added the capability of workflow modes, that enable people + to customize user interface and configure application for specific workflow. + - The idea is to re-use the functionalities that extensions provide and create a workflow. + Brain segmentation workflow is different from prostate segmentation in UI for sure; however, they share the segmentation tools that can be re-used. + - Our vision is that technical people focus of developing extensions which provides + core functionalities, and experts to build modes by picking the appropriate functionalities + from each extension. + + +- Redux store has been removed from the viewer, and a cleaner, more powerful +- tailwind CSS +- End-to-end test suite + + +Below, you can find the gap analysis between the `OHIF-v2` and `OHIF-v3`: + + +{% include "./_gap_analysis.md" %} + + + +## Where to next? + The Open Health Imaging Foundation intends to provide a simple general purpose DICOM Viewer which can be easily extended for specific uses. If you find @@ -56,7 +68,6 @@ yourself unable to extend the viewer for your purposes, please reach out via our [GitHub issues][gh-issues]. We are actively seeking feedback on ways to improve our integration and extension points. -## Where to next? Check out these helpful links: @@ -66,12 +77,19 @@ Check out these helpful links: [Learn how you can be more involved.](./development/contributing.md) - Feeling lost? Read our [help page](./help.md). + + + + + + [ohif-org]: http://www.ohif.org +[ohif-demo]: http://viewer.ohif.org/ [dicom-web]: https://en.wikipedia.org/wiki/DICOMweb [gh-issues]: https://github.com/OHIF/Viewers/issues diff --git a/docs/latest/SUMMARY.md b/platform/docs/docs/SUMMARY.md similarity index 100% rename from docs/latest/SUMMARY.md rename to platform/docs/docs/SUMMARY.md diff --git a/docs/latest/_gap_analysis.md b/platform/docs/docs/_gap_analysis.md similarity index 100% rename from docs/latest/_gap_analysis.md rename to platform/docs/docs/_gap_analysis.md diff --git a/docs/latest/architecture/index.md b/platform/docs/docs/architecture/index.md similarity index 100% rename from docs/latest/architecture/index.md rename to platform/docs/docs/architecture/index.md diff --git a/docs/latest/assets/img/OHIF-Viewer.png b/platform/docs/docs/assets/img/OHIF-Viewer.png similarity index 100% rename from docs/latest/assets/img/OHIF-Viewer.png rename to platform/docs/docs/assets/img/OHIF-Viewer.png diff --git a/docs/latest/assets/img/OHIF-e2e-test-studies.png b/platform/docs/docs/assets/img/OHIF-e2e-test-studies.png similarity index 100% rename from docs/latest/assets/img/OHIF-e2e-test-studies.png rename to platform/docs/docs/assets/img/OHIF-e2e-test-studies.png diff --git a/docs/latest/assets/img/custom-logo.png b/platform/docs/docs/assets/img/custom-logo.png similarity index 100% rename from docs/latest/assets/img/custom-logo.png rename to platform/docs/docs/assets/img/custom-logo.png diff --git a/docs/latest/assets/img/docker-pacs.png b/platform/docs/docs/assets/img/docker-pacs.png similarity index 100% rename from docs/latest/assets/img/docker-pacs.png rename to platform/docs/docs/assets/img/docker-pacs.png diff --git a/docs/latest/assets/img/e2e-cypress-final.png b/platform/docs/docs/assets/img/e2e-cypress-final.png similarity index 100% rename from docs/latest/assets/img/e2e-cypress-final.png rename to platform/docs/docs/assets/img/e2e-cypress-final.png diff --git a/docs/latest/assets/img/e2e-cypress.png b/platform/docs/docs/assets/img/e2e-cypress.png similarity index 100% rename from docs/latest/assets/img/e2e-cypress.png rename to platform/docs/docs/assets/img/e2e-cypress.png diff --git a/docs/latest/assets/img/mode-archs.png b/platform/docs/docs/assets/img/mode-archs.png similarity index 100% rename from docs/latest/assets/img/mode-archs.png rename to platform/docs/docs/assets/img/mode-archs.png diff --git a/docs/latest/assets/img/panel-module-left-right.png b/platform/docs/docs/assets/img/panel-module-left-right.png similarity index 100% rename from docs/latest/assets/img/panel-module-left-right.png rename to platform/docs/docs/assets/img/panel-module-left-right.png diff --git a/docs/latest/assets/img/panel-module-v3.png b/platform/docs/docs/assets/img/panel-module-v3.png similarity index 100% rename from docs/latest/assets/img/panel-module-v3.png rename to platform/docs/docs/assets/img/panel-module-v3.png diff --git a/docs/latest/assets/img/panelmodule-icon.png b/platform/docs/docs/assets/img/panelmodule-icon.png similarity index 100% rename from docs/latest/assets/img/panelmodule-icon.png rename to platform/docs/docs/assets/img/panelmodule-icon.png diff --git a/docs/latest/assets/img/services-data.png b/platform/docs/docs/assets/img/services-data.png similarity index 100% rename from docs/latest/assets/img/services-data.png rename to platform/docs/docs/assets/img/services-data.png diff --git a/docs/latest/assets/img/services-measurements.png b/platform/docs/docs/assets/img/services-measurements.png similarity index 100% rename from docs/latest/assets/img/services-measurements.png rename to platform/docs/docs/assets/img/services-measurements.png diff --git a/docs/latest/assets/img/services-ui.png b/platform/docs/docs/assets/img/services-ui.png similarity index 100% rename from docs/latest/assets/img/services-ui.png rename to platform/docs/docs/assets/img/services-ui.png diff --git a/docs/latest/assets/img/toolbarModule-layout.png b/platform/docs/docs/assets/img/toolbarModule-layout.png similarity index 100% rename from docs/latest/assets/img/toolbarModule-layout.png rename to platform/docs/docs/assets/img/toolbarModule-layout.png diff --git a/docs/latest/assets/img/toolbarModule-nested-buttons.png b/platform/docs/docs/assets/img/toolbarModule-nested-buttons.png similarity index 100% rename from docs/latest/assets/img/toolbarModule-nested-buttons.png rename to platform/docs/docs/assets/img/toolbarModule-nested-buttons.png diff --git a/docs/latest/assets/img/toolbarModule-zoom.png b/platform/docs/docs/assets/img/toolbarModule-zoom.png similarity index 100% rename from docs/latest/assets/img/toolbarModule-zoom.png rename to platform/docs/docs/assets/img/toolbarModule-zoom.png diff --git a/docs/latest/assets/img/toolbarModule.png b/platform/docs/docs/assets/img/toolbarModule.png similarity index 100% rename from docs/latest/assets/img/toolbarModule.png rename to platform/docs/docs/assets/img/toolbarModule.png diff --git a/docs/latest/assets/img/ui-modal.gif b/platform/docs/docs/assets/img/ui-modal.gif similarity index 100% rename from docs/latest/assets/img/ui-modal.gif rename to platform/docs/docs/assets/img/ui-modal.gif diff --git a/docs/latest/assets/img/user-hotkeys-default.png b/platform/docs/docs/assets/img/user-hotkeys-default.png similarity index 100% rename from docs/latest/assets/img/user-hotkeys-default.png rename to platform/docs/docs/assets/img/user-hotkeys-default.png diff --git a/docs/latest/assets/img/user-hotkeys.png b/platform/docs/docs/assets/img/user-hotkeys.png similarity index 100% rename from docs/latest/assets/img/user-hotkeys.png rename to platform/docs/docs/assets/img/user-hotkeys.png diff --git a/docs/latest/assets/img/user-measurement-edit-name.png b/platform/docs/docs/assets/img/user-measurement-edit-name.png similarity index 100% rename from docs/latest/assets/img/user-measurement-edit-name.png rename to platform/docs/docs/assets/img/user-measurement-edit-name.png diff --git a/docs/latest/assets/img/user-measurement-edit.png b/platform/docs/docs/assets/img/user-measurement-edit.png similarity index 100% rename from docs/latest/assets/img/user-measurement-edit.png rename to platform/docs/docs/assets/img/user-measurement-edit.png diff --git a/docs/latest/assets/img/user-measurement-export.png b/platform/docs/docs/assets/img/user-measurement-export.png similarity index 100% rename from docs/latest/assets/img/user-measurement-export.png rename to platform/docs/docs/assets/img/user-measurement-export.png diff --git a/docs/latest/assets/img/user-measurement-name.png b/platform/docs/docs/assets/img/user-measurement-name.png similarity index 100% rename from docs/latest/assets/img/user-measurement-name.png rename to platform/docs/docs/assets/img/user-measurement-name.png diff --git a/docs/latest/assets/img/user-measurement-panel-modal.png b/platform/docs/docs/assets/img/user-measurement-panel-modal.png similarity index 100% rename from docs/latest/assets/img/user-measurement-panel-modal.png rename to platform/docs/docs/assets/img/user-measurement-panel-modal.png diff --git a/docs/latest/assets/img/user-measurement-panel-tracked.png b/platform/docs/docs/assets/img/user-measurement-panel-tracked.png similarity index 100% rename from docs/latest/assets/img/user-measurement-panel-tracked.png rename to platform/docs/docs/assets/img/user-measurement-panel-tracked.png diff --git a/docs/latest/assets/img/user-measurement-prevnext.png b/platform/docs/docs/assets/img/user-measurement-prevnext.png similarity index 100% rename from docs/latest/assets/img/user-measurement-prevnext.png rename to platform/docs/docs/assets/img/user-measurement-prevnext.png diff --git a/docs/latest/assets/img/user-open-viewer.png b/platform/docs/docs/assets/img/user-open-viewer.png similarity index 100% rename from docs/latest/assets/img/user-open-viewer.png rename to platform/docs/docs/assets/img/user-open-viewer.png diff --git a/docs/latest/assets/img/user-study-filter.png b/platform/docs/docs/assets/img/user-study-filter.png similarity index 100% rename from docs/latest/assets/img/user-study-filter.png rename to platform/docs/docs/assets/img/user-study-filter.png diff --git a/docs/latest/assets/img/user-study-list.png b/platform/docs/docs/assets/img/user-study-list.png similarity index 100% rename from docs/latest/assets/img/user-study-list.png rename to platform/docs/docs/assets/img/user-study-list.png diff --git a/docs/latest/assets/img/user-study-next.png b/platform/docs/docs/assets/img/user-study-next.png similarity index 100% rename from docs/latest/assets/img/user-study-next.png rename to platform/docs/docs/assets/img/user-study-next.png diff --git a/docs/latest/assets/img/user-study-panel.png b/platform/docs/docs/assets/img/user-study-panel.png similarity index 100% rename from docs/latest/assets/img/user-study-panel.png rename to platform/docs/docs/assets/img/user-study-panel.png diff --git a/docs/latest/assets/img/user-study-summary.png b/platform/docs/docs/assets/img/user-study-summary.png similarity index 100% rename from docs/latest/assets/img/user-study-summary.png rename to platform/docs/docs/assets/img/user-study-summary.png diff --git a/docs/latest/assets/img/user-studyist-modespecific.png b/platform/docs/docs/assets/img/user-studyist-modespecific.png similarity index 100% rename from docs/latest/assets/img/user-studyist-modespecific.png rename to platform/docs/docs/assets/img/user-studyist-modespecific.png diff --git a/docs/latest/assets/img/user-studylist-all.png b/platform/docs/docs/assets/img/user-studylist-all.png similarity index 100% rename from docs/latest/assets/img/user-studylist-all.png rename to platform/docs/docs/assets/img/user-studylist-all.png diff --git a/docs/latest/assets/img/user-toolbar-download-icon.png b/platform/docs/docs/assets/img/user-toolbar-download-icon.png similarity index 100% rename from docs/latest/assets/img/user-toolbar-download-icon.png rename to platform/docs/docs/assets/img/user-toolbar-download-icon.png diff --git a/docs/latest/assets/img/user-toolbar-download.png b/platform/docs/docs/assets/img/user-toolbar-download.png similarity index 100% rename from docs/latest/assets/img/user-toolbar-download.png rename to platform/docs/docs/assets/img/user-toolbar-download.png diff --git a/docs/latest/assets/img/user-toolbar-extra.png b/platform/docs/docs/assets/img/user-toolbar-extra.png similarity index 100% rename from docs/latest/assets/img/user-toolbar-extra.png rename to platform/docs/docs/assets/img/user-toolbar-extra.png diff --git a/docs/latest/assets/img/user-toolbar-preset.png b/platform/docs/docs/assets/img/user-toolbar-preset.png similarity index 100% rename from docs/latest/assets/img/user-toolbar-preset.png rename to platform/docs/docs/assets/img/user-toolbar-preset.png diff --git a/docs/latest/assets/img/user-viewer-components.png b/platform/docs/docs/assets/img/user-viewer-components.png similarity index 100% rename from docs/latest/assets/img/user-viewer-components.png rename to platform/docs/docs/assets/img/user-viewer-components.png diff --git a/docs/latest/assets/img/user-viewer-layout.png b/platform/docs/docs/assets/img/user-viewer-layout.png similarity index 100% rename from docs/latest/assets/img/user-viewer-layout.png rename to platform/docs/docs/assets/img/user-viewer-layout.png diff --git a/docs/latest/assets/img/user-viewer-main.png b/platform/docs/docs/assets/img/user-viewer-main.png similarity index 100% rename from docs/latest/assets/img/user-viewer-main.png rename to platform/docs/docs/assets/img/user-viewer-main.png diff --git a/docs/latest/assets/img/user-viewer-toolbar-measurements.png b/platform/docs/docs/assets/img/user-viewer-toolbar-measurements.png similarity index 100% rename from docs/latest/assets/img/user-viewer-toolbar-measurements.png rename to platform/docs/docs/assets/img/user-viewer-toolbar-measurements.png diff --git a/docs/latest/assets/img/user-viewer-toolbar.png b/platform/docs/docs/assets/img/user-viewer-toolbar.png similarity index 100% rename from docs/latest/assets/img/user-viewer-toolbar.png rename to platform/docs/docs/assets/img/user-viewer-toolbar.png diff --git a/docs/latest/assets/img/user-viewer.png b/platform/docs/docs/assets/img/user-viewer.png similarity index 100% rename from docs/latest/assets/img/user-viewer.png rename to platform/docs/docs/assets/img/user-viewer.png diff --git a/docs/latest/assets/img/viewportModule-layout.png b/platform/docs/docs/assets/img/viewportModule-layout.png similarity index 100% rename from docs/latest/assets/img/viewportModule-layout.png rename to platform/docs/docs/assets/img/viewportModule-layout.png diff --git a/docs/latest/assets/img/viewportModule.png b/platform/docs/docs/assets/img/viewportModule.png similarity index 100% rename from docs/latest/assets/img/viewportModule.png rename to platform/docs/docs/assets/img/viewportModule.png diff --git a/docs/latest/book.json b/platform/docs/docs/book.json similarity index 100% rename from docs/latest/book.json rename to platform/docs/docs/book.json diff --git a/platform/docs/docs/configuring/data-source.md b/platform/docs/docs/configuring/data-source.md index cc932255f..f47b12241 100644 --- a/platform/docs/docs/configuring/data-source.md +++ b/platform/docs/docs/configuring/data-source.md @@ -1,22 +1,17 @@ ---- -sidebar_position: 2 ---- -# Data Source - -After following the steps outlined in -[Getting Started](./../development/getting-started.md), you'll notice that the -OHIF Viewer has data for several studies and their images. You didn't add this -data, so where is it coming from? - -By default, the viewer is configured to connect to a remote server hosted by the -nice folks over at [dcmjs.org][dcmjs-org]. While convenient for getting started, -the time may come when you want to develop using your own data either locally or -remotely. - ## Set up a local DICOM server -> ATTENTION! Already have a remote or local server? Skip to the -> [configuration section](#configuration-learn-more) below. +- [Set up a local DICOM server](#set-up-a-local-dicom-server) + - [Requirements](#requirements) +- [Open Source DICOM Image Archives](#open-source-dicom-image-archives) + - [Running Orthanc](#running-orthanc) + - [Orthanc: Learn More](#orthanc-learn-more) + - [Connecting to Orthanc](#connecting-to-orthanc) + - [Configuration: Learn More](#configuration-learn-more) + - [Running DCM4CHEE](#running-dcm4chee) + + +ATTENTION! Already have a remote or local server? Skip to the +[configuration section](#configuration-learn-more) below. While the OHIF Viewer can work with any data source, the easiest to configure are the ones that follow the [DICOMWeb][dicom-web] spec. @@ -44,6 +39,27 @@ in command prompt or terminal_ > the ip docker-machine ip throws. This is the value [`WebPack`][webpack-proxy] > uses to proxy requests + + +## Open Source DICOM Image Archives + +There are a lot of options available to you to use as a local DICOM server. Here are some of the more popular ones: + +| Archive | Installation | +| --------------------------------------------- | ---------------------------------- | +| [DCM4CHEE Archive 5.x][dcm4chee] | [W/ Docker][dcm4chee-docker] | +| [Orthanc][orthanc] | [W/ Docker][orthanc-docker] | +| [DICOMcloud][dicomcloud] (**DICOM Web only**) | [Installation][dicomcloud-install] | +| [OsiriX][osirix] (**Mac OSX only**) | Desktop Client | +| [Horos][horos] (**Mac OSX only**) | Desktop Client | + +_Feel free to make a Pull Request if you want to add to this list._ + + +Below, we will focus on `DCM4CHEE` and `Orthanc` usage: + + + ### Running Orthanc _Start Orthanc:_ @@ -68,7 +84,7 @@ You can see the `docker-compose.yml` file this command runs at [`/.docker/Nginx-Orthanc/`][orthanc-docker-compose], and more on Orthanc for Docker in [Orthanc's documentation][orthanc-docker]. -### Connecting to Orthanc +#### Connecting to Orthanc Now that we have a local Orthanc instance up and running, we need to configure our web application to connect to it. Open a new terminal window, navigate to @@ -91,7 +107,7 @@ yarn run dev:orthanc > [Essentials Configuration](./index.md) guide. Let's take a look at what's going on under the hood here. `yarn run dev:orthanc` -is running the `dev:orthanc` script in our project's `package.json`. That script +is running the `dev:orthanc` script in our project's `package.json` (inside `platform/viewer`). That script is: ```js @@ -110,57 +126,61 @@ to `Orthanc`. This allows us to bypass CORS issues that normally occur when requesting resources that live at a different domain. The `APP_CONFIG` value tells our app which file to load on to `window.config`. -Here is what that +By default, our app uses the file at +`/platform/viewer/public/config/default.js`. Here is what that configuration looks like: -```js title="/platform/viewer/public/config/default.js" +```js window.config = { routerBasename: '/', - servers: { - dicomWeb: [ - { - name: 'Orthanc', - wadoUriRoot: 'http://localhost:8899/wado', - qidoRoot: 'http://localhost:8899/dicom-web', - wadoRoot: 'http://localhost:8899/dicom-web', - qidoSupportsIncludeField: false, + extensions: [], + modes: [], + showStudyList: true, + dataSources: [ + { + friendlyName: 'dcmjs DICOMWeb Server', + namespace: 'org.ohif.default.dataSourcesModule.dicomweb', + sourceName: 'dicomweb', + configuration: { + name: 'DCM4CHEE', + wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado', + qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', + wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', + qidoSupportsIncludeField: true, + supportsReject: true, imageRendering: 'wadors', thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: true, + supportsWildcard: true, }, - ], - }, + }, + ], + defaultDataSourceName: 'dicomweb', }; ``` To learn more about how you can configure the OHIF Viewer, check out our [Configuration Guide](./index.md). -## Open Source DICOM Image Archives -Our example uses `Orthanc`, but there are a lot of options available to you. -Here are some of the more popular ones: -| Archive | Installation | -| --------------------------------------------- | ---------------------------------- | -| [DCM4CHEE Archive 5.x][dcm4chee] | [W/ Docker][dcm4chee-docker] | -| [Orthanc][orthanc] | [W/ Docker][orthanc-docker] | -| [DICOMcloud][dicomcloud] (**DICOM Web only**) | [Installation][dicomcloud-install] | -| [OsiriX][osirix] (**Mac OSX only**) | Desktop Client | -| [Horos][horos] (**Mac OSX only**) | Desktop Client | +### Running DCM4CHEE + +dcm4che is a collection of open source applications for healthcare enterprise written in Java programming language +which implements DICOM standard. dcm4chee (extra 'e' at the end) is dcm4che project +for an Image Manager/Image Archive which provides storage, retrieval and other functionalities. +You can read more about dcm4chee in their website [here](https://www.dcm4che.org/) + +DCM4chee installation is out of scope for these tutorials and can be found [here](https://github.com/dcm4che/dcm4chee-arc-light/wiki/Run-minimum-set-of-archive-services-on-a-single-host) + +An overview of steps for running OHIF Viewer using a local DCM4CHEE is shown below: + +
+ -_Feel free to make a Pull Request if you want to add to this list._ - - -[dcmjs-org]: https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado -[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb -[storescu]: http://support.dcmtk.org/docs/storescu.html -[webpack-proxy]: https://webpack.js.org/configuration/dev-server/#devserverproxy -[orthanc-docker-compose]: https://github.com/OHIF/Viewers/tree/master/.docker/Nginx-Orthanc - [dcm4chee]: https://github.com/dcm4che/dcm4chee-arc-light [dcm4chee-docker]: https://github.com/dcm4che/dcm4chee-arc-light/wiki/Running-on-Docker [orthanc]: https://www.orthanc-server.com/ @@ -169,4 +189,6 @@ _Feel free to make a Pull Request if you want to add to this list._ [dicomcloud-install]: https://github.com/DICOMcloud/DICOMcloud#running-the-code [osirix]: http://www.osirix-viewer.com/ [horos]: https://www.horosproject.org/ - +[default-config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/public/config/default.js +[html-templates]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/html-templates +[config-files]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/config diff --git a/platform/docs/docs/configuring/index.md b/platform/docs/docs/configuring/index.md index 3d602fa40..fc0b8b62d 100644 --- a/platform/docs/docs/configuring/index.md +++ b/platform/docs/docs/configuring/index.md @@ -1,16 +1,27 @@ ---- -sidebar_position: 1 ---- - -# Configuration - -> This step assumes you have an imaging archive. If you need assistance setting -> one up, check out the [`Data Source` Guide](./data-source.md) or a deployment -> recipe that contains an open Image Archive +# Configuring Data Source -### Configuration Files +- [Configuring Data Source](#configuring-data-source) + - [Overview](#overview) + - [Configuration Files](#configuration-files) + - [Environment Variables](#environment-variables) + + +## Overview + +After following the steps outlined in +[Getting Started](./../development/getting-started.md), you'll notice that the +OHIF Viewer has data for several studies and their images. You didn't add this +data, so where is it coming from? + +By default, the viewer is configured to connect to a remote server hosted by the +nice folks over at [dcmjs.org][dcmjs-org]. While convenient for getting started, +the time may come when you want to develop using your own data either locally or +remotely. + + +## Configuration Files The configuration for our viewer is in the `platform/viewer/public/config` directory. Our build process knows which configuration file to use based on the @@ -18,54 +29,57 @@ directory. Our build process knows which configuration file to use based on the [`config/default.js`][default-config]. The majority of the viewer's features, and registered extension's features, are configured using this file. -**Embedded Use Note:** - -Alternatively, when using the `umd` bundle for embedded use cases, these same -values are what you'll pass to `installViewer` method: - -`OHIFStandaloneViewer.installViewer(window.config)` - -### Environment Variables - -We use environment variables at build and dev time to change the Viewer's -behavior. We can update the `HTML_TEMPLATE` to easily change which extensions -are registered, and specify a different `APP_CONFIG` to connect to an -alternative data source (or even specify different default hotkeys). - -| Environment Variable | Description | Default | -| -------------------- | -------------------------------------------------------------------------------------------------- | ------------------- | -| `HTML_TEMPLATE` | Which [HTML template][html-templates] to use as our web app's entry point. Specific to PWA builds. | `index.html` | -| `PUBLIC_URL` | The route relative to the host that the app will be served from. Specific to PWA builds. | `/` | -| `APP_CONFIG` | Which [configuration file][config-file] to copy to output as `app-config.js` | `config/default.js` | -| `PROXY_TARGET` | When developing, proxy requests that match this pattern to `PROXY_DOMAIN` | `undefined` | -| `PROXY_DOMAIN` | When developing, proxy requests from `PROXY_TARGET` to `PROXY_DOMAIN` | `undefined` | - -## How do I configure my project? - The simplest way is to update the existing default config: +> _/platform/viewer/public/config/default.js_ -```js title="/platform/viewer/public/config/default.js" +```js window.config = { routerBasename: '/', - servers: { - dicomWeb: [ - { + extensions: [], + modes: [], + showStudyList: true, + dataSources: [ + { + friendlyName: 'dcmjs DICOMWeb Server', + namespace: 'org.ohif.default.dataSourcesModule.dicomweb', + sourceName: 'dicomweb', + configuration: { name: 'DCM4CHEE', wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado', qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', qidoSupportsIncludeField: true, + supportsReject: true, imageRendering: 'wadors', thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: true, + supportsWildcard: true, }, - ], - }, + }, + ], + defaultDataSourceName: 'dicomweb', }; + ``` + +> As you can see a new change in `OHIF-v3` is the addition of `dataSources`. +> You can build your own datasource and map it to the internal data structure of OHIF’s > metadata and enjoy using other peoples developed mode on your own data! +> +> You can read more about data sources at [Data Source section in Modes](../modes/index.md) +> + + + + + + + The configuration can also be written as a JS Function in case you need to inject dependencies like external services: + ```js window.config = ({ servicesManager } = {}) => { const { UIDialogService } = servicesManager.services; @@ -80,23 +94,59 @@ window.config = ({ servicesManager } = {}) => { }, }, routerBasename: '/', - servers: { - dicomWeb: [ - { - name: 'DCM4CHEE', - wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado', - qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', - wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', - qidoSupportsIncludeField: true, - imageRendering: 'wadors', - thumbnailRendering: 'wadors', - }, - ], + dataSources: [ + { + friendlyName: 'dcmjs DICOMWeb Server', + namespace: 'org.ohif.default.dataSourcesModule.dicomweb', + sourceName: 'dicomweb', + configuration: { + name: 'DCM4CHEE', + wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado', + qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', + wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', + qidoSupportsIncludeField: true, + supportsReject: true, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: true, + supportsWildcard: true, + }, }, + ], + defaultDataSourceName: 'dicomweb', }; }; ``` + + +## Environment Variables + +We use environment variables at build and dev time to change the Viewer's +behavior. We can update the `HTML_TEMPLATE` to easily change which extensions +are registered, and specify a different `APP_CONFIG` to connect to an +alternative data source (or even specify different default hotkeys). + +| Environment Variable | Description | Default | +| -------------------- | -------------------------------------------------------------------------------------------------- | ------------------- | +| `HTML_TEMPLATE` | Which [HTML template][html-templates] to use as our web app's entry point. Specific to PWA builds. | `index.html` | +| `PUBLIC_URL` | The route relative to the host that the app will be served from. Specific to PWA builds. | `/` | +| `APP_CONFIG` | Which [configuration file][config-file] to copy to output as `app-config.js` | `config/default.js` | +| `PROXY_TARGET` | When developing, proxy requests that match this pattern to `PROXY_DOMAIN` | `undefined` | +| `PROXY_DOMAIN` | When developing, proxy requests from `PROXY_TARGET` to `PROXY_DOMAIN` | `undefined` | + + + + + + + You can also create a new config file and specify its path relative to the build output's root by setting the `APP_CONFIG` environment variable. You can set the value of this environment variable a few different ways: @@ -113,11 +163,27 @@ value of this environment variable a few different ways: After updating the configuration, `yarn run build` to generate updated build output. + + +[dcmjs-org]: https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado +[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb +[storescu]: http://support.dcmtk.org/docs/storescu.html +[webpack-proxy]: https://webpack.js.org/configuration/dev-server/#devserverproxy +[orthanc-docker-compose]: https://github.com/OHIF/Viewers/tree/master/.docker/Nginx-Orthanc + +[dcm4chee]: https://github.com/dcm4che/dcm4chee-arc-light +[dcm4chee-docker]: https://github.com/dcm4che/dcm4chee-arc-light/wiki/Running-on-Docker +[orthanc]: https://www.orthanc-server.com/ +[orthanc-docker]: http://book.orthanc-server.com/users/docker.html +[dicomcloud]: https://github.com/DICOMcloud/DICOMcloud +[dicomcloud-install]: https://github.com/DICOMcloud/DICOMcloud#running-the-code +[osirix]: http://www.osirix-viewer.com/ +[horos]: https://www.horosproject.org/ [default-config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/public/config/default.js [html-templates]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/html-templates [config-files]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/config diff --git a/docs/latest/connecting-to-image-archives/google-cloud-healthcare.md b/platform/docs/docs/connecting-to-image-archives/google-cloud-healthcare.md similarity index 100% rename from docs/latest/connecting-to-image-archives/google-cloud-healthcare.md rename to platform/docs/docs/connecting-to-image-archives/google-cloud-healthcare.md diff --git a/docs/latest/deployment/_embedded-viewer-diagram.md b/platform/docs/docs/deployment/_embedded-viewer-diagram.md similarity index 100% rename from docs/latest/deployment/_embedded-viewer-diagram.md rename to platform/docs/docs/deployment/_embedded-viewer-diagram.md diff --git a/docs/latest/deployment/_nginx-image-archive-diagram.md b/platform/docs/docs/deployment/_nginx-image-archive-diagram.md similarity index 100% rename from docs/latest/deployment/_nginx-image-archive-diagram.md rename to platform/docs/docs/deployment/_nginx-image-archive-diagram.md diff --git a/docs/latest/deployment/_user-account-control-flow-diagram.md b/platform/docs/docs/deployment/_user-account-control-flow-diagram.md similarity index 100% rename from docs/latest/deployment/_user-account-control-flow-diagram.md rename to platform/docs/docs/deployment/_user-account-control-flow-diagram.md diff --git a/platform/docs/docs/deployment/index.md b/platform/docs/docs/deployment/index.md index 995a48077..e60e4ed39 100644 --- a/platform/docs/docs/deployment/index.md +++ b/platform/docs/docs/deployment/index.md @@ -1,7 +1,3 @@ ---- -sidebar_position: 1 -sidebar_label: Overview ---- # Deployment The OHIF Viewer can be embedded in other web applications via it's [packaged @@ -11,6 +7,7 @@ either case, you will need to configure your instance of the Viewer so that it can connect to your data source (the database or PACS that provides the data your Viewer will display). +## Overview Our goal is to make deployment as simple and painless as possible; however, there is an inherent amount of complexity in configuring and deploying web @@ -19,18 +16,10 @@ applications. If you find yourself a little lost, please don't hesitate to ## Deployment Scenarios -### Embedded Viewer - -The quickest and easiest way to get the OHIF Viewer up and running is to embed -it into an existing web application. It allows us to forego a "build step", and -add a powerful medical imaging viewer to an existing web page using only a few -include tags. - -- Read more about it here: [Embedded Viewer](./recipes/embedded-viewer.md) -- And check out our [live demo on CodeSandbox][code-sandbox] - -![embeddedViewer](../assets/img/embedded-viewer-diagram.png) - +### Embedded Viewer (deprecated) +`OHIF-v3` has deprecated deploying the viewer as an embedded viewer the number of underlying +libraries that run web workers are increasing for OHIF. An example of these libraries is +OHIF's 3D rendering functionality that is provided by `vtk-js`. ### Stand-alone Viewer @@ -39,7 +28,7 @@ benefits, but comes at the cost of time and complexity. Some benefits include: _Today:_ -- Leverage [extensions](/extensions/index.md) to drop-in powerful new features +- Leverage [extensions](/extensions/index.md) and [modes](/modes/index.md) to drop-in powerful new features - Add routes and customize the viewer's workflow - Finer control over styling and whitelabeling @@ -105,7 +94,7 @@ the steps layed out in our #### What if I don't have an imaging archive? We provide some guidance on configuring a local image archive in our -[Data Source Essentials](./../configuring/data-source.md) guide. Hosting an +[Data Source Essentials](./../configuring/index.md#set-up-a-local-DICOM-server) guide. Hosting an archive remotely is a little trickier. You can check out some of our [advanced recipes](#recipes) for modeled setups that may work for you. @@ -140,15 +129,17 @@ It should reference an endpoint that returns **application/json** formatted text If you do not have an API, you can simply return a text file containing the JSON from any web server. -You tell the OHIF viewer to use JSON by appending the `'?url='` query to the `/Viewer` route: +You tell the OHIF viewer to use JSON by using the `dicomjson` datasource and appending `'?url='` query to your mode's route: + +eg. `https://my-test-ohif-server/myMode/dicomjson?url=https://my-json-server/study-uid.json` -eg. `https://my-test-ohif-server/viewer?url=https://my-json-server/study-uid.json` The returned JSON object must contain a single root object with a 'studies' array. +You can read more about using different data sources for mode's routes [here](../modes/routes.md#route-path) *Sample JSON format:* -```json +```JSON { "studies": [ { @@ -203,11 +194,11 @@ More info on this JSON format can be found here [Issue #1500](https://github.com **Implementation Notes:** -1. When hosting the viewer, you will also need to host a /viewer route on the server - or the browser may not be able to find the route. -2. For each instance url (dicom object) in the returned JSON, you must prefix the `url` with `dicomweb:` in order for the cornerstone image loader to retrieve it correctly. - eg. `https://image-server/my-image.dcm` ---> `dicomweb:https://image-server/my-image.dcm` -3. The JSON format above is compatible with >= v3.7.8 of the application. Older versions of the viewer used a different JSON format. As of 20/04/20 the public [https://viewer.ohif.org/] is a pre 3.0 version that does not support this format yet. -4. The JSON format is case-sensitive. Please ensure you have matched casing with the naturalised Dicom format referenced in [Issue #1500](https://github.com/OHIF/Viewers/issues/1500). + +1. For each instance url (dicom object) in the returned JSON, you must prefix the `url` with `dicomjson:` in order for the cornerstone image loader to retrieve it correctly. + eg. `https://image-server/my-image.dcm` ---> `dicomjson:https://image-server/my-image.dcm` +2. The JSON format above is compatible with >= v3.7.8 of the application in `V2` version. Older versions of the viewer used a different JSON format. As of 20/04/20 the public [https://viewer.ohif.org/] is a pre 3.0 version that does not support this format yet. +3. The JSON format is case-sensitive. Please ensure you have matched casing with the naturalised Dicom format referenced in [Issue #1500](https://github.com/OHIF/Viewers/issues/1500). *CORS Issues (Cross-Origin Resource Sharing)* @@ -223,7 +214,7 @@ Your JSON API is hosted on `https://my-json-api.aws.com` And your images are stored on Amazon S3 at `https://my-s3-bucket.aws.com` -When you first start your application, browsing to `https://my-ohif-server.com/viewer?url=https://my-json-api.aws.com/api/my-json-study-info.json`, you will likely get a CORS error in the browser console as it tries to connect to `https://my-json-api.aws.com`. +When you first start your application, browsing to `https://my-ohif-server.com/myMode/dicomjson?url=https://my-json-api.aws.com/api/my-json-study-info.json`, you will likely get a CORS error in the browser console as it tries to connect to `https://my-json-api.aws.com`. Adding a setting on the JSON server to allow the CORS origin = `https://my-ohif-server.com` should solve this. @@ -234,7 +225,9 @@ Essentially, whenever the application connects to a remote resource, you will ne ### Securing Your Data +Coming soon + + ### Recipes We've included a few recipes for common deployment scenarios. There are many, many possible configurations, so please don't feel limited to these setups. Please feel free to suggest or contribute your own recipes. -- Script Include - - [Embedding the Viewer](./recipes/embedded-viewer.md) -- Stand-Alone - - [Build for Production](./recipes/build-for-production.md) - - [Static](./recipes/static-assets.md) - - [Nginx + Image Archive](./recipes/nginx--image-archive.md) - - [User Account Control](./recipes/user-account-control.md) + +- [Build for Production](./recipes/build-for-production.md) +- [Static](./recipes/static-assets.md) +- [Nginx + Image Archive](./recipes/nginx--image-archive.md) +- [User Account Control](./recipes/user-account-control.md) [circleci]: https://circleci.com/gh/OHIF/Viewers diff --git a/platform/docs/docs/deployment/recipes/nginx--image-archive.md b/platform/docs/docs/deployment/recipes/nginx--image-archive.md index 9931687c3..869988b48 100644 --- a/platform/docs/docs/deployment/recipes/nginx--image-archive.md +++ b/platform/docs/docs/deployment/recipes/nginx--image-archive.md @@ -1,6 +1,3 @@ ---- -sidebar_position: 4 ---- # Nginx + Image Archive > DISCLAIMER! We make no claims or guarantees of this approach's security. If in @@ -61,9 +58,7 @@ CORS][understanding-cors] This setup allows us to create a setup similar to the one pictured below: - -![nginX](../../assets/img/nginx-image-archive.png) - +{% include "./../_nginx-image-archive-diagram.md" %} - All web requests are routed through `nginx` on our `OpenResty` image - `/pacs` is a reverse proxy for `orthanc`'s `DICOM Web` endpoints @@ -84,6 +79,20 @@ in command prompt or terminal_ ### Setup +- Navigate to `viewer` folder inside `platform` +- then: `cd .recipes/OpenResty-Orthanc` +- run: `docker-compose up --build` +- Navigate to `127.0.0.1` for the viewer +- Navigate to `127.0.0.1/pacs-admin` for uploading studies + + +You can see the overview of the mentioned steps: + + +
+ + ### Troubleshooting @@ -115,6 +124,14 @@ Stop running all containers: - Win: `docker ps -a -q | ForEach { docker stop $_ }` - Linux: `docker stop $(docker ps -a -q)` + +_Traceback (most recent call last):_ + _File "urllib3/connectionpool.py", line 670, in urlopen_ + _...._ + +Are you sure your docker is running? see explanation [here](https://github.com/docker/compose/issues/7896) + + ### Configuration After verifying that everything runs with default configuration values, you will diff --git a/platform/docs/docs/deployment/recipes/static-assets.md b/platform/docs/docs/deployment/recipes/static-assets.md index de217f4c0..ebcf9a100 100644 --- a/platform/docs/docs/deployment/recipes/static-assets.md +++ b/platform/docs/docs/deployment/recipes/static-assets.md @@ -1,7 +1,3 @@ ---- -sidebar_position: 3 -sidebar_label: Assets ---- # Deploy Static Assets > WARNING! All of these solutions stand-up a publicly accessible web viewer. Do @@ -44,11 +40,8 @@ _Advanced_ ### Netlify: Drop -> Build, deploy, and manage modern web projects. An all-in-one workflow that -> combines global deployment, continuous integration, and automatic HTTPS. And -> that’s just the beginning. +
-![netlify-drop example](../../assets/img/netlify-drop.gif) _GIF demonstrating deployment with Netlify Drop_ diff --git a/platform/docs/docs/deployment/recipes/user-account-control.md b/platform/docs/docs/deployment/recipes/user-account-control.md index c8cefe955..9535682aa 100644 --- a/platform/docs/docs/deployment/recipes/user-account-control.md +++ b/platform/docs/docs/deployment/recipes/user-account-control.md @@ -1,293 +1,3 @@ ---- -sidebar_position: 5 ---- # User Account Control -> DISCLAIMER! We make no claims or guarantees of this approach's security. If in -> doubt, enlist the help of an expert and conduct proper audits. - -Making a viewer and its medical imaging data accessible on the open web can -provide a lot of benefits, but requires additional security to make sure -sensitive information can only be viewed by authorized individuals. Most image -archives are equipped with basic security measures, but they are not -robust/secure enough for the open web. - -This guide covers one of many potential production setups that secure our -sensitive data. - -## Overview - -This guide builds on top of our -[Nginx + Image Archive guide](/deployment/recipes/nginx--image-archive.md), -wherein we used a [`reverse proxy`](https://en.wikipedia.org/wiki/Reverse_proxy) -to retrieve resources from our image archive (Orthanc). - -To add support for "User Account Control" we introduce -[Keycloak](https://www.keycloak.org/about.html). Keycloak is an open source -Identity and Access Management solution that makes it easy to secure -applications and services with little to no code. We improve upon our -`reverse proxy` setup by integrating Keycloak and Nginx to create an -`authenticating reverse proxy`. - -> An authenticating reverse proxy is a reverse proxy that only retrieves the -> resources on behalf of a client if the client has been authenticated. If a -> client is not authenticated they can be redirected to a login page. - -This setup allows us to create a setup similar to the one pictured below: - -![userControlFlow](../../assets/img/user-access-control-request-flow.png) - - - -- All web requests are routed through `nginx` on our `OpenResty` image -- `/pacs` is a reverse proxy for `orthanc`'s `DICOM Web` endpoints - - Requires valid `Authorization: Bearer ` header -- `/pacs-admin` is a reverse proxy for `orthanc`'s Web Admin -- `/auth` is a reverse proxy for `keycloak` -- All static resources for OHIF Viewer are unprotected and accessible. We have - application logic that will redirect unauthenticated users to the appropriate - `keycloak` login screen. - -## Getting Started - -### Requirements - -- Docker - - [Docker for Mac](https://docs.docker.com/docker-for-mac/) - - [Docker for Windows](https://docs.docker.com/docker-for-windows/) - -_Not sure if you have `docker` installed already? Try running `docker --version` -in command prompt or terminal_ - -### Setup - -_Spin Things Up_ - -- Navigate to `/docker/OpenResty-Orthanc-Keycloak` in your shell -- Run `docker-compose up` - -_Create Your First User_ - -- Navigate to: `http://127.0.0.1/auth/admin` -- Sign in with: `admin`/`password` -- From the top left dropdown, select the `Ohif` realm -- From the left sidebar, under `Manage`, select `Users` -- Click `Add User` - - Username: `test` - - Email Verified: `ON` - - Click `Save` -- Click the `Credentials` Tab - - New Pasword: `test` - - Password Confirmation: `test` - - Temporary: `OFF` - - Click: `Reset Password` -- From the top right dropdown, select `Admin`, then `Sign Out` - -_Sign In_ - -- Navigate to `http://127.0.0.1/` -- Username: `test`, Password: `test` -- Click `Log In` - -_Upload Your First Study_ - -- Navigate to `http://127.0.0.1/pacs-admin` -- If you're not already logged in, use `test`/`test` -- From the top right, select "Upload" -- Click "Select files to upload..." (DICOM) -- Click "Start the upload" -- Navigate back to `http://127.0.0.1/` to view your studies in the Study List - -### Troubleshooting - -_Exit code 137_ - -This means Docker ran out of memory. Open Docker Desktop, go to the `advanced` -tab, and increase the amount of Memory available. - -_Cannot create container for service X_ - -Use this one with caution: `docker system prune` - -_X is already running_ - -Stop running all containers: - -- Win: `docker ps -a -q | ForEach { docker stop $_ }` -- Linux: `docker stop $(docker ps -a -q)` - -### Configuration - -After verifying that everything runs with default configuration values, you will -likely want to update: - -- The domain: `http://127.0.0.1` -- Set secure, non-default passwords -- Regenerate Keycloak Client Secrets - -#### OHIF Viewer - -The OHIF Viewer's configuration is imported from a static `.js` file. The -configuration we use is set to a specific file when we build the viewer, and -determined by the env variable: `APP_CONFIG`. You can see where we set its value -in the `dockerfile` for this solution: - -`ENV APP_CONFIG=config/docker_openresty-orthanc-keycloak.js` - -You can find the configuration we're using here: -`/public/config/docker_openresty-orthanc-keycloak.js` - -To rebuild the `webapp` image created by our `dockerfile` after updating the -Viewer's configuration, you can run: - -- `docker-compose build` OR -- `docker-compose up --build` - -#### Other - -All other files are found in: `/docker/OpenResty-Orthanc-Keycloak/` - -| Service | Configuration | Docs | -| ----------------- | ------------------------------------------------ | ------------------------------------------- | -| OHIF Viewer | [dockerfile][dockerfile] / [config.js][config] | You're reading them now! | -| OpenResty (Nginx) | [`/nginx.conf`][config-nginx] | [lua-resty-openidc][lua-resty-openidc-docs] | -| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] | -| Keycloak | [`/ohif-keycloak-realm.json`][config-keycloak]\* | | - -\* These are the seed values for Keycloak. They can be manually updated at -`http://127.0.0.1/auth/admin` - -#### Keycloak Themeing - -The `Login` screen for the `ohif-viewer` client is using a Custom Keycloak -theme. You can find the source files for it in -`/docker/OpenResty-Orthanc-Keycloak/volumes/keycloak-themes/`. You can see how -we add it to Keycloak in the `docker-compose` file, and you can read up on how -to leverage custom themes in -[Keycloak's own docs](https://www.keycloak.org/docs/latest/server_development/index.html#_themes). - -| Default Theme | OHIF Theme | -| ---------------------------------------------------------------------- | ---------------------------------------------------------------- | -| ![Keycloak Default Theme](../../assets/img/keycloak-default-theme.png) | ![Keycloak OHIF Theme](../../assets/img/keycloak-ohif-theme.png) | - -## Next Steps - -### Deploying to Production - -While these configuration and docker-compose files model an environment suitable -for production, they are not easy to deploy "as is". You can either: - -- Manually recreate this environment and deploy built application files **OR** -- Deploy to a cloud kubernetes provider like - [Digital Ocean](https://www.digitalocean.com/products/kubernetes/) **OR** - - [See a full list of cloud providers here](https://landscape.cncf.io/category=cloud&format=card-mode&grouping=category) -- Find and follow your preferred provider's guide on setting up - [swarms and stacks](https://docs.docker.com/get-started/) - -### Adding SSL - -Adding SSL registration and renewal for your domain with Let's Encrypt that -terminates at Nginx is an incredibly important step toward securing your data. -Here are some resources, specific to this setup, that may be helpful: - -- [lua-resty-auto-ssl](https://github.com/GUI/lua-resty-auto-ssl) -- [Let's Encrypt + Nginx](https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/) - -While we terminate SSL at Nginx, it may be worth using self signed certificates -for communication between services. - -- [SSL Termination for TCP Upstream Servers](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-tcp/) - -### Use PostgresSQL w/ Orthanc - -Orthanc can handle a large amount of data and requests, but if you find that -requests start to slow as you add more and more studies, you may want to -configure your Orthanc instance to use PostgresSQL. Instructions on how to do -that can be found in the -[`Orthanc Server Book`](http://book.orthanc-server.com/users/docker.html), under -"PostgreSQL and Orthanc inside Docker" - -### Improving This Guide - -Here are some improvements this guide would benefit from, and that we would be -more than happy to accept Pull Requests for: - -- SSL Support -- Complete configuration with `.env` file (or something similar) -- Keycloak Theme improvements -- Any security issues -- One-click deploy to a cloud provider - -## Resources - -### Misc. Helpful Commands - -_Check if `nginx.conf` is valid:_ - -```bash -docker run --rm -t -a stdout --name my-openresty -v $PWD/config/:/usr/local/openresty/nginx/conf/:ro openresty/openresty:alpine-fat openresty -c /usr/local/openresty/nginx/conf/nginx.conf -t -``` - -_Interact w/ running container:_ - -`docker exec -it CONTAINER_NAME bash` - -_List running containers:_ - -`docker ps` - -_Clear Keycloak DB so you can re-seed values:_ - -- `docker volume prune` OR -- `docker volume ls` and `docker volume rm VOLUME_NAME VOLUME_NAME` - -### Referenced Articles - -The inspiration for our setup was driven largely by these articles: - -- [Securing Nginx with Keycloak](https://edhull.co.uk/blog/2018-06-06/keycloak-nginx) -- [Authenticating Reverse Proxy with Keycloak](https://eclipsesource.com/blogs/2018/01/11/authenticating-reverse-proxy-with-keycloak/) -- [Securing APIs with Kong and Keycloak](https://www.jerney.io/secure-apis-kong-keycloak-1/) - -For more documentation on the software we've chosen to use, you may find the -following resources helpful: - -- [Orthanc for Docker](http://book.orthanc-server.com/users/docker.html) -- [OpenResty Guide](http://www.staticshin.com/programming/definitely-an-open-resty-guide/) -- [Lua Ngx API](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/) -- [Auth0: Picking a Grant Type](https://auth0.com/docs/api-auth/which-oauth-flow-to-use) - -We chose to use a generic OpenID Connect library on the client, but it's worth -noting that Keycloak comes packaged with its own: - -- [redux-oidc](https://github.com/maxmantz/redux-oidc) (Which wraps - [oidc-client-js](https://github.com/IdentityModel/oidc-client-js/wiki)) -- [Keycloak JavaScript Adapter](https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter) - -If you're not already drowning in links, here are some good security resources -for OAuth: - -- [Diagrams of OpenID Connect Flows](https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660) -- [KeyCloak: OpenID Connect Flows](https://www.keycloak.org/docs/latest/securing_apps/index.html#authorization-code) - -For a different take on this setup, check out the repositories our community -members put together: - -- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker) -- [trypag/ohif-orthanc-postgres-docker](https://github.com/trypag/ohif-orthanc-postgres-docker) - - - - - -[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration -[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc - -[config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/src/config.js -[dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/dockerfile -[config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/nginx.conf -[config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/orthanc.json -[config-keycloak]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/ohif-keycloak-realm.json - +> Coming soon - We are working on bringing the User Account Control to OHIF-v3 diff --git a/platform/docs/docs/development/continous-integration.md b/platform/docs/docs/development/continous-integration.md index fd3bbb45e..cfc9a09a0 100644 --- a/platform/docs/docs/development/continous-integration.md +++ b/platform/docs/docs/development/continous-integration.md @@ -1,7 +1,3 @@ ---- -sidebar_position: 3 -title: Continous Integration ---- # Continous Integration (CI) This repository uses `CircleCI` and `Netlify` for continous integration. @@ -29,15 +25,12 @@ The PR_CHECKS workflow (Pull Request Checks) runs our automated unit and end-to-end tests for every code check-in. These tests must all pass before code can be merged to our `master` branch. - - - -![PR_CHECKS](../assets/img/WORKFLOW_PR_CHECKS.png) + ### Workflow: PR_OPTIONAL_DOCKER_PUBLISH @@ -45,16 +38,12 @@ The PR_OPTIONAL_DOCKER_PUBLISH workflow allows for "manual approval" to publish the pull request as a tagged docker image. This is helpful when changes need to be tested with the Google Adapter before merging to `master`. - - -![publish](../assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png) - - + > NOTE: This workflow will fail unless it's for a branch on our `upstream` > repository. If you need this functionality, but the branch is from a fork, @@ -67,14 +56,12 @@ It uses the Netlify CLI to deploy assets created as part of the repository's PWA Build process (`yarn run build`). The workflow allows for "Manual Approval" to promote the build to `STAGING` and `PRODUCTION` environments. - - -![deploy](../assets/img/WORKFLOW_DEPLOY.png) + | Environment | Description | URL | | ----------- | ---------------------------------------------------------------------------------- | --------------------------------------------- | @@ -95,15 +82,12 @@ GitHub hosts the `gh-pages` branch with GitHub Pages. - Extension Packages: https://github.com/ohif/viewers/#extensions - Documentation: https://docs.ohif.org/ - - - -![release](../assets/img/WORKFLOW_RELEASE.png) + ### HOTFIX diff --git a/platform/docs/docs/development/contributing.md b/platform/docs/docs/development/contributing.md index cc64fd317..9f602a617 100644 --- a/platform/docs/docs/development/contributing.md +++ b/platform/docs/docs/development/contributing.md @@ -1,7 +1,3 @@ ---- -sidebar_position: 2 -title: Contributing ---- # Contributing ## How can I help? @@ -11,8 +7,8 @@ like to discuss the changes you intend to make to clarify where or how they should be implemented, please don't hesitate to create a new issue. At a minimum, you may want to read the following documentation: -- [Getting Started](./getting-started.md) -- [Architecture](../architecture/index.md) +- [Getting Started](/development/getting-started.md) +- [Architecture](/architecture/index.md) Pull requests that are: @@ -38,7 +34,8 @@ dependencies: On your local file system: -```bash title="/my-projects/" +```bash +# code/my-projects/ . ├── cornerstonejs/cornerstone-tools └── ohif/viewers @@ -46,11 +43,13 @@ On your local file system: - Open a terminal/shell - Navigate to `cornerstonejs/cornerstone-tools` - - `npm install` + - `yarn install` - [`yarn link`](https://yarnpkg.com/en/docs/cli/link) - - `npm run dev` + - `yarn run dev` + + - Open a new terminal/shell -- Navigate to `ohif/viewers`. +- Navigate to `ohif/viewers` (the root of ohif project) - `yarn install` - [`yarn link cornerstone-tools`](https://yarnpkg.com/en/docs/cli/link) - `yarn run dev` @@ -58,10 +57,12 @@ On your local file system: As you make changed to `cornerstone-tools`, and it's output is rebuilt, you should see the following behavior: - -![example of linked cornerstone-tools package](../assets/img/cornerstone-tools-link.gif) - - +
+ + Example of linked cornerstone-tools package + +
example of linked cornerstone-tools package
+
If you wish to stop using your local package, run the following commands in the `ohif/viewers` repository root: @@ -69,11 +70,13 @@ If you wish to stop using your local package, run the following commands in the - `yarn unlink cornerstone-tools` - `yarn install --force` + #### Other linkage notes @@ -97,6 +100,10 @@ there are a lot of factors that can influence the behavior of our development server and bundler. If you encounter issues not addressed here, please don't hesitate to reach out on GitHub. +Sometimes you might encounter a situation where the linking doesn't work as expected. +This might happen when there are multiple linked packages with the same name. +You can [remove][unlink] the linked packages inside yarn and try again. + ## Any guidance on submitting changes? While we do appreciate code contributions, triaging and integrating contributed @@ -112,7 +119,7 @@ working on your pull requests: We will provide feedback on your pull requests as soon as possible. Following the tips above will help ensure your changes are reviewed. -## Testing contribution pull requests + diff --git a/platform/docs/docs/development/getting-started.md b/platform/docs/docs/development/getting-started.md index 0474ef1f0..f60ecb826 100644 --- a/platform/docs/docs/development/getting-started.md +++ b/platform/docs/docs/development/getting-started.md @@ -1,7 +1,3 @@ ---- -sidebar_position: 1 -title: Getting Started ---- # Getting Started ## Setup @@ -51,6 +47,9 @@ Navigate to the root of the project's directory in your terminal and run the following commands: ```bash +# Switch to the v3 branch +git switch feat/v2-main + # Restore dependencies yarn install @@ -71,14 +70,7 @@ You should see the following output: ### 🎉 Celebrate 🎉 - - - -
- -
- - +
### Building for Production diff --git a/platform/docs/docs/development/testing.md b/platform/docs/docs/development/testing.md index 4ca92dcbd..06a46f317 100644 --- a/platform/docs/docs/development/testing.md +++ b/platform/docs/docs/development/testing.md @@ -1,11 +1,72 @@ ---- -sidebar_position: 4 -title: Testing ---- -# Tests +# Running Tests for OHIF + +We introduce here various test types that is available +for OIHF, and how to run each test in order to make sure your contribution +hasn't broken any existing functionalities. Idea and philosophy of each testing category is discussed in the second +part of this page. + +## Unit test +To run the unit test: +``` +yarn run test:unit:ci +``` + +Note: You should have already installed all the packages with `yarn install`. + + +Running unit test will generate a report at the end showing the successful and +unsuccessful tests with detailed explanations. + +## End-to-end test +For running the OHIF e2e test you need to run the following steps: + +- Create a mini-pacs for OHIF to access the images for testing. We download +and run our lightweight implementation which provides a collection of DICOM studies ([source code][mini-pacs]). + + ``` + docker run -p 5985:5985 -p 5984:5984 -e USE_POUCHDB=true -e DB_SERVER=http://0.0.0.0 ohif/viewer-testdata:0.1-test + ``` + + Successful execution should be + + ![](../assets/img/docker-pacs.png) + +- Open a new terminal, navigate to the OHIF project, and run OHIF with the dicom-server config + + ``` + APP_CONFIG=config/dicomweb-server.js yarn start + ``` + + You should be able to see test studies in the study list + + ![OHIF-e2e-test-studies](../assets/img/OHIF-e2e-test-studies.png) + +- Open a new terminal inside the OIHF project, and run the e2e cypress test + + ``` + yarn run test:e2e + ``` + + You should be able to see the cypress window open + + ![e2e-cypress](../assets/img/e2e-cypress.png) + + Run the tests by clicking on the `Run #number integration tests` . + + A new window will open and you will see e2e tests being executed one after + each other. + + ![e2e-cypress-final](../assets/img/e2e-cypress-final.png) + + + + + + +# Testing Philosiphy > Testing is an opinionated topic. Here is a rough overview of our testing -> philosiphy. See something you want to discuss or think should be changed? Open +> philosophy. See something you want to discuss or think should be changed? Open > a PR and let's discuss. You're an engineer. You know how to write code, and writing tests isn't all that @@ -132,6 +193,7 @@ feature `X` or scenario `Y`? Open an issue and let's discuss. [eslint-rules]: https://eslint.org/docs/rules/ +[mini-pacs]: https://github.com/OHIF/viewer-testdata [typescript-docs]: https://www.typescriptlang.org/docs/home.html [flow-org]: https://flow.org/ diff --git a/docs/latest/extensions/_maintained-extensions-table.md b/platform/docs/docs/extensions/_maintained-extensions-table.md similarity index 100% rename from docs/latest/extensions/_maintained-extensions-table.md rename to platform/docs/docs/extensions/_maintained-extensions-table.md diff --git a/docs/latest/extensions/_modules.md b/platform/docs/docs/extensions/_modules.md similarity index 100% rename from docs/latest/extensions/_modules.md rename to platform/docs/docs/extensions/_modules.md diff --git a/platform/docs/docs/extensions/index.md b/platform/docs/docs/extensions/index.md index 0f4c12221..c88e05d56 100644 --- a/platform/docs/docs/extensions/index.md +++ b/platform/docs/docs/extensions/index.md @@ -1,49 +1,50 @@ ---- -sidebar_position: 1 -sidebar_label: Overview ---- -# Overview +# Extensions + +- [Extensions](#extensions) + - [Overview](#overview) + - [Extension Skeleton](#extension-skeleton) + - [OHIF-Maintained Extensions](#ohif-maintained-extensions) + - [Registering an Extension](#registering-an-extension) + - [Registering at Runtime](#registering-at-runtime) + - [Registering at Build Time](#registering-at-build-time) + - [Lifecycle Hooks](#lifecycle-hooks) + - [Modules](#modules) + - [Contexts](#contexts) + +## Overview +We have re-designed the architecture of the `OHIF-v3` to enable building applications +that are easily extensible to various use cases (modes) that behind the scene would utilize desired functionalities (extensions) to reach the goal of the use case. + +Previously, extensions were “additive” and could not easily be mixed and matched within the same viewer for different use cases. Previous `OHIF-v2` architecture meant that +any minor extension alteration usually would require the user to hard fork. E.g. removing some of the tools from the toolbar of the cornerstone extension meant you had to hard fork it, which was frustrating if the implementation was otherwise the same as master. + + +> - Developers should make packages of *reusable* functionality as extensions, and can consume +> publicly available extensions. +> - Any conceivable radiological workflow or viewer setup will be able to be built with the platform through *modes*. -We use extensions to help us isolate and package groups of related features. -Extensions provide functionality, ui components, and new behaviors. Ideally, -they're built in a way that allows them to extend entirely different -implementations of the `@ohif/viewer` project. +Practical examples of extensions include: - -![extensions](../assets/img/extensions-diagram.png) -The `@ohif/viewer`'s application level configuration gives us the ability to add -and configure extensions. When the application starts, extensions are registered -with the `ExtensionManager`. Different portions of the `@ohif/viewer` project -will use registered extensions to influence application behavior. - -Extensions allow us to: - -- Wrap and integrate functionality of 3rd party dependencies in a reusable way -- Change how application data is mapped and transformed -- Display a consistent/cohesive UI -- Inject custom components to override built-in components - -Practical examples of extensions include: - -- A set of segmentation tools that build on top of the `cornerstone` viewport -- Showing ML/AI report summaries for the selected study/series/image -- Support for parsing DICOM structured reports and displaying them in a user - friendly way -- [See our maintained extensions for more examples of what's possible](#maintained-extensions) - -## Concepts - -### Extension Skeleton +## Extension Skeleton An extension is a plain JavaScript object that has an `id` property, and one or more [modules](#modules) and/or [lifecycle hooks](#lifecycle-hooks). @@ -58,45 +59,77 @@ export default { // Lifecyle preRegistration() { /* */ }, + onModeEnter() { /* */ }, + onModeExit() { /* */ }, // Modules - getCommandsModule() { /* */ }, - getToolbarModule() { /* */ }, + getLayoutTemplateModule() { /* */ }, + getDataSourcesModule() { /* */ }, + getSopClassHandlerModule() { /* */ }, getPanelModule() { /* */ }, - getSopClassHandler() { /* */ }, getViewportModule() { /* */ }, + getCommandsModule() { /* */ }, + getContextModule() { /* */ }, + getToolbarModule() { /* */ }, + getHangingProtocolModule() { /* */ }, } ``` -### Registering an Extension +## OHIF-Maintained Extensions +A small number of powerful extensions for popular use cases are maintained by +OHIF. They're co-located in the [`OHIF/Viewers`][viewers-repo] repository, in +the top level [`extensions/`][ext-source] directory. -There are two different ways to register and configure extensions: At +{% include "./_maintained-extensions-table.md" %} + + +## Registering an Extension + +Extensions are building blocks that need to be registered. There are two different ways to register and configure extensions: At [runtime](#registering-at-runtime) and at [build time](#registering-at-build-time). - You can leverage one or both strategies. Which one(s) you choose depend on your -application's requirements. Each [module](#modules) defined by the extension -becomes available to the core application via the `ExtensionManager`. +application's requirements. -#### Registering at Runtime +Each [module](#modules) defined by the extension +becomes available to the modes via the `ExtensionManager` by requesting it via +its id. [Read more about Extension Manager](#extension-manager) + + + +### Registering at Runtime The `@ohif/viewer` uses a [configuration file](../viewer/configuration.md) at -startup. The schema for that file includes an `Extensions` key that supports an +startup. The schema for that file includes an `extensions` key that supports an array of extensions to register. ```js +import MyFirstExtension from '@ohif/extension-first' +import MySecondExtension from '@ohif/extension-second' + +const extensionConfig = {/* extension configuration */} + // prettier-ignore const config = { + routerBasename: '/', extensions: [ MyFirstExtension, [ MySecondExtension, - { /* MySecondExtensions Configuration */ }, + extensionConfig ], - ]; + ], + modes: [/* modes */], + showStudyList: true, + dataSources: [ /* data source config */] } ``` -#### Registering at Build Time +Then, behind the scene, the runtime-added extensions will get merged with the +default app extensions (note: default app extensions include: `OHIFDefaultExtension`, + `OHIFCornerstoneExtension`, `OHIFDICOMSRExtension`, + `OHIFMeasurementTrackingExtension`) + +### Registering at Build Time The `@ohif/viewer` works best when built as a "Progressive Web Application" (PWA). If you know the extensions your application will need, you can specify @@ -110,37 +143,62 @@ You can update the list of bundled extensions by: 1. Having your `@ohif/viewer` project depend on the extension 2. Importing and adding it to the list of extensions in the - `/platform/src/index.js` entrypoint. + `/platform/src/index.js` entrypoint: + + ```js + import OHIFDefaultExtension from '@ohif/extension-default'; + import OHIFCornerstoneExtension from '@ohif/extension-cornerstone'; + import OHIFMeasurementTrackingExtension from '@ohif/extension-measurement-tracking'; + import OHIFDICOMSRExtension from '@ohif/extension-dicom-sr'; + import MyFirstExtension from '@ohif/extension-first' + + /** Combine our appConfiguration and "baked-in" extensions */ + const appProps = { + config: window ? window.config : {}, + defaultExtensions: [ + OHIFDefaultExtension, + OHIFCornerstoneExtension, + OHIFMeasurementTrackingExtension, + OHIFDICOMSRExtension, + MyFirstExtension + ], + }; + ``` + +## Lifecycle Hooks + +Currently, there are three lifecycle hook for extensions: -### Lifecycle Hooks -Currently, there is only a single lifecycle hook for extensions: [`preRegistration`](./lifecycle/pre-registration.md) - -If an extension defines the [`preRegistration`](./lifecycle/pre-registration.md) +This hook is called once on initialization of the entire viewer application, used to initialize the extensions state, and consume user defined extension configuration. If an extension defines the [`preRegistration`](./lifecycle/pre-registration.md) lifecycle hook, it is called before any modules are registered in the `ExtensionManager`. It's most commonly used to wire up extensions to [services](./../services/index.md) and [commands](./modules/commands.md), and to bootstrap 3rd party libraries. -### Modules -Modules are the meat of extensions. They provide "definitions", components, and -filtering/mapping logic that are then made available by various managers and -services. +[`onModeEnter`](./lifecycle/on-mode-enter.md): This hook is called whenever a new mode is entered, or a mode’s data or datasource is switched. This hook can be used to initialize data. + +[`onModeExit`](./lifecycle/on-mode-exit.md): Similarly to onModeEnter, this hook is called when navigating away from a mode, or before a mode’s data or datasource is changed. This can be used to clean up data (e.g. remove annotations that do not need to be persisted) + + + +## Modules +Modules are the meat of extensions, the `blocks` that we have been talking about a lot. +They provide "definitions", components, and filtering/mapping logic that are then made available to modes and services. Each module type has a special purpose, and is consumed by our viewer differently. -| Type | Description | Examples | -| ------------------------------------------------- | ---------------------------------------------------------------- | ------------------------------------------------- | -| [Commands](./modules/commands.md) | Adds named commands, scoped to a context, to the CommandsManager | `setToolActive()`, `nextSeries()` | -| [Panel](./modules/panel.md) | Adds left or right hand side panels | ``, `` | -| [SOPClassHandler](./modules/sop-class-handler.md) | Determines how retrieved study data is split into "DisplaySets" | `getDisplaySetFromSeries()` | -| [Toolbar](./modules/toolbar.md) | Adds buttons or custom components to the toolbar | Toolbar button, nested buttons, custom | -| [Viewport](./modules/viewport.md) | Adds a component responsible for rendering a "DisplaySet" | ``, `` | -Tbl. Module types with abridged descriptions and examples. Each module links to a dedicated documentation page. +{% include "./_modules.md" %} + + +
Tbl. Module types with abridged descriptions and examples. Each module links to a dedicated documentation page.
+ + + ### Contexts @@ -159,6 +217,8 @@ module that supports this behavior can add a command with the same name, scoped to the appropriate context. When the `command` is fired, the "active contexts" are used to determine the appropriate implementation of the rotate behavior. + + -### `ExtensionManager` -The `ExtensionManager` is a class made available to us via the `@ohif/core` -project (platform/core). Our application instantiates a single instance of it, -and provides a `ServicesManager` and `CommandsManager` along with the -application's configuration through the appConfig key (optional). -```js -const commandsManager = new CommandsManager(); -const servicesManager = new ServicesManager(); -const extensionManager = new ExtensionManager({ - commandsManager, - servicesManager, - appConfig, -}); -``` -The `ExtensionManager` only has a few public members: -- `registerExtension` - Registers a single extension -- `registerExtensions` - Registers an array of extensions -- `modules` - An object containing registered extensions by `MODULE_TYPE` - -During registration, lifecycle hooks and modules have access to the extension's -config, the application's config and `ExtensionManager`'s `ServicesManager` and -`CommandsManager` instances. - -Our `@ohif/viewer` uses the `modules` member to access registered extensions at -appropriate places in our application. - -## Maintained Extensions - -A small number of powerful extensions for popular use cases are maintained by -OHIF. They're co-located in the [`OHIF/Viewers`][viewers-repo] repository, in -the top level [`extensions/`][ext-source] directory. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ExtensionDescriptionModules
- - Cornerstone - - - A viewport powered by cornerstone.js. Adds support for 2D DICOM rendering and manipulation, as well as support for the tools features in cornerstone-tools. Also adds "CINE Dialog" to the Toolbar. - Viewport, Toolbar
- - VTK.js - - - A viewport powered by vtk.js. Adds support for volume renderings and advanced features like MPR. Also adds "3D Rotate" to the Toolbar. - Viewport, Toolbar
- DICOM HTML - - Renders text and HTML content for specific SopClassUIDs. - Viewport, SopClassHandler
- DICOM PDF - - Renders PDFs for a specific SopClassUID. - Viewport, SopClassHandler
- DICOM Microscopy - - Renders Microscopy images for a specific SopClassUID. - Viewport, SopClassHandler
- DICOM Segmentation - - Renders segmentation images for a specific SopClassUID. - Panel, Toolbar
The `menuOptions`'s `target` key points to a registered `components`'s `id`. A `defaultContext` is applied to all `menuOption`s; however, each `menuOption` can @@ -20,46 +19,83 @@ optional provide it's own `context` value. The `getPanelModule` receives an object containing the `ExtensionManager`'s associated `ServicesManager` and `CommandsManager`. +![panel-module-v3](../../assets/img/panel-module-v3.png) + ```js -import MyComponent from './MyComponent.js'; +import PanelMeasurementTable from './PanelMeasurementTable.js'; + + +function getPanelModule({ + commandsManager, + extensionManager, + servicesManager, +}) { + const wrappedMeasurementPanel = () => { + return ( + + ); + }; + + return [ + { + name: 'measure', + iconName: 'list-bullets', + iconLabel: 'Measure', + label: 'Measurements', + isDisabled: studies => {}, // optional + component: wrappedMeasurementPanel, + }, + ]; +} +``` + + + + +## Consuming Panels Inside Modes +As explained earlier, extensions make the functionalities and components available and +`modes` utilize them to build an app. So, as seen above, we are not actually defining +which side the panel should be opened. Our extension is providing the component with +its. + +New: You can easily add multiple panels to the left/right side of the viewer using the mode +configuration. As seen below, the `leftPanels` and `rightPanels` accept an `Array` of +the `IDs`. + +```js +export default function mode({ modeConfiguration }) { + return { + id: "viewer", + routes: [ + { + path: "longitudinal", + layoutTemplate: ({ location, servicesManager }) => { + return { + id, + props: { + leftPanels: [ + "org.ohif.measurement-tracking.panelModule.seriesList", + ], + rightPanels: [ + "org.ohif.measurement-tracking.panelModule.trackedMeasurements", + ], + viewports, + }, + }; + }, + }, + ], + extensions: [ + "org.ohif.default", + "org.ohif.cornerstone", + "org.ohif.measurement-tracking", + "org.ohif.dicom-sr", + ], + }; +} -export default { - id: 'example-panel-module', - /** - * @param {object} params - * @param {ServicesManager} params.servicesManager - * @param {CommandsManager} params.commandsManager - */ - getPanelModule({ servicesManager, commandsManager }) { - return { - menuOptions: [ - { - // A suggested icon - // Available icons determined by consuming app - icon: 'list', - // A suggested label - label: 'Magic', - // 'right' or 'left' - from: 'right', - // The target component to toggle open/close - target: 'target-component-id', - // UI Hint; If the target panel is in a "disabled" state - isDisabled: studies => { - return false; - }, - // Overrides `defaultContext`, if specified - context: ['ACTIVE_VIEWPORT:MAGIC'], - }, - ], - components: [ - { - id: 'target-component-id', - component: MyComponent, - }, - ], - defaultContext: ['ROUTE:VIEWER'], - }; - }, -}; ``` diff --git a/platform/docs/docs/extensions/modules/sop-class-handler.md b/platform/docs/docs/extensions/modules/sop-class-handler.md index c6763e400..57f2f487a 100644 --- a/platform/docs/docs/extensions/modules/sop-class-handler.md +++ b/platform/docs/docs/extensions/modules/sop-class-handler.md @@ -1,12 +1,13 @@ ---- -sidebar_position: 3 -sidebar_label: SOP Class Handler ---- # Module: SOP Class Handler -An extension can register a [SOP Class][sop-class-link] Handler Module by -defining a `getSopClassHandlerModule` method. The [SOP Class][sop-class-link] -Handler is a bit different from the other modules, as it doesn't provide a `1:1` + +## Overview +This module defines how a specific DICOM SOP class should be processed to make a displaySet, something that can be hung in a viewport. An extension can register a [SOP Class][sop-class-link] Handler Module by defining a `getSopClassHandlerModule` method. The [SOP Class][sop-class-link]. + +The mode chooses what SOPClassHandlers to use, so you could process a series in a different way depending on mode within the same application. + + +SOPClassHandler is a bit different from the other modules, as it doesn't provide a `1:1` schema for UI or provide it's own components. It instead defines: - `sopClassUIDs`: an array of string SOP Class UIDs that the @@ -18,87 +19,82 @@ A `displaySet` has the following shape: ```js return { - plugin: 'html', - Modality: 'SR', - displaySetInstanceUID: 0, - wadoRoot: study.getData().wadoRoot, - wadoUri: instance.getData().wadouri, - SOPInstanceUID: instance.getSOPInstanceUID(), - SeriesInstanceUID: series.getSeriesInstanceUID(), - StudyInstanceUID: study.getStudyInstanceUID(), - authorizationHeaders, -}; + Modality: 'MR', + displaySetInstanceUIDD + SeriesDate, + SeriesTime, + SeriesInstanceUID, + StudyInstanceUID, + SeriesNumber, + FrameRate, + SeriesDescription, + isMultiFrame, + numImageFrames, + SOPClassHandlerId, +} ``` -Where the `plugin` key is used to influence the default `ViewportComponent` for -rendering the `displaySet`. Additional properties are passed to the -`ViewportComponent` and used by the default `StudyBrowser` to render -"thumbnails" for each `displaySet` - ## Example SOP Class Handler Module ```js -const SOP_CLASS_UIDS = { - BASIC_TEXT_SR: '1.2.840.10008.5.1.4.1.1.88.11', - ENHANCED_SR: '1.2.840.10008.5.1.4.1.1.88.22', +import ImageSet from '@ohif/core/src/classes/ImageSet'; + + +const sopClassDictionary = { + CTImageStorage: "1.2.840.10008.5.1.4.1.1.2", + MRImageStorage: "1.2.840.10008.5.1.4.1.1.4", }; -export default { - id: 'example-sop-class-handler-module', -/** - * @param {object} params - * @param {ServicesManager} params.servicesManager - * @param {CommandsManager} params.commandsManager - */ -getSopClassHandlerModule({ servicesManager, commandsManager }) { - return { - id: 'OHIFDicomHtmlSopClassHandler', - sopClassUIDs: Object.values(SOP_CLASS_UIDS), +// It is important to note that the used SOPClassUIDs in the modes are in the order that is specified in the array. +const sopClassUids = [ + sopClassDictionary.CTImageStorage, + sopClassDictionary.MRImageStorage, +; - /** - * @param {object} series - - * @param {object} study - - * @param {object} dicomWebClient - - * @param {object} authorizationHeaders - - */ - getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) { - const instance = series.getFirstInstance(); +const makeDisplaySet = (instances) => { + const instance = instances[0]; + const imageSet = new ImageSet(instances); - return { - plugin: 'html', - displaySetInstanceUID: 0, - wadoRoot: study.getData().wadoRoot, - wadoUri: instance.getData().wadouri, - SOPInstanceUID: instance.getSOPInstanceUID(), - SeriesInstanceUID: series.getSeriesInstanceUID(), - StudyInstanceUID: study.getStudyInstanceUID(), - authorizationHeaders, - }; + imageSet.setAttributes({ + displaySetInstanceUID: imageSet.uid, + SeriesDate: instance.SeriesDate, + SeriesTime: instance.SeriesTime, + SeriesInstanceUID: instance.SeriesInstanceUID, + StudyInstanceUID: instance.StudyInstanceUID, + SeriesNumber: instance.SeriesNumber, + FrameRate: instance.FrameTime, + SeriesDescription: instance.SeriesDescription, + Modality: instance.Modality, + isMultiFrame: isMultiFrame(instance), + numImageFrames: instances.length, + SOPClassHandlerId: `${id}.sopClassHandlerModule.${sopClassHandlerName}`, + }); + + return imageSet; +}; + +getSopClassHandlerModule = () => { + return [ + { + name: 'stack, + sopClassUids, + getDisplaySetsFromSeries: makeDisplaySet, }, - } + ]; }; + ``` ### More examples : - -- [Dicom-HTML SOP][dicom-html-sop] -- [Dicom-PDF SOP][dicom-pdf-sop] -- [Dicom-Microscopy SOP][dicom-micro-sop] -- [Dicom-Segmentation SOP][dicom-seg-sop] +You can find another example for this mapping between raw metadata and displaySet for +`DICOM-SR` extension. ## `@ohif/viewer` usage -We use the `sopClassHandlerModule`s in three different places: - -- `ViewerLocalFileData.js` -- `ViewerRetrieveStudyData.js` -- `StandaloneRouting.js` - -Each time, it is used to map study and series data to `displaySets`. It does -this by working alongside the `StudyMetadataManager` in `@ohif/core`. That -manager has the method `createDisplaySets` that takes an array of -`sopClassHandlerModules`. +We use the `sopClassHandlerModule`s in `DisplaySetService` where we +transform instances from the raw metadata format to a OHIF displaySet format. +You can read more about DisplaySetService here. [sop-class-link]: http://dicom.nema.org/dicom/2013/output/chtml/part04/sect_B.5.html diff --git a/platform/docs/docs/extensions/modules/toolbar.md b/platform/docs/docs/extensions/modules/toolbar.md index 87e7050a8..95da9acb6 100644 --- a/platform/docs/docs/extensions/modules/toolbar.md +++ b/platform/docs/docs/extensions/modules/toolbar.md @@ -1,136 +1,257 @@ ---- -sidebar_position: 4 -sidebar_label: Toolbar ---- # Module: Toolbar An extension can register a Toolbar Module by defining a `getToolbarModule` -method. This module is commonly used to define: +method. `OHIF-v3`'s `default` extension (`"ohif.org.default"`) provides 5 main toolbar button types: - [Module: Toolbar](#module-toolbar) - [Example Toolbar Module](#example-toolbar-module) + - [Toolbar buttons consumed in modes](#toolbar-buttons-consumed-in-modes) - [Button Definitions](#button-definitions) - - [Nested Toolbar Menus](#nested-toolbar-menus) - - [Custom Components](#custom-components) + - [Nested Buttons](#nested-buttons) + - [Layout Template](#layout-template) + - [Custom Button](#custom-button) + - [Custom tool](#custom-tool) + + + +![toolbarModule](../../assets/img/toolbarModule.png) -![Toolbar Extension](../../assets/img/extensions-toolbar.gif) -
Example toolbar button using the Dialog Service to show CINE controls.
## Example Toolbar Module - -The Toolbar Module should return an array of `definitions` and a -`defaultContext`. There are currently a few different variations of definitions, +The Toolbar Module should return an array of `objects`. There are currently a few different variations of definitions, each one is detailed further down. ```js -export default { - id: 'example-toolbar-module', +export default function getToolbarModule({ commandsManager, servicesManager }) { - /** - * @param {object} params - * @param {ServicesManager} params.servicesManager - * @param {CommandsManager} params.commandsManager - */ - getToolbarModule({ servicesManager, commandsManager }) { - return { - definitions: [ - /* Array of definitions */ - ], - defaultContext: ['ROUTE:VIEWER'], - }; - }, -}; + return [ + { + name: 'ohif.divider', + defaultComponent: ToolbarDivider, + clickHandler: () => {}, + }, + { + name: 'ohif.action', + defaultComponent: ToolbarButton, + clickHandler: () => {}, + }, + { + name: 'ohif.radioGroup', + defaultComponent: ToolbarButton, + clickHandler: () => {}, + }, + { + name: 'ohif.splitButton', + defaultComponent: ToolbarSplitButton, + clickHandler: () => {}, + }, + { + name: 'ohif.layoutSelector', + defaultComponent: ToolbarLayoutSelector, + clickHandler: (evt, clickedBtn, btnSectionName) => {}, + } + ] +} ``` + +## Toolbar buttons consumed in modes +Below we can see a simplified version of the `longitudinal` mode that shows how +a mode can add buttons to the toolbar by calling `ToolBarService.addButtons(toolbarButtons)`. +`toolbarButtons` is an array of `toolDefinitions` which we will learn next. + + +```js +export default function mode({ modeConfiguration }) { + return { + id: 'viewer', + displayName: 'Basic Viewer', + + onModeEnter: ({ servicesManager, extensionManager }) => { + const { ToolBarService } = servicesManager.services; + + ToolBarService.init(extensionManager); + ToolBarService.addButtons(toolbarButtons); + }, + routes: [ + { + path: 'longitudinal', + layoutTemplate: ({ location, servicesManager }) => { + return {/* */}; + }, + }, + ], + extensions: [ + 'org.ohif.default', + 'org.ohif.cornerstone', + 'org.ohif.measurement-tracking', + 'org.ohif.dicom-sr', + ], + }; +} + +``` + + + + ## Button Definitions -The simplest definition has the following properties: +The simplest toolbarButtons definition has the following properties: + +![toolbarModule-zoom](../../assets/img/toolbarModule-zoom.png) ```js { - id: 'StackScroll', - label: 'Stack Scroll', - icon: 'bars', - type: 'setToolActive', - commandName: 'setToolActive', - commandOptions: { toolName: 'StackScroll' }, + id: 'Zoom', + type: 'ohif.radioGroup', + props: { + type: 'tool', + icon: 'tool-zoom', + label: 'Zoom', + commandOptions: { toolName: 'Zoom' }, + }, }, ``` + + | property | description | values | | ---------------- | ----------------------------------------------------------------- | ----------------------------------------- | | `id` | Unique string identifier for the definition | \* | | `label` | User/display friendly to show in UI | \* | | `icon` | A string name for an icon supported by the consuming application. | \* | -| `type` | Used to determine the button's component and behavior | `"setToolActive"`, `"command"` | -| `commandName` | (optional) The command to run when the button is used. | Any command registed by a `CommandModule` | +| `type` | Used to determine the button's behaviour | "tool", "toggle", "action" | +| `commandName` | (optional) The command to run when the button is used. | Any command registered by a `CommandModule` | | `commandOptions` | (optional) Options to pass the target `commandName` | \* | -| `context` | (optional) Overrides module's `defaultContext` | Array of string context names | -Where a button with a `type` of `setToolActive` has an "active" styling applied -when clicked; removing the active styling from all other buttons. -## Nested Toolbar Menus +There are three main types of toolbar buttons: -You can indicate that buttons should be grouped and nested in a submenu by -including `buttons` property in a definition: +- `tool`: buttons that enable a tool by running the `setToolActive` command with the `commandOptions` +- `toggle`: buttons that acts as a toggle: e.g., linking viewports +- `action`: buttons that executes an action: e.g., capture button to save screenshot + + + + + + +## Nested Buttons +You can use the `ohif.splitButton` type to build a button with extra tools in the dropdown. + +- First you need to give your `primary` tool definition to the split button +- the `secondary` properties can be a simple arrow down (`chevron-down` icon) +- For adding the extra tools add them to the `items` list. + +You can see below how `longitudinal` mode is using the available toolbarModule to create +`MeasurementTools` nested button + +![toolbarModule-nested-buttons](../../assets/img/toolbarModule-nested-buttons.png) ```js +// modes/longitudinal/src/toolbarButtons.js + { - id: 'More', - label: 'More', - icon: 'ellipse-circle', - buttons: [ - { - id: 'cstInvert', - label: 'Invert', - icon: 'circle', - type: 'command', - commandName: 'invertViewport', + id: 'MeasurementTools', + type: 'ohif.splitButton', + props: { + groupId: 'MeasurementTools', + isRadio: true, + primary: { + id: 'Length', + icon: 'tool-length', + label: 'Length', + type: 'tool', + commandOptions: { + toolName: 'Length', + } }, - ], -}, + secondary: { + icon: 'chevron-down', + label: '', + isActive: true, + tooltip: 'More Measure Tools', + }, + items: [ + // Length tool + { + id: 'Length', + icon: 'tool-length', + label: 'Length', + type: 'tool', + commandOptions: { + toolName: 'Length', + } + }, + // Bidirectional tool + { + id: 'Bidirectional', + icon: 'tool-bidirectional', + label: 'Length', + type: 'tool', + commandOptions: { + toolName: 'Bidirectional', + } + }, + // Ellipse tool + { + id: 'EllipticalRoi', + icon: 'tool-elipse', + label: 'Ellipse', + type: 'tool', + commandOptions: { + toolName: 'EllipticalRoi', + } + }, + ], + }, +} ``` -![Toolbar Extension](../../assets/img/extensions-toolbar-nested.gif) +
-
Example toolbar button demonstrating nested buttons.
- -## Custom Components - -The Toolbar Modules supports rendering custom components in place of the -application's default. In place of the `type`, `commandName`, and -`commandOptions` properties, we instead specify a `CustomComponent`. +## Layout Template +Layout selector button and logic is also provided by the OHIF-v3 `default` extension. +To use it, you can just add the following definition to the list of `toolDefinitions` +![toolbarModule-layout](../../assets/img/toolbarModule-layout.png) ```js { - id: 'Custom', - label: 'Custom', - icon: 'custom-icon', - CustomComponent: CustomToolbarComponent, + id: 'Layout', + type: 'ohif.layoutSelector', } - ``` -The `CustomComponent` components will receive the following props: +
-```html - + +## Custom Button +You can also create your own extension, and add your new custom tool appearance (e.g., split horizantlly instead of vertically for split tool). +Simply add `getToolbarModule` to your extension, and pass your tool react component to its +`defaultComponent` property in the returned object. +You can use `@ohif/ui` components such as `IconButton, Icon, Tooltip, ToolbarButton` to +build your own component. + + +```js +import myToolComponent from './myToolComponent' + + +export default function getToolbarModule({ commandsManager, servicesManager }) { + + return [ + { + name: 'new-tool-type', + defaultComponent: myToolComponent, + clickHandler: () => {}, + }, + ] +} ``` -| Property | Type | Description | -| ---------------------- | -------- | ------------------------------- | -| `activeButtons` | string[] | list of active buttons | -| `button` | object | its own definition object | -| `key` | string | React key prop | -| `isActive` | boolean | If current button is active | -| `parentContext` | ? | The parent component's context? | -| `toolbarClickCallback` | func | Callback method for clicks | + + +## Custom tool + I want to create a new tool diff --git a/platform/docs/docs/extensions/modules/viewport.md b/platform/docs/docs/extensions/modules/viewport.md index 4291c99f3..a6d7c9d82 100644 --- a/platform/docs/docs/extensions/modules/viewport.md +++ b/platform/docs/docs/extensions/modules/viewport.md @@ -1,50 +1,95 @@ ---- -sidebar_position: 5 -sidebar_label: Viewport ---- # Module: Viewport -An extension can register a Viewport Module by defining a `getViewportModule` + +## Overview +Viewports consume a displaySet and display/allow the user to interact with data. An extension can register a Viewport Module by defining a `getViewportModule` method that returns a React component. Currently, we use viewport components to add support for: - 2D Medical Image Viewing (cornerstone ext.) -- Structured Reports as HTML (dicom html ext.) -- Encapsulated PDFs as PDFs (dicom pdf ext.) +- Structured Reports as SR (DICOM SR ext.) +- Structured Reports as HTML (DICOM html ext.) +- Encapsulated PDFs as PDFs (DICOM pdf ext.) - Whole Slide Microscopy Viewing (whole slide ext.) - etc. -The general pattern is, the [`sopClassHandlerModule`](#) helps us determine -which Viewport Component a set of `sopClassUIDs` should default to. The Viewport -Component receives props containing a display set it should know how to render. -## Viewport Component Props -Each `ViewportComponent` will receive the following props: +The general pattern is that a mode can define which `Viewport` to use for which specific +`SOPClassHandlerUID`, so if you want to fork just a single Viewport component for a specialized mode, this is possible. -```html - +```JSX +// displaySet, viewportIndex, dataSource +const getViewportModule = () => { + const wrappedViewport = props => { + return ( + { + commandsManager.runCommand('commandName', data); + }} + /> + ); + }; + + return [{ name: 'example', component: wrappedViewport }]; +}; ``` -| Property | Type | Description | -| --------------- | --------------- | --------------------------------- | -| `children` | React.element[] | | -| `viewportData` | object | `viewportSpecificData` (probably) | -| `viewportIndex` | number | | +## Example Viewport Component +A simplified version of the tracked CornerstoneViewport is shown below, which creates +a cornerstone viewport and and action bar on top of it. + + + + +```JSX +function TrackedCornerstoneViewport({ + children, + dataSource, + displaySet, + viewportIndex, + servicesManager, + extensionManager, + commandsManager, +}) { + const renderViewport = () => { + const { component: Component } = extensionManager.getModuleEntry( + 'org.ohif.cornerstone.viewportModule.cornerstone' + ) + return ( + + ) + } + + return ( + <> + +
+ {renderViewport()} +
+ + ) +} + +``` + + +![viewportModule](../../assets/img/viewportModule.png) ### `@ohif/viewer` Viewport components are managed by the `ViewportGrid` Component. Which Viewport component is used depends on: +- Hanging Protocols - The Layout Configuration - Registered SopClassHandlers -- The SopClassUID for visible/selected datasets -![Cornerstone Viewport](../../assets/img/extensions-viewport.png) +![viewportModule-layout](../../assets/img/viewportModule-layout.png)
An example of three cornerstone Viewports
diff --git a/platform/docs/docs/faq/browser-support.md b/platform/docs/docs/faq/browser-support.md index 8d664095a..d5f1a1cf3 100644 --- a/platform/docs/docs/faq/browser-support.md +++ b/platform/docs/docs/faq/browser-support.md @@ -1,6 +1,3 @@ ---- -sidebar_position: 3 ---- # Browser Support The browsers that we support are specified in the `.browserlistrc` file located diff --git a/platform/docs/docs/faq/index.md b/platform/docs/docs/faq/index.md index c75a50028..7f8c9e670 100644 --- a/platform/docs/docs/faq/index.md +++ b/platform/docs/docs/faq/index.md @@ -1,8 +1,3 @@ ---- -sidebar_position: 1 -sidebar_label: Overview - ---- # Frequently Asked Questions ## Index diff --git a/platform/docs/docs/faq/pwa-vs-packaged.md b/platform/docs/docs/faq/pwa-vs-packaged.md index cf4c0bb0e..a6f001abc 100644 --- a/platform/docs/docs/faq/pwa-vs-packaged.md +++ b/platform/docs/docs/faq/pwa-vs-packaged.md @@ -1,6 +1,3 @@ ---- -sidebar_position: 4 ---- # PWA vs Packaged It's important to know that the OHIF Viewer project provides two different build @@ -33,27 +30,4 @@ You can read more about this particular strategy in our ## Commonjs Bundle (Packaged Script) -The [@ohif/viewer][viewer-npm] package is built with WebPack to provide a React -component that can be dropped into a larger application. The `OHIFViewer` -component is the entire viewer, configurable via React `props`. This is useful -for including the OHIF Viewer in a larger web application, as the entire -application can be provided via a ` ## Interface @@ -35,12 +25,16 @@ is expected to support, [check out it's interface in `@ohif/core`][interface] ## Implementations -| Implementation | Consumer | -| ---------------------------------- | ----------------------------- | -| [Modal Provider][modal-provider]\* | [OHIFModal][modal-consumer]\* | +| Implementation | Consumer | +| ---------------------------------- | --------- | +| [Modal Provider][modal-provider]\* | Modal.jsx | `*` - Denotes maintained by OHIF + + + + > 3rd Party implementers may be added to this table via pull requests. - -![UINotificationService](../../assets/img/notification-example.gif) +
## Interface For a more detailed look on the options and return values each of these methods diff --git a/docs/latest/services/ui/ui-viewport-dialog-service.md b/platform/docs/docs/services/ui/ui-viewport-dialog-service.md similarity index 100% rename from docs/latest/services/ui/ui-viewport-dialog-service.md rename to platform/docs/docs/services/ui/ui-viewport-dialog-service.md diff --git a/docs/latest/userManuals/index.md b/platform/docs/docs/userManuals/index.md similarity index 100% rename from docs/latest/userManuals/index.md rename to platform/docs/docs/userManuals/index.md diff --git a/docs/latest/userManuals/studyList/index.md b/platform/docs/docs/userManuals/studyList/index.md similarity index 100% rename from docs/latest/userManuals/studyList/index.md rename to platform/docs/docs/userManuals/studyList/index.md diff --git a/docs/latest/userManuals/viewer/Hotkeys/hotkeys.md b/platform/docs/docs/userManuals/viewer/Hotkeys/hotkeys.md similarity index 100% rename from docs/latest/userManuals/viewer/Hotkeys/hotkeys.md rename to platform/docs/docs/userManuals/viewer/Hotkeys/hotkeys.md diff --git a/docs/latest/userManuals/viewer/Language/Language.md b/platform/docs/docs/userManuals/viewer/Language/Language.md similarity index 100% rename from docs/latest/userManuals/viewer/Language/Language.md rename to platform/docs/docs/userManuals/viewer/Language/Language.md diff --git a/docs/latest/userManuals/viewer/Viewport/viewport.md b/platform/docs/docs/userManuals/viewer/Viewport/viewport.md similarity index 100% rename from docs/latest/userManuals/viewer/Viewport/viewport.md rename to platform/docs/docs/userManuals/viewer/Viewport/viewport.md diff --git a/docs/latest/userManuals/viewer/index.md b/platform/docs/docs/userManuals/viewer/index.md similarity index 100% rename from docs/latest/userManuals/viewer/index.md rename to platform/docs/docs/userManuals/viewer/index.md diff --git a/docs/latest/userManuals/viewer/measurementPanel/measurement-panel.md b/platform/docs/docs/userManuals/viewer/measurementPanel/measurement-panel.md similarity index 100% rename from docs/latest/userManuals/viewer/measurementPanel/measurement-panel.md rename to platform/docs/docs/userManuals/viewer/measurementPanel/measurement-panel.md diff --git a/docs/latest/userManuals/viewer/studyPanel/study-panel.md b/platform/docs/docs/userManuals/viewer/studyPanel/study-panel.md similarity index 100% rename from docs/latest/userManuals/viewer/studyPanel/study-panel.md rename to platform/docs/docs/userManuals/viewer/studyPanel/study-panel.md diff --git a/docs/latest/userManuals/viewer/toolbar/toolbar.md b/platform/docs/docs/userManuals/viewer/toolbar/toolbar.md similarity index 100% rename from docs/latest/userManuals/viewer/toolbar/toolbar.md rename to platform/docs/docs/userManuals/viewer/toolbar/toolbar.md diff --git a/platform/docs/docs/viewer/configuration.md b/platform/docs/docs/viewer/configuration.md index ce8a9b0c7..1f0314347 100644 --- a/platform/docs/docs/viewer/configuration.md +++ b/platform/docs/docs/viewer/configuration.md @@ -1,24 +1,10 @@ ---- -sidebar_position: 1 -sidebar_label: Configuration ---- # Viewer: Configuration -The OHIF Viewing Platform strives to be highly configurable and extensible. This -makes it easier for our community members to keep their "secret sauce" private, -and incentivises contributions back to the platform. The `@ohif/viewer` project -of the platform is the lynchpin that combines everything to create our -application. - - - We maintain a number of common viewer application configurations at -[`/platform/viewer/public/configs`][config-dir]. How these values are -passed to the viewer depend on how it's deployed, but the two most common paths -are: +[`/platform/viewer/public/configs`][config-dir]. + +You can take a look at how to use different configs in the [Environment Variables](../configuring/index.md#environment-variables) -- `index.html` looks for `https://your-website.com/app-config.js` OR -- `index.html` passes the values to `OHIF.installViewer()` ```js window.config = { @@ -32,93 +18,34 @@ window.config = { * Viewer project, or extensions. */ whiteLabeling: { - /* Optional: Should return a React component to be rendered in the "Logo" section of the application's Top Navigation bar */ - createLogoComponentFn: function(React) { - return React.createElement('a', { - target: '_self', - rel: 'noopener noreferrer', - className: 'header-brand', - href: '/', - style: { - display: 'block', - textIndent: '-9999px', - background: 'url(/svg-file-hosted-at-domain-root.svg)', - backgroundSize: 'contain', - backgroundRepeat: 'no-repeat', - width: '200px', - }, - }); - }, + /** ... **/ }, - /** - * Internally, the OHIF Viewer fetches data primarily with the - * `cornerstoneWADOImageLoader` and the `DICOMWebClient`. If either of these - * receive a non-200 response, this method allows you to handle that error. - * - * Common use cases include: - * - Showing a notification with the UINotificationService - * - Redirecting the user - * - Refreshing an auth token - * - * @param {Object} error - JS new Error() - * @param {XMLHttpRequest} error.request - The XHR request that's onreadystate change triggered this callback - * @param {string} error.response - The XHR's response property - * @param {number} error.status - The XHR's status property - */ - httpErrorHandler: error => { - const { request: xhr, response, status } = err; - const { responseType, statusText } = xhr; - - // In local files, status is 0 upon success in Firefox - if (xhr.readyState === XMLHttpRequest.DONE) { - console.log(statusText, response, responseType); - } else { - console.warn('Likely CORS error'); - } + httpErrorHandler: { + /** coming soon **/ }, extensions: [], showStudyList: true, filterQueryParam: false, - servers: { - dicomWeb: [ - { + dataSources: [ + { + friendlyName: 'dcmjs DICOMWeb Server', + namespace: 'org.ohif.default.dataSourcesModule.dicomweb', + sourceName: 'dicomweb', + configuration: { name: 'DCM4CHEE', wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado', qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs', qidoSupportsIncludeField: true, + supportsReject: true, imageRendering: 'wadors', thumbnailRendering: 'wadors', enableStudyLazyLoad: true, + supportsFuzzyMatching: true, + supportsWildcard: true, }, - ], - }, - // Supported Keys: https://craig.is/killing/mice - hotkeys: [ - { commandName: 'rotateViewportCW', label: 'Rotate Right', keys: ['r'] }, - { commandName: 'rotateViewportCCW', label: 'Rotate Left', keys: ['l'] }, - { commandName: 'invertViewport', label: 'Invert', keys: ['i'] }, - { - commandName: 'flipViewportVertical', - label: 'Flip Horizontally', - keys: ['h'], - }, - { - commandName: 'flipViewportHorizontal', - label: 'Flip Vertically', - keys: ['v'], }, ], - /* Configuration passed to the bundled cornerstone extension - * - * The cornerstone extension is currently tightly coupled to the platform. - * Until we're able to decouple it, this key will serve as a workaround to - * pass it configuration. - */ - cornerstoneExtensionConfig: { - /* Whether to show/hide annotation "handles" */ - hideHandles: true, - }, }; ``` diff --git a/platform/docs/docs/viewer/environment-variables.md b/platform/docs/docs/viewer/environment-variables.md index 4fd2691a4..ce18f9849 100644 --- a/platform/docs/docs/viewer/environment-variables.md +++ b/platform/docs/docs/viewer/environment-variables.md @@ -1,7 +1,3 @@ ---- -sidebar_position: 3 -sidebar_label: Environment Variables ---- # Environment Variables There are a number of environment variables we use at build time to influence the output application's behavior. diff --git a/docs/latest/viewer/hotkeys.md b/platform/docs/docs/viewer/hotkeys.md similarity index 100% rename from docs/latest/viewer/hotkeys.md rename to platform/docs/docs/viewer/hotkeys.md diff --git a/docs/latest/viewer/index.md b/platform/docs/docs/viewer/index.md similarity index 100% rename from docs/latest/viewer/index.md rename to platform/docs/docs/viewer/index.md diff --git a/platform/docs/docs/viewer/internationalization.md b/platform/docs/docs/viewer/internationalization.md index 6087b0557..9c41713b5 100644 --- a/platform/docs/docs/viewer/internationalization.md +++ b/platform/docs/docs/viewer/internationalization.md @@ -1,29 +1,30 @@ ---- -sidebar_position: 4 -sidebar_label: Internationalization ---- # Viewer: Internationalization OHIF supports internationalization using [i18next](https://www.i18next.com/) through the npm package [@ohif/i18n](https://www.npmjs.com/package/@ohif/i18n), where is the main instance of i18n containing several languages and tools. - - -
-

Our translation management is powered by - Locize - through their generous support of open source.

- - Locize Translation Management Logo + + Locize Translation Management Logo +
+ +## How to change language for the viewer? +You can take a look into user manuals to see how to change the viewer's language. +In summary you can change the language: + +- In the preference modals +- Using the language query in the URL: `lng=Test-LNG` + + + ## Installing ```bash @@ -72,35 +73,27 @@ This section will introduce you to [react-i18next](https://react.i18next.com/) basics and show how to implement the [t](https://www.i18next.com/overview/api#t) function easily. -#### Using HOCs -In most cases we used -[High Order Components](https://react.i18next.com/latest/withtranslation-hoc) to -share the `t` function among OHIF's components. +#### Using Hooks +You can use `useTranslation` hooks that is provided by `react-i18next` + + +You can read more about this [here](https://react.i18next.com/latest/usetranslation-hook). -E.g. ```js import React from 'react'; -import { withTranslation } from '@ohif/i18n'; +import { useTranslation } from 'react-i18next'; + + +function MyComponent() { + const { t } = useTranslation(); -function MyComponent({ t, i18n }) { return

{t('my translated text')}

; } -export default withTranslation('MyNameSpace')(MyComponent); ``` -> Important: if you are using React outside the OHIF Viewer, check the -> [I18nextProvider](#using-outside-of-ohif-viewer) section, `withTranslation` -> HOC doesnt works without a I18nextProvider - -#### Using Hooks - -Also, it's possible to get the `t` tool using -[React Hooks](https://react.i18next.com/latest/usetranslation-hook), but it -requires at least React > 16.8 😉 - ### Using outside of OHIF viewer OHIF Viewer already sets a main @@ -156,6 +149,10 @@ becomes a new namespace automatically. - Header: translations related to OHIF's Header Top Bar - MeasurementTable - Translations for the `@ohif/ui` Measurement Table - UserPreferencesModal - Translations for the `@ohif/ui` Preferences Modal +- Modals - Translations available for other modals +- PatientInfo - Translations for patients info hover +- SidePanel - Translations for side panels +- ToolTip - Translations for tool tips ### How to use another NameSpace inside the current NameSpace? @@ -163,7 +160,7 @@ i18next provides a parsing feature able to get translations strings from any NameSpace, like this following example getting data from `Common` NameSpace: ``` -$t(Common:Reset) +$t('Common:Reset') ``` ## Extending Languages in @ohif/i18n @@ -201,21 +198,21 @@ languages tool. You will need to export all Json files in your `index.js` file, mounting an object like this: -```json -{ - en: { - NameSpace: { - keyWord1: 'keyWord1Translation', - keyWord2: 'keyWord2Translation', - keyWord3: 'keyWord3Translation', - } - }, - 'en-UK': { - NameSpace: { - keyWord1: 'keyWord1DifferentTranslation', - } - } -} +```js + { + en: { + NameSpace: { + keyWord1: 'keyWord1Translation', + keyWord2: 'keyWord2Translation', + keyWord3: 'keyWord3Translation', + } + }, + 'en-UK': { + NameSpace: { + keyWord1: 'keyWord1DifferentTranslation', + } + } + } ``` Please check the `index.js` files inside locales folder for an example of this @@ -248,7 +245,7 @@ To set a brand new language you can do it in two different ways: - Setting it only in your project or extension: -You'll need a a final object like the following, what is setting French as +You'll need a final object like the following, what is setting French as language, and send it to `addLocales` method. ```js @@ -284,6 +281,15 @@ You can also set them manually, one by one, using this --- + + +## Test Language +We have created a test language that its translations can be seen in the locales folder. You can copy paste the folder and its `.json` namespaces and add your custom +language translations. + +> If you apply the test-LNG you can see all the elements get appended with 'Test {}'. +> For instance `Study list` becomes `Test Study list`. + ## Language Detections @ohif/i18n uses diff --git a/platform/docs/docs/viewer/themeing.md b/platform/docs/docs/viewer/themeing.md index 420ebbf7e..abe20ac4e 100644 --- a/platform/docs/docs/viewer/themeing.md +++ b/platform/docs/docs/viewer/themeing.md @@ -1,97 +1,163 @@ ---- -sidebar_position: 2 -sidebar_label: Themeing ---- -# Viewer: Themeing +# Viewer: Theming -Themeing is currently accomplished with color variables that are defined within -the [`:root`](https://css-tricks.com/almanac/selectors/r/root/) selector -(allowing them to cascade across all elements). This repository's components, -and the ones we consume from our -[`@ohif/ui` component library](https://react.ohif.org/styling-and-theming) -utilize them. We are interested in pursuing more robust themeing options, and -open to pull requests and discussion issues. -```css -:root { - /* Interface UI Colors */ - --default-color: #9ccef9; - --hover-color: #ffffff; - --active-color: #20a5d6; - --ui-border-color: #44626f; - --ui-border-color-dark: #3c5d80; - --ui-border-color-active: #00a4d9; - --primary-background-color: #000000; - --box-background-color: #3e5975; +`OHIF-v3` has introduced the [`LayoutTemplateModule`](../extensions/modules/layout-template.md) which enables addition of custom layouts. You can easily design your custom components inside an extension and consume it via the layoutTemplate module you write. - --text-primary-color: #ffffff; - --text-secondary-color: #91b9cd; - --input-background-color: #2c363f; - --input-placeholder-color: #d3d3d3; - --table-hover-color: #2c363f; - --table-text-primary-color: #ffffff; - --table-text-secondary-color: #91b9cd; - --large-numbers-color: #6fbde2; - --state-error: #ffcccc; - --state-error-border: #ffcccc; - --state-error-text: #ffcccc; +## Tailwind CSS +[Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework for creating custom user interfaces. - /* Common palette */ - --ui-yellow: #e29e4a; - --ui-sky-blue: #6fbde2; - /* State palette */ - --ui-state-error: #ffcccc; - --ui-state-error-border: #993333; - --ui-state-error-text: #661111; - --ui-gray-lighter: #436270; - --ui-gray-light: #516873; - --ui-gray: #263340; - --ui-gray-dark: #16202b; - --ui-gray-darker: #151a1f; - --ui-gray-darkest: #14202a; +Below you can see a compiled version of the tailwind configs. +Each section can be edited accordingly. For instance screen size break points, primary +and secondary colors, etc. - --calendar-day-color: #d3d3d3; - --calendar-day-border-color: #d3d3d3; - --calendar-day-active-hover-background-color: #516873; - --calendar-main-color: #263340; - --viewport-border-thickness: 1px; + + +```js +module.exports = { + prefix: '', + important: false, + separator: ':', + theme: { + screens: { + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + }, + colors: { + overlay: 'rgba(0, 0, 0, 0.8)', + transparent: 'transparent', + black: '#000', + white: '#fff', + initial: 'initial', + inherit: 'inherit', + + indigo: { + dark: '#0b1a42', + }, + aqua: { + pale: '#7bb2ce', + }, + + primary: { + light: '#5acce6', + main: '#0944b3', + dark: '#090c29', + active: '#348cfd', + }, + + secondary: { + light: '#3a3f99', + main: '#2b166b', + dark: '#041c4a', + active: '#1f1f27', + }, + + common: { + bright: '#e1e1e1', + light: '#a19fad', + main: '#fff', + dark: '#726f7e', + active: '#2c3074', + }, + + customgreen: { + 100: '#05D97C', + }, + + customblue: { + 100: '#c4fdff', + 200: '#38daff', + }, + }, + }, } ``` + +You can also use the color variable like before. For instance: + + +```js +primary: { + default: ‘var(--default-color)‘, + light: ‘#5ACCE6’, + main: ‘#0944B3’, + dark: ‘#090C29’, + active: ‘#348CFD’, +} +``` + + ## White Labeling -> A white-label product is a product or service produced by one company (the -> producer) that other companies (the marketers) rebrand to make it appear as if -> they had made it - [Wikipedia: White-Label Product][wikipedia] +A white-label product is a product or service produced by one company (the producer) that other companies (the marketers) rebrand to make it appear as if they had made it - [Wikipedia: White-Label Product](https://en.wikipedia.org/wiki/White-label_product) -Current white-labeling options are limited. We expose the ability to replace the -"Logo" section of the application with a custom "Logo" component. You can do -this by adding a `whiteLabeling` key to your -[configuration file](./configuration.md). +Current white-labeling options are limited. +We expose the ability to replace the "Logo" section of the application with a custom "Logo" component. You can do this by adding a whiteLabeling key to your configuration file. ```js -function RadicalImagingLogo(React) { - return React.createElement( - 'a', - { - target: '_blank', - rel: 'noopener noreferrer', - className: 'header-brand', - href: 'http://radicalimaging.com', +window.config = { + /** .. **/ + whiteLabeling: { + createLogoComponentFn: function (React) { + return React.createElement( + 'a', + { + target: '_blank', + rel: 'noopener noreferrer', + className: 'text-white underline', + href: 'http://radicalimaging.com', + }, + React.createElement('h5', {}, 'RADICAL IMAGING') + ) }, - React.createElement('h5', {}, 'RADICAL IMAGING') - ); + }, + /** .. **/ } - -props.whiteLabeling = { - createLogoComponentFn: RadicalImagingLogo, -}; ``` +> You can simply use the stylings from tailwind CSS in the whiteLabeling + + +In addition to text, you can also add your custom logo + + +```js +window.config = { + /** .. **/ + whiteLabeling: { + createLogoComponentFn: function (React) { + return React.createElement( + 'a', + { + target: '_self', + rel: 'noopener noreferrer', + className: 'text-purple-600 line-through', + href: '/', + }, + React.createElement('img', { + src: './customLogo.svg', + // className: 'w-8 h-8', + }) + ) + }, + }, + /** .. **/ +} +``` + +The output will look like + + +![custom-logo](../assets/img/custom-logo.png) + + + diff --git a/docs/latest/yarn.lock b/platform/docs/docs/yarn.lock similarity index 100% rename from docs/latest/yarn.lock rename to platform/docs/docs/yarn.lock