Our app allows users to attach an uploaded document to a patient note, and since users tend to be … user-y … I’d like to add a confirmation step when clicking the upload button if there is already a document attached, to make sure they really want to overwrite it. I’m not seeing a way to do that; there’s no on-click event or anything.
Hello @jon.craig,
You can use FileUploadDialog
, which will open when you click an additional button. Then instead of the FileUploadField
component you will need to use the composite component - FileUploadField
(to display fileName),Button
(to upload file) and Button
(to clear field).
- XML descriptor
<hbox spacing="true">
<upload id="uploadField" showFileName="true" editable="false"/>
<button id="showUploadDialogBtn" caption="Upload"/>
<button id="clearBtn" caption="Clear"/>
</hbox>
- Screen controller
@Inject
private Dialogs dialogs;
@Inject
private Screens screens;
@Inject
private FileUploadingAPI fileUploadingAPI;
@Inject
private DataManager dataManager;
@Inject
private FileUploadField uploadField;
@Subscribe("showUploadDialogBtn")
protected void onShowUploadDialogBtnClick(Button.ClickEvent event) {
// check your permission here
if (uploadField.getValue() != null) {
dialogs.createOptionDialog(Dialogs.MessageType.CONFIRMATION)
.withCaption("Confirm")
.withMessage("Are you sure you want to overwrite the file?")
.withActions(
new DialogAction(DialogAction.Type.YES, Action.Status.PRIMARY)
.withHandler(actionPerformedEvent -> openFileUploadDialog()),
new DialogAction(DialogAction.Type.NO)
)
.show();
} else {
openFileUploadDialog();
}
}
protected void openFileUploadDialog() {
FileUploadDialog dialog = (FileUploadDialog) screens.create("fileUploadDialog", OpenMode.DIALOG);
dialog.addAfterCloseListener(afterCloseEvent -> {
UUID fileId = dialog.getFileId();
String fileName = dialog.getFileName();
if (fileId != null) {
File file = fileUploadingAPI.getFile(fileId);
FileDescriptor fileDescriptor = fileUploadingAPI.getFileDescriptor(fileId, fileName);
try {
fileUploadingAPI.putFileIntoStorage(fileId, fileDescriptor);
dataManager.commit(fileDescriptor);
uploadField.setValue(fileDescriptor);
} catch (FileStorageException e) {
throw new RuntimeException(e.getMessage());
}
}
});
screens.show(dialog);
}
@Subscribe("clearBtn")
public void onClearBtnClick(Button.ClickEvent event) {
uploadField.clear();
}
To use such a component in different screens, you can create a composite component by following the instructions in our documentation. You can find an example of using the FileUploadDialog
in our documentation.
Regards,
Gleb
That may work. How then does the reference to the uploaded file get stored into the patient note record? I don’t see anywhere that is wired up.
The openFileUploadDialog()
method sets the FileDescriptor
to the FileUploadField
using the uploadField.setValue(fileDescriptor)
function. Define dataContainer and property to FileUploadField
in XML to bind the field and the entity you need.
Then your XML descriptor might look something like this:
<hbox spacing="true">
<upload id="uploadField"
showFileName="true"
editable="false"
dataContainer="patientDc"
property="file"/>
<button id="showUploadDialogBtn" caption="Upload"/>
<button id="clearBtn" caption="Clear"/>
</hbox>
Regards,
Gleb
Ah, right, it’s all automatic. I’m still used to much, much more limited frameworks and environments.
This functionality in our existing system is several hundred lines of code, and it’s much more limited even still.
Thank you @durygin =)
Just a little note @durygin - the editable property of the FileUploadField is NOT on the property sheet in the designer. Bug/oversight?
Hmmm - another issue @durygin - the suggested solution seems to work fine (file uploads correctly, etc, etc) but when I go to save the record, I get “Unique constraint violation occurred (sys_file_pkey)” with the following in the log:
10:45:59.316 ERROR c.h.cuba.core.sys.ServiceInterceptor - Exception:
javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.3.6-cuba): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "sys_file_pkey"
Detail: Key (id)=(65246079-db62-46a2-97e4-6d5ef5e23e78) already exists.
Error Code: 0
Call: INSERT INTO SYS_FILE (ID, CREATE_DATE, CREATE_TS, CREATED_BY, DELETE_TS, DELETED_BY, EXT, NAME, FILE_SIZE, UPDATE_TS, UPDATED_BY, VERSION) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
bind => [65246079-db62-46a2-97e4-6d5ef5e23e78, 2020-02-06 10:45:43.443, 2020-02-06 10:45:59.255, admin, null, null, jpg, quad-and-tx.jpg, 1899678, 2020-02-06 10:45:59.255, null, 1]
Query: InsertObjectQuery(com.haulmont.cuba.core.entity.FileDescriptor-65246079-db62-46a2-97e4-6d5ef5e23e78 [new,managed])
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:979) ~[org.eclipse.persistence.jpa-2.7.3-6-cuba.jar:na]
at com.haulmont.cuba.core.sys.persistence.PersistenceImplSupport$ContainerResourceSynchronization.detachAll(PersistenceImplSupport.java:496) ~[cuba-core-7.1.3.jar:7.1.3]
at com.haulmont.cuba.core.sys.persistence.PersistenceImplSupport$ContainerResourceSynchronization.beforeCommit(PersistenceImplSupport.java:449) ~[cuba-core-7.1.3.jar:7.1.3]
at org.springframework.transaction.support.TransactionSynchronizationUtils.triggerBeforeCommit(TransactionSynchronizationUtils.java:96) ~[spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCommit(AbstractPlatformTransactionManager.java:922) ~[spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE]
So it seems something else in the framework is trying to save the file’s record again?
Interestingly, commenting out the dataManager.commit call makes everything work - no pkey exception and everything is stored correctly in the DB and in file storage.
Hi @jon.craig,
This is normal behavior, since a dataManager.commit(fileDescriprtor)
function call saves the file to the database. And then you try to save the same file a second time and therefore an exception is thrown. A fileUploadingAPI.putFileIntoStorage(fileId, fileDescriptor)
function call saves the file to FileStorage.
If you want to save the file yourself, then remove the dataManager.commit(fileDescriprtor)
function call (as described in your message above).
Regards,
Gleb
Well - I’m not saving it myself per se - this is in a standard edit screen. The screen itself is saving it (that second time), so removing the commit fixes the issue.
@jon.craig Try adding fileStoragePutMode="MANUAL"
attribute for FileUploadField
. This step should prevent the file from being saved again.
Regards,
Gleb
Is there a downside to letting the screen do it? That’s how I have it now (dm.commit removed) and it seems to be working fine. Is there something I should be aware of that makes that a bad idea?
Hello @jon.craig,
The fileStoragePutMode
attribute defines how the file and the corresponding FileDescriptor
are stored.
- In the
IMMEDIATE
mode it is done right after uploading file to the temporary storage of the client tier. - In the
MANUAL
mode, you should do it programmatically in aFileUploadSucceedListener
.
Setting an IMMEDIATE
value for fileStoragePutMode
attribute is equivalent to calling functions:
fileUploadingAPI.putFileIntoStorage(fileIdId, fileDescriptor);
dataManager.commit(fileDescriptor);
Regards,
Gleb