diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ProcessAlertWidget.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/AlertWidgetRenderer.java similarity index 94% rename from qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ProcessAlertWidget.java rename to qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/AlertWidgetRenderer.java index 0d0ccc0b..3fa50066 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ProcessAlertWidget.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/AlertWidgetRenderer.java @@ -40,9 +40,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData; ** - alertType - name of entry in AlertType enum (ERROR, WARNING, SUCCESS) ** - alertHtml - html to display inside the alert (other than its icon) *******************************************************************************/ -public class ProcessAlertWidget extends AbstractWidgetRenderer implements MetaDataProducerInterface +public class AlertWidgetRenderer extends AbstractWidgetRenderer implements MetaDataProducerInterface { - public static final String NAME = "ProcessAlertWidget"; + public static final String NAME = "AlertWidgetRenderer"; diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ChildRecordListRenderer.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ChildRecordListRenderer.java index af63d1ac..e8f654ed 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ChildRecordListRenderer.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ChildRecordListRenderer.java @@ -301,6 +301,9 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer } } + widgetData.setAllowRecordEdit(BooleanUtils.isTrue(ValueUtils.getValueAsBoolean(input.getQueryParams().get("allowRecordEdit")))); + widgetData.setAllowRecordDelete(BooleanUtils.isTrue(ValueUtils.getValueAsBoolean(input.getQueryParams().get("allowRecordDelete")))); + return (new RenderWidgetOutput(widgetData)); } catch(Exception e) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QValueFormatter.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QValueFormatter.java index 0758dc44..1e824b59 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QValueFormatter.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QValueFormatter.java @@ -28,7 +28,6 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -468,7 +467,8 @@ public class QValueFormatter { for(QFieldMetaData field : table.getFields().values()) { - if(field.getType().equals(QFieldType.BLOB)) + Optional fileDownloadAdornment = field.getAdornment(AdornmentType.FILE_DOWNLOAD); + if(fileDownloadAdornment.isPresent()) { ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // file name comes from: // @@ -478,20 +478,7 @@ public class QValueFormatter // - tableLabel primaryKey fieldLabel // // - and - if the FILE_DOWNLOAD adornment had a DEFAULT_EXTENSION, then it gets added (preceded by a dot) // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Optional fileDownloadAdornment = field.getAdornment(AdornmentType.FILE_DOWNLOAD); - Map adornmentValues = Collections.emptyMap(); - - if(fileDownloadAdornment.isPresent()) - { - adornmentValues = fileDownloadAdornment.get().getValues(); - } - else - { - /////////////////////////////////////////////////////// - // don't change blobs unless they are file-downloads // - /////////////////////////////////////////////////////// - continue; - } + Map adornmentValues = fileDownloadAdornment.get().getValues(); String fileNameField = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FIELD)); String fileNameFormat = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FORMAT)); @@ -542,7 +529,13 @@ public class QValueFormatter } } - record.setValue(field.getName(), "/data/" + table.getName() + "/" + primaryKey + "/" + field.getName() + "/" + fileName); + ///////////////////////////////////////////// + // if field type is blob, update its value // + ///////////////////////////////////////////// + if(QFieldType.BLOB.equals(field.getType())) + { + record.setValue(field.getName(), "/data/" + table.getName() + "/" + primaryKey + "/" + field.getName() + "/" + fileName); + } record.setDisplayValue(field.getName(), fileName); } } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/storage/StorageInput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/storage/StorageInput.java index 5407faeb..526c7c78 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/storage/StorageInput.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/storage/StorageInput.java @@ -31,6 +31,7 @@ import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput; public class StorageInput extends AbstractTableActionInput { private String reference; + private String contentType; @@ -74,4 +75,35 @@ public class StorageInput extends AbstractTableActionInput return (this); } + + + /******************************************************************************* + ** Getter for contentType + *******************************************************************************/ + public String getContentType() + { + return (this.contentType); + } + + + + /******************************************************************************* + ** Setter for contentType + *******************************************************************************/ + public void setContentType(String contentType) + { + this.contentType = contentType; + } + + + + /******************************************************************************* + ** Fluent setter for contentType + *******************************************************************************/ + public StorageInput withContentType(String contentType) + { + this.contentType = contentType; + return (this); + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/AlertData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/AlertData.java index 5c6567fb..b5cf3538 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/AlertData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/AlertData.java @@ -22,6 +22,9 @@ package com.kingsrook.qqq.backend.core.model.dashboard.widgets; +import java.util.List; + + /******************************************************************************* ** Model containing datastructure expected by frontend alert widget ** @@ -40,8 +43,10 @@ public class AlertData extends QWidgetData - private String html; - private AlertType alertType; + private String html; + private AlertType alertType; + private Boolean hideWidget = false; + private List bulletList; @@ -139,4 +144,66 @@ public class AlertData extends QWidgetData return (this); } + + + /******************************************************************************* + ** Getter for hideWidget + *******************************************************************************/ + public boolean getHideWidget() + { + return (this.hideWidget); + } + + + + /******************************************************************************* + ** Setter for hideWidget + *******************************************************************************/ + public void setHideWidget(boolean hideWidget) + { + this.hideWidget = hideWidget; + } + + + + /******************************************************************************* + ** Fluent setter for hideWidget + *******************************************************************************/ + public AlertData withHideWidget(boolean hideWidget) + { + this.hideWidget = hideWidget; + return (this); + } + + + + /******************************************************************************* + ** Getter for bulletList + *******************************************************************************/ + public List getBulletList() + { + return (this.bulletList); + } + + + + /******************************************************************************* + ** Setter for bulletList + *******************************************************************************/ + public void setBulletList(List bulletList) + { + this.bulletList = bulletList; + } + + + + /******************************************************************************* + ** Fluent setter for bulletList + *******************************************************************************/ + public AlertData withBulletList(List bulletList) + { + this.bulletList = bulletList; + return (this); + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/ChildRecordListData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/ChildRecordListData.java index 1e8403ee..e3917b7d 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/ChildRecordListData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/ChildRecordListData.java @@ -39,9 +39,13 @@ public class ChildRecordListData extends QWidgetData private QueryOutput queryOutput; private QTableMetaData childTableMetaData; + private String tableName; private String tablePath; private String viewAllLink; private Integer totalRows; + private Boolean disableRowClick = false; + private Boolean allowRecordEdit = false; + private Boolean allowRecordDelete = false; private boolean canAddChildRecord = false; private Map defaultValuesForNewChildRecords; @@ -352,4 +356,141 @@ public class ChildRecordListData extends QWidgetData return (this); } + + + /******************************************************************************* + ** Getter for tableName + *******************************************************************************/ + public String getTableName() + { + return (this.tableName); + } + + + + /******************************************************************************* + ** Setter for tableName + *******************************************************************************/ + public void setTableName(String tableName) + { + this.tableName = tableName; + } + + + + /******************************************************************************* + ** Fluent setter for tableName + *******************************************************************************/ + public ChildRecordListData withTableName(String tableName) + { + this.tableName = tableName; + return (this); + } + + + + /******************************************************************************* + ** Fluent setter for tablePath + *******************************************************************************/ + public ChildRecordListData withTablePath(String tablePath) + { + this.tablePath = tablePath; + return (this); + } + + + + /******************************************************************************* + ** Getter for disableRowClick + *******************************************************************************/ + public Boolean getDisableRowClick() + { + return (this.disableRowClick); + } + + + + /******************************************************************************* + ** Setter for disableRowClick + *******************************************************************************/ + public void setDisableRowClick(Boolean disableRowClick) + { + this.disableRowClick = disableRowClick; + } + + + + /******************************************************************************* + ** Fluent setter for disableRowClick + *******************************************************************************/ + public ChildRecordListData withDisableRowClick(Boolean disableRowClick) + { + this.disableRowClick = disableRowClick; + return (this); + } + + + + /******************************************************************************* + ** Getter for allowRecordEdit + *******************************************************************************/ + public Boolean getAllowRecordEdit() + { + return (this.allowRecordEdit); + } + + + + /******************************************************************************* + ** Setter for allowRecordEdit + *******************************************************************************/ + public void setAllowRecordEdit(Boolean allowRecordEdit) + { + this.allowRecordEdit = allowRecordEdit; + } + + + + /******************************************************************************* + ** Fluent setter for allowRecordEdit + *******************************************************************************/ + public ChildRecordListData withAllowRecordEdit(Boolean allowRecordEdit) + { + this.allowRecordEdit = allowRecordEdit; + return (this); + } + + + + /******************************************************************************* + ** Getter for allowRecordDelete + *******************************************************************************/ + public Boolean getAllowRecordDelete() + { + return (this.allowRecordDelete); + } + + + + /******************************************************************************* + ** Setter for allowRecordDelete + *******************************************************************************/ + public void setAllowRecordDelete(Boolean allowRecordDelete) + { + this.allowRecordDelete = allowRecordDelete; + } + + + + /******************************************************************************* + ** Fluent setter for allowRecordDelete + *******************************************************************************/ + public ChildRecordListData withAllowRecordDelete(Boolean allowRecordDelete) + { + this.allowRecordDelete = allowRecordDelete; + return (this); + } } + + + diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/CountingHash.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/CountingHash.java index 8db0e364..e3abc974 100755 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/CountingHash.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/CountingHash.java @@ -94,6 +94,29 @@ public class CountingHash extends AbstractMap alwaysMutable = new CountingHash<>(Map.of("A", 5)); + alwaysMutable.put("A", 25); + assertEquals(25, alwaysMutable.get("A")); + alwaysMutable.put("A"); + assertEquals(26, alwaysMutable.get("A")); + } + +} diff --git a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/s3/actions/S3StorageAction.java b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/s3/actions/S3StorageAction.java index 96c72bde..bbffb63d 100644 --- a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/s3/actions/S3StorageAction.java +++ b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/s3/actions/S3StorageAction.java @@ -58,7 +58,7 @@ public class S3StorageAction extends AbstractS3Action implements QStorageInterfa AmazonS3 amazonS3 = getS3Utils().getAmazonS3(); String fullPath = getFullPath(storageInput); - S3UploadOutputStream s3UploadOutputStream = new S3UploadOutputStream(amazonS3, backend.getBucketName(), fullPath); + S3UploadOutputStream s3UploadOutputStream = new S3UploadOutputStream(amazonS3, backend.getBucketName(), fullPath, storageInput.getContentType()); return (s3UploadOutputStream); } catch(Exception e) diff --git a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/s3/utils/S3UploadOutputStream.java b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/s3/utils/S3UploadOutputStream.java index f67493ee..2edfbbc3 100644 --- a/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/s3/utils/S3UploadOutputStream.java +++ b/qqq-backend-module-filesystem/src/main/java/com/kingsrook/qqq/backend/module/filesystem/s3/utils/S3UploadOutputStream.java @@ -53,6 +53,7 @@ public class S3UploadOutputStream extends OutputStream private final AmazonS3 amazonS3; private final String bucketName; private final String key; + private final String contentType; private byte[] buffer = new byte[5 * 1024 * 1024]; private int offset = 0; @@ -68,11 +69,12 @@ public class S3UploadOutputStream extends OutputStream ** Constructor ** *******************************************************************************/ - public S3UploadOutputStream(AmazonS3 amazonS3, String bucketName, String key) + public S3UploadOutputStream(AmazonS3 amazonS3, String bucketName, String key, String contentType) { this.amazonS3 = amazonS3; this.bucketName = bucketName; this.key = key; + this.contentType = contentType; } @@ -96,6 +98,13 @@ public class S3UploadOutputStream extends OutputStream *******************************************************************************/ private void uploadIfNeeded() { + ObjectMetadata objectMetadata = null; + if(this.contentType != null) + { + objectMetadata = new ObjectMetadata(); + objectMetadata.setContentType(this.contentType); + } + if(offset == buffer.length) { ////////////////////////////////////////// @@ -104,7 +113,8 @@ public class S3UploadOutputStream extends OutputStream if(initiateMultipartUploadResult == null) { LOG.info("Initiating a multipart upload", logPair("key", key)); - initiateMultipartUploadResult = amazonS3.initiateMultipartUpload(new InitiateMultipartUploadRequest(bucketName, key)); + initiateMultipartUploadResult = amazonS3.initiateMultipartUpload(new InitiateMultipartUploadRequest(bucketName, key, objectMetadata)); + uploadPartResultList = new ArrayList<>(); } @@ -115,7 +125,8 @@ public class S3UploadOutputStream extends OutputStream .withInputStream(new ByteArrayInputStream(buffer)) .withBucketName(bucketName) .withKey(key) - .withPartSize(buffer.length); + .withPartSize(buffer.length) + .withObjectMetadata(objectMetadata); uploadPartResultList.add(amazonS3.uploadPart(uploadPartRequest)); @@ -166,6 +177,13 @@ public class S3UploadOutputStream extends OutputStream return; } + ObjectMetadata objectMetadata = null; + if(this.contentType != null) + { + objectMetadata = new ObjectMetadata(); + objectMetadata.setContentType(this.contentType); + } + if(initiateMultipartUploadResult != null) { if(offset > 0) @@ -180,7 +198,8 @@ public class S3UploadOutputStream extends OutputStream .withInputStream(new ByteArrayInputStream(buffer, 0, offset)) .withBucketName(bucketName) .withKey(key) - .withPartSize(offset); + .withPartSize(offset) + .withObjectMetadata(objectMetadata); uploadPartResultList.add(amazonS3.uploadPart(uploadPartRequest)); } @@ -193,8 +212,12 @@ public class S3UploadOutputStream extends OutputStream } else { + if(objectMetadata == null) + { + objectMetadata = new ObjectMetadata(); + } + LOG.info("Putting object (non-multipart)", logPair("key", key), logPair("length", offset)); - ObjectMetadata objectMetadata = new ObjectMetadata(); objectMetadata.setContentLength(offset); PutObjectResult putObjectResult = amazonS3.putObject(bucketName, key, new ByteArrayInputStream(buffer, 0, offset), objectMetadata); } diff --git a/qqq-backend-module-filesystem/src/test/java/com/kingsrook/qqq/backend/module/filesystem/s3/utils/S3UploadOutputStreamTest.java b/qqq-backend-module-filesystem/src/test/java/com/kingsrook/qqq/backend/module/filesystem/s3/utils/S3UploadOutputStreamTest.java index 96d43bce..dc0a15b0 100644 --- a/qqq-backend-module-filesystem/src/test/java/com/kingsrook/qqq/backend/module/filesystem/s3/utils/S3UploadOutputStreamTest.java +++ b/qqq-backend-module-filesystem/src/test/java/com/kingsrook/qqq/backend/module/filesystem/s3/utils/S3UploadOutputStreamTest.java @@ -31,7 +31,7 @@ import org.junit.jupiter.api.Test; /******************************************************************************* - ** Unit test for S3UploadOutputStream + ** Unit test for S3UploadOutputStream *******************************************************************************/ class S3UploadOutputStreamTest extends BaseS3Test { @@ -57,11 +57,11 @@ class S3UploadOutputStreamTest extends BaseS3Test outputStream.write("\n]\n".getBytes(StandardCharsets.UTF_8)); outputStream.close(); - S3UploadOutputStream s3UploadOutputStream = new S3UploadOutputStream(getS3Utils().getAmazonS3(), bucketName, key); + S3UploadOutputStream s3UploadOutputStream = new S3UploadOutputStream(getS3Utils().getAmazonS3(), bucketName, key, null); s3UploadOutputStream.write(outputStream.toByteArray(), 0, 5 * 1024 * 1024); s3UploadOutputStream.write(outputStream.toByteArray(), 0, 3 * 1024 * 1024); s3UploadOutputStream.write(outputStream.toByteArray(), 0, 3 * 1024 * 1024); s3UploadOutputStream.close(); } -} \ No newline at end of file +}