OE2-mam-button/WebApplication/src/components/ResourceButtonGroup.vue

1006 lines
49 KiB
Vue

<script>
import Modal from "./Modal.vue"
import ShareModal from "./ShareModal.vue"
import ModifyModal from "./ModifyModal.vue"
import AddSeriesModal from "./AddSeriesModal.vue"
import $ from "jquery"
import { mapState } from "vuex"
import api from "../orthancApi"
import resourceHelpers from "../helpers/resource-helpers"
import clipboardHelpers from "../helpers/clipboard-helpers"
import TokenLinkButton from "./TokenLinkButton.vue"
import BulkLabelsModal from "./BulkLabelsModal.vue"
import SourceType from '../helpers/source-type';
export default {
props: ["resourceOrthancId", "resourceDicomUid", "resourceLevel", "customClass", "seriesMainDicomTags", "studyMainDicomTags", "patientMainDicomTags", "instanceTags", "instanceHeaders", "studySeries", "seriesInstances"],
setup() {
return {
}
},
data() {
return {
isBulkLabelModalVisible: false,
isWsiSeries: false,
modalitiesList: []
};
},
watch: {
async studySeries(newValue, oldValue) {
if (this.resourceLevel == 'study') {
// build the modalitiesList to enable/disable viewers
let modalitiesSet = new Set();
for (let seriesDetail of this.studySeries) {
modalitiesSet.add(seriesDetail["MainDicomTags"]["Modality"])
}
this.modalitiesList = Array.from(modalitiesSet);
} else if (this.resourceLevel == 'bulk') {
// build the modalitiesList to enable/disable viewers
let modalitiesSet = new Set();
for (let selectedStudyId of this.selectedStudiesIds) {
let seriesDetails = await api.getStudySeries(selectedStudyId);
for (let seriesDetail of seriesDetails) {
modalitiesSet.add(seriesDetail["MainDicomTags"]["Modality"])
}
}
this.modalitiesList = Array.from(modalitiesSet);
}
},
async seriesInstances(newValue, oldValue) {
if (this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.resourceLevel == 'series') {
let firstInstanceTags = await api.getSimplifiedInstanceTags(this.seriesInstances[0]['ID']);
this.modalitiesList = [firstInstanceTags["Modality"]];
this.isWsiSeries = firstInstanceTags["SOPClassUID"] == "1.2.840.10008.5.1.4.1.1.77.1.6" || firstInstanceTags["Modality"] == "SM";
}
},
},
async mounted() {
},
methods: {
toggleSubMenu(event) {
let parent = event.target.parentElement; // TODO: find a clean way to go to next ul
if (event.target.tagName == "I") {
parent = parent.parentElement;
}
$(parent.querySelector(".dropdown-menu")).toggle();
event.stopPropagation();
event.preventDefault();
},
deleteResource(event) {
if (this.resourceLevel == 'bulk') {
api.deleteResources(this.resourcesOrthancId)
.then(() => {
window.location.reload();
})
.catch((reason) => {
console.error("failed to delete resources : ", this.resourceOrthancId, reason);
});
} else {
api.deleteResource(this.resourceLevel, this.resourceOrthancId)
.then(() => {
this.$emit("deletedResource");
})
.catch((reason) => {
console.error("failed to delete resource : ", this.resourceOrthancId, reason);
});
}
},
triggerMammographyPlugin(event) {
const requestBodyData = {
instance: this.resourceOrthancId
};
api.triggerMammographyPlugin(requestBodyData)
.then(() => {
window.location.reload();
})
.catch((reason) => {
console.error("Failed to process mammography image: ", this.resourceOrthancId, reason);
});
},
getApiUrl(subRoute) {
return api.getApiUrl(this.resourceLevel, this.resourceOrthancId, subRoute);
},
async sendToDicomWebServer(server) {
const jobId = await api.sendToDicomWebServer(this.resourcesOrthancId, server);
this.$store.dispatch('jobs/addJob', { jobId: jobId, name: 'Send to DicomWeb (' + server + ')' });
},
async sendToOrthancPeer(peer) {
const jobId = await api.sendToOrthancPeer(this.resourcesOrthancId, peer);
this.$store.dispatch('jobs/addJob', { jobId: jobId, name: 'Send to Peer (' + peer + ')' });
},
async sendToOrthancPeerWithTransfers(peer) {
const jobId = await api.sendToOrthancPeerWithTransfers(this.resourcesForTransfer, peer);
this.$store.dispatch('jobs/addJob', { jobId: jobId, name: 'Transfer to Peer (' + peer + ')' });
},
async sendToDicomModality(modality) {
const jobId = await api.sendToDicomModality(this.resourcesOrthancId, modality);
this.$store.dispatch('jobs/addJob', { jobId: jobId, name: 'Send to DICOM (' + modality + ')' });
},
async retrieve() {
if (this.studiesSourceType == SourceType.REMOTE_DICOM) {
if (this.resourceLevel == "bulk") {
let moveQueries = this.selectedStudies.map(s => { return {"StudyInstanceUID": s['ID'], "PatientID": s['PatientMainDicomTags']['PatientID']}});
const jobId = await api.remoteDicomRetrieveResources("Study", this.studiesRemoteSource, moveQueries, this.system.DicomAet);
this.$store.dispatch('jobs/addJob', { jobId: jobId, name: 'Retrieve ' + this.selectedStudies.length + ' studies from (' + this.studiesRemoteSource + ')'});
} else {
let moveQuery = {
"StudyInstanceUID": this.studyMainDicomTags.StudyInstanceUID,
"PatientID": this.patientMainDicomTags.PatientID
};
if (this.resourceLevel == 'series' || this.resourceLevel == 'instance') {
moveQuery["SeriesInstanceUID"] = this.seriesMainDicomTags.SeriesInstanceUID;
}
if (this.resourceLevel == 'instance') {
moveQuery["SOPInstanceUID"] = this.instanceTags["0008,0018"].Value;
}
const jobId = await api.remoteDicomRetrieveResource(this.capitalizeFirstLetter(this.resourceLevel), this.studiesRemoteSource, moveQuery, this.system.DicomAet);
this.$store.dispatch('jobs/addJob', { jobId: jobId, name: 'Retrieve ' + this.capitalizeFirstLetter(this.resourceLevel) + ' from (' + this.studiesRemoteSource + ')'});
}
} else if (this.studiesSourceType == SourceType.REMOTE_DICOM_WEB) {
let resources;
if (this.resourceLevel == "bulk") {
resources = this.selectedStudies.map(s => { return {"Study": s['ID']}});
} else if (this.resourceLevel == "study") {
resources = [{"Study": this.studyMainDicomTags.StudyInstanceUID}];
} else if (this.resourceLevel == "series") {
resources = [{
"Study": this.studyMainDicomTags.StudyInstanceUID,
"Series": this.seriesMainDicomTags.SeriesInstanceUID
}];
} else if (this.resourceLevel == "instance") {
resources = [{
"Study": this.studyMainDicomTags.StudyInstanceUID,
"Series": this.seriesMainDicomTags.SeriesInstanceUID,
"Instance": this.instanceTags.SOPInstanceUID
}];
}
const jobId = await api.wadoRsRetrieve(this.studiesRemoteSource, resources);
this.$store.dispatch('jobs/addJob', { jobId: jobId, name: 'Retrieve ' + this.capitalizeFirstLetter(this.resourceLevel) + ' from (' + this.studiesRemoteSource + ')'});
}
},
capitalizeFirstLetter(level) {
return level.charAt(0).toUpperCase() + level.slice(1);
},
copyIdToClipboard() {
clipboardHelpers.copyToClipboard(this.resourceOrthancId);
},
getViewerIcon(forViewer) {
const orderedViewerList = this.uiOptions.ViewersOrdering;
const viewersIcons = this.uiOptions.ViewersIcons;
for (let viewer of orderedViewerList) {
if (viewer == forViewer) {
if (this.hasOsimisViewer && forViewer == "osimis-web-viewer") {
return viewersIcons[viewer];
}
if (this.hasStoneViewer && forViewer == "stone-webviewer") {
return viewersIcons[viewer];
}
if (this.hasVolView && forViewer == "volview") {
return viewersIcons[viewer];
}
if (this.hasOhifViewer && ["ohif", "ohif-vr", "ohif-tmtv", "ohif-seg", "ohif-micro"].includes(forViewer)) {
return viewersIcons[viewer];
}
if (this.hasMedDreamViewer && forViewer == "meddream") {
return viewersIcons[viewer];
}
if (this.hasWsiViewer && forViewer == "wsi") {
return viewersIcons[viewer];
}
}
}
return "bi bi-eye";
},
showBulkLabelModal() {
// this modal is re-created everytime we open it (to interface correctly with bootstrap5-tags
this.isBulkLabelModalVisible = true;
// wait that the DOM element is created !
setTimeout(() => {
this.$refs['bulk-label-modal'].showModal();
}, 50);
},
async onBulkModalClosed() {
this.isBulkLabelModalVisible = false;
await this.$store.dispatch('labels/refresh');
},
getOhifViewerUrl(mode) {
if (this.resourceLevel == 'bulk') {
const selectedStudiesDicomIds = this.selectedStudies.map(s => s['MainDicomTags']['StudyInstanceUID']);
const url = api.getOhifViewerUrlForDicomWebBulkStudies(mode, selectedStudiesDicomIds);
return url;
} else {
if (this.ohifDataSource == 'dicom-web') {
return api.getOhifViewerUrlForDicomWeb(mode, this.resourceDicomUid);
} else if (this.ohifDataSource == 'dicom-json') {
return api.getOhifViewerUrlForDicomJson(mode, this.resourceOrthancId);
}
}
},
},
computed: {
...mapState({
uiOptions: state => state.configuration.uiOptions,
studiesSourceType: state => state.studies.sourceType,
studiesRemoteSource: state => state.studies.remoteSource,
ohifDataSource: state => state.configuration.ohifDataSource,
installedPlugins: state => state.configuration.installedPlugins,
targetDicomWebServers: state => state.configuration.targetDicomWebServers,
targetDicomModalities: state => state.configuration.targetDicomModalities,
selectedStudiesIds: state => state.studies.selectedStudiesIds,
selectedStudies: state => state.studies.selectedStudies,
orthancPeers: state => state.configuration.orthancPeers,
tokens: state => state.configuration.tokens,
system: state => state.configuration.system,
}),
componentId() {
if (this.resourceLevel == 'bulk') {
return 'bulk-id';
} else {
return this.resourceOrthancId;
}
},
hasSendTo() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.uiOptions.EnableSendTo &&
(this.hasSendToDicomWeb || this.hasSendToPeers || this.hasSendToDicomModalities || this.hasSendToPeersWithTransfer);
},
isSendToEnabled() {
if (this.resourceLevel == 'bulk') {
return this.selectedStudiesIds.length > 0
} else {
return true;
}
},
hasLabelsButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC &&
this.uiOptions.EnableEditLabels && this.resourceLevel == 'bulk';
},
isLabelsEnabled() {
if (this.resourceLevel == 'bulk') {
return this.selectedStudiesIds.length > 0
} else {
return false;
}
},
hasDeleteButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC &&
this.uiOptions.EnableDeleteResources;
},
isDeleteEnabled() {
if (this.resourceLevel == 'bulk') {
return this.selectedStudiesIds.length > 0
} else {
return true;
}
},
hasShareButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC &&
(this.uiOptions.EnableShares && "authorization" in this.installedPlugins && ['bulk', 'study'].includes(this.resourceLevel));
},
isShareEnabled() {
if (this.resourceLevel == 'bulk') {
return this.selectedStudiesIds.length > 0
} else {
return true;
}
},
hasAddSeriesButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC &&
(this.uiOptions.EnableAddSeries && this.resourceLevel == 'study');
},
hasWsiButton() {
if (this.studiesSourceType != SourceType.LOCAL_ORTHANC) {
return false;
}
if (this.resourceLevel != 'series' || !this.hasWsiViewer) {
return false;
}
return true;
},
isWsiButtonEnabled() {
return this.isWsiSeries;
},
wsiViewerUrl() {
return api.getWsiViewerUrl(this.resourceOrthancId);
},
hasSendToPeers() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.orthancPeers.length > 0;
},
hasSendToPeersWithTransfer() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.hasSendToPeers && ("transfers" in this.installedPlugins);
},
hasSendToDicomWeb() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.targetDicomWebServers.length > 0;
},
hasSendToDicomModalities() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && Object.keys(this.targetDicomModalities).length > 0;
},
hasOsimisViewer() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && "osimis-web-viewer" in this.installedPlugins;
},
osimisViewerUrl() {
return api.getOsimisViewerUrl(this.resourceLevel, this.resourceOrthancId);
},
hasWsiViewer() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && "wsi" in this.installedPlugins;
},
hasStoneViewer() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && "stone-webviewer" in this.installedPlugins;
},
stoneViewerUrl() {
if (this.resourceLevel == 'bulk') {
const selectedStudiesDicomIds = this.selectedStudies.map(s => s['MainDicomTags']['StudyInstanceUID']);
const url = api.getStoneViewerUrlForBulkStudies(selectedStudiesDicomIds);
return url;
} else {
return api.getStoneViewerUrl(this.resourceLevel, this.resourceDicomUid);
}
},
hasStoneViewerButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC &&
this.hasStoneViewer && (this.resourceLevel == 'study' || this.resourceLevel == 'bulk');
},
isStoneViewerButtonEnabled() {
return (this.resourceLevel == 'study' || (this.resourceLevel == 'bulk' && this.selectedStudiesIds.length > 0));
},
hasVolView() {
return "volview" in this.installedPlugins;
},
volViewUrl() {
return api.getVolViewUrl(this.resourceLevel, this.resourceOrthancId);
},
hasVolViewButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.hasVolView && (this.resourceLevel == 'study' || this.resourceLevel == 'series');
},
isVolViewButtonEnabled() {
return (this.resourceLevel == 'study' || this.resourceLevel == 'series');
},
hasOhifViewer() {
return this.uiOptions.EnableOpenInOhifViewer || this.uiOptions.EnableOpenInOhifViewer3;
},
hasOhifViewerButton() { // Basic viewer
if (!this.uiOptions.ViewersOrdering.includes("ohif") || this.studiesSourceType != SourceType.LOCAL_ORTHANC) {
return false;
}
// disable if it only contains non images modalities:
let modalities = this.modalitiesList;
modalities = modalities.filter(x => !['SM', 'ECG', 'SR', 'SEG'].includes(x)); // since Set.difference is not supported on Firefox as of March 2024
if (modalities.length == 0 && this.resourceLevel != "bulk") // we can not check the modalities list for bulk mode
{
return false;
}
if (this.uiOptions.EnableOpenInOhifViewer3) {
return this.hasOhifViewer && (this.resourceLevel == 'study' || (this.resourceLevel == 'bulk' && this.ohifDataSource == 'dicom-web'));
} else {
return this.hasOhifViewer && this.resourceLevel == 'study';
}
},
hasOhifViewerButtonVr() {
if (!this.uiOptions.ViewersOrdering.includes("ohif-vr") || this.studiesSourceType != SourceType.LOCAL_ORTHANC) {
return false;
}
// only for CT, PT and MR
if (!(this.modalitiesList.includes("CT") || this.modalitiesList.includes("PT") || this.modalitiesList.includes("MR"))
&& this.resourceLevel != "bulk") // we can not check the modalities list for bulk mode)
{
return false;
}
if (this.uiOptions.EnableOpenInOhifViewer3) {
return this.hasOhifViewer && (this.resourceLevel == 'study' || (this.resourceLevel == 'bulk' && this.ohifDataSource == 'dicom-web'));
} else {
return false;
}
},
hasOhifViewerButtonTmtv() {
if (!this.uiOptions.ViewersOrdering.includes("ohif-tmtv") || this.studiesSourceType != SourceType.LOCAL_ORTHANC) {
return false;
}
// from isValidMode() in OHIF code
if (!(this.modalitiesList.includes("CT") && this.modalitiesList.includes("PT") && !this.modalitiesList.includes("SM"))
&& this.resourceLevel != "bulk") // we can not check the modalities list for bulk mode)
{
return false;
}
if (this.uiOptions.EnableOpenInOhifViewer3) {
return this.hasOhifViewer && (this.resourceLevel == 'study' || (this.resourceLevel == 'bulk' && this.ohifDataSource == 'dicom-web'));
} else {
return false;
}
},
hasOhifViewerButtonSeg() {
if (!this.uiOptions.ViewersOrdering.includes("ohif-seg") || this.studiesSourceType != SourceType.LOCAL_ORTHANC) {
return false;
}
if (this.uiOptions.EnableOpenInOhifViewer3 && this.hasOhifViewer) {
if (this.resourceLevel == "bulk" && this.ohifDataSource == 'dicom-web') {
return true; // unable to check the list of modalities in this case -> allow it
} else if (this.resourceLevel == "study") {
// // from isValidMode() in OHIF code
// Don't show the mode if the selected studies have only one modality that is not supported by the mode
if (this.modalitiesList.length == 1 && ['SM', 'ECG', 'OT', 'DOC'].includes(this.modalitiesList[0])) {
return false;
} else {
return true;
}
}
}
return false;
},
hasOhifViewerButtonMicroscopy() {
if (!this.uiOptions.ViewersOrdering.includes("ohif-micro") || this.studiesSourceType != SourceType.LOCAL_ORTHANC) {
return false;
}
// Must have at least one SM series
if (!this.modalitiesList.includes("SM") && this.resourceLevel != "bulk") {// we can not check the modalities list for bulk mode)
return false;
}
if (this.uiOptions.EnableOpenInOhifViewer3) {
return this.hasOhifViewer && (this.resourceLevel == 'study' || (this.resourceLevel == 'bulk' && this.ohifDataSource == 'dicom-web'));
} else {
return false;
}
},
ohifViewerUrl() {
return this.getOhifViewerUrl('basic');
},
ohifViewerUrlVr() {
return this.getOhifViewerUrl('vr');
},
ohifViewerUrlTmtv() {
return this.getOhifViewerUrl('tmtv');
},
ohifViewerUrlSeg() {
return this.getOhifViewerUrl('seg');
},
ohifViewerUrlMicro() {
return this.getOhifViewerUrl('microscopy');
},
isOhifButtonBasicViewerEnabled() {
if (this.uiOptions.EnableOpenInOhifViewer3) { // OHIF V3
return (this.resourceLevel == 'study' || (this.resourceLevel == 'bulk' && this.selectedStudiesIds.length > 0));
} else { // OHIF V2
return this.resourceLevel == 'study';
}
},
isOhifButtonVrEnabled() {
if (this.uiOptions.EnableOpenInOhifViewer3) { // OHIF V3
return (this.resourceLevel == 'study' || (this.resourceLevel == 'bulk' && this.selectedStudiesIds.length > 0));
} else { // OHIF V2
return false;
}
},
isOhifButtonTmtvEnabled() {
if (this.uiOptions.EnableOpenInOhifViewer3) { // OHIF V3
return (this.resourceLevel == 'study' || (this.resourceLevel == 'bulk' && this.selectedStudiesIds.length > 0));
} else { // OHIF V2
return false;
}
},
isOhifButtonMicroscopyEnabled() {
if (this.uiOptions.EnableOpenInOhifViewer3) { // OHIF V3
return (this.resourceLevel == 'study' || (this.resourceLevel == 'bulk' && this.selectedStudiesIds.length > 0));
} else { // OHIF V2
return false;
}
},
isOhifButtonSegEnabled() {
if (this.uiOptions.EnableOpenInOhifViewer3) { // OHIF V3
return (this.resourceLevel == 'study' || (this.resourceLevel == 'bulk' && this.selectedStudiesIds.length > 0));
} else { // OHIF V2
return false;
}
},
hasMedDreamViewer() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.uiOptions.EnableOpenInMedDreamViewer;
},
hasMedDreamViewerButton() {
return this.hasMedDreamViewer && (this.resourceLevel == 'study' || this.resourceLevel == 'bulk');
},
isMedDreamViewerButtonEnabled() {
return (this.resourceLevel == 'study' || (this.resourceLevel == 'bulk' && this.selectedStudiesIds.length > 0));
},
medDreamViewerUrl() {
return this.uiOptions.MedDreamViewerPublicRoot + "?study=" + this.resourceDicomUid;
},
isPdfPreview() {
if (this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.resourceLevel == 'instance') {
return this.instanceHeaders["0002,0002"]["Value"] == "1.2.840.10008.5.1.4.1.1.104.1";
} else {
return false;
}
},
hasInstancePreviewButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.resourceLevel == 'instance';
},
instancePreviewUrl() {
if (this.isPdfPreview) {
return api.getInstancePdfUrl(this.resourceOrthancId);
} else {
return api.getInstancePreviewUrl(this.resourceOrthancId);
}
},
hasInstanceDownloadButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC &&
this.uiOptions.EnableDownloadDicomFile && this.resourceLevel == 'instance';
},
hasInstanceMammographyButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC &&
this.uiOptions.EnabledMammographyPlugin &&
this.seriesMainDicomTags?.["Modality"] == "MG" && this.resourceLevel == 'instance';
},
instanceDownloadUrl() {
return api.getInstanceDownloadUrl(this.resourceOrthancId);
},
downloadBulkZipUrl() {
return api.getBulkDownloadZipUrl(this.resourcesOrthancId);
},
downloadBulkDicomDirUrl() {
return api.getBulkDownloadDicomDirUrl(this.resourcesOrthancId);
},
downloadZipUrl() {
return api.getDownloadZipUrl(this.resourceLevel, this.resourceOrthancId);
},
downloadDicomDirUrl() {
return api.getDownloadDicomDirUrl(this.resourceLevel, this.resourceOrthancId);
},
resourceTitle() {
return resourceHelpers.getResourceTitle(this.resourceLevel, this.patientMainDicomTags, this.studyMainDicomTags, this.seriesMainDicomTags, this.instanceTags);
},
osimisViewerIcon() {
return this.getViewerIcon("osimis-web-viewer");
},
stoneViewerIcon() {
return this.getViewerIcon("stone-webviewer");
},
volViewIcon() {
return this.getViewerIcon("volview");
},
medDreamViewerIcon() {
return this.getViewerIcon("meddream");
},
ohifViewerIcon() {
return this.getViewerIcon("ohif");
},
ohifViewerIconVr() {
return this.getViewerIcon("ohif-vr");
},
ohifViewerIconTmtv() {
return this.getViewerIcon("ohif-tmtv");
},
ohifViewerIconSeg() {
return this.getViewerIcon("ohif-seg");
},
ohifViewerIconMicro() {
return this.getViewerIcon("ohif-micro");
},
wsiViewerIcon() {
return this.getViewerIcon("wsi");
},
deleteResourceTitle() {
const texts = {
"study": "delete_study_title",
"series": "delete_series_title",
"instance": "delete_instance_title",
"bulk": "delete_studies_title"
}
return texts[this.resourceLevel];
},
deleteResourceBody() {
const texts = {
"study": "delete_study_body",
"series": "delete_series_body",
"instance": "delete_instance_body",
"bulk": "delete_studies_body"
}
return texts[this.resourceLevel];
},
hasDownloadZipButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.uiOptions.EnableDownloadZip && this.resourceLevel != 'instance' && this.resourceLevel != 'bulk';
},
hasBulkDownloadZipButton() {
if (this.system.ApiVersion < 22) // the /tools/create-archive GET route has been introduced in 1.12.2
{
return false;
}
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.uiOptions.EnableDownloadZip && this.resourceLevel == 'bulk'
},
isBulkDownloadZipEnabled() {
return this.selectedStudiesIds.length > 0;
},
hasDownloadDicomDirButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.uiOptions.EnableDownloadDicomDir && this.resourceLevel != 'instance' && this.resourceLevel != 'bulk';
},
hasBulkDownloadDicomDirButton() {
if (this.system.ApiVersion < 22) // the /tools/create-media GET route has been introduced in 1.12.2
{
return false;
}
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.uiOptions.EnableDownloadDicomDir && this.resourceLevel == 'bulk'
},
isBulkDownloadDicomDirEnabled() {
return this.selectedStudiesIds.length > 0;
},
hasRetrieveButton() {
return this.studiesSourceType == SourceType.REMOTE_DICOM || this.studiesSourceType == SourceType.REMOTE_DICOM_WEB;
},
isRetrieveButtonEnabled() {
return (this.resourceLevel != 'bulk' || (this.resourceLevel == 'bulk' && this.selectedStudiesIds.length > 0));
},
hasAnonymizationButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.resourceLevel != 'bulk';
},
isAnonymizationEnabled() {
if (this.resourceLevel == "study" || this.resourceLevel == "series") {
return this.uiOptions.EnableAnonymization;
} else {
return false;
}
},
hasModificationButton() {
return this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.resourceLevel != 'bulk';
},
isModificationEnabled() {
if (this.resourceLevel == "study" || this.resourceLevel == "series") {
return this.uiOptions.EnableModification;
} else {
return false;
}
},
hasApiViewButton() {
return this.resourceLevel != 'bulk' && this.studiesSourceType == SourceType.LOCAL_ORTHANC && this.uiOptions.EnableApiViewMenu;
},
resourcesOrthancId() {
if (this.resourceLevel == 'bulk') {
return this.selectedStudiesIds;
} else {
return [this.resourceOrthancId];
}
},
resourcesForTransfer() {
if (this.resourceLevel == 'bulk') {
return this.selectedStudiesIds.map(id => ({ "Level": "Study", "ID": id }));
} else {
return [{ "Level": this.capitalizeFirstLetter(this.resourceLevel), "ID": this.resourceOrthancId }];
}
},
computedResourceLevel() {
if (this.resourceLevel == 'bulk') {
return "study";
} else {
return this.resourceLevel;
}
}
},
components: { Modal, ShareModal, ModifyModal, TokenLinkButton, BulkLabelsModal, AddSeriesModal }
}
</script>
<template>
<div>
<div class="btn-group">
<span v-for="viewer in uiOptions.ViewersOrdering" :key="viewer">
<TokenLinkButton v-if="viewer == 'meddream' && hasMedDreamViewerButton"
:disabled="!isMedDreamViewerButtonEnabled"
:iconClass="medDreamViewerIcon" :level="computedResourceLevel" :linkUrl="medDreamViewerUrl"
:resourcesOrthancId="resourcesOrthancId" :title="$t('view_in_meddream')"
:tokenType="'meddream-instant-link'" :opensInNewTab="true">
</TokenLinkButton>
<TokenLinkButton
v-if="hasOsimisViewer && viewer == 'osimis-web-viewer' && (this.resourceLevel == 'study' || this.resourceLevel == 'series')"
:iconClass="osimisViewerIcon" :level="this.resourceLevel" :linkUrl="osimisViewerUrl"
:resourcesOrthancId="resourcesOrthancId" :title="$t('view_in_osimis')"
:tokenType="'viewer-instant-link'" :opensInNewTab="true">
</TokenLinkButton>
<TokenLinkButton v-if="viewer == 'stone-webviewer' && hasStoneViewerButton"
:disabled="!isStoneViewerButtonEnabled"
:iconClass="stoneViewerIcon" :level="computedResourceLevel" :linkUrl="stoneViewerUrl"
:resourcesOrthancId="resourcesOrthancId" :title="$t('view_in_stone')"
:tokenType="'viewer-instant-link'" :opensInNewTab="true">
</TokenLinkButton>
<TokenLinkButton v-if="viewer == 'volview' && hasVolViewButton"
:disabled="!isVolViewButtonEnabled"
:iconClass="volViewIcon" :level="computedResourceLevel" :linkUrl="volViewUrl"
:resourcesOrthancId="resourcesOrthancId" :title="$t('view_in_volview')"
:tokenType="'viewer-instant-link'" :opensInNewTab="true">
</TokenLinkButton>
<TokenLinkButton v-if="viewer == 'ohif' && hasOhifViewerButton"
:disabled="!isOhifButtonBasicViewerEnabled"
:iconClass="ohifViewerIcon" :level="computedResourceLevel" :linkUrl="ohifViewerUrl"
:resourcesOrthancId="resourcesOrthancId" :title="$t('view_in_ohif')"
:tokenType="'viewer-instant-link'" :opensInNewTab="true">
</TokenLinkButton>
<TokenLinkButton v-if="viewer == 'ohif-vr' && hasOhifViewerButtonVr"
:disabled="!isOhifButtonVrEnabled"
:iconClass="ohifViewerIconVr" :level="computedResourceLevel" :linkUrl="ohifViewerUrlVr"
:resourcesOrthancId="resourcesOrthancId" :title="$t('view_in_ohif_vr')"
:tokenType="'viewer-instant-link'" :opensInNewTab="true">
</TokenLinkButton>
<TokenLinkButton v-if="viewer == 'ohif-tmtv' && hasOhifViewerButtonTmtv"
:disabled="!isOhifButtonTmtvEnabled"
:iconClass="ohifViewerIconTmtv" :level="computedResourceLevel" :linkUrl="ohifViewerUrlTmtv"
:resourcesOrthancId="resourcesOrthancId" :title="$t('view_in_ohif_tmtv')"
:tokenType="'viewer-instant-link'" :opensInNewTab="true">
</TokenLinkButton>
<TokenLinkButton v-if="viewer == 'ohif-seg' && hasOhifViewerButtonSeg"
:disabled="!isOhifButtonSegEnabled"
:iconClass="ohifViewerIconSeg" :level="computedResourceLevel" :linkUrl="ohifViewerUrlSeg"
:resourcesOrthancId="resourcesOrthancId" :title="$t('view_in_ohif_seg')"
:tokenType="'viewer-instant-link'" :opensInNewTab="true">
</TokenLinkButton>
<TokenLinkButton v-if="viewer == 'ohif-micro' && hasOhifViewerButtonMicroscopy"
:disabled="!isOhifButtonMicroscopyEnabled"
:iconClass="ohifViewerIconMicro" :level="computedResourceLevel" :linkUrl="ohifViewerUrlMicro"
:resourcesOrthancId="resourcesOrthancId" :title="$t('view_in_ohif_microscopy')"
:tokenType="'viewer-instant-link'" :opensInNewTab="true">
</TokenLinkButton>
</span>
<TokenLinkButton v-if="hasInstancePreviewButton"
:iconClass="'bi bi-binoculars'" :level="this.resourceLevel" :linkUrl="instancePreviewUrl"
:resourcesOrthancId="[resourceOrthancId]" :title="$t('preview')"
:tokenType="'download-instant-link'" :opensInNewTab="true">
</TokenLinkButton>
<TokenLinkButton v-if="hasWsiButton"
:hidden="!isWsiButtonEnabled"
:iconClass="wsiViewerIcon" :level="this.resourceLevel" :linkUrl="wsiViewerUrl"
:resourcesOrthancId="resourcesOrthancId" :title="$t('view_in_wsi_viewer')"
:tokenType="'viewer-instant-link'" :opensInNewTab="true">
</TokenLinkButton>
</div>
<div class="btn-group">
<TokenLinkButton v-if="hasDownloadZipButton"
:iconClass="'bi bi-download'" :level="this.resourceLevel" :linkUrl="downloadZipUrl"
:resourcesOrthancId="resourcesOrthancId" :title="$t('download_zip')" :tokenType="'download-instant-link'">
</TokenLinkButton>
<TokenLinkButton v-if="hasBulkDownloadZipButton"
:iconClass="'bi bi-download'" :level="'bulk-study'" :linkUrl="downloadBulkZipUrl"
:resourcesOrthancId="resourcesOrthancId" :title="$t('download_zip')" :tokenType="'download-instant-link'"
:disabled="!isBulkDownloadZipEnabled">
</TokenLinkButton>
<TokenLinkButton v-if="hasDownloadDicomDirButton"
:iconClass="'bi bi-box-arrow-down'" :level="this.resourceLevel" :linkUrl="downloadDicomDirUrl"
:resourcesOrthancId="[resourceOrthancId]" :title="$t('download_dicomdir')"
:tokenType="'download-instant-link'">
</TokenLinkButton>
<TokenLinkButton v-if="hasBulkDownloadDicomDirButton"
:iconClass="'bi bi-box-arrow-down'" :level="'bulk-study'" :linkUrl="downloadBulkDicomDirUrl"
:resourcesOrthancId="resourcesOrthancId" :title="$t('download_dicomdir')" :tokenType="'download-instant-link'"
:disabled="!isBulkDownloadDicomDirEnabled">
</TokenLinkButton>
<TokenLinkButton v-if="hasInstanceDownloadButton"
:iconClass="'bi bi-download'" :level="this.resourceLevel" :linkUrl="instanceDownloadUrl"
:resourcesOrthancId="[resourceOrthancId]" :title="$t('download_dicom_file')"
:tokenType="'download-instant-link'">
</TokenLinkButton>
</div>
<div class="btn-group">
<button v-if="hasInstanceMammographyButton"
class="btn btn-sm btn-secondary m-1"
type="button"
@click="triggerMammographyPlugin">
<i class="bi bi-vignette" data-bs-toggle="tooltip" :title="$t('mammography_plugin')"></i>
</button>
</div>
<div class="btn-group" >
<button v-if="hasDeleteButton" class="btn btn-sm btn-secondary m-1" type="button"
data-bs-toggle="modal" v-bind:data-bs-target="'#delete-modal-' + this.resourceOrthancId" :disabled="!isDeleteEnabled">
<i class="bi bi-trash" data-bs-toggle="tooltip" :title="$t('delete')" ></i>
</button>
<Modal v-if="hasDeleteButton" :id="'delete-modal-' + this.resourceOrthancId"
:headerText="$t(this.deleteResourceTitle) + ' ' + this.resourceTitle" :okText="$t('delete')"
:cancelText="$t('cancel')" :bodyText="$t(this.deleteResourceBody)" @ok="deleteResource($event)">
</Modal>
</div>
<div v-if="hasShareButton" class="btn-group">
<button class="btn btn-sm btn-secondary m-1" type="button" data-bs-toggle="modal"
v-bind:data-bs-target="'#share-modal-' + this.resourceOrthancId" :disabled="!isShareEnabled">
<i class="bi bi-share" data-bs-toggle="tooltip" :title="$t('share.button_title')"></i>
</button>
<ShareModal :id="'share-modal-' + this.resourceOrthancId"
:orthancId="this.resourceOrthancId" :studyMainDicomTags="this.studyMainDicomTags"
:patientMainDicomTags="this.patientMainDicomTags"></ShareModal>
</div>
<div v-if="hasModificationButton" class="btn-group">
<button v-if="isModificationEnabled" class="btn btn-sm btn-secondary m-1" type="button" data-bs-toggle="modal"
v-bind:data-bs-target="'#modify-modal-' + this.resourceOrthancId">
<i class="bi bi-pencil" data-bs-toggle="tooltip" :title="$t('modify.modify_button_title')"></i>
</button>
<ModifyModal v-if="isModificationEnabled" :id="'modify-modal-' + this.resourceOrthancId"
:orthancId="this.resourceOrthancId" :resourceLevel="this.resourceLevel"
:seriesMainDicomTags="this.seriesMainDicomTags" :studyMainDicomTags="this.studyMainDicomTags"
:patientMainDicomTags="this.patientMainDicomTags" :isAnonymization="false"></ModifyModal>
</div>
<div v-if="hasAnonymizationButton" class="btn-group">
<button v-if="isAnonymizationEnabled" class="btn btn-sm btn-secondary m-1" type="button" data-bs-toggle="modal"
v-bind:data-bs-target="'#anonymize-modal-' + this.resourceOrthancId">
<i class="bi bi-person-slash" data-bs-toggle="tooltip" :title="$t('modify.anonymize_button_title')"></i>
</button>
<ModifyModal v-if="isAnonymizationEnabled" :id="'anonymize-modal-' + this.resourceOrthancId"
:orthancId="this.resourceOrthancId" :resourceLevel="this.resourceLevel"
:seriesMainDicomTags="this.seriesMainDicomTags" :studyMainDicomTags="this.studyMainDicomTags"
:patientMainDicomTags="this.patientMainDicomTags" :isAnonymization="true"></ModifyModal>
</div>
<div v-if="hasAddSeriesButton" class="btn-group" >
<button class="btn btn-sm btn-secondary m-1" type="button" data-bs-toggle="modal"
v-bind:data-bs-target="'#add-series-modal-' + this.resourceOrthancId">
<i class="bi bi-file-earmark-plus" data-bs-toggle="tooltip" :title="$t('add_series.button_title')"></i>
</button>
<AddSeriesModal :id="'add-series-modal-' + this.resourceOrthancId"
:orthancStudyId="this.resourceOrthancId" :studyMainDicomTags="this.studyMainDicomTags"
:patientMainDicomTags="this.patientMainDicomTags"></AddSeriesModal>
</div>
<div class="btn-group" v-if="hasApiViewButton">
<div class="dropdown">
<button class="dropdown btn btn-sm btn-secondary m-1 dropdown-toggle"
type="button" id="apiDropdownMenuId" data-bs-toggle="dropdown" aria-expanded="false">
<span data-bs-toggle="tooltip" title="API">
<i class="bi bi-code-slash"></i>
</span>
</button>
<ul class="dropdown-menu" aria-labelledby="apiDropdownMenuId">
<li>
<button class="dropdown-item" href="#" @click="copyIdToClipboard">{{ $t('copy_orthanc_id')
}}</button>
</li>
<li>
<TokenLinkButton :linkType="'dropdown-item'" :level="this.resourceLevel" :linkUrl="getApiUrl('')"
:resourcesOrthancId="[resourceOrthancId]" :title="'/'" :tokenType="'download-instant-link'"
:opensInNewTab="true">
</TokenLinkButton>
</li>
<li v-if="this.resourceLevel == 'instance'">
<TokenLinkButton :linkType="'dropdown-item'" :level="this.resourceLevel" :linkUrl="getApiUrl('/tags?simplify')"
:resourcesOrthancId="[resourceOrthancId]" :title="'/tags?simplify'" :tokenType="'download-instant-link'"
:opensInNewTab="true">
</TokenLinkButton>
</li>
<li>
<TokenLinkButton :linkType="'dropdown-item'" :level="this.resourceLevel" :linkUrl="getApiUrl('/metadata?expand')"
:resourcesOrthancId="[resourceOrthancId]" :title="'/metadata?expand'" :tokenType="'download-instant-link'"
:opensInNewTab="true">
</TokenLinkButton>
</li>
<li>
<TokenLinkButton :linkType="'dropdown-item'" :level="this.resourceLevel" :linkUrl="getApiUrl('/statistics')"
:resourcesOrthancId="[resourceOrthancId]" :title="'/statistics'" :tokenType="'download-instant-link'"
:opensInNewTab="true">
</TokenLinkButton>
</li>
<li>
<TokenLinkButton :linkType="'dropdown-item'" :level="this.resourceLevel" :linkUrl="getApiUrl('/attachments?expand')"
:resourcesOrthancId="[resourceOrthancId]" :title="'/attachments?expand'" :tokenType="'download-instant-link'"
:opensInNewTab="true">
</TokenLinkButton>
</li>
</ul>
</div>
</div>
<div class="btn-group" v-if="this.resourceLevel == 'bulk'">
<!-- <button v-if="hasLabelsButton" class="btn btn-sm btn-secondary m-1" type="button"
data-bs-toggle="modal" v-bind:data-bs-target="'#labels2-modal2-' + this.componentId" :disabled="!isLabelsEnabled">
<i class="bi bi-tag" data-bs-toggle="tooltip" :title="$t('labels.edit_labels_button')" ></i>
</button> -->
<button v-if="hasLabelsButton" class="btn btn-sm btn-secondary m-1" type="button"
:disabled="!isLabelsEnabled" @click="showBulkLabelModal()">
<i class="bi bi-tag" data-bs-toggle="tooltip" :title="$t('labels.edit_labels_button')" ></i>
</button>
<BulkLabelsModal v-if="uiOptions.EnableEditLabels && isBulkLabelModalVisible" :id="'labels2-modal2-' + this.componentId" ref="bulk-label-modal"
:resourceLevel="this.resourceLevel" :resourcesOrthancId="selectedStudiesIds" @bulkModalClosed="onBulkModalClosed">
</BulkLabelsModal>
</div>
<div class="btn-group">
<div class="dropdown">
<button v-if="hasSendTo" class="dropdown btn btn-sm btn-secondary m-1 dropdown-toggle" type="button"
id="sendToDropdownMenuId" data-bs-toggle="dropdown" aria-expanded="false" :disabled="!isSendToEnabled">
<span data-bs-toggle="tooltip" :title="$t('send_to.button_title')">
<i class="bi bi-send"></i>
</span>
</button>
<ul class="dropdown-menu" aria-labelledby="sendToDropdownMenuId" v-if="hasSendTo">
<li v-if="hasSendToPeers" class="dropdown-submenu">
<a class="dropdown-item" @click="toggleSubMenu" href="#">
{{ $t('send_to.orthanc_peer') }}
<i class="bi bi-caret-down"></i>
</a>
<ul class="dropdown-menu">
<li v-for="peer in orthancPeers" :key="peer">
<a class="dropdown-item" @click="sendToOrthancPeer(peer)">{{ peer }}</a>
</li>
</ul>
</li>
<li v-if="hasSendToPeersWithTransfer" class="dropdown-submenu">
<a class="dropdown-item" @click="toggleSubMenu" href="#">
{{ $t('send_to.transfers') }}
<i class="bi bi-caret-down"></i>
</a>
<ul class="dropdown-menu">
<li v-for="peer in orthancPeers" :key="peer">
<a class="dropdown-item" @click="sendToOrthancPeerWithTransfers(peer)">{{ peer }}</a>
</li>
</ul>
</li>
<li v-if="hasSendToDicomWeb" class="dropdown-submenu">
<a class="dropdown-item" @click="toggleSubMenu" href="#">
{{ $t('send_to.dicom_web') }}
<i class="bi bi-caret-down"></i>
</a>
<ul class="dropdown-menu">
<li v-for="dwServer in targetDicomWebServers" :key="dwServer">
<a class="dropdown-item" @click="sendToDicomWebServer(dwServer)">{{ dwServer }}</a>
</li>
</ul>
</li>
<li v-if="hasSendToDicomModalities" class="dropdown-submenu">
<a class="dropdown-item" @click="toggleSubMenu" href="#">
{{ $t('send_to.dicom') }}
<i class="bi bi-caret-down"></i>
</a>
<ul class="dropdown-menu">
<li v-for="modality in Object.keys(targetDicomModalities)" :key="modality">
<a class="dropdown-item" @click="sendToDicomModality(modality)">{{ modality }}</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
<div v-if="hasRetrieveButton" class="btn-group" >
<button class="btn btn-sm btn-secondary m-1" type="button"
:disabled="!isRetrieveButtonEnabled" @click="retrieve">
<i class="bi bi-box-arrow-in-down" data-bs-toggle="tooltip" :title="$t('retrieve')" ></i>
</button>
</div>
</div>
</template>
<style>
.dropdown-submenu {
position: relative;
}
.dropdown-submenu .dropdown-menu {
top: 0;
right: 100%;
margin-top: -1px;
}
.fa-button {
line-height: 1.5;
}
</style>