Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instrument sharing #3035

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion WebApp/WebContent/dataset/dataset_list.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@
actionListener="#{dataSetsBean.updateDatasetList}"
oncomplete="updateControls()" onerror="updateFailed()"/>

<p:remoteCommand name="setListView" action="#{dataSetsBean.setListView()}"/>
<p:remoteCommand name="setListView" action="#{dataSetsBean.setListView()}"/>

<ui:include src="/WEB-INF/templates/dialog.xhtml" />

Expand Down
158 changes: 121 additions & 37 deletions WebApp/WebContent/instrument/instrument_list.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,120 @@
<ui:define name="title">Instruments</ui:define>
<ui:define name="localHead">
<c:set var="mainMenuActiveIndex" value="2" scope="request" />
<script>
function goToStandards(id) {
$('#instrumentListForm\\:standardsInstrumentId').val(id);
$('#instrumentListForm\\:showStandardsLink').click();
return false;
}

function goToDiagnosticsQC(id) {
$('#instrumentListForm\\:diagnosticQCInstrumentId').val(id);
$('#instrumentListForm\\:diagnostQCLink').click();
return false;
}

function goToCalibrations(id) {
$('#instrumentListForm\\:calibrationsInstrumentId').val(id);
$('#instrumentListForm\\:showCalibrationsLink').click();
return false;
}

function goToCalculationCoefficients(id) {
$('#instrumentListForm\\:calculationCoefficientsInstrumentId').val(id);
$('#instrumentListForm\\:showCalculationCoefficientsLink').click();
return false;
}

function confirmInstrumentDelete(id, name) {
$('#instrumentListForm\\:deleteInstrumentId').val(id);
$('#deleteInstrumentName')[0].innerHTML = name;
PF('confirmDelete').show();
}

function deleteInstrument() {
$('#instrumentListForm\\:deleteInstrumentLink').click();
return false;
}
</script>
<h:outputScript name="script/instruments.js" />
</ui:define>
<ui:define name="content">
<h:form id="instrumentListForm" method="post" accept-charset="utf8">
<h:form id="shareForm" method="post" accept-charset="utf8">
<h:inputHidden id="ajaxOK" value="#{instrumentListBean.ajaxOK}"/>

<p:dialog widgetVar="ownershipDialog" modal="true" resizable="false"
header="Ownership and Sharing" style="max-width: 80%">

<h4><h:outputText id="ownerInstrumentName"
value="#{instrumentListBean.ownerInstrumentName}"/></h4>

<table>
<tr>
<td class="label">Owner:</td>
<td><h:outputText id="ownerName" value="#{instrumentListBean.ownerName}"/></td>
</tr>
<tr>
<td class="label" style="white-space: nowrap; vertical-align: top; padding-top: 13px">
Shared with:
</td>
<td>
<p:dataTable id="sharedList" var="user" widgetVar="sharedList"
styleClass="tableNoHeader tableNarrow" emptyMessage=""
value="#{instrumentListBean.sharedUsers}">

<p:column width="200px" style="white-space: nowrap">
<h:outputText value="#{user.name}"/>
</p:column>

<p:column>
<p:commandLink onclick="removeShare(#{user.id})">
<p:confirm header="Remove Share"
message="Are you sure you want to remove share access for #{user.name}?"
icon="pi pi-exclamation-triangle"/>
<h:graphicImage value="/resources/image/x-red.svg"
styleClass="actionIconSmall" alt="Remove Share"
title="Remove Share" />
</p:commandLink>
</p:column>
<p:column>
<p:commandLink onclick="initTransferOwnership(#{user.id})">
<p:confirm header="Transfer Ownership"
message="Are you sure you want to transfer ownership to #{user.name}? The instrument will still be shared with you."
icon="pi pi-exclamation-triangle"/>
<h:graphicImage value="/resources/image/transfer.svg"
styleClass="actionIconSmall" alt="Transfer Ownership"
title="Transfer Ownership" />
</p:commandLink>
</p:column>
</p:dataTable>
</td>
</tr>
</table>

<h:panelGrid columns="1" cellpadding="5" styleClass="buttonPanel">
<p:button value="Add Share..." styleClass="tightTextButton"
onclick="return startAddShare()"/>
</h:panelGrid>
<h:panelGrid columns="1" cellpadding="5" styleClass="buttonPanel">
<p:button value="Close" onclick="PF('ownershipDialog').hide(); return false;"/>
</h:panelGrid>
</p:dialog>

<p:confirmDialog global="true" responsive="true">
<p:commandButton value="No" type="button" styleClass="ui-confirmdialog-no ui-button-flat"/>
<p:commandButton value="Yes" type="button" styleClass="ui-confirmdialog-yes" />
</p:confirmDialog>

<p:dialog widgetVar="addShareDialog" modal="true" resizable="false"
header="Add Share">

<div class="instructions">
Enter the email address of the user that you want to share this instrument with.
</div>
<div style="text-align: center">
<p:inputText id="shareEmail" widgetVar="shareEmail" style="width: 325px"
value="#{instrumentListBean.shareEmail}"/>
<br/>
<p:message for="shareEmail" id="shareEmailMessage" widgetVar="shareEmailMessage"
style="margin-top: 5px"/>
</div>
<h:panelGrid columns="2" cellpadding="5" styleClass="buttonPanel">
<p:button value="Cancel"
onclick="PF('addShareDialog').hide(); return false;"/>
<p:button value="Add"
onclick="saveShare(); return false;"/>
</h:panelGrid>
</p:dialog>

<h:inputHidden id="ownershipInstrId" value="#{instrumentListBean.ownershipInstrId}"/>
<h:inputHidden id="shareId" value="#{instrumentListBean.shareId}"/>

<p:remoteCommand name="loadOwnership"
update="ownerInstrumentName ownerName sharedList"
process="ownershipInstrId" ajax="true"
action="#{instrumentListBean.noop}"
onsuccess="PF('ownershipDialog').show()"/>

<h:inputHidden id="shareAction" value="#{instrumentListBean.shareAction}"/>

<p:remoteCommand name="saveShare"
process="shareAction shareEmail shareId"
update="ownerName sharedList shareEmailMessage ajaxOK"
ajax="true" action="#{instrumentListBean.saveShare}"
oncomplete="shareSaveComplete()"/>

<p:remoteCommand name="transferOwnership"
process="shareId"
update="ownerName sharedList"
ajax="false" action="#{instrumentListBean.transferOwnership}"/>
</h:form>

<h:form id="instrumentListForm" method="post" accept-charset="utf8">
<p:dialog widgetVar="confirmDelete" modal="true" resizable="false"
closeable="false" header="Delete Instrument?">
Are you sure you want to delete the instrument
Expand Down Expand Up @@ -156,6 +230,16 @@
<h:graphicImage value="/resources/image/spacer.svg" alt="" title=""
styleClass="actionIconLarge"/>
</ui:fragment>
<ui:fragment rendered="#{instrument.canShare}">
<p:commandLink onclick="showOwnershipDialog(#{instrument.id})">
<h:graphicImage value="/resources/image/share.svg" alt="Ownership and Sharing"
title="Ownership and Sharing" styleClass="actionIconLarge"/>
</p:commandLink>
</ui:fragment>
<ui:fragment rendered="#{not instrument.canShare}">
<h:graphicImage value="/resources/image/spacer.svg" alt="" title=""
styleClass="actionIconLarge"/>
</ui:fragment>
<ui:fragment rendered="#{not instrumentListBean.hasDatasets(instrument.id)}">
<p:commandLink onclick="confirmInstrumentDelete(#{instrument.id}, '#{instrument.displayName}')">
<h:graphicImage value="/resources/image/trash.svg" alt="Delete instrument"
Expand Down
6 changes: 6 additions & 0 deletions WebApp/WebContent/resources/image/share.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions WebApp/WebContent/resources/image/transfer.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 77 additions & 0 deletions WebApp/WebContent/resources/script/instruments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const SHARE_ADD = 1;
const SHARE_REMOVE = -1;

$(document).ready(function() {
$(window).keydown(function(event){
if(event.keyCode == 13) {
event.preventDefault();
return false;
}
});
});

function goToStandards(id) {
$('#instrumentListForm\\:standardsInstrumentId').val(id);
$('#instrumentListForm\\:showStandardsLink').click();
return false;
}

function goToDiagnosticsQC(id) {
$('#instrumentListForm\\:diagnosticQCInstrumentId').val(id);
$('#instrumentListForm\\:diagnostQCLink').click();
return false;
}

function goToCalibrations(id) {
$('#instrumentListForm\\:calibrationsInstrumentId').val(id);
$('#instrumentListForm\\:showCalibrationsLink').click();
return false;
}

function goToCalculationCoefficients(id) {
$('#instrumentListForm\\:calculationCoefficientsInstrumentId').val(id);
$('#instrumentListForm\\:showCalculationCoefficientsLink').click();
return false;
}

function confirmInstrumentDelete(id, name) {
$('#instrumentListForm\\:deleteInstrumentId').val(id);
$('#deleteInstrumentName')[0].innerHTML = name;
PF('confirmDelete').show();
}

function deleteInstrument() {
$('#instrumentListForm\\:deleteInstrumentLink').click();
return false;
}

function showOwnershipDialog(id) {
$('#shareForm\\:ownershipInstrId').val(id);
loadOwnership(); // PF Remotecommand
return false;
}

function startAddShare() {
$('#shareForm\\:shareAction').val(SHARE_ADD);
PF('shareEmail').jq.val('');
PF('shareEmailMessage').jq.hide();
PF('addShareDialog').show();
return false;
}

function removeShare(id) {
$('#shareForm\\:shareAction').val(SHARE_REMOVE);
$('#shareForm\\:shareId').val(id);
saveShare(); // PF remoteCommand
}

function shareSaveComplete() {
if ($('#shareForm\\:ajaxOK').val() == 'true') {
PF('addShareDialog').hide();
}
}

function initTransferOwnership(id) {
$('#shareForm\\:shareId').val(id);
transferOwnership(); // PF remoteCommand
}
18 changes: 18 additions & 0 deletions WebApp/WebContent/resources/style/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,12 @@ table.fileDates {
height: 25px;
}

.actionIconSmall {
margin-top: 7px;
width: 20px;
height: 20px;
}

.actionIconLarge {
margin-right: 7px;
width: 30px;
Expand Down Expand Up @@ -835,6 +841,18 @@ table.dataTable > tbody > tr > .selected {
display: none;
}

/* Use this class on a p:dataTable to hide the column headers */
.tableNoHeader thead th {
border: none !important;
background: none !important;
padding: 0px !important;
}

/* Make table not default to 100% width */
.tableNarrow table {
width: auto !important;
}

@keyframes rowFlash {
0% {background-color: #fff;}
25% {background-color: #fd0;}
Expand Down
31 changes: 30 additions & 1 deletion WebApp/src/uk/ac/exeter/QuinCe/User/User.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package uk.ac.exeter.QuinCe.User;

import java.sql.Timestamp;
import java.util.Objects;

import uk.ac.exeter.QuinCe.utils.MissingParam;
import uk.ac.exeter.QuinCe.utils.MissingParamException;
Expand Down Expand Up @@ -269,7 +270,18 @@ public boolean isAdminUser() {
* @return {@code true} if this user can access the API; {@code false} if not
*/
public boolean isApiUser() {
return (permissions & BIT_API_USER) > 0;
return hasApiPermission(this.permissions);
}

/**
* Determine whether or not the specified permissions entry represents an API
* user.
*
* @return {@code true} if this permissions give access to the API;
* {@code false} if not
*/
public static boolean hasApiPermission(int permissionsEntry) {
return (permissionsEntry & BIT_API_USER) > 0;
}

/**
Expand All @@ -281,4 +293,21 @@ public boolean isApiUser() {
public boolean isApprovalUser() {
return (permissions & BIT_APPROVAL_USER) > 0;
}

@Override
public int hashCode() {
return Objects.hash(databaseId);
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
return databaseId == other.databaseId;
}
}
Loading
Loading