From ccbcf4b236b4d323a975178b3c4cc73228bbafa7 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Tue, 1 Oct 2024 22:04:32 +0300 Subject: [PATCH 01/76] Ad import export structures buttons --- .../AmpStructuresFormSectionFeature.html | 32 ++++---- .../AmpStructuresFormSectionFeature.java | 75 +++++++++++++++++++ 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index f6f56f4004f..b8df6cb789d 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -3,6 +3,12 @@ + + + + + + @@ -14,12 +20,12 @@ - Title - Description - Latitude + Title + Description + Latitude Longitude - Shape -   + Shape +   @@ -27,21 +33,21 @@ - +
- +
- -
+ +
-
- + + +
+
+ + +
+
+ + +
+
+ + + diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.java new file mode 100644 index 00000000000..841b2c664e6 --- /dev/null +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.java @@ -0,0 +1,52 @@ +package org.dgfoundation.amp.onepager.components.features; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.form.upload.FileUpload; +import org.apache.wicket.markup.html.form.upload.FileUploadField; +import org.apache.wicket.markup.html.panel.Panel; +import java.io.File; + +public class FileUploadModalPanel extends Panel { + + private FileUploadField fileUploadField; + + public FileUploadModalPanel(String id, ModalWindow modalWindow) { + super(id); + + // Create the form and mark it as multi-part for file uploads + Form form = new Form<>("uploadForm"); + form.setMultiPart(true); // Necessary for file upload + + // Add file upload field + fileUploadField = new FileUploadField("fileUploadField"); + form.add(fileUploadField); + + // Add AjaxSubmitLink to handle file upload via Ajax + form.add(new AjaxSubmitLink("uploadButton", form) { + protected void onSubmit(AjaxRequestTarget target) { + FileUpload upload = fileUploadField.getFileUpload(); + if (upload != null) { + try { + // Save the uploaded file or process it + File file = new File("/path/to/save/" + upload.getClientFileName()); + upload.writeTo(file); + System.out.println("File uploaded: " + upload.getClientFileName()); + } catch (Exception e) { + e.printStackTrace(); + } + } + // Close the modal window after successful upload + modalWindow.close(target); + } + + protected void onError(AjaxRequestTarget target) { + // Handle error scenario here + System.out.println("File upload failed"); + } + }); + + add(form); + } +} diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 01988f256ba..2ebf2bf651d 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -4,6 +4,7 @@ +
@@ -11,15 +12,7 @@ -
- -
+ diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index edae2d843f5..6b154517327 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -41,11 +41,13 @@ import org.dgfoundation.amp.onepager.components.PagingListNavigator; import org.dgfoundation.amp.onepager.components.features.CustomResourceLinkResourceLink; import org.dgfoundation.amp.onepager.components.features.ExportExcelResourceReference; +import org.dgfoundation.amp.onepager.components.features.FileUploadModalPanel; import org.dgfoundation.amp.onepager.components.features.tables.AmpLocationFormTableFeature; import org.dgfoundation.amp.onepager.components.fields.AmpAjaxLinkField; import org.dgfoundation.amp.onepager.components.fields.AmpTextAreaFieldPanel; import org.dgfoundation.amp.onepager.components.fields.AmpTextFieldPanel; import org.dgfoundation.amp.onepager.components.fields.LatAndLongValidator; +import org.dgfoundation.amp.onepager.components.upload.FileUploadPanel; import org.dgfoundation.amp.onepager.helper.structure.ColorData; import org.dgfoundation.amp.onepager.helper.structure.CoordinateData; import org.dgfoundation.amp.onepager.helper.structure.MapData; @@ -329,39 +331,18 @@ public void onClick(AjaxRequestTarget target) { addbutton.getButton().add(new AttributeModifier("class", new Model("addStructure button_green_btm"))); add(addbutton); - final ModalWindow uploadModal = new ModalWindow("uploadModal"); + final ModalWindow modalWindow = new ModalWindow("modalWindow"); + add(modalWindow); - final FileUploadField fileUploadField = new FileUploadField("fileUpload"); + // Set the content of the modal window to the FileUploadModalPanel + modalWindow.setContent(new FileUploadModalPanel(modalWindow.getContentId(), modalWindow)); - uploadModal.setContent(new WebMarkupContainer(uploadModal.getContentId()) { - @Override - protected void onInitialize() { - super.onInitialize(); - add(fileUploadField); - add(new Button("uploadButton") { - @Override - public void onSubmit() { - // Handle file upload and reading here - try { - handleFileUpload(fileUploadField.getFileUpload()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }); - } - }); - -// Add the modal to the page - add(uploadModal); AmpAjaxLinkField importStructures = new AmpAjaxLinkField("importStructures", "Import Structures", "Import Structures") { @Override public void onClick(AjaxRequestTarget target) { - + modalWindow.show(target); // Show the modal when clicked } }; - - importStructures.getButton().add(new AttributeModifier("class", new Model("importStructures button_red_btm"))); add(importStructures); ExportExcelResourceReference resourceReference = new ExportExcelResourceReference(); CustomResourceLinkResourceLink downloadLink = new CustomResourceLinkResourceLink<>("downloadLink",resourceReference ); From 5e96f3f16e3e03de98c0ebe07a390b5837da06ff Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 4 Oct 2024 10:55:31 +0300 Subject: [PATCH 05/76] Ad import export structures buttons --- .../AmpStructuresFormSectionFeature.html | 7 ++- .../AmpStructuresFormSectionFeature.java | 54 ++++++++++--------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 2ebf2bf651d..8d49116fb13 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -4,8 +4,11 @@ -
- + +
+ + +
Download diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 6b154517327..52190c0794a 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -7,47 +7,32 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; -import org.apache.poi.xssf.usermodel.XSSFRow; -import org.apache.poi.xssf.usermodel.XSSFSheet; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.behavior.AttributeAppender; -import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.markup.html.TransparentWebMarkupContainer; -import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.form.Button; +import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.form.upload.FileUpload; import org.apache.wicket.markup.html.form.upload.FileUploadField; -import org.apache.wicket.markup.html.link.ResourceLink; import org.apache.wicket.model.AbstractReadOnlyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; -import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler; -import org.apache.wicket.request.http.WebResponse; -import org.apache.wicket.request.resource.ContentDisposition; -import org.apache.wicket.request.resource.ResourceReference; -import org.apache.wicket.util.resource.AbstractResourceStreamWriter; -import org.apache.wicket.util.resource.IResourceStream; -import org.apache.wicket.validation.validator.RangeValidator; +import org.apache.wicket.util.lang.Bytes; import org.dgfoundation.amp.onepager.OnePagerUtil; import org.dgfoundation.amp.onepager.components.ListEditorRemoveButton; -import org.dgfoundation.amp.onepager.components.ListItem; import org.dgfoundation.amp.onepager.components.PagingListEditor; import org.dgfoundation.amp.onepager.components.PagingListNavigator; import org.dgfoundation.amp.onepager.components.features.CustomResourceLinkResourceLink; import org.dgfoundation.amp.onepager.components.features.ExportExcelResourceReference; -import org.dgfoundation.amp.onepager.components.features.FileUploadModalPanel; -import org.dgfoundation.amp.onepager.components.features.tables.AmpLocationFormTableFeature; import org.dgfoundation.amp.onepager.components.fields.AmpAjaxLinkField; import org.dgfoundation.amp.onepager.components.fields.AmpTextAreaFieldPanel; import org.dgfoundation.amp.onepager.components.fields.AmpTextFieldPanel; import org.dgfoundation.amp.onepager.components.fields.LatAndLongValidator; -import org.dgfoundation.amp.onepager.components.upload.FileUploadPanel; import org.dgfoundation.amp.onepager.helper.structure.ColorData; import org.dgfoundation.amp.onepager.helper.structure.CoordinateData; import org.dgfoundation.amp.onepager.helper.structure.MapData; @@ -331,19 +316,38 @@ public void onClick(AjaxRequestTarget target) { addbutton.getButton().add(new AttributeModifier("class", new Model("addStructure button_green_btm"))); add(addbutton); - final ModalWindow modalWindow = new ModalWindow("modalWindow"); - add(modalWindow); - // Set the content of the modal window to the FileUploadModalPanel - modalWindow.setContent(new FileUploadModalPanel(modalWindow.getContentId(), modalWindow)); + final FileUploadField fileUploadField = new FileUploadField("fileUpload"); - AmpAjaxLinkField importStructures = new AmpAjaxLinkField("importStructures", "Import Structures", "Import Structures") { + // create the form + final Form form = new Form("form") + { + private static final long serialVersionUID = 1L; @Override - public void onClick(AjaxRequestTarget target) { - modalWindow.show(target); // Show the modal when clicked + protected void onSubmit() + { + // display uploaded info + FileUpload upload = fileUploadField.getFileUpload(); + if (upload == null) + { + logger.info("No file uploaded"); + } + else + { + logger.info("File-Name: " + upload.getClientFileName() + " File-Size: " + + Bytes.bytes(upload.getSize()).toString()); + } } }; - add(importStructures); + form.setMultiPart(true); // Enable file upload + form.setMaxSize(Bytes.megabytes(10)); + form.add(fileUploadField); + + + Button importStructures = new Button("importStructures"); + importStructures.setOutputMarkupId(true); + form.add(importStructures); + add(form); ExportExcelResourceReference resourceReference = new ExportExcelResourceReference(); CustomResourceLinkResourceLink downloadLink = new CustomResourceLinkResourceLink<>("downloadLink",resourceReference ); downloadLink.setOutputMarkupId(true); From 26fd0d7eefa79ac228aa7b69d0a482912c447d82 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 4 Oct 2024 11:35:35 +0300 Subject: [PATCH 06/76] Ad import export structures buttons --- .../AmpStructuresFormSectionFeature.java | 62 ++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 52190c0794a..ad5144fd678 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -5,8 +5,14 @@ package org.dgfoundation.amp.onepager.components.features.sections; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -324,20 +330,43 @@ public void onClick(AjaxRequestTarget target) { { private static final long serialVersionUID = 1L; @Override - protected void onSubmit() - { + protected void onSubmit() { // display uploaded info FileUpload upload = fileUploadField.getFileUpload(); - if (upload == null) - { + if (upload == null) { logger.info("No file uploaded"); - } - else - { + } else { logger.info("File-Name: " + upload.getClientFileName() + " File-Size: " + - Bytes.bytes(upload.getSize()).toString()); + Bytes.bytes(upload.getSize())); + try { + XSSFWorkbook workbook = new XSSFWorkbook(upload.getInputStream()); + XSSFSheet sheet = workbook.getSheetAt(0); + Iterator rowIterator = sheet.iterator(); + rowIterator.next(); + + while (rowIterator.hasNext()) { + XSSFRow row = (XSSFRow) rowIterator.next(); + String title = getStringValueFromCell(row.getCell(0)); + String description = getStringValueFromCell(row.getCell(1)); + String latitude = getStringValueFromCell(row.getCell(2)); + String longitude = getStringValueFromCell(row.getCell(3)); + + AmpStructure stru = new AmpStructure(); + stru.setTitle(title); + stru.setDescription(description); + stru.setLatitude(latitude); + stru.setLongitude(longitude); + list.addItem(stru); + list.goToLastPage(); + } + + + } catch (IOException e) { + throw new RuntimeException(e); + } } } + }; form.setMultiPart(true); // Enable file upload form.setMaxSize(Bytes.megabytes(10)); @@ -370,6 +399,23 @@ public void onClick(AjaxRequestTarget target) { } + private static String getStringValueFromCell(Cell cell) { + if (cell == null) { + return null; + } + switch (cell.getCellType()) { + case Cell.CELL_TYPE_STRING: + return cell.getStringCellValue(); + case Cell.CELL_TYPE_NUMERIC: + return String.valueOf(cell.getNumericCellValue()); + case Cell.CELL_TYPE_BOOLEAN: + return String.valueOf(cell.getBooleanCellValue()); + case Cell.CELL_TYPE_FORMULA: + return cell.getCellFormula(); + default: + return null; + } + } public StructureData getDataFromStructureModel(IModel structureModel) { StructureData structureData = new StructureData(); From 8897dda4ace89598d08c05a1d4f78443c5e0536f Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 4 Oct 2024 22:29:16 +0300 Subject: [PATCH 07/76] Ad import export structures buttons --- .../AmpStructuresFormSectionFeature.html | 11 +- .../AmpStructuresFormSectionFeature.java | 109 ++++++++++++++++-- 2 files changed, 105 insertions(+), 15 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 8d49116fb13..0eca8515640 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -3,21 +3,20 @@ - - +
- +
- + Download - + - + diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index ad5144fd678..42491e25ef0 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -18,19 +18,26 @@ import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.behavior.AttributeAppender; +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.markup.html.TransparentWebMarkupContainer; import org.apache.wicket.markup.html.form.Button; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.form.upload.FileUpload; import org.apache.wicket.markup.html.form.upload.FileUploadField; +import org.apache.wicket.markup.html.link.ResourceLink; import org.apache.wicket.model.AbstractReadOnlyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.request.cycle.RequestCycle; +import org.apache.wicket.request.resource.AbstractResource; +import org.apache.wicket.request.resource.IResource; +import org.apache.wicket.request.resource.ResourceReference; import org.apache.wicket.util.lang.Bytes; import org.dgfoundation.amp.onepager.OnePagerUtil; import org.dgfoundation.amp.onepager.components.ListEditorRemoveButton; +import org.dgfoundation.amp.onepager.components.ListItem; import org.dgfoundation.amp.onepager.components.PagingListEditor; import org.dgfoundation.amp.onepager.components.PagingListNavigator; import org.dgfoundation.amp.onepager.components.features.CustomResourceLinkResourceLink; @@ -51,7 +58,9 @@ import org.digijava.module.categorymanager.util.CategoryConstants; import org.digijava.module.categorymanager.util.CategoryManagerUtil; +import javax.servlet.http.HttpServletResponse; import java.io.*; +import java.time.LocalDateTime; import java.util.*; public class AmpStructuresFormSectionFeature extends @@ -324,14 +333,13 @@ public void onClick(AjaxRequestTarget target) { final FileUploadField fileUploadField = new FileUploadField("fileUpload"); + fileUploadField.setOutputMarkupId(true); - // create the form final Form form = new Form("form") { private static final long serialVersionUID = 1L; @Override protected void onSubmit() { - // display uploaded info FileUpload upload = fileUploadField.getFileUpload(); if (upload == null) { logger.info("No file uploaded"); @@ -368,17 +376,56 @@ protected void onSubmit() { } }; - form.setMultiPart(true); // Enable file upload + form.setMultiPart(true); form.setMaxSize(Bytes.megabytes(10)); form.add(fileUploadField); Button importStructures = new Button("importStructures"); +// importStructures.add(new AttributeModifier("disabled", "true")); importStructures.setOutputMarkupId(true); form.add(importStructures); + fileUploadField.add(new AjaxFormComponentUpdatingBehavior("change") { + private static final long serialVersionUID = 1L; + + @Override + protected void onUpdate(AjaxRequestTarget target) { + importStructures.setEnabled(true); + importStructures.add(new AttributeModifier("disabled", (String) null)); // Enable the button + target.add(importStructures); + + } + }); + add(form); - ExportExcelResourceReference resourceReference = new ExportExcelResourceReference(); - CustomResourceLinkResourceLink downloadLink = new CustomResourceLinkResourceLink<>("downloadLink",resourceReference ); + + + ResourceReference resourceReference = new ResourceReference("exportData-"+ System.currentTimeMillis()) { + @Override + public IResource getResource() { + return new AbstractResource() { + @Override + protected ResourceResponse newResourceResponse(Attributes attributes) { + ResourceResponse response = new ResourceResponse(); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setFileName("exported-structures-activity-"+am.getObject().getAmpId()+".xlsx"); + response.disableCaching(); + response.setWriteCallback(new WriteCallback() { + @Override + public void writeData(Attributes attributes) { + try (OutputStream out = attributes.getResponse().getOutputStream()) { + writeExcelFile(out, list); + } catch (IOException e) { + logger.error("Error writing data to file", e); + } + } + }); + return response; + } + }; + } + }; + ResourceLink downloadLink = new ResourceLink<>("downloadLink", resourceReference); downloadLink.setOutputMarkupId(true); add(downloadLink); @@ -386,13 +433,12 @@ protected void onSubmit() { @Override public void onClick(AjaxRequestTarget target) { logger.info("Preparing to download data"); - resourceReference.setList(list); - downloadLink.setReference(resourceReference); - // Trigger the non-AJAX file download String downloadLinkMarkupId = downloadLink.getMarkupId(); - target.appendJavaScript("setTimeout(function() { document.getElementById('" + downloadLinkMarkupId + "').click(); }, 100);"); + target.add(list.getParent()); + target.appendJavaScript("document.getElementById('" + downloadLinkMarkupId + "').click();"); } }; + add(exportStructures); exportStructures.getButton().add(new AttributeModifier("class", new Model("exportStructures button_blue_btm"))); add(exportStructures); @@ -417,6 +463,51 @@ private static String getStringValueFromCell(Cell cell) { } } + private void writeExcelFile(OutputStream out,PagingListEditor list) throws IOException { + try(XSSFWorkbook workbook = new XSSFWorkbook()) { + // Create Excel sheet + XSSFSheet sheet = workbook.createSheet("Structures"); + + // Create header row + XSSFRow headerRow = sheet.createRow(0); + headerRow.createCell(0).setCellValue("Title"); + headerRow.createCell(1).setCellValue("Description"); + headerRow.createCell(2).setCellValue("Latitude"); + headerRow.createCell(3).setCellValue("Longitude"); + + int rowIndex = 1; + for (Component child : list) { + if (child instanceof ListItem) { + ListItem listItem = (ListItem) child; + AmpStructure structure = listItem.getModelObject(); + + // Create a new row for each structure + XSSFRow row = sheet.createRow(rowIndex); + if (structure != null) { + createCellIfNotNull(row, 0, structure.getTitle()); + createCellIfNotNull(row, 1, structure.getDescription()); + createCellIfNotNull(row, 2, structure.getLatitude()); + createCellIfNotNull(row, 3, structure.getLongitude()); + } + rowIndex++; + } + } + + // Write workbook content to the output stream + workbook.write(out); + + // Respond with the written content + } catch (IOException e) { + logger.error("Error exporting data to Excel", e); + } + } + + private void createCellIfNotNull(XSSFRow row, int columnIndex, Object value) { + if (value != null) { + row.createCell(columnIndex).setCellValue(value.toString()); + } + } + public StructureData getDataFromStructureModel(IModel structureModel) { StructureData structureData = new StructureData(); AmpStructure structure = structureModel.getObject(); From 35e7f5587f84b0552172a32d87d0491eabe5f67a Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 6 Oct 2024 21:53:57 +0300 Subject: [PATCH 08/76] Add import export structures buttons --- .../features/sections/AmpStructuresFormSectionFeature.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 0eca8515640..2a722a42aed 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -1,19 +1,20 @@ - - - +

From d13e4a0142f1b9861dbcb0afa66a2680aea5fb04 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 14:30:21 +0300 Subject: [PATCH 10/76] GGW-369: Resolve project saving ajax issue --- amp/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amp/pom.xml b/amp/pom.xml index 2be3156cdb3..8ae0d098824 100644 --- a/amp/pom.xml +++ b/amp/pom.xml @@ -7,7 +7,7 @@ war Aid Management Platform - 6.29.0 + 6.30.0 8.5.73 5.6.11 5.3.28 @@ -1762,4 +1762,4 @@ Development Gateway http://developmentgateway.org - \ No newline at end of file + From e35889cb133591c8b0b5b2b1d65836bdc857d294 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 14:52:54 +0300 Subject: [PATCH 11/76] GGW-369: Resolve project saving ajax issue --- .../amp/onepager/OnePagerApp.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/OnePagerApp.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/OnePagerApp.java index 551aa6d1370..9cab82dd787 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/OnePagerApp.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/OnePagerApp.java @@ -41,7 +41,7 @@ * @author mihai */ public class OnePagerApp extends AuthenticatedWebApplication { - + public static boolean IS_DEVELOPMENT_MODE = false; private static Logger logger = Logger.getLogger(OnePagerApp.class); @@ -79,19 +79,19 @@ public void init() { //TODO:1.5 //TODO: /* - * - if (true) { + * + if (true) { ResourceMount.mountWicketResources("script", this); ResourceMount mount = new ResourceMount(); //.setResourceVersionProvider(new RevisionVersionProvider()); - - + + LinkedList csslist = new LinkedList(); //csslist.add(new ResourceSpec(YuiLib.class, "calendar/assets/skins/sam/calendar.css")); //csslist.add(new ResourceSpec(new ResourceReference("TEMPLATE/ampTemplate/css_2/amp-wicket.css"))); - - + + LinkedList jslist = new LinkedList(); jslist.add(new ResourceSpec(JQueryBehavior.class, JQueryBehavior.JQUERY_FILE_NAME)); //jslist.add(new ResourceSpec(AutoCompleteBehavior.class, "wicket-autocomplete.js")); @@ -99,8 +99,8 @@ public void init() { jslist.add(new ResourceSpec(IHeaderContributor.class, "wicket-event.js")); jslist.add(new ResourceSpec(AmpSubsectionFeaturePanel.class, "subsectionSlideToggle.js")); jslist.add(new ResourceSpec(AmpStructuresFormSectionFeature.class, "gisPopup.js")); -// jslist.add(new ResourceSpec(YuiLib.class, "yahoo/yahoo-min.js")); -// jslist.add(new ResourceSpec(YuiLib.class, "yahoodomevent/yahoo-dom-event.js")); +// jslist.add(new ResourceSpec(YuiLib.class, "yahoo/yahoo-min.js")); +// jslist.add(new ResourceSpec(YuiLib.class, "yahoodomevent/yahoo-dom-event.js")); // jslist.add(new ResourceSpec(YuiLib.class, "yuiloader.js")); //can't use the min version, because the normal one will be included too // jslist.add(new ResourceSpec(YuiLib.class, "calendar/calendar-min.js")); // jslist.add(new ResourceSpec(DatePicker.class, "wicket-date.js")); @@ -108,12 +108,12 @@ public void init() { jslist.add(new ResourceSpec(AmpAjaxBehavior.class, "translationsOnDocumentReady.js")); jslist.add(new ResourceSpec(AmpActivityFormFeature.class, "previewLogframe.js")); jslist.add(new ResourceSpec(AmpActivityFormFeature.class, "saveNavigationPanel.js")); - + mount.clone() .setPath("/style/all-23.css") .addResourceSpecs(csslist) .mount(this); - + mount.clone() .setPath("/style/all-2.js") .addResourceSpecs(jslist) @@ -221,14 +221,15 @@ protected WebResponse newWebResponse(WebRequest webRequest, HttpServletResponse return new ServletWebResponse((ServletWebRequest) webRequest, httpServletResponse) { @Override public void flush() { + // Add the X-Frame-Options header + getContainerResponse().addHeader("X-Frame-Options", "SAMEORIGIN"); try { getContainerResponse().flushBuffer(); } catch (SocketException e) { logger.warn("Socket exception encountered, ignoring", e); } catch (IOException e) { - // Socket Exception can be wrapped by a container specific exception. - // So we check the cause of the container exception - Throwable rootCause = null != e.getCause() ? e.getCause() : e; + // Check if the root cause is a SocketException + Throwable rootCause = (e.getCause() != null) ? e.getCause() : e; if (rootCause instanceof SocketException) { logger.warn("Socket exception encountered, ignoring.", rootCause); return; From b14ca23c88c3e6fd43687431247e57191468c502 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 15:07:23 +0300 Subject: [PATCH 12/76] GGW-369: Resolve project saving ajax issue --- .../src/org/dgfoundation/amp/onepager/OnePagerApp.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/OnePagerApp.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/OnePagerApp.java index 9cab82dd787..623da7c8065 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/OnePagerApp.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/OnePagerApp.java @@ -218,11 +218,14 @@ public void setAuthenticationManager(AuthenticationManager authenticationManager @Override protected WebResponse newWebResponse(WebRequest webRequest, HttpServletResponse httpServletResponse) { + httpServletResponse.addHeader( "X-Frame-Options", "SAMEORIGIN" ); + logger.info("Headers"+ httpServletResponse.getHeaderNames()+httpServletResponse.getHeader("X-Frame-Options")); + return new ServletWebResponse((ServletWebRequest) webRequest, httpServletResponse) { + @Override public void flush() { // Add the X-Frame-Options header - getContainerResponse().addHeader("X-Frame-Options", "SAMEORIGIN"); try { getContainerResponse().flushBuffer(); } catch (SocketException e) { From 23f05c2da1386dce22b8fd19f76e6a200ab3c629 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 15:23:28 +0300 Subject: [PATCH 13/76] GGW-369: Resolve project saving ajax issue --- .../amp/onepager/OnePagerApp.java | 4 +-- .../amp/onepager/XFrameOptionsFilter.java | 30 +++++++++++++++++++ amp/WEB-INF/web.xml | 8 +++++ 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/OnePagerApp.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/OnePagerApp.java index 623da7c8065..903bff8ed32 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/OnePagerApp.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/OnePagerApp.java @@ -73,6 +73,7 @@ public void init() { IS_DEVELOPMENT_MODE = true; } + //getResourceSettings().setStripJavaScriptCommentsAndWhitespace(true); //getResourceSettings().setAddLastModifiedTimeToResourceReferenceUrl(true); //TODO: @@ -218,9 +219,6 @@ public void setAuthenticationManager(AuthenticationManager authenticationManager @Override protected WebResponse newWebResponse(WebRequest webRequest, HttpServletResponse httpServletResponse) { - httpServletResponse.addHeader( "X-Frame-Options", "SAMEORIGIN" ); - logger.info("Headers"+ httpServletResponse.getHeaderNames()+httpServletResponse.getHeader("X-Frame-Options")); - return new ServletWebResponse((ServletWebRequest) webRequest, httpServletResponse) { @Override diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java new file mode 100644 index 00000000000..5cf3e84017e --- /dev/null +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java @@ -0,0 +1,30 @@ +package org.dgfoundation.amp.onepager; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class XFrameOptionsFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) throws ServletException { + // Initialization code if needed + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.setHeader("X-Frame-Options", "SAMEORIGIN"); // or "SAMEORIGIN" + chain.doFilter(request, response); + } + + @Override + public void destroy() { + // Cleanup code if needed + } +} diff --git a/amp/WEB-INF/web.xml b/amp/WEB-INF/web.xml index 2b6c2ee5a96..726c3d74c18 100644 --- a/amp/WEB-INF/web.xml +++ b/amp/WEB-INF/web.xml @@ -13,6 +13,14 @@ 5 + + XFrameOptionsFilter + org.dgfoundation.amp.onepager.XFrameOptionsFilter + + + XFrameOptionsFilter + /wicket/onepager/* + javamelody net.bull.javamelody.MonitoringFilter From b5c2f772dd72f20d0ea117f0866dabd3e86ed885 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 15:33:29 +0300 Subject: [PATCH 14/76] GGW-369: Resolve project saving ajax issue --- .../org/dgfoundation/amp/onepager/XFrameOptionsFilter.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java index 5cf3e84017e..d8be1a1f19a 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java @@ -19,7 +19,10 @@ public void init(FilterConfig filterConfig) throws ServletException { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse httpResponse = (HttpServletResponse) response; - httpResponse.setHeader("X-Frame-Options", "SAMEORIGIN"); // or "SAMEORIGIN" + httpResponse.setHeader("Access-Control-Allow-Origin", "https://amp-ggw-pr-4349.stg.ampsite.net"); + httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); + httpResponse.setHeader("Access-Control-Max-Age", "3600"); + httpResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with"); chain.doFilter(request, response); } From cddc9f2219bae6bd9037aa01ed55831156951283 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 15:50:07 +0300 Subject: [PATCH 15/76] GGW-369: Resolve project saving ajax issue --- .../src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java | 1 + amp/WEB-INF/web.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java index d8be1a1f19a..410e6df2e5a 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java @@ -23,6 +23,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); httpResponse.setHeader("Access-Control-Max-Age", "3600"); httpResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with"); + httpResponse.setHeader("X-Frame-Options", "SAMEORIGIN"); chain.doFilter(request, response); } diff --git a/amp/WEB-INF/web.xml b/amp/WEB-INF/web.xml index 726c3d74c18..dd58e06a66d 100644 --- a/amp/WEB-INF/web.xml +++ b/amp/WEB-INF/web.xml @@ -19,7 +19,7 @@ XFrameOptionsFilter - /wicket/onepager/* + /wicket/* javamelody From a7d570d0e638eb5dcef6d0a7d5802262ac88227f Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 16:39:13 +0300 Subject: [PATCH 16/76] GGW-369: Resolve project saving ajax issue --- .../amp/onepager/XFrameOptionsFilter.java | 5 +- .../features/AmpActivityFormFeature.java | 203 +++++++++--------- amp/WEB-INF/web.xml | 16 +- 3 files changed, 112 insertions(+), 112 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java index 410e6df2e5a..8f4463db1f4 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/XFrameOptionsFilter.java @@ -19,10 +19,7 @@ public void init(FilterConfig filterConfig) throws ServletException { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse httpResponse = (HttpServletResponse) response; - httpResponse.setHeader("Access-Control-Allow-Origin", "https://amp-ggw-pr-4349.stg.ampsite.net"); - httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); - httpResponse.setHeader("Access-Control-Max-Age", "3600"); - httpResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with"); + httpResponse.setHeader("Access-Control-Allow-Origin", "*"); httpResponse.setHeader("X-Frame-Options", "SAMEORIGIN"); chain.doFilter(request, response); } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/AmpActivityFormFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/AmpActivityFormFeature.java index a8d4a74a264..fb2e50a7600 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/AmpActivityFormFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/AmpActivityFormFeature.java @@ -33,6 +33,7 @@ import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.request.flow.RedirectToUrlException; +import org.apache.wicket.request.http.WebResponse; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.request.resource.PackageResourceReference; import org.apache.wicket.util.time.Duration; @@ -94,7 +95,7 @@ * @since Jun 7, 2011 */ public class AmpActivityFormFeature extends AmpFeaturePanel { - + private static final long serialVersionUID = 1L; protected Form activityForm; private static final Integer GO_TO_DESKTOP=1; @@ -116,13 +117,13 @@ public ListView getFeatureList() { TranslatorUtil.getTranslatedText("The sum of disbursements is greater than the sum of commitments"); private static final String EXPENDITURES_BIGGER_ERROR = TranslatorUtil.getTranslatedText("The sum of expenditures is greater than the sum of disbursements"); - + /** - * Toggles the validation of semantic validators. + * Toggles the validation of semantic validators. * @param enabled whether these validators are enabled * @param form the form to set the validators - * @param target + * @param target * @see AmpSemanticValidatorField * @see AmpSemanticValidator */ @@ -143,7 +144,7 @@ public void component(AmpPercentageTextField ifs, //target.add(ifs); } }); - + // visit all the semantic validator fields and enable/disable them form.visitChildren(AmpSemanticValidatorField.class, new IVisitor, Void>() { @@ -167,8 +168,8 @@ public void component(AmpSemanticValidatorField ifs, public void component( AmpIdentificationFormSectionFeature ifs, IVisit visit) { - toggleFormComponent (enabled,target,ifs,visit); - toggleFormRichTextComponent(enabled,target, ifs,visit); + toggleFormComponent (enabled,target,ifs,visit); + toggleFormRichTextComponent(enabled,target, ifs,visit); } }); @@ -200,16 +201,16 @@ public void component( IVisit visit) { /* Validator for date will make the date field mandatory only when there is an amount entered * in proposed project code field AMP-17234 - * Review to make is more readable + * Review to make is more readable */ AmpDatePickerFieldPanel proposedDate= null; - + //if it is saving as draft, disable required validator for proposed date if (!enabled) { proposedDate = (AmpDatePickerFieldPanel) component.get("funDate"); } //if proposed amount is not entered then, disable required validator for proposed Date - else if(component.getParent().getParent().getId().equalsIgnoreCase("funDate") && + else if(component.getParent().getParent().getId().equalsIgnoreCase("funDate") && component.getParent().getParent().getParent().get("amount").getDefaultModel().getObject()==null){ proposedDate = (AmpDatePickerFieldPanel)component.getParent().getParent(); } @@ -218,11 +219,11 @@ else if(component.getParent().getParent().getId().equalsIgnoreCase("funDate") && String js = String.format("$('#%s').change();",proposedDate.getDate().getMarkupId()); target.appendJavaScript(js); } - + } }); - - + + } private void toggleFormComponent( @@ -244,7 +245,7 @@ private void toggleFormComponent (boolean enabled, final AjaxRequestTarget targe AmpRequiredComponentContainer ifs, IVisit visit) { toggleFormComponent(enabled, target, ifs, visit, true); } - + private void toggleFormComponent (boolean enabled, final AjaxRequestTarget target, AmpRequiredComponentContainer ifs, IVisit visit, boolean stopVisit) { List > requiredComponents = ifs.getRequiredFormComponents(); @@ -267,41 +268,41 @@ private void toggleFormComponent (boolean enabled, final AjaxRequestTarget targe * @param id * @param am * @param fmName - * @param newActivity - * @param listModel + * @param newActivity + * @param listModel * @throws Exception */ public AmpActivityFormFeature(String id, final IModel am, String fmName, final boolean newActivity, AbstractReadOnlyModel> listModel) throws Exception { super(id, am, fmName, true); - + this.enabledFmButton.setVisible(false); this.visibleFmButton.setVisible(false); this.ignoreFmVisibility = true; this.ignoreFmButtonsVisibility = true; this.setVisible(true); - - activityForm=new Form("activityForm") { + + activityForm=new Form("activityForm") { @Override protected void onError() { super.onError(); } }; activityForm.setOutputMarkupId(true); - + String actNameStr = am.getObject().getName(); if (actNameStr != null && !actNameStr.trim().isEmpty()) { actNameStr = "(" + actNameStr + ")"; } Label activityName = new Label("activityName", actNameStr); add(activityName); - + final FeedbackPanel feedbackPanel = new FeedbackPanel("feedbackPanel"); feedbackPanel.setOutputMarkupPlaceholderTag(true); feedbackPanel.setOutputMarkupId(true); //this will be use to decorate all submit buttons AttributePrepender isSubmit = new AttributePrepender("data-is_submit", new Model("true"), ""); - + //do not show errors in this feedbacklabel (they will be shown for each component) int[] filteredErrorLevels = new int[]{FeedbackMessage.ERROR}; feedbackPanel.setFilter(new ErrorLevelsFeedbackMessageFilter(filteredErrorLevels)); @@ -382,8 +383,8 @@ public void onClick(AjaxRequestTarget target) { //add ajax submit button final AmpButtonField saveAndSubmit = new AmpButtonField("saveAndSubmit","Save and Submit", AmpFMTypes.MODULE, true) { - - + + @Override protected void onSubmit(final AjaxRequestTarget target, Form form) { processAndUpdateForm(true, am, form, target, this.getButton()); @@ -435,7 +436,7 @@ protected void onSubmit(final AjaxRequestTarget target, Form form) { //we only remove disable on buttons tagged as submit ones //target.appendJavaScript("enableButtons2();"); } - + @Override protected void onError(final AjaxRequestTarget target, Form form) { @@ -451,18 +452,18 @@ protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { generateEnableButtonsOnError(attributes); } }; - - - + + + AttributePrepender updateEditors = new AttributePrepender("onclick", new Model("window.onbeforeunload = null; for (instance in CKEDITOR.instances) CKEDITOR.instances[instance].updateElement();disableButton();"), ""); saveAndSubmit.getButton().add(isSubmit); - + AttributePrepender closeEditors = new AttributePrepender("onclick", new Model("$('a[data-is_close=true]:visible').click();"), ""); AttributePrepender clickMonEval = new AttributePrepender("onclick", new Model("$('.mon_eval_button:visible').click();"), ""); AttributePrepender closeDialogs = new AttributePrepender("onclick", new Model( "$('.ui-dialog-content').dialog('close');"), ""); - + saveAndSubmit.getButton().add(new AttributeModifier("class", new Model("sideMenuButtons"))); saveAndSubmit.getButton().add(updateEditors); saveAndSubmit.getButton().add(closeEditors); @@ -471,16 +472,16 @@ protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { saveAndSubmit.getButton().setDefaultFormProcessing(false); saveAndSubmit.setAffectedByFreezing(false); activityForm.add(saveAndSubmit); - + AmpAjaxLinkField rejectActivityLink = new AmpAjaxLinkField("saveReject", "Reject Activity", "Reject activity") { @Override protected void onClick(AjaxRequestTarget target) { } - + @Override protected void onBeforeRender() { super.onBeforeRender(); - + AmpAuthWebSession wicketSession = (AmpAuthWebSession) org.apache.wicket.Session.get(); AmpTeamMember ampCurrentMember = wicketSession.getAmpCurrentMember(); this.setVisible(ActivityUtil.canReject(ampCurrentMember, am.getObject().getDraft(), newActivity)); @@ -495,15 +496,17 @@ protected void onBeforeRender() { rejectActivityLink.getButton().add(new AttributeModifier("class", new Model<>("sideMenuButtons rejectButton"))); activityForm.add(rejectActivityLink); - + AmpAjaxLinkField saveAsDraft = new AmpAjaxLinkField("saveAsDraft", "Save as Draft", "Save as Draft") { @Override protected void onClick(AjaxRequestTarget target) { + WebResponse response = (WebResponse)getRequestCycle().getResponse(); + response.setHeader("Access-Control-Allow-Origin", "*"); } }; - + saveAsDraft.getButton().add(isSubmit); - + // saveAsDraft.getButton().add(new AttributeModifier("onclick", "showDraftPanel();disableButton();")); saveAsDraft.getButton().add(new AttributePrepender("onclick", new Model("showDraftPanel();disableButton();"), "")); saveAsDraft.getButton().add(closeEditors); @@ -523,7 +526,7 @@ public void renderHead(Component component, IHeaderResponse response) { response.render(JavaScriptHeaderItem.forReference(new PackageResourceReference(AmpActivityFormFeature.class, "enableDisableButtons.js"))); } }); - + final RadioGroup myDraftOpts = new RadioGroup("draftRedirectedGroup", new Model(GO_TO_DESKTOP)); Radio radioDesktop=new Radio("draftRedirectedDesktop", new Model(GO_TO_DESKTOP)); @@ -551,12 +554,12 @@ protected void onEvent(final AjaxRequestTarget target) { myDraftOpts.add(radioStay); activityForm.add(myDraftOpts); - + final AmpAjaxLinkField cancelLink = new AmpAjaxLinkField("saveAsDraftCanceld", "Cancel", "Cancel") { @Override protected void onClick(AjaxRequestTarget target) { // TODO Auto-generated method stub - + } }; cancelLink.getButton().add(new AttributeModifier("onclick", "hideDraftPanel();enableButtons2();")); @@ -573,7 +576,7 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { onSaveAsDraft(am, feedbackPanel, redirected, new ActionButtonCancelLink(this, cancelLink), target, form); } - + @Override protected void onError(final AjaxRequestTarget target, Form form) { super.onError(target, form); @@ -596,13 +599,13 @@ protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { saveAsDraftAction.add(isSubmit); saveAsDraftAction.setAffectedByFreezing(false); activityForm.add(saveAsDraftAction); - + //text area for the message AmpTextAreaFieldPanel rejectMessage = new AmpTextAreaFieldPanel("rejectMessage", new PropertyModel(am,"rejectMessage"), "Reject Message", false); activityForm.add(rejectMessage); //buttons for the reject activity panel //cancelrejectActivity - + AmpAjaxLinkField cancelRejectActivity=new AmpAjaxLinkField("cancelRejectActivity", "Cancel Reject activity", "Cancel") { @Override protected void onClick(AjaxRequestTarget target) { @@ -614,8 +617,8 @@ protected void onClick(AjaxRequestTarget target) { cancelRejectActivity.setVisible(true); cancelRejectActivity.getButton().add(new AttributeModifier("class", new Model("sideMenuButtons"))); activityForm.add(cancelRejectActivity); - - + + AmpButtonField rejectActivityAction = new AmpButtonField("rejectActivityAction", "Reject Activity", AmpFMTypes.MODULE, true) { @Override protected void onSubmit(AjaxRequestTarget target, Form form) { @@ -627,7 +630,7 @@ protected void onError(final AjaxRequestTarget target, Form form) { super.onError(target, form); onErrorSaveAsDraftOrReject(feedbackPanel, target, form); } - + @Override protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { generateEnableButtonsOnError(attributes); @@ -639,13 +642,13 @@ protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { rejectActivityAction.getButton().add(new AttributePrepender("onclick", new Model("disableButton();"), "")); rejectActivityAction.getButton().add(updateEditors); rejectActivityAction.add(isSubmit); - activityForm.add(rejectActivityAction); - - - - - - + activityForm.add(rejectActivityAction); + + + + + + // this div will be "submitted" by the autoSaveTimer final WebMarkupContainer autoSaveDiv = new WebMarkupContainer( "autoSaveDiv"); @@ -663,8 +666,8 @@ protected void onSubmit(AjaxRequestTarget target) { op.getEditLockRefresher().setEnabled(false); if (op.getTimer() != null) { op.getTimer().stop(target); - } - + } + am.setObject(am.getObject()); toggleSemanticValidation(false, activityForm, target); // process the form for this request @@ -686,7 +689,7 @@ protected void onSubmit(AjaxRequestTarget target) { autoSaveTimer.restart(target); } } - + } @@ -703,7 +706,7 @@ public boolean getDefaultProcessing() { // will fire int autoSaveSeconds = Integer.parseInt(FeaturesUtil .getGlobalSettingValue(GlobalSettingsConstants.ACTIVITY_AUTO_SAVE_SECONDS)); - + autoSaveTimer = null; if (autoSaveSeconds != 0) { autoSaveTimer = new AbstractAjaxTimerBehavior( @@ -716,8 +719,8 @@ protected void onTimer(AjaxRequestTarget target) { op.getEditLockRefresher().setEnabled(false); if (op.getTimer() != null) { op.getTimer().stop(target); - } - //we disable the stop timer so it doesn't get called after its being processed + } + //we disable the stop timer so it doesn't get called after its being processed this.stop(target); target.appendJavaScript(String.format("$('#%s').click()", autoSaveDiv.getMarkupId())); @@ -748,7 +751,7 @@ protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { logframe.getButton().add(new AttributeModifier("class", true, new Model("sideMenuButtons"))); */ //activityForm.add(logframe); - + AmpButtonField preview = new AmpButtonField("preview", "Preview", AmpFMTypes.MODULE, true) { @Override protected void onSubmit(AjaxRequestTarget target, Form form) { @@ -762,7 +765,7 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { + am.getObject().getAmpActivityId() + "~isPreview=1\");"); } } - + @Override protected void onError(AjaxRequestTarget target, Form form) { super.onError(target, form); @@ -784,7 +787,7 @@ protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { preview.getButton().add(isSubmit); preview.setAffectedByFreezing(false); activityForm.add(preview); - + featureList = new ListView("featureList", listModel) { private static final long serialVersionUID = 7218457979728871528L; @Override @@ -796,7 +799,7 @@ protected void populateItem(final ListItem item) { tmp.add(new AttributeModifier("style", "font-size: medium; font-style: bold; color: red; margin: 15px;")); item.add(tmp); } - + String activityFormOnePager = FeaturesUtil.getGlobalSettingValue( GlobalSettingsConstants.ACTIVITY_FORM_ONE_PAGER); if ("false".equals(activityFormOnePager)){ @@ -804,7 +807,7 @@ protected void populateItem(final ListItem item) { item.add(new AttributeModifier("style", "display: none;")); } } - + } }; featureList.setReuseItems(true); @@ -828,7 +831,7 @@ private void processAndUpdateForm(boolean notDraft, IModel a toggleSemanticValidation(notDraft, form, target); form.process(button); - + form.visitChildren(AmpAgreementItemPanel.class,new IVisitor() { @Override @@ -837,7 +840,7 @@ public void component(AmpAgreementItemPanel object, object.validateIsNewAgreementFormClosed(target); visit.dontGoDeeper(); } - + }); form.visitChildren(AbstractTextComponent.class, @@ -846,7 +849,7 @@ public void component(AmpAgreementItemPanel object, public void component(Component component, IVisit objectIVisit) { IModel model = component.getDefaultModel(); AbstractTextComponent atc = (AbstractTextComponent) component; - //logger.error(component.getParent().getId()); + //logger.error(component.getParent().getId()); boolean required = false; List validators = atc.getValidators(); for (IValidator validator : validators) { @@ -1026,7 +1029,7 @@ private double sumUp(Collection collection, int transactionType, Object parent){ return total; } - + protected void formSubmitErrorHandle(Form form, final AjaxRequestTarget target, FeedbackPanel feedbackPanel) { //once an error has happened if in tabsview we process the funding section to //highlight the tabs with errors @@ -1034,8 +1037,8 @@ protected void formSubmitErrorHandle(Form form, final AjaxRequestTarget targe showFundingTabsErrors(form, target); } // visit form children and add to the ajax request the invalid ones - - + + form.visitChildren(FormComponent.class, new IVisitor() { @Override public void component(FormComponent component, IVisit visit) { @@ -1092,7 +1095,7 @@ public void component(FormComponent component, IVisit visit) { protected void saveMethod(AjaxRequestTarget target, IModel am, FeedbackPanel feedbackPanel, boolean draft, Model redirected,boolean rejected) { - + AmpActivityModel a = (AmpActivityModel) am; AmpActivityVersion activity = am.getObject(); Long oldId = activity.getAmpActivityId(); @@ -1101,7 +1104,7 @@ protected void saveMethod(AjaxRequestTarget target, AmpAuthWebSession wicketSession = (AmpAuthWebSession)org.apache.wicket.Session.get(); long currentUserId = wicketSession.getCurrentMember().getMemberId(); activity.setChangeType(ChangeType.MANUAL.toString()); - + AmpTeamMember ampCurrentMember = wicketSession.getAmpCurrentMember(); @@ -1110,13 +1113,13 @@ protected void saveMethod(AjaxRequestTarget target, //Someone else has grabbed the lock ... maybe connection slow and lock refresh timed out throw new RedirectToUrlException(ActivityGatekeeper.buildRedirectLink(String.valueOf(a.getId()), currentUserId)); } - + ActivityUtil.saveActivity((AmpActivityModel) am, draft, rejected); info(TranslatorUtil.getTranslatedText("Activity saved successfully")); /* - * if activity created or created as draft + * if activity created or created as draft * and then saved the message should be sent to the list */ AmpActivityVersion newActivity=am.getObject(); @@ -1127,7 +1130,7 @@ protected void saveMethod(AjaxRequestTarget target, //if validation is off in team setup no messages should be generated String validation = DbUtil.getValidationFromTeamAppSettings(ampCurrentMember.getAmpTeam().getAmpTeamId()); - + if (activity.getDraft() != null && !activity.getDraft() && !(Constants.PROJECT_VALIDATION_OFF.equals(validation))) { if (ActivityUtil.isApproved(newActivity)) { @@ -1161,7 +1164,7 @@ protected void saveMethod(AjaxRequestTarget target, } p.set(1,actId); - //The folllogin exception will provide a redirection + //The folllogin exception will provide a redirection throw new RestartResponseException( new PageProvider( OnePager.class,p),RedirectPolicy.ALWAYS_REDIRECT); @@ -1213,29 +1216,29 @@ public CharSequence getFailureHandler(Component component) { }; attributes.getAjaxCallListeners().add(listener); } - + protected void onSaveAsDraft(final IModel am, final FeedbackPanel feedbackPanel, final Model redirected, final ActionButtonCancelLink actionButtonCancelLink, AjaxRequestTarget target, Form form) { - + target.appendJavaScript("hideDraftPanel();"); - + onSaveAsDraftOrReject(am, feedbackPanel, redirected, actionButtonCancelLink, target, form, false); } - + protected void onReject(final IModel am, final FeedbackPanel feedbackPanel, final Model redirected, final ActionButtonCancelLink actionButtonCancelLink, AjaxRequestTarget target, Form form) { - + target.appendJavaScript("hideRejectActivityPanel();"); - + onSaveAsDraftOrReject(am, feedbackPanel, redirected, actionButtonCancelLink, target, form, true); } - + /** * Method used from saveAsDraft and Reject activity(the only difference between them is that reject prior to saving * the activity changes the status of the activity and sends the messages if the user has chosen to - * + * * @param am * @param feedbackPanel * @param redirected @@ -1247,10 +1250,10 @@ protected void onSaveAsDraftOrReject(final IModel am, final final Model redirected, final ActionButtonCancelLink actionButtonCancelLink, AjaxRequestTarget target, Form form, boolean isRejected) { - + final AmpButtonField actionButton = actionButtonCancelLink.getActionButton(); final AmpAjaxLinkField cancelAction = actionButtonCancelLink.getCancelActionLink(); - + processAndUpdateForm(false, am, form, target, actionButton.getButton()); // only in the eventuality that the title field is valid (is not empty) we proceed with the real save! @@ -1261,12 +1264,12 @@ protected void onSaveAsDraftOrReject(final IModel am, final if (op.getTimer() != null) { op.getTimer().restart(target); } - + target.add(actionButton); target.add(cancelAction); - + onErrorSaveAsDraftOrReject(feedbackPanel, target, form); - + if (autoSaveTimer != null && autoSaveTimer.isStopped()) { autoSaveTimer.restart(target); } @@ -1274,12 +1277,12 @@ protected void onSaveAsDraftOrReject(final IModel am, final //if no error happend and we are rejecting we change the approval status to rejected // send a message to the creator of the activity saveMethod(target, am, feedbackPanel, true, redirected, isRejected); - + if (isRejected) { sendRejectMessage(am); } } - + target.appendJavaScript("enableButtons2();"); } @@ -1292,10 +1295,10 @@ private void sendRejectMessage(IModel activityModel) { AmpActivityVersion activity = activityModel.getObject(); String messageToSend = activity.getRejectMessage(); AmpTeamMember tmTo = activity.getActivityCreator(); - + AmpAuthWebSession wicketSession = (AmpAuthWebSession) org.apache.wicket.Session.get(); TeamMember tmFrom = wicketSession.getCurrentMember(); - + AmpMessage message = new AmpAlert(); String senderName; Long activityId; @@ -1305,24 +1308,24 @@ private void sendRejectMessage(IModel activityModel) { message.setName(TranslatorWorker.translateText("Activity Rejected")); message.setSenderType(MessageConstants.SENDER_TYPE_USER); message.setSenderId(tmFrom.getMemberId()); - + senderName = user.getFirstNames() + " " + user.getLastName() + "<" + user.getEmailUsedForNotification() + ">;" + tmFrom.getTeamName(); message.setSenderName(senderName); - + message.setRelatedActivityId(activityId); - + /*String fullModuleURL=RequestUtils.getFullModuleUrl(request);*/ String objUrl = "/aim/viewActivityPreview.do~activityId=" + activityId; message.setObjectURL(objUrl); - - + + message.setPriorityLevel(MessageConstants.PRIORITY_LEVEL_CRITICAL); if(messageToSend==null){ messageToSend=""; } message.setDescription(messageToSend); - + message.setCreationDate(new Date(System.currentTimeMillis())); message.setDraft(false); @@ -1332,7 +1335,7 @@ private void sendRejectMessage(IModel activityModel) { AmpMessageState state = new AmpMessageState(); state.setMessage(message); state.setSender(tmFrom.getMemberName() + ";" + tmFrom.getTeamName()); - + try { AmpMessageUtil.saveOrUpdateMessageState(state); AmpMessageUtil.createMessageState(message, tmTo); @@ -1340,7 +1343,7 @@ private void sendRejectMessage(IModel activityModel) { logger.error("Cannot create reject message", e); throw new RuntimeException(e); } - + message.addMessageReceiver(tmTo); AmpMessageUtil.saveOrUpdateMessage(message); diff --git a/amp/WEB-INF/web.xml b/amp/WEB-INF/web.xml index dd58e06a66d..10e20d1d745 100644 --- a/amp/WEB-INF/web.xml +++ b/amp/WEB-INF/web.xml @@ -13,14 +13,14 @@ 5 - - XFrameOptionsFilter - org.dgfoundation.amp.onepager.XFrameOptionsFilter - - - XFrameOptionsFilter - /wicket/* - + + + + + + + + javamelody net.bull.javamelody.MonitoringFilter From 64710e5f1cede1591b67e4daf53bc80bf168e34f Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 19:40:49 +0300 Subject: [PATCH 17/76] GGW-369: Resolve project saving ajax issue --- .../AmpStructuresFormSectionFeature.html | 29 ++- .../AmpStructuresFormSectionFeature.java | 186 ++++++++++++------ 2 files changed, 155 insertions(+), 60 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 942cb988eb6..9e2b3d18217 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -4,10 +4,31 @@ - From 26c05baec67ea1f42a2686de164f4aa7fc644b91 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 7 Nov 2024 22:31:03 +0300 Subject: [PATCH 25/76] GGW-369: Structures section styling --- .../AmpStructuresFormSectionFeature.html | 12 +-- .../AmpStructuresFormSectionFeature.java | 81 +------------------ 2 files changed, 8 insertions(+), 85 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index a0921941d2a..f5e6ca9f966 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -7,23 +7,23 @@
+ +
+ Download
From 0c38bc1cc9ca6fc2ad09265fe1fe45e37f4637cd Mon Sep 17 00:00:00 2001 From: brianbrix Date: Tue, 8 Oct 2024 06:23:16 +0300 Subject: [PATCH 09/76] Import button color --- .../features/sections/AmpStructuresFormSectionFeature.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 2a722a42aed..942cb988eb6 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -6,12 +6,12 @@
- +
Download - +
-
- - -
+ + + + +
+
+ + + + + + + + +
+
+ +
+
+
+ +
+
+
+
Download diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 42491e25ef0..c706fb016ec 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -20,6 +20,7 @@ import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.markup.html.TransparentWebMarkupContainer; +import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.form.Button; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextField; @@ -35,6 +36,8 @@ import org.apache.wicket.request.resource.IResource; import org.apache.wicket.request.resource.ResourceReference; import org.apache.wicket.util.lang.Bytes; +import org.apache.wicket.util.upload.FileItem; +import org.dgfoundation.amp.onepager.OnePagerConst; import org.dgfoundation.amp.onepager.OnePagerUtil; import org.dgfoundation.amp.onepager.components.ListEditorRemoveButton; import org.dgfoundation.amp.onepager.components.ListItem; @@ -42,10 +45,9 @@ import org.dgfoundation.amp.onepager.components.PagingListNavigator; import org.dgfoundation.amp.onepager.components.features.CustomResourceLinkResourceLink; import org.dgfoundation.amp.onepager.components.features.ExportExcelResourceReference; -import org.dgfoundation.amp.onepager.components.fields.AmpAjaxLinkField; -import org.dgfoundation.amp.onepager.components.fields.AmpTextAreaFieldPanel; -import org.dgfoundation.amp.onepager.components.fields.AmpTextFieldPanel; -import org.dgfoundation.amp.onepager.components.fields.LatAndLongValidator; +import org.dgfoundation.amp.onepager.components.fields.*; +import org.dgfoundation.amp.onepager.components.upload.FileUploadPanel; +import org.dgfoundation.amp.onepager.helper.TemporaryActivityDocument; import org.dgfoundation.amp.onepager.helper.structure.ColorData; import org.dgfoundation.amp.onepager.helper.structure.CoordinateData; import org.dgfoundation.amp.onepager.helper.structure.MapData; @@ -57,6 +59,7 @@ import org.digijava.module.categorymanager.dbentity.AmpCategoryValue; import org.digijava.module.categorymanager.util.CategoryConstants; import org.digijava.module.categorymanager.util.CategoryManagerUtil; +import org.digijava.module.contentrepository.util.DocumentManagerUtil; import javax.servlet.http.HttpServletResponse; import java.io.*; @@ -332,74 +335,145 @@ public void onClick(AjaxRequestTarget target) { add(addbutton); - final FileUploadField fileUploadField = new FileUploadField("fileUpload"); - fileUploadField.setOutputMarkupId(true); +// final FileUploadField fileUploadField = new FileUploadField("fileUpload"); +// fileUploadField.setOutputMarkupId(true); +// +// final Form form = new Form("form") +// { +// private static final long serialVersionUID = 1L; +// @Override +// protected void onSubmit() { +// FileUpload upload = fileUploadField.getFileUpload(); +// if (upload == null) { +// logger.info("No file uploaded"); +// } else { +// logger.info("File-Name: " + upload.getClientFileName() + " File-Size: " + +// Bytes.bytes(upload.getSize())); +// try { +// XSSFWorkbook workbook = new XSSFWorkbook(upload.getInputStream()); +// XSSFSheet sheet = workbook.getSheetAt(0); +// Iterator rowIterator = sheet.iterator(); +// rowIterator.next(); +// +// while (rowIterator.hasNext()) { +// XSSFRow row = (XSSFRow) rowIterator.next(); +// String title = getStringValueFromCell(row.getCell(0)); +// String description = getStringValueFromCell(row.getCell(1)); +// String latitude = getStringValueFromCell(row.getCell(2)); +// String longitude = getStringValueFromCell(row.getCell(3)); +// +// AmpStructure stru = new AmpStructure(); +// stru.setTitle(title); +// stru.setDescription(description); +// stru.setLatitude(latitude); +// stru.setLongitude(longitude); +// list.addItem(stru); +// list.goToLastPage(); +// } +// +// +// } catch (IOException e) { +// throw new RuntimeException(e); +// } +// } +// } +// +// }; +// form.setMultiPart(true); +// form.setMaxSize(Bytes.megabytes(10)); +// form.add(fileUploadField); +// +// +// Button importStructures = new Button("importStructures"); +//// importStructures.add(new AttributeModifier("disabled", "true")); +// importStructures.setOutputMarkupId(true); +// form.add(importStructures); +// fileUploadField.add(new AjaxFormComponentUpdatingBehavior("change") { +// private static final long serialVersionUID = 1L; +// +// @Override +// protected void onUpdate(AjaxRequestTarget target) { +// importStructures.setEnabled(true); +// importStructures.add(new AttributeModifier("disabled", (String) null)); // Enable the button +// target.add(importStructures); +// +// } +// }); +// +// add(form); + + final Model fileItemModel = new Model(); + FileUploadPanel fileUpload = new FileUploadPanel("file",String.valueOf(am.getObject().getAmpActivityId()), fileItemModel); + final Form form = new Form("form") { + - final Form form = new Form("form") - { - private static final long serialVersionUID = 1L; @Override protected void onSubmit() { - FileUpload upload = fileUploadField.getFileUpload(); - if (upload == null) { - logger.info("No file uploaded"); - } else { - logger.info("File-Name: " + upload.getClientFileName() + " File-Size: " + - Bytes.bytes(upload.getSize())); - try { - XSSFWorkbook workbook = new XSSFWorkbook(upload.getInputStream()); - XSSFSheet sheet = workbook.getSheetAt(0); - Iterator rowIterator = sheet.iterator(); - rowIterator.next(); - - while (rowIterator.hasNext()) { - XSSFRow row = (XSSFRow) rowIterator.next(); - String title = getStringValueFromCell(row.getCell(0)); - String description = getStringValueFromCell(row.getCell(1)); - String latitude = getStringValueFromCell(row.getCell(2)); - String longitude = getStringValueFromCell(row.getCell(3)); - - AmpStructure stru = new AmpStructure(); - stru.setTitle(title); - stru.setDescription(description); - stru.setLatitude(latitude); - stru.setLongitude(longitude); - list.addItem(stru); - list.goToLastPage(); + if (fileItemModel.getObject() != null) { + FileUpload upload = new FileUpload(fileItemModel.getObject()); + if (upload == null) { + logger.info("No file uploaded"); + } else { + logger.info("File-Name: " + upload.getClientFileName() + " File-Size: " + + Bytes.bytes(upload.getSize())); + try { + XSSFWorkbook workbook = new XSSFWorkbook(upload.getInputStream()); + XSSFSheet sheet = workbook.getSheetAt(0); + Iterator rowIterator = sheet.iterator(); + rowIterator.next(); + + while (rowIterator.hasNext()) { + XSSFRow row = (XSSFRow) rowIterator.next(); + String title = getStringValueFromCell(row.getCell(0)); + String description = getStringValueFromCell(row.getCell(1)); + String latitude = getStringValueFromCell(row.getCell(2)); + String longitude = getStringValueFromCell(row.getCell(3)); + + AmpStructure stru = new AmpStructure(); + stru.setTitle(title); + stru.setDescription(description); + stru.setLatitude(latitude); + stru.setLongitude(longitude); + list.addItem(stru); + list.goToLastPage(); + fileItemModel.setObject(null); + } + } catch (Exception e) { + logger.error("Error reading excel file", e); } - - - } catch (IOException e) { - throw new RuntimeException(e); } } - } + } }; - form.setMultiPart(true); - form.setMaxSize(Bytes.megabytes(10)); - form.add(fileUploadField); - Button importStructures = new Button("importStructures"); -// importStructures.add(new AttributeModifier("disabled", "true")); - importStructures.setOutputMarkupId(true); - form.add(importStructures); - fileUploadField.add(new AjaxFormComponentUpdatingBehavior("change") { - private static final long serialVersionUID = 1L; + WebMarkupContainer rc = new WebMarkupContainer("resourcePanel"); +// rc.add(new AttributeModifier("id", getToggleId())); + rc.add(form); +// rc.add(name); + rc.add(fileUpload); + rc.setOutputMarkupId(true); + add(rc); + + form.add(fileUpload); + + // create the ajax button used to submit the form + org.dgfoundation.amp.onepager.components.fields.AmpButtonField submit = new AmpButtonField("ajaxSubmit", "Add", true){ @Override - protected void onUpdate(AjaxRequestTarget target) { - importStructures.setEnabled(true); - importStructures.add(new AttributeModifier("disabled", (String) null)); // Enable the button - target.add(importStructures); + protected void onSubmit(AjaxRequestTarget target, Form form) { + logger.info("Submitting form"); +// if (fileItemModel.getObject() != null) +// tmp.setFile(new FileUpload(fileItemModel.getObject())); + target.add(list); - } - }); - add(form); + } + }; + form.add(submit); ResourceReference resourceReference = new ResourceReference("exportData-"+ System.currentTimeMillis()) { @Override public IResource getResource() { From 4efac492b0a614304ac20435463f40ecfa655c84 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 19:51:28 +0300 Subject: [PATCH 18/76] GGW-369: Resolve project saving ajax issue --- .../features/sections/AmpStructuresFormSectionFeature.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 9e2b3d18217..329fd98a05d 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -8,7 +8,7 @@ -
+
From ac17a61c73b4a7b5bc7b3ba644e9eae9f624bd3f Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 20:05:03 +0300 Subject: [PATCH 19/76] GGW-369: Resolve project saving ajax issue --- .../features/sections/AmpStructuresFormSectionFeature.html | 2 +- .../features/sections/AmpStructuresFormSectionFeature.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 329fd98a05d..135bfeb6930 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -21,7 +21,7 @@ diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index c706fb016ec..15d948888fc 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -460,12 +460,10 @@ protected void onSubmit() { form.add(fileUpload); // create the ajax button used to submit the form - org.dgfoundation.amp.onepager.components.fields.AmpButtonField submit = new AmpButtonField("ajaxSubmit", "Add", true){ + org.dgfoundation.amp.onepager.components.fields.AmpButtonField submit = new AmpButtonField("ajaxSubmit", "Import Structures", true){ @Override protected void onSubmit(AjaxRequestTarget target, Form form) { logger.info("Submitting form"); -// if (fileItemModel.getObject() != null) -// tmp.setFile(new FileUpload(fileItemModel.getObject())); target.add(list); From a368cf53d50780bd159ba23815897711796b032a Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 20:21:21 +0300 Subject: [PATCH 20/76] GGW-369: Resolve project saving ajax issue --- .../features/sections/AmpStructuresFormSectionFeature.html | 4 +--- .../features/sections/AmpStructuresFormSectionFeature.java | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 135bfeb6930..fa8b40768ea 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -21,9 +21,7 @@
-
+
-
- -
+
diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 15d948888fc..5f0511df9c5 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -459,8 +459,7 @@ protected void onSubmit() { form.add(fileUpload); - // create the ajax button used to submit the form - org.dgfoundation.amp.onepager.components.fields.AmpButtonField submit = new AmpButtonField("ajaxSubmit", "Import Structures", true){ + AmpButtonField submit = new AmpButtonField("ajaxSubmit", "Import Structures", true){ @Override protected void onSubmit(AjaxRequestTarget target, Form form) { logger.info("Submitting form"); From a925a5c21dc3ff93f2c5c83cfc3b084527cc209a Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 20:21:34 +0300 Subject: [PATCH 21/76] GGW-369: Resolve project saving ajax issue --- .../features/sections/AmpStructuresFormSectionFeature.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index fa8b40768ea..4624187d37c 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -10,7 +10,7 @@
- +
From 6bda7951832ed4948bb57c1f69b38715a346a216 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 20:32:12 +0300 Subject: [PATCH 22/76] GGW-369: Resolve project saving ajax issue --- .../AmpStructuresFormSectionFeature.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 5f0511df9c5..721e4d996cb 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -458,17 +458,18 @@ protected void onSubmit() { form.add(fileUpload); + Button submit = new Button("ajaxSubmit"); - AmpButtonField submit = new AmpButtonField("ajaxSubmit", "Import Structures", true){ - @Override - protected void onSubmit(AjaxRequestTarget target, Form form) { - logger.info("Submitting form"); - target.add(list); - - - - } - }; +// AmpButtonField submit = new AmpButtonField("ajaxSubmit", "Import Structures", true){ +// @Override +// protected void onSubmit(AjaxRequestTarget target, Form form) { +// logger.info("Submitting form"); +// target.add(list); +// +// +// +// } +// }; form.add(submit); ResourceReference resourceReference = new ResourceReference("exportData-"+ System.currentTimeMillis()) { From 964d3525c095effba88160a6385e9bcd408a431c Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 20:41:47 +0300 Subject: [PATCH 23/76] GGW-369: Resolve project saving ajax issue --- .../features/sections/AmpStructuresFormSectionFeature.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 4624187d37c..e9320ea64a7 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -21,7 +21,7 @@
- +
From 82f28d04b22629af2ba9a7a4a72f6ad358067876 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 6 Nov 2024 21:04:08 +0300 Subject: [PATCH 24/76] GGW-369: Resolve project saving ajax issue --- .../sections/AmpStructuresFormSectionFeature.html | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index e9320ea64a7..a0921941d2a 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -4,13 +4,9 @@
- - - -
- +
@@ -27,8 +23,9 @@
+

+ Download
- +
-
- -
+
- +
+ +
-

+
Download diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 721e4d996cb..0a22a0eb265 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -335,72 +335,6 @@ public void onClick(AjaxRequestTarget target) { add(addbutton); -// final FileUploadField fileUploadField = new FileUploadField("fileUpload"); -// fileUploadField.setOutputMarkupId(true); -// -// final Form form = new Form("form") -// { -// private static final long serialVersionUID = 1L; -// @Override -// protected void onSubmit() { -// FileUpload upload = fileUploadField.getFileUpload(); -// if (upload == null) { -// logger.info("No file uploaded"); -// } else { -// logger.info("File-Name: " + upload.getClientFileName() + " File-Size: " + -// Bytes.bytes(upload.getSize())); -// try { -// XSSFWorkbook workbook = new XSSFWorkbook(upload.getInputStream()); -// XSSFSheet sheet = workbook.getSheetAt(0); -// Iterator rowIterator = sheet.iterator(); -// rowIterator.next(); -// -// while (rowIterator.hasNext()) { -// XSSFRow row = (XSSFRow) rowIterator.next(); -// String title = getStringValueFromCell(row.getCell(0)); -// String description = getStringValueFromCell(row.getCell(1)); -// String latitude = getStringValueFromCell(row.getCell(2)); -// String longitude = getStringValueFromCell(row.getCell(3)); -// -// AmpStructure stru = new AmpStructure(); -// stru.setTitle(title); -// stru.setDescription(description); -// stru.setLatitude(latitude); -// stru.setLongitude(longitude); -// list.addItem(stru); -// list.goToLastPage(); -// } -// -// -// } catch (IOException e) { -// throw new RuntimeException(e); -// } -// } -// } -// -// }; -// form.setMultiPart(true); -// form.setMaxSize(Bytes.megabytes(10)); -// form.add(fileUploadField); -// -// -// Button importStructures = new Button("importStructures"); -//// importStructures.add(new AttributeModifier("disabled", "true")); -// importStructures.setOutputMarkupId(true); -// form.add(importStructures); -// fileUploadField.add(new AjaxFormComponentUpdatingBehavior("change") { -// private static final long serialVersionUID = 1L; -// -// @Override -// protected void onUpdate(AjaxRequestTarget target) { -// importStructures.setEnabled(true); -// importStructures.add(new AttributeModifier("disabled", (String) null)); // Enable the button -// target.add(importStructures); -// -// } -// }); -// -// add(form); final Model fileItemModel = new Model(); FileUploadPanel fileUpload = new FileUploadPanel("file",String.valueOf(am.getObject().getAmpActivityId()), fileItemModel); @@ -449,9 +383,7 @@ protected void onSubmit() { WebMarkupContainer rc = new WebMarkupContainer("resourcePanel"); -// rc.add(new AttributeModifier("id", getToggleId())); rc.add(form); -// rc.add(name); rc.add(fileUpload); rc.setOutputMarkupId(true); add(rc); @@ -459,17 +391,8 @@ protected void onSubmit() { form.add(fileUpload); Button submit = new Button("ajaxSubmit"); + submit.add(new AttributeModifier("class", new Model("addStructure button_green_btm"))); -// AmpButtonField submit = new AmpButtonField("ajaxSubmit", "Import Structures", true){ -// @Override -// protected void onSubmit(AjaxRequestTarget target, Form form) { -// logger.info("Submitting form"); -// target.add(list); -// -// -// -// } -// }; form.add(submit); ResourceReference resourceReference = new ResourceReference("exportData-"+ System.currentTimeMillis()) { @@ -512,7 +435,7 @@ public void onClick(AjaxRequestTarget target) { }; add(exportStructures); - exportStructures.getButton().add(new AttributeModifier("class", new Model("exportStructures button_blue_btm"))); + exportStructures.getButton().add(new AttributeModifier("class", new Model("addStructure button_blue_btm"))); add(exportStructures); From 32b470bcfc8da1634d5ad5d328cad08d4c46c872 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 7 Nov 2024 22:41:02 +0300 Subject: [PATCH 26/76] GGW-369: Structures section styling --- .../features/sections/AmpStructuresFormSectionFeature.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 0a22a0eb265..a0f550166ce 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -435,9 +435,6 @@ public void onClick(AjaxRequestTarget target) { }; add(exportStructures); - exportStructures.getButton().add(new AttributeModifier("class", new Model("addStructure button_blue_btm"))); - add(exportStructures); - } private static String getStringValueFromCell(Cell cell) { From b188988466f07043e661a9be0f5a45792f34bf40 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 7 Nov 2024 22:55:41 +0300 Subject: [PATCH 27/76] GGW-369: Structures section styling --- .../features/sections/AmpStructuresFormSectionFeature.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index f5e6ca9f966..57e025b3df3 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -6,7 +6,7 @@
- +
From 516e7668d412e25813bd79eed4c89a3e9ab0dfa4 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 7 Nov 2024 23:30:51 +0300 Subject: [PATCH 28/76] GGW-369: Structures section styling --- .../sections/AmpStructuresFormSectionFeature.html | 2 +- .../sections/AmpStructuresFormSectionFeature.java | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 57e025b3df3..73b6a2cb2ed 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -9,7 +9,7 @@ diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index a0f550166ce..3e33c0a926c 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -390,7 +390,14 @@ protected void onSubmit() { form.add(fileUpload); - Button submit = new Button("ajaxSubmit"); + AmpButtonField submit = new AmpButtonField("ajaxSubmit", "Add", true) { + + @Override + protected void onSubmit(AjaxRequestTarget target, Form form) { + // TODO: 07/11/2024 Add your code here + } + }; + submit.add(new AttributeModifier("class", new Model("addStructure button_green_btm"))); From 42d6ea9b64140bc445e98be4be20adf8fca883f2 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 7 Nov 2024 23:55:59 +0300 Subject: [PATCH 29/76] GGW-369: Structures section styling --- .../sections/AmpStructuresFormSectionFeature.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 3e33c0a926c..d5596263cfd 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -390,13 +390,7 @@ protected void onSubmit() { form.add(fileUpload); - AmpButtonField submit = new AmpButtonField("ajaxSubmit", "Add", true) { - - @Override - protected void onSubmit(AjaxRequestTarget target, Form form) { - // TODO: 07/11/2024 Add your code here - } - }; + Button submit = new Button("ajaxSubmit"); submit.add(new AttributeModifier("class", new Model("addStructure button_green_btm"))); From 4e3d3ed3942ad990807b62fe651d9e7ac83a4cf3 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 7 Nov 2024 23:56:47 +0300 Subject: [PATCH 30/76] GGW-369: Structures section styling --- .../features/sections/AmpStructuresFormSectionFeature.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 73b6a2cb2ed..57e025b3df3 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -9,7 +9,7 @@
- +
From 7ca91096f9a666a62e1725ce02bf84e6b65eb237 Mon Sep 17 00:00:00 2001 From: Julian de Anquin Date: Mon, 11 Nov 2024 10:24:33 -0300 Subject: [PATCH 31/76] GGW-369 arrange colors --- .../features/sections/AmpStructuresFormSectionFeature.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 57e025b3df3..f6fe8c68366 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -9,7 +9,7 @@
- +
@@ -27,7 +27,7 @@
From bf2927a2b8443446cff63323051d479b7a1c5a32 Mon Sep 17 00:00:00 2001 From: Julian de Anquin Date: Mon, 11 Nov 2024 13:09:35 -0300 Subject: [PATCH 32/76] GGW-369 arrange colors --- .../features/sections/AmpStructuresFormSectionFeature.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index f6fe8c68366..b4864f9ab94 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -27,7 +27,7 @@
From 563c0a6fe55744af323e82eaa168e2fc0416626d Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 16 Sep 2026 10:03:47 +0300 Subject: [PATCH 33/76] AMP-31212 : Enhance security by securing cookies and ensuring coss-site restrictions --- amp/context.xml | 8 +++----- amp/docker/server.xml | 4 ++-- amp/src/main/java/org/digijava/kernel/util/DgUtil.java | 2 ++ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/amp/context.xml b/amp/context.xml index ac48b2a4f43..6fff5c76335 100644 --- a/amp/context.xml +++ b/amp/context.xml @@ -1,14 +1,12 @@ - - - + + @@ -63,7 +63,7 @@ + protocolHeader="x-forwarded-proto" portHeader="x-forwarded-port" /> diff --git a/amp/src/main/java/org/digijava/kernel/util/DgUtil.java b/amp/src/main/java/org/digijava/kernel/util/DgUtil.java index cf6fdd0976b..c8d858abc47 100644 --- a/amp/src/main/java/org/digijava/kernel/util/DgUtil.java +++ b/amp/src/main/java/org/digijava/kernel/util/DgUtil.java @@ -265,6 +265,8 @@ private static void setLanguageCookie(Locale language, HttpServletRequest reques cookiePath = "/"; } cookie.setPath(cookiePath); + cookie.setHttpOnly(true); + cookie.setSecure(request.isSecure()); response.addCookie(cookie); } From f258f39fe17a4807c88bc936df406c30e1596640 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 16 Sep 2026 12:36:49 +0300 Subject: [PATCH 34/76] AMP-31212 : Fixing failing docker build --- amp/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/Dockerfile b/amp/Dockerfile index 903d1a37a87..e83174d1297 100644 --- a/amp/Dockerfile +++ b/amp/Dockerfile @@ -233,7 +233,7 @@ RUN --mount=type=cache,target=/root/.m2 \ && rm -rf target; \ fi -FROM tomcat:8.5.79-jdk8 +FROM tomcat:8.5-jdk8-temurin RUN apt-get update \ && apt-get install -y apt-transport-https ca-certificates curl gnupg --no-install-recommends \ From 9c62ce9aa1f5fe2fea9d2bf126ce7bc0c966cff2 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 16 Sep 2026 13:17:57 +0300 Subject: [PATCH 35/76] AMP-31212 : Fixing failing docker build --- amp/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/Dockerfile b/amp/Dockerfile index e83174d1297..95ddca69fff 100644 --- a/amp/Dockerfile +++ b/amp/Dockerfile @@ -233,7 +233,7 @@ RUN --mount=type=cache,target=/root/.m2 \ && rm -rf target; \ fi -FROM tomcat:8.5-jdk8-temurin +FROM tomcat:8.5.94-jdk8-temurin-focal RUN apt-get update \ && apt-get install -y apt-transport-https ca-certificates curl gnupg --no-install-recommends \ From 4d6f010019e105cba7a4ca418bc7217560fc4e3e Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 16 Sep 2026 13:55:12 +0300 Subject: [PATCH 36/76] AMP-31212 : Wrongly formatted patch --- .../main/resources/xmlpatches/4.0/Update-Import-Tables-v4.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/src/main/resources/xmlpatches/4.0/Update-Import-Tables-v4.xml b/amp/src/main/resources/xmlpatches/4.0/Update-Import-Tables-v4.xml index 3485a20f517..a3c82f121fd 100644 --- a/amp/src/main/resources/xmlpatches/4.0/Update-Import-Tables-v4.xml +++ b/amp/src/main/resources/xmlpatches/4.0/Update-Import-Tables-v4.xml @@ -12,4 +12,4 @@ - \ No newline at end of file From 9db5021a450f28fe3bc5102cf247bc9b7f64be62 Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Thu, 1 Oct 2026 07:41:10 +0300 Subject: [PATCH 37/76] Remove HttpOnly setting from cookie Remove HttpOnly flag from the cookie. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- amp/src/main/java/org/digijava/kernel/util/DgUtil.java | 1 - 1 file changed, 1 deletion(-) diff --git a/amp/src/main/java/org/digijava/kernel/util/DgUtil.java b/amp/src/main/java/org/digijava/kernel/util/DgUtil.java index c8d858abc47..a9495ded28b 100644 --- a/amp/src/main/java/org/digijava/kernel/util/DgUtil.java +++ b/amp/src/main/java/org/digijava/kernel/util/DgUtil.java @@ -265,7 +265,6 @@ private static void setLanguageCookie(Locale language, HttpServletRequest reques cookiePath = "/"; } cookie.setPath(cookiePath); - cookie.setHttpOnly(true); cookie.setSecure(request.isSecure()); response.addCookie(cookie); } From e715e5fecfff1555e8672ca27915230517a641a5 Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Thu, 1 Oct 2026 07:42:01 +0300 Subject: [PATCH 38/76] Change SameSite cookie policy from strict to lax Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- amp/context.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amp/context.xml b/amp/context.xml index 6fff5c76335..4ee99628277 100644 --- a/amp/context.xml +++ b/amp/context.xml @@ -5,8 +5,8 @@ allowLinking="true" antiJARLocking="" antiResourceLocking="" clearReferencesStop clearReferencesThreadLocals="true" jndiExceptionOnFailedWrite="false" processTlds="false"> - - + + Date: Thu, 1 Oct 2026 08:18:00 +0300 Subject: [PATCH 39/76] Failing to load assets --- amp/docker/server.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/docker/server.xml b/amp/docker/server.xml index 2d07439dd30..cebe84a9a0e 100644 --- a/amp/docker/server.xml +++ b/amp/docker/server.xml @@ -63,7 +63,7 @@ + protocolHeader="x-forwarded-proto" /> From 103c23c1dac17fc16c66db4e24a94fe1246be7c4 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 1 Oct 2026 08:22:14 +0300 Subject: [PATCH 40/76] Failing to load assets --- amp/context.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/amp/context.xml b/amp/context.xml index 4ee99628277..baf94594a1d 100644 --- a/amp/context.xml +++ b/amp/context.xml @@ -3,10 +3,11 @@ - + - - + + + Date: Thu, 1 Oct 2026 08:56:23 +0300 Subject: [PATCH 41/76] Failing to load assets --- amp/src/main/webapp/WEB-INF/web.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/amp/src/main/webapp/WEB-INF/web.xml b/amp/src/main/webapp/WEB-INF/web.xml index 1fff2ca99f7..33125996bcb 100644 --- a/amp/src/main/webapp/WEB-INF/web.xml +++ b/amp/src/main/webapp/WEB-INF/web.xml @@ -7,6 +7,27 @@ AMP 4.0 Aid Management Platform + + reampv2BootstrapCacheFilter + org.apache.catalina.filters.ExpiresFilter + + ExpiresDefault + access plus 0 seconds + + + + reampv2BootstrapCacheFilter + /TEMPLATE/reampv2/packages/reampv2-app/build/index.html + + + reampv2BootstrapCacheFilter + /TEMPLATE/reampv2/packages/reampv2-app/build/main.bundle.js + + + reampv2BootstrapCacheFilter + /TEMPLATE/reampv2/packages/reampv2-app/build/remoteEntry.js + + From 09f0ee7f0e45a720f92cc48f8427c83d624aeb40 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 1 Oct 2026 09:16:30 +0300 Subject: [PATCH 42/76] Failing to load assets --- .../components/table/Table.tsx | 2 +- .../config/initialTranslations.json | 8 +++---- .../pages/OutcomeOutputManagementPage.tsx | 2 +- .../indicator_manager/utils/validator.ts | 2 +- amp/src/main/webapp/WEB-INF/web.xml | 21 ------------------- 5 files changed, 7 insertions(+), 28 deletions(-) diff --git a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/components/table/Table.tsx b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/components/table/Table.tsx index 46ef98ebe87..56a0e851a60 100644 --- a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/components/table/Table.tsx +++ b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/components/table/Table.tsx @@ -326,7 +326,7 @@ const SkeletonTable: React.FC = (props) => { > {' '} - {t('amp.indicatormanager:export-csv')} + {t('amp.indicatormanager:exportCsv')} diff --git a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/config/initialTranslations.json b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/config/initialTranslations.json index 76953308c87..1857f6da8e3 100644 --- a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/config/initialTranslations.json +++ b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/config/initialTranslations.json @@ -5,7 +5,7 @@ "amp.indicatormanager:sectors": "Sectors", "amp.indicatormanager:all-sectors": "All Sectors", "amp.indicatormanager:search": "Search by name or code", - "amp.indicatormanager:export-csv": "Export CSV", + "amp.indicatormanager:exportCsv": "Export CSV", "amp.indicatormanager:table-header-id": "ID", "amp.indicatormanager:table-header-code": "Code", "amp.indicatormanager:table-header-indicator-name": "Indicator Name", @@ -35,7 +35,7 @@ "amp.indicatormanager:enable-target": "Enable Target Values Input", "amp.indicatormanager:base-values": "Base Values", "amp.indicatormanager:base-date": "Base Date", - "amp.indicatormanager:target-date": "Target Date", + "amp.indicatormanager:targetDate": "Target Date", "amp.indicatormanager:actual-date": "Actual Date", "amp.indicatormanager:target-values": "Target Values", "amp.indicatormanager:original-value": "Original Value", @@ -93,7 +93,7 @@ "amp.indicatormanager:errors-revised-base-value-invalid": "Revised base value must be greater than original base value", "amp.indicatormanager:errors-revised-base-date-invalid": "Revised base date must be greater than or equal to original base date", "amp.indicatormanager:errors-revised-target-value-invalid": "Revised target value must be greater than original target value", - "amp.indicatormanager:errors-revised-target-date-invalid": "Revised target date must be greater than or equal to original target date", + "amp.indicatormanager:errorsRevisedTargetDateInvalid": "Revised target date must be greater than or equal to original target date", "amp.indicatormanager:errors-program-is-required": "Program is required", "amp.indicatormanager:true": "True", "amp.indicatormanager:false": "False", @@ -114,7 +114,7 @@ "amp.outcomeoutput:cancel": "Cancel", "amp.outcomeoutput:add-new-outcome": "Add New Outcome", "amp.outcomeoutput:add-new-output": "Add New Output", - "amp.outcomeoutput:export-csv": "Export CSV", + "amp.outcomeoutput:exportCsv": "Export CSV", "amp.outcomeoutput:search-placeholder": "Search Outcomes", "amp.outcomeoutput:management-title": "Outcome and Output Management", "amp.indicatormanager:core-info": "Core Indicator Information", diff --git a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/pages/OutcomeOutputManagementPage.tsx b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/pages/OutcomeOutputManagementPage.tsx index 516eb0e0ab9..4d3736144b1 100644 --- a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/pages/OutcomeOutputManagementPage.tsx +++ b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/pages/OutcomeOutputManagementPage.tsx @@ -498,7 +498,7 @@ const OutcomeOutputManagementPage: React.FC = () => { {' '} diff --git a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/utils/validator.ts b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/utils/validator.ts index 700c7ab34db..324b1630766 100644 --- a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/utils/validator.ts +++ b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/admin/indicator_manager/utils/validator.ts @@ -45,7 +45,7 @@ export const translatedIndicatorValidationSchema = (translations: DefaultTransla revisedValue: Yup.number().optional().nullable(), revisedValueDate: Yup.date().optional().nullable().when('originalValueDate', (originalValueDate: any) => { if (originalValueDate) { - return Yup.date().min(originalValueDate, translations["amp.indicatormanager:errors-revised-target-date-invalid"]).optional().nullable(); + return Yup.date().min(originalValueDate, translations["amp.indicatormanager:errorsRevisedTargetDateInvalid"]).optional().nullable(); } return Yup.date().optional().nullable(); }), diff --git a/amp/src/main/webapp/WEB-INF/web.xml b/amp/src/main/webapp/WEB-INF/web.xml index 33125996bcb..1fff2ca99f7 100644 --- a/amp/src/main/webapp/WEB-INF/web.xml +++ b/amp/src/main/webapp/WEB-INF/web.xml @@ -7,27 +7,6 @@ AMP 4.0 Aid Management Platform - - reampv2BootstrapCacheFilter - org.apache.catalina.filters.ExpiresFilter - - ExpiresDefault - access plus 0 seconds - - - - reampv2BootstrapCacheFilter - /TEMPLATE/reampv2/packages/reampv2-app/build/index.html - - - reampv2BootstrapCacheFilter - /TEMPLATE/reampv2/packages/reampv2-app/build/main.bundle.js - - - reampv2BootstrapCacheFilter - /TEMPLATE/reampv2/packages/reampv2-app/build/remoteEntry.js - - From 153fd4b2478ea054aa8fb9c71d9f88cf0ee750fe Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 1 Oct 2026 09:53:41 +0300 Subject: [PATCH 43/76] Secure cookies in HttpLoginManager --- .../java/org/digijava/kernel/security/HttpLoginManager.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java b/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java index 0c1cff28782..a80eccb5327 100644 --- a/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java +++ b/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java @@ -926,6 +926,8 @@ public static void saveSessionIdToCookie(HttpServletRequest request, } sessionIdCookie.setPath(cookiePath); + sessionIdCookie.setSecure(request.isSecure()); + sessionIdCookie.setHttpOnly(true); response.addCookie(sessionIdCookie); } @@ -1038,6 +1040,8 @@ private static void removeLoginCookie(HttpServletRequest request, } cookies[loop].setPath(cookiePath); + cookies[loop].setSecure(request.isSecure()); + cookies[loop].setHttpOnly(true); response.addCookie(cookies[loop]); logger.debug("Removing cookie: " + cookies[loop].getDomain() + @@ -1079,6 +1083,8 @@ private static void saveLoginCookie(HttpServletRequest request, } userNameCookie.setPath(cookiePath); + userNameCookie.setSecure(request.isSecure()); + userNameCookie.setHttpOnly(true); response.addCookie(userNameCookie); } From 53f13f1c352f79f7780f3f4f805eaf165109874c Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 1 Oct 2026 20:50:51 +0300 Subject: [PATCH 44/76] Upgrade tomcat to v9 --- amp/Dockerfile | 2 +- amp/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/amp/Dockerfile b/amp/Dockerfile index 95ddca69fff..01a080c3738 100644 --- a/amp/Dockerfile +++ b/amp/Dockerfile @@ -233,7 +233,7 @@ RUN --mount=type=cache,target=/root/.m2 \ && rm -rf target; \ fi -FROM tomcat:8.5.94-jdk8-temurin-focal +FROM tomcat:9.0.122-jdk8-temurin-jammy RUN apt-get update \ && apt-get install -y apt-transport-https ca-certificates curl gnupg --no-install-recommends \ diff --git a/amp/pom.xml b/amp/pom.xml index cb9831d9485..e3af99ee7b9 100644 --- a/amp/pom.xml +++ b/amp/pom.xml @@ -8,7 +8,7 @@ Aid Management Platform 6.29.0 - 8.5.73 + 9.0.122 5.6.11 5.3.28 UTF-8 From 557175247fe32619748b2a88b797a63f5de39716 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 1 Oct 2026 21:29:37 +0300 Subject: [PATCH 45/76] Downgrade back to tomcat 8.5.100 --- amp/Dockerfile | 2 +- amp/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/amp/Dockerfile b/amp/Dockerfile index 01a080c3738..5a885b87b94 100644 --- a/amp/Dockerfile +++ b/amp/Dockerfile @@ -233,7 +233,7 @@ RUN --mount=type=cache,target=/root/.m2 \ && rm -rf target; \ fi -FROM tomcat:9.0.122-jdk8-temurin-jammy +FROM tomcat:8.5.100-jdk8-temurin-focal RUN apt-get update \ && apt-get install -y apt-transport-https ca-certificates curl gnupg --no-install-recommends \ diff --git a/amp/pom.xml b/amp/pom.xml index e3af99ee7b9..5ee959a773c 100644 --- a/amp/pom.xml +++ b/amp/pom.xml @@ -8,7 +8,7 @@ Aid Management Platform 6.29.0 - 9.0.122 + 8.5.100 5.6.11 5.3.28 UTF-8 From 3937b68130cd63d23b0e88b1fda57d44dc16ef0c Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Thu, 1 Oct 2026 21:54:28 +0300 Subject: [PATCH 46/76] Set secure flag for userNameCookie to true Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../java/org/digijava/kernel/security/HttpLoginManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java b/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java index a80eccb5327..39f56626e03 100644 --- a/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java +++ b/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java @@ -1083,7 +1083,7 @@ private static void saveLoginCookie(HttpServletRequest request, } userNameCookie.setPath(cookiePath); - userNameCookie.setSecure(request.isSecure()); + userNameCookie.setSecure(true); userNameCookie.setHttpOnly(true); response.addCookie(userNameCookie); From 941fe4a4ee079196dceacecf002b9ff72e25053c Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Thu, 1 Oct 2026 21:54:45 +0300 Subject: [PATCH 47/76] Set session cookie to secure mode Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../java/org/digijava/kernel/security/HttpLoginManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java b/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java index 39f56626e03..46090936bbd 100644 --- a/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java +++ b/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java @@ -926,7 +926,7 @@ public static void saveSessionIdToCookie(HttpServletRequest request, } sessionIdCookie.setPath(cookiePath); - sessionIdCookie.setSecure(request.isSecure()); + sessionIdCookie.setSecure(true); sessionIdCookie.setHttpOnly(true); response.addCookie(sessionIdCookie); From c1883c323585da2db08bc4298b92155f3043bc86 Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Tue, 6 Oct 2026 21:52:46 +0300 Subject: [PATCH 48/76] Refactor Excel export to use AmpStructure list directly Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../ExportExcelResourceReference.java | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/ExportExcelResourceReference.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/ExportExcelResourceReference.java index 71479731a82..d370109ba00 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/ExportExcelResourceReference.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/ExportExcelResourceReference.java @@ -60,21 +60,15 @@ protected ResourceResponse newResourceResponse(Attributes attributes) { headerRow.createCell(3).setCellValue("Longitude"); int rowIndex = 1; - for (Component child : getList()) { - if (child instanceof ListItem) { - ListItem listItem = (ListItem) child; - AmpStructure structure = listItem.getModelObject(); - - // Create a new row for each structure - XSSFRow row = sheet.createRow(rowIndex); - if (structure != null) { - createCellIfNotNull(row, 0, structure.getTitle()); - createCellIfNotNull(row, 1, structure.getDescription()); - createCellIfNotNull(row, 2, structure.getLatitude()); - createCellIfNotNull(row, 3, structure.getLongitude()); - } - rowIndex++; + for (AmpStructure structure : getList().getModel().getObject()) { + XSSFRow row = sheet.createRow(rowIndex); + if (structure != null) { + createCellIfNotNull(row, 0, structure.getTitle()); + createCellIfNotNull(row, 1, structure.getDescription()); + createCellIfNotNull(row, 2, structure.getLatitude()); + createCellIfNotNull(row, 3, structure.getLongitude()); } + rowIndex++; } // Write workbook content to the output stream From 64abb3eb8c07483a39b6703bc50be1ef0f101eda Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Tue, 6 Oct 2026 21:55:41 +0300 Subject: [PATCH 49/76] Refactor loop to iterate over AmpStructure list Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../AmpStructuresFormSectionFeature.java | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index d5596263cfd..dde46efad2e 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -469,21 +469,15 @@ private void writeExcelFile(OutputStream out,PagingListEditor list headerRow.createCell(3).setCellValue("Longitude"); int rowIndex = 1; - for (Component child : list) { - if (child instanceof ListItem) { - ListItem listItem = (ListItem) child; - AmpStructure structure = listItem.getModelObject(); - - // Create a new row for each structure - XSSFRow row = sheet.createRow(rowIndex); - if (structure != null) { - createCellIfNotNull(row, 0, structure.getTitle()); - createCellIfNotNull(row, 1, structure.getDescription()); - createCellIfNotNull(row, 2, structure.getLatitude()); - createCellIfNotNull(row, 3, structure.getLongitude()); - } - rowIndex++; + for (AmpStructure structure : list.getModel().getObject()) { + XSSFRow row = sheet.createRow(rowIndex); + if (structure != null) { + createCellIfNotNull(row, 0, structure.getTitle()); + createCellIfNotNull(row, 1, structure.getDescription()); + createCellIfNotNull(row, 2, structure.getLatitude()); + createCellIfNotNull(row, 3, structure.getLongitude()); } + rowIndex++; } // Write workbook content to the output stream From 910940a303e17cec0398f768b078f04baa1fee18 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 7 Oct 2026 09:46:00 +0300 Subject: [PATCH 50/76] Close file stream automatically --- .../features/FileUploadModalPanel.html | 9 +---- .../features/FileUploadModalPanel.java | 37 ++++--------------- .../AmpStructuresFormSectionFeature.java | 15 +++----- 3 files changed, 15 insertions(+), 46 deletions(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.html b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.html index 3244e22bc54..5d4cbd2fc59 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.html +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.html @@ -5,14 +5,9 @@ - - + +
- - -
-
-
diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.java index 841b2c664e6..e8350a3e506 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.java @@ -3,47 +3,26 @@ import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.markup.html.form.Form; -import org.apache.wicket.markup.html.form.upload.FileUpload; -import org.apache.wicket.markup.html.form.upload.FileUploadField; import org.apache.wicket.markup.html.panel.Panel; -import java.io.File; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.util.upload.FileItem; +import org.dgfoundation.amp.onepager.components.upload.FileUploadPanel; public class FileUploadModalPanel extends Panel { - private FileUploadField fileUploadField; - public FileUploadModalPanel(String id, ModalWindow modalWindow) { super(id); - // Create the form and mark it as multi-part for file uploads Form form = new Form<>("uploadForm"); - form.setMultiPart(true); // Necessary for file upload - - // Add file upload field - fileUploadField = new FileUploadField("fileUploadField"); - form.add(fileUploadField); + IModel fileItemModel = new Model<>(); + form.add(new FileUploadPanel("fileUploadField", "new", fileItemModel)); - // Add AjaxSubmitLink to handle file upload via Ajax form.add(new AjaxSubmitLink("uploadButton", form) { protected void onSubmit(AjaxRequestTarget target) { - FileUpload upload = fileUploadField.getFileUpload(); - if (upload != null) { - try { - // Save the uploaded file or process it - File file = new File("/path/to/save/" + upload.getClientFileName()); - upload.writeTo(file); - System.out.println("File uploaded: " + upload.getClientFileName()); - } catch (Exception e) { - e.printStackTrace(); - } + if (fileItemModel.getObject() != null) { + modalWindow.close(target); } - // Close the modal window after successful upload - modalWindow.close(target); - } - - protected void onError(AjaxRequestTarget target) { - // Handle error scenario here - System.out.println("File upload failed"); } }); diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index d5596263cfd..26fab2c9ac4 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -351,7 +351,8 @@ protected void onSubmit() { logger.info("File-Name: " + upload.getClientFileName() + " File-Size: " + Bytes.bytes(upload.getSize())); try { - XSSFWorkbook workbook = new XSSFWorkbook(upload.getInputStream()); + try (InputStream inputStream = upload.getInputStream(); + XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) { XSSFSheet sheet = workbook.getSheetAt(0); Iterator rowIterator = sheet.iterator(); rowIterator.next(); @@ -372,6 +373,7 @@ protected void onSubmit() { list.goToLastPage(); fileItemModel.setObject(null); } + } } catch (Exception e) { logger.error("Error reading excel file", e); } @@ -535,20 +537,13 @@ private boolean hasCoordinates(IModel structureModel) { } private void handleFileUpload(final FileUpload uploadedFile) throws IOException { - // Write the uploaded file to a temporary location - File tempFile = new File("temp_" + uploadedFile.getClientFileName()); - uploadedFile.writeTo(tempFile); - - try (InputStream inputStream = new FileInputStream(tempFile)) { - Workbook workbook = WorkbookFactory.create(inputStream); + try (InputStream inputStream = uploadedFile.getInputStream(); + Workbook workbook = WorkbookFactory.create(inputStream)) { // ... } catch (IOException e) { // Handle exception } catch (InvalidFormatException e) { throw new RuntimeException(e); - } finally { - // Delete the temporary file - tempFile.delete(); } } } From 332ef3c03a2c8d96cc63886a669cba77e47e2065 Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Wed, 7 Oct 2026 10:13:24 +0300 Subject: [PATCH 51/76] Refactor resource panel setup in AmpStructuresFormSection Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../sections/AmpStructuresFormSectionFeature.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index d75c935fa04..9e986296819 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -384,14 +384,13 @@ protected void onSubmit() { }; - WebMarkupContainer rc = new WebMarkupContainer("resourcePanel"); - rc.add(form); - rc.add(fileUpload); - rc.setOutputMarkupId(true); - add(rc); +WebMarkupContainer rc = new WebMarkupContainer("resourcePanel"); +rc.add(form); +rc.setOutputMarkupId(true); +add(rc); - form.add(fileUpload); +form.add(fileUpload); Button submit = new Button("ajaxSubmit"); submit.add(new AttributeModifier("class", new Model("addStructure button_green_btm"))); From 524dec3e08a1be4223d0191d0afa2af1863b6746 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 7 Oct 2026 10:17:07 +0300 Subject: [PATCH 52/76] Copilot comments resolution --- .../AmpStructuresFormSectionFeature.java | 46 +++++++------------ .../AmpStructuresFormSectionFeature.html | 31 ++++++------- 2 files changed, 30 insertions(+), 47 deletions(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index d75c935fa04..4cca12a9894 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -21,7 +21,6 @@ import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.markup.html.TransparentWebMarkupContainer; import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.form.Button; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.form.upload.FileUpload; @@ -338,21 +337,16 @@ public void onClick(AjaxRequestTarget target) { final Model fileItemModel = new Model(); FileUploadPanel fileUpload = new FileUploadPanel("file",String.valueOf(am.getObject().getAmpActivityId()), fileItemModel); - final Form form = new Form("form") { - - + final WebMarkupContainer rc = new WebMarkupContainer("resourcePanel"); + AmpButtonField importStructures = new AmpButtonField("ajaxSubmit", "Import Structures", true) { @Override - protected void onSubmit() { + protected void onSubmit(AjaxRequestTarget target, Form form) { if (fileItemModel.getObject() != null) { FileUpload upload = new FileUpload(fileItemModel.getObject()); - if (upload == null) { - logger.info("No file uploaded"); - } else { - logger.info("File-Name: " + upload.getClientFileName() + " File-Size: " + - Bytes.bytes(upload.getSize())); - try { - try (InputStream inputStream = upload.getInputStream(); - XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) { + logger.info("File-Name: " + upload.getClientFileName() + " File-Size: " + + Bytes.bytes(upload.getSize())); + try (InputStream inputStream = upload.getInputStream(); + XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) { XSSFSheet sheet = workbook.getSheetAt(0); Iterator rowIterator = sheet.iterator(); rowIterator.next(); @@ -370,34 +364,26 @@ protected void onSubmit() { stru.setLatitude(latitude); stru.setLongitude(longitude); list.addItem(stru); - list.goToLastPage(); - fileItemModel.setObject(null); - } } - } catch (Exception e) { - logger.error("Error reading excel file", e); - } + list.goToLastPage(); + fileItemModel.setObject(null); + } catch (Exception e) { + logger.error("Error reading excel file", e); } } - - } + target.add(containter); + target.add(rc); + } }; + importStructures.getButton().setDefaultFormProcessing(false); - WebMarkupContainer rc = new WebMarkupContainer("resourcePanel"); - rc.add(form); rc.add(fileUpload); + rc.add(importStructures); rc.setOutputMarkupId(true); add(rc); - form.add(fileUpload); - Button submit = new Button("ajaxSubmit"); - - submit.add(new AttributeModifier("class", new Model("addStructure button_green_btm"))); - - - form.add(submit); ResourceReference resourceReference = new ResourceReference("exportData-"+ System.currentTimeMillis()) { @Override public IResource getResource() { diff --git a/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index b4864f9ab94..a76629bfcb6 100644 --- a/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -5,23 +5,20 @@
From 393cc4f903da44fecfe3092bcf29c03cc29a72a6 Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Wed, 7 Oct 2026 11:56:43 +0300 Subject: [PATCH 53/76] Potential fix for pull request finding 'Secure login cookie breaks persistent login over HTTP' Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../java/org/digijava/kernel/security/HttpLoginManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java b/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java index 46090936bbd..a45290e4794 100644 --- a/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java +++ b/amp/src/main/java/org/digijava/kernel/security/HttpLoginManager.java @@ -1083,7 +1083,7 @@ private static void saveLoginCookie(HttpServletRequest request, } userNameCookie.setPath(cookiePath); - userNameCookie.setSecure(true); + userNameCookie.setSecure(request.isSecure()); userNameCookie.setHttpOnly(true); response.addCookie(userNameCookie); From 637cb43e0bd0f23646bd4f4a94c9c69b5b13c8b0 Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Wed, 7 Oct 2026 11:58:04 +0300 Subject: [PATCH 54/76] Add check for empty sheet in AmpStructuresFormSectionFeature Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../features/sections/AmpStructuresFormSectionFeature.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 4cca12a9894..6b2e5340f99 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -349,6 +349,9 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) { XSSFSheet sheet = workbook.getSheetAt(0); Iterator rowIterator = sheet.iterator(); + if (!rowIterator.hasNext()) { + return; + } rowIterator.next(); while (rowIterator.hasNext()) { From c3aa0dafc5f115892b4ab4a83de0551fcbd9275b Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 7 Oct 2026 11:58:22 +0300 Subject: [PATCH 55/76] Add missing shape column --- .../features/sections/AmpStructuresFormSectionFeature.java | 4 ++++ .../onepager/components/features/FileUploadModalPanel.html | 0 2 files changed, 4 insertions(+) rename amp/src/main/{java => resources}/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.html (100%) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 4cca12a9894..9bb18a06f1e 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -357,12 +357,14 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { String description = getStringValueFromCell(row.getCell(1)); String latitude = getStringValueFromCell(row.getCell(2)); String longitude = getStringValueFromCell(row.getCell(3)); + String shape = getStringValueFromCell(row.getCell(4)); AmpStructure stru = new AmpStructure(); stru.setTitle(title); stru.setDescription(description); stru.setLatitude(latitude); stru.setLongitude(longitude); + stru.setShape(shape); list.addItem(stru); } list.goToLastPage(); @@ -455,6 +457,7 @@ private void writeExcelFile(OutputStream out,PagingListEditor list headerRow.createCell(1).setCellValue("Description"); headerRow.createCell(2).setCellValue("Latitude"); headerRow.createCell(3).setCellValue("Longitude"); + headerRow.createCell(4).setCellValue("Shape"); int rowIndex = 1; for (AmpStructure structure : list.getModel().getObject()) { @@ -464,6 +467,7 @@ private void writeExcelFile(OutputStream out,PagingListEditor list createCellIfNotNull(row, 1, structure.getDescription()); createCellIfNotNull(row, 2, structure.getLatitude()); createCellIfNotNull(row, 3, structure.getLongitude()); + createCellIfNotNull(row, 4, structure.getShape()); } rowIndex++; } diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.html b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.html similarity index 100% rename from amp/src/main/java/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.html rename to amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/FileUploadModalPanel.html From e08f40698963dcaa8209c1118a90e8b970180194 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 7 Oct 2026 12:03:35 +0300 Subject: [PATCH 56/76] Check for structure title duplicates --- .../AmpStructuresFormSectionFeature.java | 21 ++++++++++++++++++- .../AmpStructuresFormSectionFeature.html | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 9f19bbaa188..85c34b59c44 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -19,6 +19,7 @@ import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; +import org.apache.wicket.feedback.ContainerFeedbackMessageFilter; import org.apache.wicket.markup.html.TransparentWebMarkupContainer; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.form.Form; @@ -26,6 +27,7 @@ import org.apache.wicket.markup.html.form.upload.FileUpload; import org.apache.wicket.markup.html.form.upload.FileUploadField; import org.apache.wicket.markup.html.link.ResourceLink; +import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.model.AbstractReadOnlyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; @@ -353,6 +355,8 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { return; } rowIterator.next(); + Set structureTitles = new TreeSet<>(setModel.getObject()); + List importedStructures = new ArrayList<>(); while (rowIterator.hasNext()) { XSSFRow row = (XSSFRow) rowIterator.next(); @@ -368,7 +372,18 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { stru.setLatitude(latitude); stru.setLongitude(longitude); stru.setShape(shape); - list.addItem(stru); + if (!structureTitles.add(stru)) { + this.error("Import rejected: structure title '" + title + "' on row " + + (row.getRowNum() + 1) + + " duplicates an existing structure or another imported row." + + " Structure titles must be unique."); + target.add(rc); + return; + } + importedStructures.add(stru); + } + for (AmpStructure structure : importedStructures) { + list.addItem(structure); } list.goToLastPage(); fileItemModel.setObject(null); @@ -385,6 +400,10 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { rc.add(fileUpload); rc.add(importStructures); + FeedbackPanel importFeedback = new FeedbackPanel("importFeedback", + new ContainerFeedbackMessageFilter(importStructures)); + importFeedback.setOutputMarkupId(true); + rc.add(importFeedback); rc.setOutputMarkupId(true); add(rc); diff --git a/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index a76629bfcb6..6b7dae74698 100644 --- a/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -5,6 +5,7 @@
- +
Download - +
Download - +
-
- - - - - - - - -
- -
-
- -
-
-
+ + + + + + + +
+ +
+
+ +
+

+
- - - From 9f32bf795c02c4493acdb7e5329bfd14ea91c678 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 7 Oct 2026 18:57:26 +0300 Subject: [PATCH 58/76] Avoid automatic upload on file chooser --- .../AmpStructuresFormSectionFeature.java | 5 +- .../components/upload/FileUploadBehavior.java | 17 ++++- .../components/upload/FileUploadBehavior.js | 62 ++++++++++++++++++- .../components/upload/FileUploadPanel.java | 7 ++- .../AmpStructuresFormSectionFeature.html | 30 ++++----- 5 files changed, 100 insertions(+), 21 deletions(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 0b76df248eb..b9a36323502 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -397,9 +397,11 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { } }; importStructures.getButton().setDefaultFormProcessing(false); + importStructures.getButton().setOutputMarkupId(true); + rc.add(importStructures); FileUploadPanel fileUpload = new FileUploadPanel("file", String.valueOf(am.getObject().getAmpActivityId()), - fileItemModel) { + fileItemModel, importStructures.getButton().getMarkupId()) { private static final long serialVersionUID = 1L; @Override @@ -409,7 +411,6 @@ protected void onConfigure() { } }; - rc.add(importStructures); rc.add(fileUpload); FeedbackPanel importFeedback = new FeedbackPanel("importFeedback", new ContainerFeedbackMessageFilter(importStructures)); diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java index c23ae20feed..969120466f7 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java @@ -32,6 +32,8 @@ public class FileUploadBehavior extends Behavior { private final String activityId; private final IModel fileItemModel; + private final boolean deferUpload; + private final String importButtonMarkupId; /** * The name of the request parameter used for the multipart * Ajax request @@ -39,8 +41,19 @@ public class FileUploadBehavior extends Behavior { public static final String PARAM_NAME = "FILE-UPLOAD"; public FileUploadBehavior(String activityId, IModel fileItemModel) { + this(activityId, fileItemModel, false, null); + } + + public FileUploadBehavior(String activityId, IModel fileItemModel, boolean deferUpload) { + this(activityId, fileItemModel, deferUpload, null); + } + + public FileUploadBehavior(String activityId, IModel fileItemModel, boolean deferUpload, + String importButtonMarkupId) { this.activityId = activityId; this.fileItemModel = fileItemModel; + this.deferUpload = deferUpload; + this.importButtonMarkupId = importButtonMarkupId; } /** @@ -96,7 +109,9 @@ public Map getObject() { }; response.render(JavaScriptHeaderItem.forReference( new TextTemplateResourceReference(FileUploadBehavior.class, "FileUploadBehavior.js", variablesModel), String.valueOf(System.currentTimeMillis()), true)); - response.render(OnLoadHeaderItem.forScript("setupFileUpload('#" + markupId + "', '" + uploadUrl + "', '" + PARAM_NAME + "');")); + response.render(OnLoadHeaderItem.forScript("setupFileUpload('#" + markupId + "', '" + uploadUrl + "', '" + + PARAM_NAME + "', " + deferUpload + ", '" + + (importButtonMarkupId == null ? "" : importButtonMarkupId) + "');")); } static String appendSpringCsrfToken(String url) { diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js index 70d07f3caaa..9473b7c90b3 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js @@ -4,8 +4,50 @@ Wicket.Event.add(window, "domready", function(event){ $.getScript("/TEMPLATE/ampTemplate/script/common/FileTypeValidator.js"); -function setupFileUpload(componentId, componentUrl, componentParamName){ +function setupFileUpload(componentId, componentUrl, componentParamName, deferUpload, importButtonMarkupId){ $(function () { + var pendingUploadData = null; + var waitForUpload = false; + var uploadInProgress = false; + var allowImportClick = false; + var importButton = importButtonMarkupId ? document.getElementById(importButtonMarkupId) : null; + var fileInput = $(componentId).find('input[type=file]').get(0); + + function submitPendingUpload() { + var uploadData = pendingUploadData; + pendingUploadData = null; + if (uploadData) { + uploadInProgress = true; + uploadData.submit(); + } + } + + if (deferUpload && importButton && fileInput) { + $(componentId).find('.fileupload-progress, .fileupload-loading').hide(); + if (importButton.ampDeferredUploadClickHandler) { + importButton.removeEventListener('click', importButton.ampDeferredUploadClickHandler, true); + } + importButton.ampDeferredUploadClickHandler = function (event) { + if (allowImportClick) { + allowImportClick = false; + return; + } + + event.preventDefault(); + event.stopImmediatePropagation(); + if (uploadInProgress) { + return; + } + waitForUpload = true; + if (pendingUploadData) { + submitPendingUpload(); + } else { + fileInput.click(); + } + }; + importButton.addEventListener('click', importButton.ampDeferredUploadClickHandler, true); + } + $(componentId).fileupload({ url: componentUrl, paramName: componentParamName, @@ -29,7 +71,14 @@ function setupFileUpload(componentId, componentUrl, componentParamName){ } else { $(this).find('[role=fileUploadedMsg]').show(); $(this).find('[role=fileUploadedMsg]').html(" \"" + "${uploadStartedMsg}" + data.files[0].size + "\" bytes"); - data.submit(); + if (deferUpload) { + pendingUploadData = data; + if (waitForUpload) { + submitPendingUpload(); + } + } else { + data.submit(); + } } } catch(err) { alert(FileTypeValidator.errorMessage); @@ -42,6 +91,12 @@ function setupFileUpload(componentId, componentUrl, componentParamName){ //alert('upload done! result[' + JSON.stringify(data.result) + '] status[' + data.textStatus + '] jqXHR[' + JSON.stringify(data.jqXHR) +']'); var result = eval(data.result)[0]; $(this).find('[role=fileUploadedMsg]').html(result.uploadTxt); + if (deferUpload && waitForUpload && importButton) { + waitForUpload = false; + uploadInProgress = false; + allowImportClick = true; + importButton.click(); + } }, fail: function (e, data){ //alert('upload failed! result[' + JSON.stringify(data.result) + '] status[' + data.textStatus + '] jqXHR[' + JSON.stringify(data.jqXHR) +']'); @@ -49,6 +104,9 @@ function setupFileUpload(componentId, componentUrl, componentParamName){ $('#uploadLabel').text("${uploadNoFileLabel}"); $(this).find('[role=fileUploadedMsg]').html(''); $(this).find('[role=fileUploadedMsg]').hide(); + waitForUpload = false; + uploadInProgress = false; + pendingUploadData = null; } }); }); diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java index 500ebda2c97..423fee3e9bb 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java @@ -14,9 +14,14 @@ public class FileUploadPanel extends Panel { public FileUploadPanel(String id, String activityId, IModel fileItemModel) { + this(id, activityId, fileItemModel, null); + } + + public FileUploadPanel(String id, String activityId, IModel fileItemModel, + String importButtonMarkupId) { super(id); add (new Label("chooseFileLabel", TranslatorWorker.translateText("Choose file"))); add (new Label("noFileLabel", TranslatorWorker.translateText("No file chosen"))); - add(new FileUploadBehavior(activityId, fileItemModel)); + add(new FileUploadBehavior(activityId, fileItemModel, importButtonMarkupId != null, importButtonMarkupId)); } } diff --git a/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 3dafeb2c70d..fabfb0b1ded 100644 --- a/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -1,24 +1,24 @@ - - - -
From 6b4453a42373453a752dbab2e83b7654988fbdf0 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 7 Oct 2026 21:06:34 +0300 Subject: [PATCH 61/76] Use rest endpoint to upload files --- .../onepager/components/fields/AmpNewResourceFieldPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java index a5b6bcd85e1..5c5a87d24a8 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java @@ -154,7 +154,7 @@ public AmpNewResourceFieldPanel(final String id, AmpButtonField submit = new AmpButtonField("ajaxSubmit", "Add", true){ @Override protected void onSubmit(AjaxRequestTarget target, Form form) { - TemporaryDocument tmp = td.getObject(); + TemporaryActivityDocument tmp = td.getObject(); String response = stagedUploadResponse.getModelObject(); if (response != null && !response.trim().isEmpty()) { try { From 2d453dec1dd856f24acbce48dfd5a41e0d5245da Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 7 Oct 2026 21:39:04 +0300 Subject: [PATCH 62/76] Change upload status message --- .../onepager/components/upload/FileUploadBehavior.java | 3 +++ .../onepager/components/upload/FileUploadBehavior.js | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java index 30c3fc85870..4aceaf434ad 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java @@ -105,6 +105,9 @@ public void renderHead(final Component component, IHeaderResponse response) { variables.put("paramName", uploadParamName); variables.put("uploadFailedMsg", TranslatorUtil.getTranslatedText("Upload failed! Please try again.")); variables.put("uploadStartedMsg", TranslatorUtil.getTranslatedText("Upload started, please wait...")); + String uploadAction = importedRowsMarkupId == null ? "Add" : "Import Structures"; + variables.put("uploadPendingMsg", TranslatorUtil.getTranslatedText( + "File selected. Click " + uploadAction + " to upload.")); variables.put("uploadFailedTooBigMsg", TranslatorUtil.getTranslatedText("The file size limit is {size} MB. This file exceeds the limit.").replace("{size}", maxFileSizeGS)); variables.put("uploadMaxFileSize", Long.toString(Bytes.megabytes(Long.parseLong(maxFileSizeGS)).bytes())); variables.put("uploadNoFileLabel", TranslatorWorker.translateText("No file chosen")); diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js index 145def5658c..850043420bf 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js @@ -19,6 +19,9 @@ function setupFileUpload(componentId, componentUrl, componentParamName, deferUpl pendingUploadData = null; if (uploadData) { uploadInProgress = true; + var fileSize = uploadData.files[0].size; + $(componentId).find('[role=fileUploadedMsg]').show() + .html(" \"" + "${uploadStartedMsg}" + fileSize + "\" bytes"); uploadData.submit(); } } @@ -73,14 +76,17 @@ function setupFileUpload(componentId, componentUrl, componentParamName, deferUpl $(this).find('[role=fileUploadedMsg]').html(''); $(this).find('[role=fileUploadedMsg]').hide(); } else { - $(this).find('[role=fileUploadedMsg]').show(); - $(this).find('[role=fileUploadedMsg]').html(" \"" + "${uploadStartedMsg}" + data.files[0].size + "\" bytes"); if (deferUpload) { pendingUploadData = data; if (waitForUpload) { submitPendingUpload(); + } else { + $(this).find('[role=fileUploadedMsg]').show() + .text("${uploadPendingMsg}"); } } else { + $(this).find('[role=fileUploadedMsg]').show() + .html(" \"" + "${uploadStartedMsg}" + data.files[0].size + "\" bytes"); data.submit(); } } From fd2374504e8319f99544eb6c74f55cb7f0923828 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 8 Oct 2026 08:32:47 +0300 Subject: [PATCH 63/76] Correct upload status message --- .../amp/onepager/components/upload/FileUploadBehavior.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java index 4aceaf434ad..ccbd0a171b4 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java @@ -105,7 +105,8 @@ public void renderHead(final Component component, IHeaderResponse response) { variables.put("paramName", uploadParamName); variables.put("uploadFailedMsg", TranslatorUtil.getTranslatedText("Upload failed! Please try again.")); variables.put("uploadStartedMsg", TranslatorUtil.getTranslatedText("Upload started, please wait...")); - String uploadAction = importedRowsMarkupId == null ? "Add" : "Import Structures"; + String uploadAction = uploadUrlOverride != null && uploadUrlOverride.endsWith("/rest/gis/structures/import") + ? "Import Structures" : "Add"; variables.put("uploadPendingMsg", TranslatorUtil.getTranslatedText( "File selected. Click " + uploadAction + " to upload.")); variables.put("uploadFailedTooBigMsg", TranslatorUtil.getTranslatedText("The file size limit is {size} MB. This file exceeds the limit.").replace("{size}", maxFileSizeGS)); From 399f2284f819ebb4183bdd66f445cd343457f7f6 Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Thu, 8 Oct 2026 08:57:23 +0300 Subject: [PATCH 64/76] Change downloadLink addition to rc.add method Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../features/sections/AmpStructuresFormSectionFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 14952e1288c..d893c970dcc 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -439,7 +439,7 @@ public void writeData(Attributes attributes) { }; ResourceLink downloadLink = new ResourceLink<>("downloadLink", resourceReference); downloadLink.setOutputMarkupId(true); - add(downloadLink); + rc.add(downloadLink); AmpAjaxLinkField exportStructures = new AmpAjaxLinkField("exportStructures", "Export Structures", "Export Structures") { @Override From 3f178291920c9cc9d0e6be99022618f50bf616bd Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Thu, 8 Oct 2026 08:57:59 +0300 Subject: [PATCH 65/76] Add stagedUploadResponse to target after setting model Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../onepager/components/fields/AmpNewResourceFieldPanel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java index 5c5a87d24a8..934995df97e 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java @@ -181,7 +181,8 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { webLinkFeedbackContainer.setVisible(true); webLinkFeedbackLabel.setDefaultModelObject(FILE_PATH_NOT_SELECTED); } - stagedUploadResponse.setModelObject(""); +stagedUploadResponse.setModelObject(""); + target.add(stagedUploadResponse); } if (fileItemModel.getObject() != null) tmp.setFile(new FileUpload(fileItemModel.getObject())); From ef918e114bae76998bde1d164636b243df0afbdd Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Thu, 8 Oct 2026 09:00:46 +0300 Subject: [PATCH 66/76] Fix method call to add exportStructures Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../features/sections/AmpStructuresFormSectionFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index d893c970dcc..0d1b97294f0 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -450,7 +450,7 @@ public void onClick(AjaxRequestTarget target) { target.appendJavaScript("document.getElementById('" + downloadLinkMarkupId + "').click();"); } }; - add(exportStructures); +rc.add(exportStructures); } From c002b690ddb376d001f6a07e7920966fd0f2c3e7 Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Thu, 8 Oct 2026 09:14:35 +0300 Subject: [PATCH 67/76] Update file size validation for uploads Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../kernel/ampapi/endpoints/gis/StructureImportEndpoint.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java index b991357a565..c79a65ddffb 100644 --- a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java +++ b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java @@ -43,8 +43,7 @@ public List importWorkbook(@FormDataParam("file") InputStream file if (file == null) { throw badRequest("A workbook file is required."); } - long contentLength = request.getContentLengthLong(); - if (contentLength > MAX_REQUEST_SIZE) { +if (contentLength < 0 || contentLength > MAX_REQUEST_SIZE) { throw new WebApplicationException("Workbook exceeds the upload size limit.", Response.Status.REQUEST_ENTITY_TOO_LARGE); } From 070e3fa7afad8920823750574412fa0632304e00 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 8 Oct 2026 13:09:39 +0300 Subject: [PATCH 68/76] Expire staged uploads and limit file size and number --- .../features/AmpActivityFormFeature.java | 26 +++- .../tables/AmpResourcesFormTableFeature.java | 10 +- .../fields/AmpNewResourceFieldPanel.java | 21 +++- .../components/upload/FileUploadBehavior.js | 12 ++ .../amp/onepager/util/ActivityUtil.java | 27 ++++- .../endpoints/resource/ResourceEndpoint.java | 18 ++- .../helper/StagedResourceUploadStore.java | 111 +++++++++++++++++- 7 files changed, 205 insertions(+), 20 deletions(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/AmpActivityFormFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/AmpActivityFormFeature.java index f950bf32845..83537e3b3fa 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/AmpActivityFormFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/AmpActivityFormFeature.java @@ -58,6 +58,7 @@ import org.dgfoundation.amp.onepager.components.features.subsections.AmpDonorFundingInfoSubsectionFeature; import org.dgfoundation.amp.onepager.components.fields.*; import org.dgfoundation.amp.onepager.helper.ActionButtonCancelLink; +import org.dgfoundation.amp.onepager.helper.TemporaryActivityDocument; import org.dgfoundation.amp.onepager.models.AmpActivityModel; import org.dgfoundation.amp.onepager.models.TranslationDecoratorModel; import org.dgfoundation.amp.onepager.translation.TranslatorUtil; @@ -84,6 +85,7 @@ import org.digijava.module.message.triggers.ApprovedActivityTrigger; import org.digijava.module.message.triggers.NotApprovedActivityTrigger; import org.digijava.module.message.util.AmpMessageUtil; +import org.digijava.module.contentrepository.helper.StagedResourceUploadStore; import java.util.*; @@ -1128,7 +1130,29 @@ protected void saveMethod(AjaxRequestTarget target, throw new RedirectToUrlException(ActivityGatekeeper.buildRedirectLink(String.valueOf(a.getId()), currentUserId)); } - ActivityUtil.saveActivity((AmpActivityModel) am, draft, rejected); + HashSet pendingResources = wicketSession.getMetaData(OnePagerConst.RESOURCES_NEW_ITEMS); + if (pendingResources != null) { + for (TemporaryActivityDocument resource : pendingResources) { + if (resource.getStagedUploadId() != null + && StagedResourceUploadStore.get(SessionUtil.getCurrentServletRequest(), + resource.getStagedUploadId()) == null) { + feedbackPanel.error(TranslatorUtil.getTranslatedText( + "A selected document upload is no longer available. Remove it and upload the file again.")); + target.add(feedbackPanel); + return; + } + } + } + + try { + ActivityUtil.saveActivity((AmpActivityModel) am, draft, rejected); + } catch (ActivityUtil.StagedResourceSaveException e) { + logger.error("Unable to save a staged activity resource.", e); + feedbackPanel.error(TranslatorUtil.getTranslatedText( + "A related document could not be saved. The activity was not saved; retry or remove the document.")); + target.add(feedbackPanel); + return; + } info(TranslatorUtil.getTranslatedText("Activity saved successfully")); diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/tables/AmpResourcesFormTableFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/tables/AmpResourcesFormTableFeature.java index 28a1b415160..767c5ce8f7d 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/tables/AmpResourcesFormTableFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/tables/AmpResourcesFormTableFeature.java @@ -221,7 +221,8 @@ protected void populateItem(final ListItem item) { else if (item.getModelObject().getStagedUploadId() != null) { TemporaryDocumentData staged = StagedResourceUploadStore.get( SessionUtil.getCurrentServletRequest(), item.getModelObject().getStagedUploadId()); - drs = new DownloadResourceStream(staged.getFormFile(), item.getModelObject().getFileName()); + drs = staged == null || staged.getFormFile() == null ? null + : new DownloadResourceStream(staged.getFormFile(), item.getModelObject().getFileName()); } else drs = new DownloadResourceStream(item.getModelObject().getFile(), item.getModelObject().getFileName()); @@ -240,9 +241,13 @@ else if (item.getModelObject().getStagedUploadId() != null) { Link downloadLink = new Link("download") { @Override public void onClick() { - getRequestCycle().scheduleRequestHandlerAfterCurrent(new ResourceStreamRequestHandler(drs, drs.getFileName())); + if (drs != null) { + getRequestCycle().scheduleRequestHandlerAfterCurrent( + new ResourceStreamRequestHandler(drs, drs.getFileName())); + } } }; + downloadLink.setEnabled(drs != null); item.add(downloadLink); String contentType = item.getModelObject().getFileName(); @@ -276,6 +281,7 @@ public void onClick(AjaxRequestTarget target) { if (newItems != null) { newItems.remove(item.getModelObject()); } + list.removeAll(); StagedResourceUploadStore.delete(SessionUtil.getCurrentServletRequest(), item.getModelObject().getStagedUploadId()); } diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java index 5c5a87d24a8..963ae022038 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java @@ -181,7 +181,6 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { webLinkFeedbackContainer.setVisible(true); webLinkFeedbackLabel.setDefaultModelObject(FILE_PATH_NOT_SELECTED); } - stagedUploadResponse.setModelObject(""); } if (fileItemModel.getObject() != null) tmp.setFile(new FileUpload(fileItemModel.getObject())); @@ -222,6 +221,7 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { tmp.setTranslatedNoteList(getTranslationsForField( tmp.getNewTemporaryDocumentId(), "description")); newItemsSet.add((TemporaryActivityDocument) tmp); + stagedUploadResponse.setModelObject(""); TemporaryActivityDocument tmpDoc = new TemporaryActivityDocument(); String docId = generateResourceKey("newResource"); newResourceIdModel.setObject(docId); @@ -248,7 +248,7 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { form.add(fileUpload); form.add(stagedUploadResponse); form.add(submit); - form.add(createCancelButton()); + form.add(createCancelButton(stagedUploadResponse, td)); createWebLinkFeedbackContainer(); form.add(webLinkFeedbackContainer); @@ -272,10 +272,23 @@ protected void onConfigure() { return addNewLink; } - protected AmpAjaxLinkField createCancelButton() { + protected AmpAjaxLinkField createCancelButton(final HiddenField stagedUploadResponse, + final IModel temporaryDocumentModel) { AmpAjaxLinkField cancel = new AmpAjaxLinkField("cancel", "Cancel", "Cancel") { @Override protected void onClick(AjaxRequestTarget target) { + TemporaryActivityDocument temporaryDocument = temporaryDocumentModel.getObject(); + if (temporaryDocument.getStagedUploadId() != null) { + StagedResourceUploadStore.delete(SessionUtil.getCurrentServletRequest(), + temporaryDocument.getStagedUploadId()); + temporaryDocument.setStagedUploadId(null); + } + String deleteUrl = SessionUtil.getCurrentServletRequest().getContextPath() + + "/rest/resource/stage-upload?uploadId="; + target.appendJavaScript("(function(){var response=document.getElementById('" + + stagedUploadResponse.getMarkupId() + "').value;try{var uploads=JSON.parse(response||'[]');" + + "uploads.forEach(function(upload){$.ajax({url:'" + deleteUrl + + "'+encodeURIComponent(upload.uploadId),type:'DELETE'});});}catch(e){}})();"); target.appendJavaScript("$('#" + getToggleId() + "').hide();"); target.appendJavaScript("$('#" + getToggleId() + "').find('[role=fileUploadedMsg]').html('');"); target.appendJavaScript("$('#uploadLabel').text('" + TranslatorWorker.translateText("No file chosen") + "');"); @@ -285,7 +298,7 @@ protected void onClick(AjaxRequestTarget target) { return cancel; } - + protected void createWebLinkFeedbackContainer() { webLinkFeedbackContainer = new WebMarkupContainer("webLinkFeedbackContainer"); webLinkFeedbackContainer.setOutputMarkupId(true); diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js index 850043420bf..e9397572b12 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js @@ -18,6 +18,18 @@ function setupFileUpload(componentId, componentUrl, componentParamName, deferUpl var uploadData = pendingUploadData; pendingUploadData = null; if (uploadData) { + if (componentUrl.indexOf('/rest/resource/stage-upload') !== -1 && importedRows) { + try { + var previousUploads = JSON.parse(importedRows.value || '[]'); + if (previousUploads.length && previousUploads[0].uploadId) { + var separator = componentUrl.indexOf('?') === -1 ? '?' : '&'; + uploadData.url = componentUrl + separator + 'replaceUploadId=' + + encodeURIComponent(previousUploads[0].uploadId); + } + } catch (error) { + // Leave the original URL; the server still enforces the session quota. + } + } uploadInProgress = true; var fileSize = uploadData.files[0].size; $(componentId).find('[role=fileUploadedMsg]').show() diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/util/ActivityUtil.java b/amp/src/main/java/org/dgfoundation/amp/onepager/util/ActivityUtil.java index f4ecaf07dc4..3744dec96a2 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/util/ActivityUtil.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/util/ActivityUtil.java @@ -135,6 +135,9 @@ public static AmpActivityVersion saveActivity(AmpActivityVersion oldA, Collectio a = saveActivityNewVersion(oldA, values, cumulativeValues, ampCurrentMember, draft, session, saveContext, editorStore, site); } catch (Exception exception) { + if (exception instanceof StagedResourceSaveException) { + throw (StagedResourceSaveException) exception; + } logger.error("Error saving activity:", exception); // Log the exception throw new RuntimeException("Can't save activity:", exception); } @@ -1211,8 +1214,7 @@ private static void insertResources(AmpActivityVersion a, HashSet createDocResource( @ApiMethod(authTypes = AuthRule.AUTHENTICATED, id = "stageResourceUpload", ui = false) @ApiOperation("Stage a resource file for a pending activity form") public List stageResourceUpload(@FormDataParam("file") InputStream uploadedInputStream, - @FormDataParam("file") FormDataContentDisposition fileDetail, @Context HttpServletRequest request) { + @FormDataParam("file") FormDataContentDisposition fileDetail, + @QueryParam("replaceUploadId") String replaceUploadId, + @Context HttpServletRequest request) { if (uploadedInputStream == null || fileDetail == null || fileDetail.getFileName() == null) { throw new WebApplicationException("A file is required.", Response.Status.BAD_REQUEST); } @@ -287,10 +289,13 @@ public List stageResourceUpload(@FormDataParam("file") Inp temporaryDocument.setYearofPublication(String.valueOf(uploadedAt.get(Calendar.YEAR))); temporaryDocument.setFormFile(new StagedFormFile(stagedFile, fileName, fileDetail.getType())); temporaryDocument.setStagedFile(stagedFile); - String uploadId = StagedResourceUploadStore.store(request, temporaryDocument); + String uploadId = StagedResourceUploadStore.store(request, temporaryDocument, replaceUploadId); stagedFile = null; return Collections.singletonList(new StagedResourceUpload(uploadId)); + } catch (StagedResourceUploadStore.UploadLimitExceededException e) { + throw new WebApplicationException("The session has too many staged uploads. Remove an upload and try again.", + Response.Status.REQUEST_ENTITY_TOO_LARGE); } catch (IOException e) { logger.error("Failed to stage resource upload.", e); throw new WebApplicationException("Failed to process the uploaded file.", e, @@ -300,6 +305,15 @@ public List stageResourceUpload(@FormDataParam("file") Inp } } + @DELETE + @Path("stage-upload") + @ApiMethod(authTypes = AuthRule.AUTHENTICATED, id = "deleteStagedResourceUpload", ui = false) + @ApiOperation("Delete a staged resource file that is no longer needed") + public void deleteStagedResourceUpload(@QueryParam("uploadId") String uploadId, + @Context HttpServletRequest request) { + StagedResourceUploadStore.delete(request, uploadId); + } + public static class StagedResourceUpload { private String uploadId; diff --git a/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java b/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java index 5e09825b088..6efb12e4c22 100644 --- a/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java +++ b/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java @@ -5,21 +5,58 @@ import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; /** Session-scoped storage for files awaiting an activity save. */ public final class StagedResourceUploadStore { private static final String SESSION_ATTRIBUTE = StagedResourceUploadStore.class.getName(); + public static final int MAX_UPLOADS_PER_SESSION = 5; + public static final long MAX_STAGED_BYTES_PER_SESSION = 100L * 1024 * 1024; + private static final long UPLOAD_TTL_MILLIS = 30L * 60 * 1000; + private static final ScheduledExecutorService EXPIRATION_EXECUTOR = Executors.newSingleThreadScheduledExecutor(task -> { + Thread thread = new Thread(task, "amp-staged-upload-expiration"); + thread.setDaemon(true); + thread.setContextClassLoader(ClassLoader.getSystemClassLoader()); + return thread; + }); private StagedResourceUploadStore() { } public static String store(HttpServletRequest request, TemporaryDocumentData upload) { + return store(request, upload, null); + } + + public static String store(HttpServletRequest request, TemporaryDocumentData upload, String replacedUploadId) { String uploadId = UUID.randomUUID().toString(); HttpSession session = request.getSession(); synchronized (session) { - getUploads(session, true).put(uploadId, upload); + StagedUploads uploads = getUploads(session, true); + uploads.removeExpired(); + TemporaryDocumentData replacedUpload = uploads.getUploadWithoutExpiry(replacedUploadId); + int retainedUploadCount = uploads.size() - (replacedUpload == null ? 0 : 1); + long retainedBytes = uploads.getStagedBytes() + - (replacedUpload == null ? 0 : replacedUpload.getTrueUploadedFileSize()); + long byteLimit = Math.max(MAX_STAGED_BYTES_PER_SESSION, upload.getTrueUploadedFileSize()); + if (retainedUploadCount >= MAX_UPLOADS_PER_SESSION + || retainedBytes + upload.getTrueUploadedFileSize() > byteLimit) { + throw new UploadLimitExceededException(); + } + if (replacedUpload != null) { + uploads.removeUpload(replacedUploadId); + replacedUpload.deleteStagedFile(); + } + StoredUpload storedUpload = new StoredUpload(upload, System.currentTimeMillis()); + storedUpload.expirationTask = EXPIRATION_EXECUTOR.schedule(upload::deleteStagedFile, + UPLOAD_TTL_MILLIS, TimeUnit.MILLISECONDS); + uploads.put(uploadId, storedUpload); } return uploadId; } @@ -31,7 +68,7 @@ public static TemporaryDocumentData get(HttpServletRequest request, String uploa HttpSession session = request.getSession(false); synchronized (session) { StagedUploads uploads = getUploads(session, false); - return uploads == null ? null : uploads.get(uploadId); + return uploads == null ? null : uploads.getUpload(uploadId); } } @@ -43,7 +80,7 @@ public static void delete(HttpServletRequest request, String uploadId) { TemporaryDocumentData upload; synchronized (session) { StagedUploads uploads = getUploads(session, false); - upload = uploads == null ? null : uploads.remove(uploadId); + upload = uploads == null ? null : uploads.removeUpload(uploadId); } if (upload != null) { upload.deleteStagedFile(); @@ -71,18 +108,80 @@ private static StagedUploads getUploads(HttpSession session, boolean create) { } } - private static class StagedUploads extends HashMap + public static class UploadLimitExceededException extends RuntimeException { + private static final long serialVersionUID = 1L; + } + + private static class StoredUpload { + private final TemporaryDocumentData upload; + private final long createdAt; + private ScheduledFuture expirationTask; + + private StoredUpload(TemporaryDocumentData upload, long createdAt) { + this.upload = upload; + this.createdAt = createdAt; + } + + private void cancelExpiration() { + if (expirationTask != null) { + expirationTask.cancel(false); + } + } + } + + private static class StagedUploads extends HashMap implements HttpSessionBindingListener { private static final long serialVersionUID = 1L; + private TemporaryDocumentData getUpload(String uploadId) { + removeExpired(); + return getUploadWithoutExpiry(uploadId); + } + + private TemporaryDocumentData getUploadWithoutExpiry(String uploadId) { + StoredUpload upload = uploadId == null ? null : get(uploadId); + return upload == null ? null : upload.upload; + } + + private TemporaryDocumentData removeUpload(String uploadId) { + removeExpired(); + StoredUpload upload = remove(uploadId); + if (upload != null) { + upload.cancelExpiration(); + } + return upload == null ? null : upload.upload; + } + + private long getStagedBytes() { + long total = 0; + for (StoredUpload upload : values()) { + total += upload.upload.getTrueUploadedFileSize(); + } + return total; + } + + private void removeExpired() { + long expiredBefore = System.currentTimeMillis() - UPLOAD_TTL_MILLIS; + Iterator> iterator = entrySet().iterator(); + while (iterator.hasNext()) { + StoredUpload upload = iterator.next().getValue(); + if (upload.createdAt <= expiredBefore) { + iterator.remove(); + upload.cancelExpiration(); + upload.upload.deleteStagedFile(); + } + } + } + @Override public void valueBound(HttpSessionBindingEvent event) { } @Override public void valueUnbound(HttpSessionBindingEvent event) { - for (TemporaryDocumentData upload : values()) { - upload.deleteStagedFile(); + for (StoredUpload upload : values()) { + upload.cancelExpiration(); + upload.upload.deleteStagedFile(); } clear(); } From 4757e87c2d2a6322cf488b0270f3f587763e1e08 Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Thu, 8 Oct 2026 13:23:13 +0300 Subject: [PATCH 69/76] Refactor byte limit calculation for uploads Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../contentrepository/helper/StagedResourceUploadStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java b/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java index 6efb12e4c22..5e01e628234 100644 --- a/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java +++ b/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java @@ -44,7 +44,7 @@ public static String store(HttpServletRequest request, TemporaryDocumentData upl int retainedUploadCount = uploads.size() - (replacedUpload == null ? 0 : 1); long retainedBytes = uploads.getStagedBytes() - (replacedUpload == null ? 0 : replacedUpload.getTrueUploadedFileSize()); - long byteLimit = Math.max(MAX_STAGED_BYTES_PER_SESSION, upload.getTrueUploadedFileSize()); + long byteLimit = MAX_STAGED_BYTES_PER_SESSION; if (retainedUploadCount >= MAX_UPLOADS_PER_SESSION || retainedBytes + upload.getTrueUploadedFileSize() > byteLimit) { throw new UploadLimitExceededException(); From 30cbb664d06c9198364e576daff8ead454c34fee Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Thu, 8 Oct 2026 13:23:41 +0300 Subject: [PATCH 70/76] Fix content length check for workbook upload Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../kernel/ampapi/endpoints/gis/StructureImportEndpoint.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java index c79a65ddffb..d473fcb05a7 100644 --- a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java +++ b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java @@ -43,8 +43,8 @@ public List importWorkbook(@FormDataParam("file") InputStream file if (file == null) { throw badRequest("A workbook file is required."); } -if (contentLength < 0 || contentLength > MAX_REQUEST_SIZE) { - throw new WebApplicationException("Workbook exceeds the upload size limit.", + long contentLength = request.getContentLengthLong(); + if (contentLength < 0 || contentLength > MAX_REQUEST_SIZE) { Response.Status.REQUEST_ENTITY_TOO_LARGE); } From eb750d117eec8727d4f938d62b75c8e7c11e6330 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 8 Oct 2026 13:51:26 +0300 Subject: [PATCH 71/76] Expire staged uploads and limit file size and number --- .../helper/StagedResourceUploadStore.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java b/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java index 6efb12e4c22..02668bba0d8 100644 --- a/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java +++ b/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java @@ -4,6 +4,9 @@ import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -54,8 +57,6 @@ public static String store(HttpServletRequest request, TemporaryDocumentData upl replacedUpload.deleteStagedFile(); } StoredUpload storedUpload = new StoredUpload(upload, System.currentTimeMillis()); - storedUpload.expirationTask = EXPIRATION_EXECUTOR.schedule(upload::deleteStagedFile, - UPLOAD_TTL_MILLIS, TimeUnit.MILLISECONDS); uploads.put(uploadId, storedUpload); } return uploadId; @@ -112,14 +113,28 @@ public static class UploadLimitExceededException extends RuntimeException { private static final long serialVersionUID = 1L; } - private static class StoredUpload { + private static class StoredUpload implements Serializable { + private static final long serialVersionUID = 1L; + private final TemporaryDocumentData upload; private final long createdAt; - private ScheduledFuture expirationTask; + private transient ScheduledFuture expirationTask; private StoredUpload(TemporaryDocumentData upload, long createdAt) { this.upload = upload; this.createdAt = createdAt; + scheduleExpiration(); + } + + private void scheduleExpiration() { + long remainingMillis = Math.max(0, createdAt + UPLOAD_TTL_MILLIS - System.currentTimeMillis()); + expirationTask = EXPIRATION_EXECUTOR.schedule(upload::deleteStagedFile, + remainingMillis, TimeUnit.MILLISECONDS); + } + + private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { + input.defaultReadObject(); + scheduleExpiration(); } private void cancelExpiration() { From 813172c4fe373bf148465e41ab7cbc3611a94b59 Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Thu, 8 Oct 2026 13:51:54 +0300 Subject: [PATCH 72/76] Add exception for exceeding workbook size limit Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../kernel/ampapi/endpoints/gis/StructureImportEndpoint.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java index d473fcb05a7..92dae8108b3 100644 --- a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java +++ b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java @@ -44,7 +44,8 @@ public List importWorkbook(@FormDataParam("file") InputStream file throw badRequest("A workbook file is required."); } long contentLength = request.getContentLengthLong(); - if (contentLength < 0 || contentLength > MAX_REQUEST_SIZE) { +if (contentLength < 0 || contentLength > MAX_REQUEST_SIZE) { + throw new WebApplicationException("The uploaded workbook exceeds the size limit.", Response.Status.REQUEST_ENTITY_TOO_LARGE); } From ff0bc221fcc835de6c11edbbde9d25a7cd935e95 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 8 Oct 2026 14:39:33 +0300 Subject: [PATCH 73/76] Wrongly placed container error --- .../features/sections/AmpStructuresFormSectionFeature.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 0d1b97294f0..14952e1288c 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -439,7 +439,7 @@ public void writeData(Attributes attributes) { }; ResourceLink downloadLink = new ResourceLink<>("downloadLink", resourceReference); downloadLink.setOutputMarkupId(true); - rc.add(downloadLink); + add(downloadLink); AmpAjaxLinkField exportStructures = new AmpAjaxLinkField("exportStructures", "Export Structures", "Export Structures") { @Override @@ -450,7 +450,7 @@ public void onClick(AjaxRequestTarget target) { target.appendJavaScript("document.getElementById('" + downloadLinkMarkupId + "').click();"); } }; -rc.add(exportStructures); + add(exportStructures); } From 3a43de1e84e9e2e8a49dec82b1c2371b100365f3 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 8 Oct 2026 15:32:00 +0300 Subject: [PATCH 74/76] Wrongly placed container error --- .../sections/AmpStructuresFormSectionFeature.java | 3 ++- .../components/upload/FileUploadBehavior.java | 11 ++++++++--- .../onepager/components/upload/FileUploadPanel.java | 8 +++++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 14952e1288c..5d6fb17a304 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -393,7 +393,8 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { rc.add(importStructures); FileUploadPanel fileUpload = new FileUploadPanel("file", String.valueOf(am.getObject().getAmpActivityId()), - null, "/rest/gis/structures/import", importStructures.getButton().getMarkupId(), importedRowsField.getMarkupId()) { + null, "/rest/gis/structures/import", importStructures.getButton().getMarkupId(), importedRowsField.getMarkupId(), + "Import Structures") { private static final long serialVersionUID = 1L; @Override diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java index ccbd0a171b4..4f8f1dfa049 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java @@ -36,6 +36,7 @@ public class FileUploadBehavior extends Behavior { private final String importButtonMarkupId; private final String importedRowsMarkupId; private final String uploadUrlOverride; + private final String uploadPendingAction; /** * The name of the request parameter used for the multipart * Ajax request @@ -48,12 +49,18 @@ public FileUploadBehavior(String activityId, IModel fileItemModel) { public FileUploadBehavior(String activityId, IModel fileItemModel, String uploadUrlOverride, String importButtonMarkupId, String importedRowsMarkupId) { + this(activityId, fileItemModel, uploadUrlOverride, importButtonMarkupId, importedRowsMarkupId, "Add"); + } + + public FileUploadBehavior(String activityId, IModel fileItemModel, String uploadUrlOverride, + String importButtonMarkupId, String importedRowsMarkupId, String uploadPendingAction) { this.activityId = activityId; this.fileItemModel = fileItemModel; this.uploadUrlOverride = uploadUrlOverride; this.deferUpload = importButtonMarkupId != null; this.importButtonMarkupId = importButtonMarkupId; this.importedRowsMarkupId = importedRowsMarkupId; + this.uploadPendingAction = uploadPendingAction; } /** @@ -105,10 +112,8 @@ public void renderHead(final Component component, IHeaderResponse response) { variables.put("paramName", uploadParamName); variables.put("uploadFailedMsg", TranslatorUtil.getTranslatedText("Upload failed! Please try again.")); variables.put("uploadStartedMsg", TranslatorUtil.getTranslatedText("Upload started, please wait...")); - String uploadAction = uploadUrlOverride != null && uploadUrlOverride.endsWith("/rest/gis/structures/import") - ? "Import Structures" : "Add"; variables.put("uploadPendingMsg", TranslatorUtil.getTranslatedText( - "File selected. Click " + uploadAction + " to upload.")); + "File selected. Click " + uploadPendingAction + " to upload.")); variables.put("uploadFailedTooBigMsg", TranslatorUtil.getTranslatedText("The file size limit is {size} MB. This file exceeds the limit.").replace("{size}", maxFileSizeGS)); variables.put("uploadMaxFileSize", Long.toString(Bytes.megabytes(Long.parseLong(maxFileSizeGS)).bytes())); variables.put("uploadNoFileLabel", TranslatorWorker.translateText("No file chosen")); diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java index bcad8df9fc6..81dd0e7c34a 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java @@ -19,9 +19,15 @@ public FileUploadPanel(String id, String activityId, IModel fileItemMo public FileUploadPanel(String id, String activityId, IModel fileItemModel, String uploadUrl, String importButtonMarkupId, String importedRowsMarkupId) { + this(id, activityId, fileItemModel, uploadUrl, importButtonMarkupId, importedRowsMarkupId, "Add"); + } + + public FileUploadPanel(String id, String activityId, IModel fileItemModel, + String uploadUrl, String importButtonMarkupId, String importedRowsMarkupId, String uploadPendingAction) { super(id); add (new Label("chooseFileLabel", TranslatorWorker.translateText("Choose file"))); add (new Label("noFileLabel", TranslatorWorker.translateText("No file chosen"))); - add(new FileUploadBehavior(activityId, fileItemModel, uploadUrl, importButtonMarkupId, importedRowsMarkupId)); + add(new FileUploadBehavior(activityId, fileItemModel, uploadUrl, importButtonMarkupId, importedRowsMarkupId, + uploadPendingAction)); } } From 74542bb0e959a7034cae1b191d903c2229eb41b3 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 8 Oct 2026 16:00:33 +0300 Subject: [PATCH 75/76] Wrongly placed container error --- .../sections/AmpStructuresFormSectionFeature.java | 3 +-- .../components/upload/FileUploadBehavior.java | 11 ++--------- .../onepager/components/upload/FileUploadBehavior.js | 7 ++++++- .../onepager/components/upload/FileUploadPanel.java | 8 +------- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 5d6fb17a304..14952e1288c 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -393,8 +393,7 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { rc.add(importStructures); FileUploadPanel fileUpload = new FileUploadPanel("file", String.valueOf(am.getObject().getAmpActivityId()), - null, "/rest/gis/structures/import", importStructures.getButton().getMarkupId(), importedRowsField.getMarkupId(), - "Import Structures") { + null, "/rest/gis/structures/import", importStructures.getButton().getMarkupId(), importedRowsField.getMarkupId()) { private static final long serialVersionUID = 1L; @Override diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java index 4f8f1dfa049..df82711fdc1 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java @@ -36,7 +36,6 @@ public class FileUploadBehavior extends Behavior { private final String importButtonMarkupId; private final String importedRowsMarkupId; private final String uploadUrlOverride; - private final String uploadPendingAction; /** * The name of the request parameter used for the multipart * Ajax request @@ -49,18 +48,12 @@ public FileUploadBehavior(String activityId, IModel fileItemModel) { public FileUploadBehavior(String activityId, IModel fileItemModel, String uploadUrlOverride, String importButtonMarkupId, String importedRowsMarkupId) { - this(activityId, fileItemModel, uploadUrlOverride, importButtonMarkupId, importedRowsMarkupId, "Add"); - } - - public FileUploadBehavior(String activityId, IModel fileItemModel, String uploadUrlOverride, - String importButtonMarkupId, String importedRowsMarkupId, String uploadPendingAction) { this.activityId = activityId; this.fileItemModel = fileItemModel; this.uploadUrlOverride = uploadUrlOverride; this.deferUpload = importButtonMarkupId != null; this.importButtonMarkupId = importButtonMarkupId; this.importedRowsMarkupId = importedRowsMarkupId; - this.uploadPendingAction = uploadPendingAction; } /** @@ -112,8 +105,8 @@ public void renderHead(final Component component, IHeaderResponse response) { variables.put("paramName", uploadParamName); variables.put("uploadFailedMsg", TranslatorUtil.getTranslatedText("Upload failed! Please try again.")); variables.put("uploadStartedMsg", TranslatorUtil.getTranslatedText("Upload started, please wait...")); - variables.put("uploadPendingMsg", TranslatorUtil.getTranslatedText( - "File selected. Click " + uploadPendingAction + " to upload.")); + variables.put("uploadPendingTemplate", TranslatorUtil.getTranslatedText( + "File selected. Click {action} to upload.")); variables.put("uploadFailedTooBigMsg", TranslatorUtil.getTranslatedText("The file size limit is {size} MB. This file exceeds the limit.").replace("{size}", maxFileSizeGS)); variables.put("uploadMaxFileSize", Long.toString(Bytes.megabytes(Long.parseLong(maxFileSizeGS)).bytes())); variables.put("uploadNoFileLabel", TranslatorWorker.translateText("No file chosen")); diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js index e9397572b12..93ee48eb4a9 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js @@ -14,6 +14,11 @@ function setupFileUpload(componentId, componentUrl, componentParamName, deferUpl var importedRows = importedRowsMarkupId ? document.getElementById(importedRowsMarkupId) : null; var fileInput = $(componentId).find('input[type=file]').get(0); + function getPendingUploadMessage() { + var action = importButton && (importButton.value || importButton.textContent); + return "${uploadPendingTemplate}".replace('{action}', action || 'Add'); + } + function submitPendingUpload() { var uploadData = pendingUploadData; pendingUploadData = null; @@ -94,7 +99,7 @@ function setupFileUpload(componentId, componentUrl, componentParamName, deferUpl submitPendingUpload(); } else { $(this).find('[role=fileUploadedMsg]').show() - .text("${uploadPendingMsg}"); + .text(getPendingUploadMessage()); } } else { $(this).find('[role=fileUploadedMsg]').show() diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java index 81dd0e7c34a..bcad8df9fc6 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java @@ -19,15 +19,9 @@ public FileUploadPanel(String id, String activityId, IModel fileItemMo public FileUploadPanel(String id, String activityId, IModel fileItemModel, String uploadUrl, String importButtonMarkupId, String importedRowsMarkupId) { - this(id, activityId, fileItemModel, uploadUrl, importButtonMarkupId, importedRowsMarkupId, "Add"); - } - - public FileUploadPanel(String id, String activityId, IModel fileItemModel, - String uploadUrl, String importButtonMarkupId, String importedRowsMarkupId, String uploadPendingAction) { super(id); add (new Label("chooseFileLabel", TranslatorWorker.translateText("Choose file"))); add (new Label("noFileLabel", TranslatorWorker.translateText("No file chosen"))); - add(new FileUploadBehavior(activityId, fileItemModel, uploadUrl, importButtonMarkupId, importedRowsMarkupId, - uploadPendingAction)); + add(new FileUploadBehavior(activityId, fileItemModel, uploadUrl, importButtonMarkupId, importedRowsMarkupId)); } } From 6ce49d139b919bd33d129fa69fb846325e127d25 Mon Sep 17 00:00:00 2001 From: Brian Mokandu Date: Thu, 8 Oct 2026 16:14:26 +0300 Subject: [PATCH 76/76] Remove deleteOnExit for staged upload file Remove deleteOnExit call for staged file as cleanup is handled by StagedResourceUploadStore. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../kernel/ampapi/endpoints/resource/ResourceEndpoint.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/resource/ResourceEndpoint.java b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/resource/ResourceEndpoint.java index b6c6a3f19d1..4016fbfe722 100644 --- a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/resource/ResourceEndpoint.java +++ b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/resource/ResourceEndpoint.java @@ -260,7 +260,7 @@ public List stageResourceUpload(@FormDataParam("file") Inp File stagedFile = null; try { stagedFile = File.createTempFile("amp-resource-upload-", ".tmp"); - stagedFile.deleteOnExit(); +// StagedResourceUploadStore owns cleanup for this temporary file. FileUtils.copyInputStreamToFile(new BoundedInputStream(uploadedInputStream, maxFileSize + 1), stagedFile); if (stagedFile.length() > maxFileSize) { throw new WebApplicationException("The file exceeds the upload size limit.",
From 5b8e49c0d273360478f81a5ec14ba7ea8da2cbb4 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 7 Oct 2026 18:03:40 +0300 Subject: [PATCH 57/76] Allign structures section correctly --- .../AmpStructuresFormSectionFeature.java | 15 ++++++++++-- .../AmpStructuresFormSectionFeature.html | 24 ++++++------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index 85c34b59c44..0b76df248eb 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -338,9 +338,10 @@ public void onClick(AjaxRequestTarget target) { final Model fileItemModel = new Model(); - FileUploadPanel fileUpload = new FileUploadPanel("file",String.valueOf(am.getObject().getAmpActivityId()), fileItemModel); final WebMarkupContainer rc = new WebMarkupContainer("resourcePanel"); AmpButtonField importStructures = new AmpButtonField("ajaxSubmit", "Import Structures", true) { + private static final long serialVersionUID = 1L; + @Override protected void onSubmit(AjaxRequestTarget target, Form form) { if (fileItemModel.getObject() != null) { @@ -397,9 +398,19 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { }; importStructures.getButton().setDefaultFormProcessing(false); + FileUploadPanel fileUpload = new FileUploadPanel("file", String.valueOf(am.getObject().getAmpActivityId()), + fileItemModel) { + private static final long serialVersionUID = 1L; + + @Override + protected void onConfigure() { + super.onConfigure(); + setVisible(importStructures.isEnabledInHierarchy()); + } + }; - rc.add(fileUpload); rc.add(importStructures); + rc.add(fileUpload); FeedbackPanel importFeedback = new FeedbackPanel("importFeedback", new ContainerFeedbackMessageFilter(importStructures)); importFeedback.setOutputMarkupId(true); diff --git a/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index 6b7dae74698..3dafeb2c70d 100644 --- a/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -1,29 +1,19 @@
+ +
- - - - - - - -
- -
-
- -
-
+
+
+ +

+ Download
- - -
-
-
-
- +
+
+
+
+
+
+
+
+ +
+
+
+ Download + +
-
-
- Download -
From d7ad2b25b1a7ecdd86bff537be3a742f25c1a300 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 7 Oct 2026 19:16:19 +0300 Subject: [PATCH 59/76] Use rest endpoitn to upload structures --- .../AmpStructuresFormSectionFeature.java | 112 +++++------ .../components/upload/FileUploadBehavior.java | 37 ++-- .../components/upload/FileUploadBehavior.js | 26 ++- .../components/upload/FileUploadPanel.java | 6 +- .../gis/StructureImportEndpoint.java | 181 ++++++++++++++++++ .../AmpStructuresFormSectionFeature.html | 1 + 6 files changed, 272 insertions(+), 91 deletions(-) create mode 100644 amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java index b9a36323502..14952e1288c 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.java @@ -4,12 +4,11 @@ */ package org.dgfoundation.amp.onepager.components.features.sections; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; -import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; @@ -23,9 +22,9 @@ import org.apache.wicket.markup.html.TransparentWebMarkupContainer; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.form.HiddenField; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.form.upload.FileUpload; -import org.apache.wicket.markup.html.form.upload.FileUploadField; import org.apache.wicket.markup.html.link.ResourceLink; import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.model.AbstractReadOnlyModel; @@ -36,8 +35,6 @@ import org.apache.wicket.request.resource.AbstractResource; import org.apache.wicket.request.resource.IResource; import org.apache.wicket.request.resource.ResourceReference; -import org.apache.wicket.util.lang.Bytes; -import org.apache.wicket.util.upload.FileItem; import org.dgfoundation.amp.onepager.OnePagerConst; import org.dgfoundation.amp.onepager.OnePagerUtil; import org.dgfoundation.amp.onepager.components.ListEditorRemoveButton; @@ -54,6 +51,7 @@ import org.dgfoundation.amp.onepager.helper.structure.MapData; import org.dgfoundation.amp.onepager.helper.structure.StructureData; import org.digijava.kernel.ampapi.endpoints.util.ObjectMapperUtils; +import org.digijava.kernel.ampapi.endpoints.gis.StructureImportEndpoint; import org.digijava.kernel.translator.TranslatorWorker; import org.digijava.module.aim.dbentity.*; import org.digijava.module.aim.util.StructuresUtil; @@ -337,61 +335,55 @@ public void onClick(AjaxRequestTarget target) { - final Model fileItemModel = new Model(); final WebMarkupContainer rc = new WebMarkupContainer("resourcePanel"); + final HiddenField importedRowsField = new HiddenField<>("importedRows", Model.of("")); + importedRowsField.setOutputMarkupId(true); + rc.add(importedRowsField); AmpButtonField importStructures = new AmpButtonField("ajaxSubmit", "Import Structures", true) { private static final long serialVersionUID = 1L; @Override protected void onSubmit(AjaxRequestTarget target, Form form) { - if (fileItemModel.getObject() != null) { - FileUpload upload = new FileUpload(fileItemModel.getObject()); - logger.info("File-Name: " + upload.getClientFileName() + " File-Size: " + - Bytes.bytes(upload.getSize())); - try (InputStream inputStream = upload.getInputStream(); - XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) { - XSSFSheet sheet = workbook.getSheetAt(0); - Iterator rowIterator = sheet.iterator(); - if (!rowIterator.hasNext()) { + String rowsJson = RequestCycle.get().getRequest().getRequestParameters() + .getParameterValue(importedRowsField.getInputName()).toString(); + if (rowsJson == null || rowsJson.trim().isEmpty()) { + this.error("No structure rows were returned from the workbook."); + } else { + try { + List rows = new ObjectMapper().readValue(rowsJson, + new TypeReference>() { }); + Set structureTitles = new TreeSet<>(setModel.getObject()); + List importedStructures = new ArrayList<>(); + + for (int index = 0; index < rows.size(); index++) { + StructureImportEndpoint.StructureRow row = rows.get(index); + AmpStructure structure = new AmpStructure(); + structure.setTitle(row.getTitle()); + structure.setDescription(row.getDescription()); + structure.setLatitude(row.getLatitude()); + structure.setLongitude(row.getLongitude()); + structure.setShape(row.getShape()); + if (!structureTitles.add(structure)) { + this.error("Import rejected: structure title '" + row.getTitle() + "' on row " + + (index + 2) + + " duplicates an existing structure or another imported row." + + " Structure titles must be unique."); + importedRowsField.setModelObject(""); + target.add(rc); return; } - rowIterator.next(); - Set structureTitles = new TreeSet<>(setModel.getObject()); - List importedStructures = new ArrayList<>(); - - while (rowIterator.hasNext()) { - XSSFRow row = (XSSFRow) rowIterator.next(); - String title = getStringValueFromCell(row.getCell(0)); - String description = getStringValueFromCell(row.getCell(1)); - String latitude = getStringValueFromCell(row.getCell(2)); - String longitude = getStringValueFromCell(row.getCell(3)); - String shape = getStringValueFromCell(row.getCell(4)); - - AmpStructure stru = new AmpStructure(); - stru.setTitle(title); - stru.setDescription(description); - stru.setLatitude(latitude); - stru.setLongitude(longitude); - stru.setShape(shape); - if (!structureTitles.add(stru)) { - this.error("Import rejected: structure title '" + title + "' on row " - + (row.getRowNum() + 1) - + " duplicates an existing structure or another imported row." - + " Structure titles must be unique."); - target.add(rc); - return; - } - importedStructures.add(stru); - } - for (AmpStructure structure : importedStructures) { - list.addItem(structure); - } - list.goToLastPage(); - fileItemModel.setObject(null); + importedStructures.add(structure); + } + for (AmpStructure structure : importedStructures) { + list.addItem(structure); + } + list.goToLastPage(); } catch (Exception e) { - logger.error("Error reading excel file", e); + logger.error("Error applying imported structures", e); + this.error("The workbook rows could not be imported."); } } + importedRowsField.setModelObject(""); target.add(containter); target.add(rc); } @@ -401,7 +393,7 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { rc.add(importStructures); FileUploadPanel fileUpload = new FileUploadPanel("file", String.valueOf(am.getObject().getAmpActivityId()), - fileItemModel, importStructures.getButton().getMarkupId()) { + null, "/rest/gis/structures/import", importStructures.getButton().getMarkupId(), importedRowsField.getMarkupId()) { private static final long serialVersionUID = 1L; @Override @@ -462,24 +454,6 @@ public void onClick(AjaxRequestTarget target) { } - private static String getStringValueFromCell(Cell cell) { - if (cell == null) { - return null; - } - switch (cell.getCellType()) { - case Cell.CELL_TYPE_STRING: - return cell.getStringCellValue(); - case Cell.CELL_TYPE_NUMERIC: - return String.valueOf(cell.getNumericCellValue()); - case Cell.CELL_TYPE_BOOLEAN: - return String.valueOf(cell.getBooleanCellValue()); - case Cell.CELL_TYPE_FORMULA: - return cell.getCellFormula(); - default: - return null; - } - } - private void writeExcelFile(OutputStream out,PagingListEditor list) throws IOException { try(XSSFWorkbook workbook = new XSSFWorkbook()) { // Create Excel sheet diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java index 969120466f7..a2c6f2b6764 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java @@ -34,6 +34,8 @@ public class FileUploadBehavior extends Behavior { private final IModel fileItemModel; private final boolean deferUpload; private final String importButtonMarkupId; + private final String importedRowsMarkupId; + private final String uploadUrlOverride; /** * The name of the request parameter used for the multipart * Ajax request @@ -41,19 +43,17 @@ public class FileUploadBehavior extends Behavior { public static final String PARAM_NAME = "FILE-UPLOAD"; public FileUploadBehavior(String activityId, IModel fileItemModel) { - this(activityId, fileItemModel, false, null); + this(activityId, fileItemModel, null, null, null); } - public FileUploadBehavior(String activityId, IModel fileItemModel, boolean deferUpload) { - this(activityId, fileItemModel, deferUpload, null); - } - - public FileUploadBehavior(String activityId, IModel fileItemModel, boolean deferUpload, - String importButtonMarkupId) { + public FileUploadBehavior(String activityId, IModel fileItemModel, String uploadUrlOverride, + String importButtonMarkupId, String importedRowsMarkupId) { this.activityId = activityId; this.fileItemModel = fileItemModel; - this.deferUpload = deferUpload; + this.uploadUrlOverride = uploadUrlOverride; + this.deferUpload = importButtonMarkupId != null; this.importButtonMarkupId = importButtonMarkupId; + this.importedRowsMarkupId = importedRowsMarkupId; } /** @@ -83,18 +83,26 @@ public void renderHead(final Component component, IHeaderResponse response) { response.render(JavaScriptHeaderItem.forReference( new JavaScriptResourceReference(FileUploadBehavior.class, "jquery.fileupload.js"), System.currentTimeMillis() +"c", true)); - String uploadUrl = RequestCycle.get().getUrlRenderer().renderFullUrl( + String uploadUrl = uploadUrlOverride; + if (uploadUrl == null) { + uploadUrl = RequestCycle.get().getUrlRenderer().renderFullUrl( Url.parse(component.urlFor(new FileUploadResourceReference(activityId, fileItemModel), null).toString())); + uploadUrl = appendQueryParameter(uploadUrl, "activityId", activityId); + uploadUrl = appendSpringCsrfToken(uploadUrl); + } else { + HttpServletRequest request = ((ServletWebRequest) RequestCycle.get().getRequest()).getContainerRequest(); + uploadUrl = request.getContextPath() + uploadUrl; + uploadUrl = appendSpringCsrfToken(uploadUrl); + } String markupId = component.getMarkupId(); String maxFileSizeGS = FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.CR_MAX_FILE_SIZE); + String uploadParamName = uploadUrlOverride == null ? PARAM_NAME : "file"; final Map variables = new HashMap(); variables.put("componentMarkupId", markupId); - uploadUrl = appendQueryParameter(uploadUrl, "activityId", activityId); - uploadUrl = appendSpringCsrfToken(uploadUrl); variables.put("url", uploadUrl); - variables.put("paramName", PARAM_NAME); + variables.put("paramName", uploadParamName); variables.put("uploadFailedMsg", TranslatorUtil.getTranslatedText("Upload failed! Please try again.")); variables.put("uploadStartedMsg", TranslatorUtil.getTranslatedText("Upload started, please wait...")); variables.put("uploadFailedTooBigMsg", TranslatorUtil.getTranslatedText("The file size limit is {size} MB. This file exceeds the limit.").replace("{size}", maxFileSizeGS)); @@ -110,8 +118,9 @@ public Map getObject() { response.render(JavaScriptHeaderItem.forReference( new TextTemplateResourceReference(FileUploadBehavior.class, "FileUploadBehavior.js", variablesModel), String.valueOf(System.currentTimeMillis()), true)); response.render(OnLoadHeaderItem.forScript("setupFileUpload('#" + markupId + "', '" + uploadUrl + "', '" - + PARAM_NAME + "', " + deferUpload + ", '" - + (importButtonMarkupId == null ? "" : importButtonMarkupId) + "');")); + + uploadParamName + "', " + deferUpload + ", '" + + (importButtonMarkupId == null ? "" : importButtonMarkupId) + "', '" + + (importedRowsMarkupId == null ? "" : importedRowsMarkupId) + "');")); } static String appendSpringCsrfToken(String url) { diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js index 9473b7c90b3..5054b75cda9 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js @@ -4,13 +4,14 @@ Wicket.Event.add(window, "domready", function(event){ $.getScript("/TEMPLATE/ampTemplate/script/common/FileTypeValidator.js"); -function setupFileUpload(componentId, componentUrl, componentParamName, deferUpload, importButtonMarkupId){ +function setupFileUpload(componentId, componentUrl, componentParamName, deferUpload, importButtonMarkupId, importedRowsMarkupId){ $(function () { var pendingUploadData = null; var waitForUpload = false; var uploadInProgress = false; var allowImportClick = false; var importButton = importButtonMarkupId ? document.getElementById(importButtonMarkupId) : null; + var importedRows = importedRowsMarkupId ? document.getElementById(importedRowsMarkupId) : null; var fileInput = $(componentId).find('input[type=file]').get(0); function submitPendingUpload() { @@ -88,14 +89,29 @@ function setupFileUpload(componentId, componentUrl, componentParamName, deferUpl } }, done: function (e, data){ - //alert('upload done! result[' + JSON.stringify(data.result) + '] status[' + data.textStatus + '] jqXHR[' + JSON.stringify(data.jqXHR) +']'); - var result = eval(data.result)[0]; - $(this).find('[role=fileUploadedMsg]').html(result.uploadTxt); - if (deferUpload && waitForUpload && importButton) { + if (deferUpload && waitForUpload && importButton && importedRows) { + var result = data.result; + try { + if (typeof result === 'string') { + result = JSON.parse(result); + } + if (!Array.isArray(result)) { + throw new Error('Unexpected structure import response'); + } + } catch (error) { + alert("${uploadFailedMsg}"); + waitForUpload = false; + uploadInProgress = false; + return; + } + importedRows.value = JSON.stringify(result); waitForUpload = false; uploadInProgress = false; allowImportClick = true; importButton.click(); + } else if (!deferUpload) { + var result = eval(data.result)[0]; + $(this).find('[role=fileUploadedMsg]').html(result.uploadTxt); } }, fail: function (e, data){ diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java index 423fee3e9bb..bcad8df9fc6 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadPanel.java @@ -14,14 +14,14 @@ public class FileUploadPanel extends Panel { public FileUploadPanel(String id, String activityId, IModel fileItemModel) { - this(id, activityId, fileItemModel, null); + this(id, activityId, fileItemModel, null, null, null); } public FileUploadPanel(String id, String activityId, IModel fileItemModel, - String importButtonMarkupId) { + String uploadUrl, String importButtonMarkupId, String importedRowsMarkupId) { super(id); add (new Label("chooseFileLabel", TranslatorWorker.translateText("Choose file"))); add (new Label("noFileLabel", TranslatorWorker.translateText("No file chosen"))); - add(new FileUploadBehavior(activityId, fileItemModel, importButtonMarkupId != null, importButtonMarkupId)); + add(new FileUploadBehavior(activityId, fileItemModel, uploadUrl, importButtonMarkupId, importedRowsMarkupId)); } } diff --git a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java new file mode 100644 index 00000000000..b991357a565 --- /dev/null +++ b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/gis/StructureImportEndpoint.java @@ -0,0 +1,181 @@ +package org.digijava.kernel.ampapi.endpoints.gis; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.digijava.kernel.ampapi.endpoints.security.AuthRule; +import org.digijava.kernel.ampapi.endpoints.util.ApiMethod; +import org.glassfish.jersey.media.multipart.FormDataParam; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** REST endpoint for parsing structure workbooks before applying them to an activity form. */ +@Path("gis/structures") +@Api("gis-structure-import") +public class StructureImportEndpoint { + + private static final long MAX_REQUEST_SIZE = 21L * 1024 * 1024; + + @POST + @Path("import") + @Consumes(MediaType.MULTIPART_FORM_DATA) + @javax.ws.rs.Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") + @ApiMethod(authTypes = AuthRule.AUTHENTICATED, id = "importStructureWorkbook", ui = false) + @ApiOperation("Parse an uploaded structure workbook and return its rows") + public List importWorkbook(@FormDataParam("file") InputStream file, + @Context HttpServletRequest request) { + if (file == null) { + throw badRequest("A workbook file is required."); + } + long contentLength = request.getContentLengthLong(); + if (contentLength > MAX_REQUEST_SIZE) { + throw new WebApplicationException("Workbook exceeds the upload size limit.", + Response.Status.REQUEST_ENTITY_TOO_LARGE); + } + + try (InputStream input = file; Workbook workbook = WorkbookFactory.create(input)) { + if (workbook.getNumberOfSheets() == 0 || workbook.getSheetAt(0).getPhysicalNumberOfRows() == 0) { + throw badRequest("The workbook does not contain a header row."); + } + + org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0); + Row header = sheet.getRow(sheet.getFirstRowNum()); + Map columns = getColumnIndexes(header); + if (!columns.containsKey("title")) { + throw badRequest("The workbook must contain a Title column."); + } + + DataFormatter formatter = new DataFormatter(); + List structures = new ArrayList<>(); + for (int rowIndex = sheet.getFirstRowNum() + 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) { + Row row = sheet.getRow(rowIndex); + if (row == null) { + continue; + } + + String title = getCellValue(row, columns.get("title"), formatter); + String description = getCellValue(row, columns.get("description"), formatter); + String latitude = getCellValue(row, columns.get("latitude"), formatter); + String longitude = getCellValue(row, columns.get("longitude"), formatter); + String shape = getCellValue(row, columns.get("shape"), formatter); + if (isBlank(title) && isBlank(description) && isBlank(latitude) && isBlank(longitude) + && isBlank(shape)) { + continue; + } + if (isBlank(title)) { + throw badRequest("A structure title is required on row " + (rowIndex + 1) + "."); + } + + structures.add(new StructureRow(title, description, latitude, longitude, shape)); + } + return structures; + } catch (WebApplicationException e) { + throw e; + } catch (Exception e) { + throw new WebApplicationException("The uploaded file is not a readable Excel workbook.", e, + Response.Status.BAD_REQUEST); + } + } + + private Map getColumnIndexes(Row header) { + Map columns = new HashMap<>(); + for (int column = 0; column < header.getLastCellNum(); column++) { + String name = new DataFormatter().formatCellValue(header.getCell(column)).trim().toLowerCase(Locale.ROOT); + if (!name.isEmpty()) { + columns.put(name, column); + } + } + return columns; + } + + private String getCellValue(Row row, Integer column, DataFormatter formatter) { + if (column == null || row.getCell(column) == null) { + return null; + } + String value = formatter.formatCellValue(row.getCell(column)); + return value.isEmpty() ? null : value; + } + + private boolean isBlank(String value) { + return value == null || value.trim().isEmpty(); + } + + private WebApplicationException badRequest(String message) { + return new WebApplicationException(message, Response.Status.BAD_REQUEST); + } + + public static class StructureRow { + private String title; + private String description; + private String latitude; + private String longitude; + private String shape; + + public StructureRow() { + } + + public StructureRow(String title, String description, String latitude, String longitude, String shape) { + this.title = title; + this.description = description; + this.latitude = latitude; + this.longitude = longitude; + this.shape = shape; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getLatitude() { + return latitude; + } + + public void setLatitude(String latitude) { + this.latitude = latitude; + } + + public String getLongitude() { + return longitude; + } + + public void setLongitude(String longitude) { + this.longitude = longitude; + } + + public String getShape() { + return shape; + } + + public void setShape(String shape) { + this.shape = shape; + } + } +} \ No newline at end of file diff --git a/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html index fabfb0b1ded..1f1fc54ac57 100644 --- a/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html +++ b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/features/sections/AmpStructuresFormSectionFeature.html @@ -5,6 +5,7 @@
+
From 67d965bae5d2a073dfb575ad3a51b45d57e9c0a0 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 7 Oct 2026 20:14:41 +0300 Subject: [PATCH 60/76] Use rest endpoint to upload files --- .../tables/AmpResourcesFormTableFeature.java | 13 +- .../fields/AmpNewResourceFieldPanel.java | 139 ++++++++------- .../components/upload/FileUploadBehavior.java | 3 +- .../components/upload/FileUploadBehavior.js | 13 +- .../helper/DownloadResourceStream.java | 22 ++- .../onepager/helper/TemporaryDocument.java | 9 + .../amp/onepager/models/AmpActivityModel.java | 3 + .../amp/onepager/util/ActivityUtil.java | 18 +- .../endpoints/resource/ResourceEndpoint.java | 158 +++++++++++++++++- .../helper/StagedResourceUploadStore.java | 90 ++++++++++ .../helper/TemporaryDocumentData.java | 14 ++ .../fields/AmpNewResourceFieldPanel.html | 1 + 12 files changed, 415 insertions(+), 68 deletions(-) create mode 100644 amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/tables/AmpResourcesFormTableFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/tables/AmpResourcesFormTableFeature.java index 112e8f3e990..28a1b415160 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/tables/AmpResourcesFormTableFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/tables/AmpResourcesFormTableFeature.java @@ -35,6 +35,8 @@ import org.digijava.module.aim.util.FeaturesUtil; import org.digijava.module.categorymanager.util.CategoryManagerUtil; import org.digijava.module.contentrepository.helper.NodeWrapper; +import org.digijava.module.contentrepository.helper.StagedResourceUploadStore; +import org.digijava.module.contentrepository.helper.TemporaryDocumentData; import org.digijava.module.contentrepository.util.DocumentManagerUtil; import org.digijava.module.translation.util.ContentTranslationUtil; @@ -216,6 +218,11 @@ protected void populateItem(final ListItem item) { if (item.getModelObject().isExisting()) drs = new DownloadResourceStream(new PersistentObjectModel<> (item.getModelObject().getExistingDocument()), item.getModelObject().getFileName()); + else if (item.getModelObject().getStagedUploadId() != null) { + TemporaryDocumentData staged = StagedResourceUploadStore.get( + SessionUtil.getCurrentServletRequest(), item.getModelObject().getStagedUploadId()); + drs = new DownloadResourceStream(staged.getFormFile(), item.getModelObject().getFileName()); + } else drs = new DownloadResourceStream(item.getModelObject().getFile(), item.getModelObject().getFileName()); @@ -266,7 +273,11 @@ public void onClick(AjaxRequestTarget target) { delItems.add(item.getModelObject().getExistingDocument()); } else { HashSet newItems = getSession().getMetaData(OnePagerConst.RESOURCES_NEW_ITEMS); - newItems.remove(item.getModelObject()); + if (newItems != null) { + newItems.remove(item.getModelObject()); + } + StagedResourceUploadStore.delete(SessionUtil.getCurrentServletRequest(), + item.getModelObject().getStagedUploadId()); } target.add(list.getParent()); } diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java index 5dc022f236f..a5b6bcd85e1 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.java @@ -4,6 +4,8 @@ */ package org.dgfoundation.amp.onepager.components.fields; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Session; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -12,6 +14,7 @@ import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.form.HiddenField; import org.apache.wicket.markup.html.form.upload.FileUpload; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; @@ -31,6 +34,8 @@ import org.dgfoundation.amp.onepager.translation.TranslatorUtil; import org.dgfoundation.amp.onepager.translation.TrnLabel; import org.dgfoundation.amp.onepager.util.AmpFMTypes; +import org.dgfoundation.amp.onepager.util.SessionUtil; +import org.digijava.kernel.ampapi.endpoints.resource.ResourceEndpoint; import org.digijava.kernel.ampapi.endpoints.filetype.FileTypeManager; import org.digijava.kernel.ampapi.endpoints.filetype.FileTypeValidationResponse; import org.digijava.kernel.ampapi.endpoints.filetype.FileTypeValidationStatus; @@ -41,7 +46,10 @@ import org.digijava.module.categorymanager.dbentity.AmpCategoryValue; import org.digijava.module.categorymanager.util.CategoryConstants; import org.digijava.module.contentrepository.util.DocumentManagerUtil; +import org.digijava.module.contentrepository.helper.StagedResourceUploadStore; +import org.digijava.module.contentrepository.helper.TemporaryDocumentData; +import javax.servlet.http.HttpServletRequest; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -106,7 +114,8 @@ public AmpNewResourceFieldPanel(final String id, if (model.getObject().getAmpActivityId() != null) activityId = Long.toString(model.getObject().getAmpActivityId()); final Model fileItemModel = new Model(); - FileUploadPanel fileUpload = new FileUploadPanel("file",activityId, fileItemModel); + final HiddenField stagedUploadResponse = new HiddenField<>("stagedUploadResponse", Model.of("")); + stagedUploadResponse.setOutputMarkupId(true); final AmpTextFieldPanel webLink = new AmpTextFieldPanel("webLink", new PropertyModel(td, "webLink"), "Web Link", true, true); @@ -123,52 +132,7 @@ public AmpNewResourceFieldPanel(final String id, TrnLabel resourceLabel = new TrnLabel("resourceLabel", resourceLabelModel); // create the form - final Form form = new Form("form") { - - /** - * @see org.apache.wicket.markup.html.form.Form#onSubmit() - */ - @Override - protected void onSubmit() { - TemporaryActivityDocument tmp = td.getObject(); - if (fileItemModel.getObject() != null) - tmp.setFile(new FileUpload(fileItemModel.getObject())); - - if (updateVisibility(td.getObject(), resourceIsURL)) { - if (tmp.getFile() != null){ - double fSize = tmp.getFile().getSize()*100/(1024*1024); - fSize = fSize/100; - tmp.setFileSize(fSize); - tmp.setFileName(tmp.getFile().getClientFileName()); - tmp.setContentType(tmp.getFile().getContentType()); - } - - if (tmp.getWebLink() != null) { - tmp.setWebLink(DocumentManagerUtil.processUrl(tmp.getWebLink(), null)); - tmp.setFileName(tmp.getWebLink()); - } - - tmp.setDate(Calendar.getInstance()); - tmp.setYear(String.valueOf((tmp.getDate()).get(Calendar.YEAR))); - HashSet newItemsSet = getSession().getMetaData(OnePagerConst.RESOURCES_NEW_ITEMS); - if (newItemsSet == null) { - newItemsSet = new HashSet(); - getSession().setMetaData(OnePagerConst.RESOURCES_NEW_ITEMS, newItemsSet); - } - - tmp.setTranslatedDescriptionList(getTranslationsForField(tmp.getNewTemporaryDocumentId(),"description")); - tmp.setTranslatedTitleList(getTranslationsForField(tmp.getNewTemporaryDocumentId(),"title")); - tmp.setTranslatedNoteList(getTranslationsForField(tmp.getNewTemporaryDocumentId(),"description")); - newItemsSet.add(tmp); - TemporaryActivityDocument tmpDoc = new TemporaryActivityDocument(); - String docId = generateResourceKey("newResource"); - newResourceIdModel.setObject(docId); - tmpDoc.setNewTemporaryDocumentId(docId); - td.setObject(tmpDoc); - fileItemModel.setObject(null); - } - } - }; + final Form form = new Form("form"); add(createAddNewLink(fmName)); @@ -176,20 +140,13 @@ protected void onSubmit() { rc.add(new AttributeModifier("id", getToggleId())); rc.add(form); rc.add(name); - rc.add(fileUpload); rc.setOutputMarkupId(true); add(rc); - if (newResourceIsWebLink){ - fileUpload.setVisible(false); - webLink.setVisibilityAllowed(true); - } - form.add(name); form.add(desc); form.add(note); form.add(type); - form.add(fileUpload); form.add(resourceLabel); form.add(webLink); @@ -198,6 +155,34 @@ protected void onSubmit() { @Override protected void onSubmit(AjaxRequestTarget target, Form form) { TemporaryDocument tmp = td.getObject(); + String response = stagedUploadResponse.getModelObject(); + if (response != null && !response.trim().isEmpty()) { + try { + List uploads = new ObjectMapper().readValue(response, + new TypeReference>() { }); + if (uploads.isEmpty()) { + throw new IOException("The upload response did not contain a staged file."); + } + String uploadId = uploads.get(0).getUploadId(); + HttpServletRequest request = SessionUtil.getCurrentServletRequest(); + TemporaryDocumentData staged = StagedResourceUploadStore.get(request, uploadId); + if (staged == null) { + throw new IOException("The staged upload is no longer available."); + } + if (tmp.getStagedUploadId() != null && !tmp.getStagedUploadId().equals(uploadId)) { + StagedResourceUploadStore.delete(request, tmp.getStagedUploadId()); + } + tmp.setStagedUploadId(uploadId); + tmp.setFileName(staged.getName()); + tmp.setContentType(staged.getContentType()); + tmp.setFileSize(staged.getFileSize()); + } catch (IOException e) { + logger.error("Unable to read staged resource upload response", e); + webLinkFeedbackContainer.setVisible(true); + webLinkFeedbackLabel.setDefaultModelObject(FILE_PATH_NOT_SELECTED); + } + stagedUploadResponse.setModelObject(""); + } if (fileItemModel.getObject() != null) tmp.setFile(new FileUpload(fileItemModel.getObject())); target.add(name); @@ -212,6 +197,37 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { target.add(resourcesList); target.add(webLinkFeedbackContainer); if (updateVisibility(td.getObject(), resourceIsURL)){ + if (tmp.getFile() != null){ + double fSize = tmp.getFile().getSize()*100/(1024*1024); + fSize = fSize/100; + tmp.setFileSize(fSize); + tmp.setFileName(tmp.getFile().getClientFileName()); + tmp.setContentType(tmp.getFile().getContentType()); + } + if (tmp.getWebLink() != null) { + tmp.setWebLink(DocumentManagerUtil.processUrl(tmp.getWebLink(), null)); + tmp.setFileName(tmp.getWebLink()); + } + tmp.setDate(Calendar.getInstance()); + tmp.setYear(String.valueOf(tmp.getDate().get(Calendar.YEAR))); + HashSet newItemsSet = getSession() + .getMetaData(OnePagerConst.RESOURCES_NEW_ITEMS); + if (newItemsSet == null) { + newItemsSet = new HashSet<>(); + getSession().setMetaData(OnePagerConst.RESOURCES_NEW_ITEMS, newItemsSet); + } + tmp.setTranslatedDescriptionList(getTranslationsForField( + tmp.getNewTemporaryDocumentId(), "description")); + tmp.setTranslatedTitleList(getTranslationsForField(tmp.getNewTemporaryDocumentId(), "title")); + tmp.setTranslatedNoteList(getTranslationsForField( + tmp.getNewTemporaryDocumentId(), "description")); + newItemsSet.add((TemporaryActivityDocument) tmp); + TemporaryActivityDocument tmpDoc = new TemporaryActivityDocument(); + String docId = generateResourceKey("newResource"); + newResourceIdModel.setObject(docId); + tmpDoc.setNewTemporaryDocumentId(docId); + td.setObject(tmpDoc); + fileItemModel.setObject(null); target.appendJavaScript("$('#" + getToggleId() + "').hide();"); target.appendJavaScript("$('#" + getToggleId() + "').find('[role=fileUploadedMsg]').html('');"); target.appendJavaScript("$('#uploadLabel').text('" + TranslatorWorker.translateText("No file chosen") + "');"); @@ -219,7 +235,18 @@ protected void onSubmit(AjaxRequestTarget target, Form form) { } } }; + submit.getButton().setOutputMarkupId(true); + FileUploadPanel fileUpload = newResourceIsWebLink + ? new FileUploadPanel("file", activityId, fileItemModel) + : new FileUploadPanel("file", activityId, fileItemModel, "/rest/resource/stage-upload", + submit.getButton().getMarkupId(), stagedUploadResponse.getMarkupId()); + if (newResourceIsWebLink) { + fileUpload.setVisible(false); + webLink.setVisibilityAllowed(true); + } + form.add(fileUpload); + form.add(stagedUploadResponse); form.add(submit); form.add(createCancelButton()); @@ -282,9 +309,9 @@ protected boolean updateVisibility(TemporaryDocument resource, boolean newResour urlSelected =true; } } else { - pathSelected = resource.getFile() != null; + pathSelected = resource.getFile() != null || resource.getStagedUploadId() != null; contentValid = true; - if (pathSelected) { + if (resource.getFile() != null) { // validate the content of the file AMP-24920 if (isEnabledMimeTypeValidation()) { try { diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java index a2c6f2b6764..30c3fc85870 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.java @@ -120,7 +120,8 @@ public Map getObject() { response.render(OnLoadHeaderItem.forScript("setupFileUpload('#" + markupId + "', '" + uploadUrl + "', '" + uploadParamName + "', " + deferUpload + ", '" + (importButtonMarkupId == null ? "" : importButtonMarkupId) + "', '" - + (importedRowsMarkupId == null ? "" : importedRowsMarkupId) + "');")); + + (importedRowsMarkupId == null ? "" : importedRowsMarkupId) + "', " + + (uploadUrlOverride != null) + ");")); } static String appendSpringCsrfToken(String url) { diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js index 5054b75cda9..145def5658c 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/upload/FileUploadBehavior.js @@ -4,7 +4,7 @@ Wicket.Event.add(window, "domready", function(event){ $.getScript("/TEMPLATE/ampTemplate/script/common/FileTypeValidator.js"); -function setupFileUpload(componentId, componentUrl, componentParamName, deferUpload, importButtonMarkupId, importedRowsMarkupId){ +function setupFileUpload(componentId, componentUrl, componentParamName, deferUpload, importButtonMarkupId, importedRowsMarkupId, fileOnlyUpload){ $(function () { var pendingUploadData = null; var waitForUpload = false; @@ -33,6 +33,9 @@ function setupFileUpload(componentId, componentUrl, componentParamName, deferUpl allowImportClick = false; return; } + if (importedRows && importedRows.value && !pendingUploadData) { + return; + } event.preventDefault(); event.stopImmediatePropagation(); @@ -49,7 +52,7 @@ function setupFileUpload(componentId, componentUrl, componentParamName, deferUpl importButton.addEventListener('click', importButton.ampDeferredUploadClickHandler, true); } - $(componentId).fileupload({ + var uploadOptions = { url: componentUrl, paramName: componentParamName, singleFileUploads: true, @@ -124,7 +127,11 @@ function setupFileUpload(componentId, componentUrl, componentParamName, deferUpl uploadInProgress = false; pendingUploadData = null; } - }); + }; + if (fileOnlyUpload) { + uploadOptions.formData = []; + } + $(componentId).fileupload(uploadOptions); }); } diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/helper/DownloadResourceStream.java b/amp/src/main/java/org/dgfoundation/amp/onepager/helper/DownloadResourceStream.java index b45202fd062..972e22a6709 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/helper/DownloadResourceStream.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/helper/DownloadResourceStream.java @@ -7,6 +7,7 @@ import org.apache.wicket.util.resource.IResourceStream; import org.apache.wicket.util.resource.ResourceStreamNotFoundException; import org.apache.wicket.util.time.Time; +import org.apache.struts.upload.FormFile; import org.dgfoundation.amp.onepager.AmpAuthWebSession; import org.dgfoundation.amp.onepager.util.SessionUtil; import org.digijava.module.contentrepository.helper.CrConstants; @@ -27,6 +28,7 @@ public class DownloadResourceStream implement private String contentType; private transient Locale locale; private FileUpload file; + private FormFile stagedFile; private boolean newResource; private IModel doc; private String fileName; @@ -38,6 +40,12 @@ public DownloadResourceStream(FileUpload f, String fileName) { this.fileName = fileName; } + public DownloadResourceStream(FormFile file, String fileName) { + this.stagedFile = file; + this.fileName = fileName; + this.newResource = true; + } + public DownloadResourceStream(FileUpload f) { this.file = f; this.newResource = true; @@ -59,11 +67,17 @@ private synchronized void initHelperFields() { //Singleton if (true || contentType == null) { if (newResource) { - contentType = file.getContentType(); - fileSize = Bytes.bytes(file.getSize()); - fileName = file.getClientFileName(); try { - fileData = file.getInputStream(); + if (stagedFile != null) { + contentType = stagedFile.getContentType(); + fileSize = Bytes.bytes(stagedFile.getFileSize()); + fileData = stagedFile.getInputStream(); + } else { + contentType = file.getContentType(); + fileSize = Bytes.bytes(file.getSize()); + fileName = file.getClientFileName(); + fileData = file.getInputStream(); + } } catch (IOException e) { logger.error(e.getMessage(), e); } diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/helper/TemporaryDocument.java b/amp/src/main/java/org/dgfoundation/amp/onepager/helper/TemporaryDocument.java index efba2a8deb6..fe1374ab309 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/helper/TemporaryDocument.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/helper/TemporaryDocument.java @@ -27,6 +27,7 @@ public class TemporaryDocument implements Serializable { private String webLink; private String fileName; private String newTemporaryDocumentId; + private String stagedUploadId; public TemporaryDocument() { existing = false; @@ -111,4 +112,12 @@ public String getNewTemporaryDocumentId() { public void setNewTemporaryDocumentId(String newTemporaryDocumentId) { this.newTemporaryDocumentId = newTemporaryDocumentId; } + + public String getStagedUploadId() { + return stagedUploadId; + } + + public void setStagedUploadId(String stagedUploadId) { + this.stagedUploadId = stagedUploadId; + } } diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/models/AmpActivityModel.java b/amp/src/main/java/org/dgfoundation/amp/onepager/models/AmpActivityModel.java index 0f5002809e3..8a5a8286bdd 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/models/AmpActivityModel.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/models/AmpActivityModel.java @@ -9,6 +9,8 @@ import org.apache.wicket.model.LoadableDetachableModel; import org.dgfoundation.amp.onepager.AmpAuthWebSession; import org.dgfoundation.amp.onepager.OnePagerConst; +import org.dgfoundation.amp.onepager.util.SessionUtil; +import org.digijava.module.contentrepository.helper.StagedResourceUploadStore; import org.dgfoundation.amp.onepager.util.ActivityUtil; import org.digijava.kernel.ampapi.endpoints.datafreeze.DataFreezeService; import org.digijava.kernel.persistence.PersistenceManager; @@ -61,6 +63,7 @@ public AmpActivityModel(Long id, String editingKey) { public void beginConversation(boolean reset) { if(reset){ AmpAuthWebSession s = (AmpAuthWebSession) org.apache.wicket.Session.get(); + StagedResourceUploadStore.clear(SessionUtil.getCurrentServletRequest()); s.setMetaData(OnePagerConst.RESOURCES_NEW_ITEMS, null); s.setMetaData(OnePagerConst.RESOURCES_DELETED_ITEMS, null); s.setMetaData(OnePagerConst.GPI_RESOURCES_NEW_ITEMS, null); diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/util/ActivityUtil.java b/amp/src/main/java/org/dgfoundation/amp/onepager/util/ActivityUtil.java index 6505d20713b..f4ecaf07dc4 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/util/ActivityUtil.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/util/ActivityUtil.java @@ -33,6 +33,7 @@ import org.digijava.module.contentrepository.helper.CrConstants; import org.digijava.module.contentrepository.helper.NodeWrapper; import org.digijava.module.contentrepository.helper.TemporaryDocumentData; +import org.digijava.module.contentrepository.helper.StagedResourceUploadStore; import org.digijava.module.contentrepository.util.DocumentManagerUtil; import org.digijava.module.editor.dbentity.Editor; import org.digijava.module.editor.exception.EditorException; @@ -1206,7 +1207,13 @@ private static void saveActivityResources(AmpActivityVersion a, Session session) private static void insertResources(AmpActivityVersion a, HashSet newResources) { if (newResources != null) { for (TemporaryActivityDocument temp : newResources) { - TemporaryDocumentData tdd = new TemporaryDocumentData(); + HttpServletRequest request = SessionUtil.getCurrentServletRequest(); + TemporaryDocumentData tdd = temp.getStagedUploadId() == null ? new TemporaryDocumentData() + : StagedResourceUploadStore.get(request, temp.getStagedUploadId()); + if (tdd == null) { + logger.error("Staged resource upload is missing for " + temp.getFileName()); + continue; + } tdd.setTitle(temp.getTitle()); tdd.setName(temp.getFileName()); tdd.setDescription(temp.getDescription()); @@ -1254,14 +1261,21 @@ private static void insertResources(AmpActivityVersion a, HashSet createDocResource( } } + @POST + @Path("stage-upload") + @Consumes(MediaType.MULTIPART_FORM_DATA) + @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") + @ApiMethod(authTypes = AuthRule.AUTHENTICATED, id = "stageResourceUpload", ui = false) + @ApiOperation("Stage a resource file for a pending activity form") + public List stageResourceUpload(@FormDataParam("file") InputStream uploadedInputStream, + @FormDataParam("file") FormDataContentDisposition fileDetail, @Context HttpServletRequest request) { + if (uploadedInputStream == null || fileDetail == null || fileDetail.getFileName() == null) { + throw new WebApplicationException("A file is required.", Response.Status.BAD_REQUEST); + } + + long maxFileSize = FeaturesUtil.getGlobalSettingValueInteger(GlobalSettingsConstants.CR_MAX_FILE_SIZE) + * FileUtils.ONE_MB; + if (request.getContentLengthLong() > maxFileSize + FileUtils.ONE_MB) { + throw new WebApplicationException("The file exceeds the upload size limit.", + Response.Status.REQUEST_ENTITY_TOO_LARGE); + } + + File stagedFile = null; + try { + stagedFile = File.createTempFile("amp-resource-upload-", ".tmp"); + stagedFile.deleteOnExit(); + FileUtils.copyInputStreamToFile(new BoundedInputStream(uploadedInputStream, maxFileSize + 1), stagedFile); + if (stagedFile.length() > maxFileSize) { + throw new WebApplicationException("The file exceeds the upload size limit.", + Response.Status.REQUEST_ENTITY_TOO_LARGE); + } + + String fileName = new File(fileDetail.getFileName()).getName(); + if (FeaturesUtil.getGlobalSettingValueBoolean(GlobalSettingsConstants.LIMIT_FILE_TYPE_FOR_UPLOAD)) { + FileTypeValidationResponse validation; + try (InputStream validationStream = new FileInputStream(stagedFile)) { + validation = FileTypeManager.getInstance().validateFileType(validationStream, fileName); + } + if (validation.getStatus() != FileTypeValidationStatus.ALLOWED) { + throw new WebApplicationException("The uploaded file type is not allowed.", + Response.Status.BAD_REQUEST); + } + } + + Calendar uploadedAt = Calendar.getInstance(); + TemporaryDocumentData temporaryDocument = new TemporaryDocumentData(); + temporaryDocument.setName(fileName); + temporaryDocument.setContentType(fileDetail.getType()); + temporaryDocument.setTrueUploadedFileSize((int) stagedFile.length()); + temporaryDocument.setFileSize(DocumentManagerUtil.bytesToMega((int) stagedFile.length())); + temporaryDocument.setDate(uploadedAt.getTime()); + temporaryDocument.setYearofPublication(String.valueOf(uploadedAt.get(Calendar.YEAR))); + temporaryDocument.setFormFile(new StagedFormFile(stagedFile, fileName, fileDetail.getType())); + temporaryDocument.setStagedFile(stagedFile); + String uploadId = StagedResourceUploadStore.store(request, temporaryDocument); + stagedFile = null; + + return Collections.singletonList(new StagedResourceUpload(uploadId)); + } catch (IOException e) { + logger.error("Failed to stage resource upload.", e); + throw new WebApplicationException("Failed to process the uploaded file.", e, + Response.Status.BAD_REQUEST); + } finally { + FileUtils.deleteQuietly(stagedFile); + } + } + + public static class StagedResourceUpload { + private String uploadId; + + public StagedResourceUpload() { + } + + public StagedResourceUpload(String uploadId) { + this.uploadId = uploadId; + } + + public String getUploadId() { + return uploadId; + } + + public void setUploadId(String uploadId) { + this.uploadId = uploadId; + } + } + + private static class StagedFormFile implements FormFile, Serializable { + private static final long serialVersionUID = 1L; + + private final File file; + private final String fileName; + private final String contentType; + + private StagedFormFile(File file, String fileName, String contentType) { + this.file = file; + this.fileName = fileName; + this.contentType = contentType; + } + + @Override + public String getContentType() { + return contentType; + } + + @Override + public void setContentType(String contentType) { + throw new UnsupportedOperationException(); + } + + @Override + public int getFileSize() { + return (int) file.length(); + } + + @Override + public void setFileSize(int fileSize) { + throw new UnsupportedOperationException(); + } + + @Override + public String getFileName() { + return fileName; + } + + @Override + public void setFileName(String fileName) { + throw new UnsupportedOperationException(); + } + + @Override + public byte[] getFileData() throws IOException { + return FileUtils.readFileToByteArray(file); + } + + @Override + public InputStream getInputStream() throws IOException { + return new FileInputStream(file); + } + + @Override + public void destroy() { + } + } + } diff --git a/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java b/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java new file mode 100644 index 00000000000..5e09825b088 --- /dev/null +++ b/amp/src/main/java/org/digijava/module/contentrepository/helper/StagedResourceUploadStore.java @@ -0,0 +1,90 @@ +package org.digijava.module.contentrepository.helper; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionBindingEvent; +import javax.servlet.http.HttpSessionBindingListener; +import java.util.HashMap; +import java.util.UUID; + +/** Session-scoped storage for files awaiting an activity save. */ +public final class StagedResourceUploadStore { + + private static final String SESSION_ATTRIBUTE = StagedResourceUploadStore.class.getName(); + + private StagedResourceUploadStore() { + } + + public static String store(HttpServletRequest request, TemporaryDocumentData upload) { + String uploadId = UUID.randomUUID().toString(); + HttpSession session = request.getSession(); + synchronized (session) { + getUploads(session, true).put(uploadId, upload); + } + return uploadId; + } + + public static TemporaryDocumentData get(HttpServletRequest request, String uploadId) { + if (request == null || request.getSession(false) == null || uploadId == null) { + return null; + } + HttpSession session = request.getSession(false); + synchronized (session) { + StagedUploads uploads = getUploads(session, false); + return uploads == null ? null : uploads.get(uploadId); + } + } + + public static void delete(HttpServletRequest request, String uploadId) { + if (request == null || request.getSession(false) == null || uploadId == null) { + return; + } + HttpSession session = request.getSession(false); + TemporaryDocumentData upload; + synchronized (session) { + StagedUploads uploads = getUploads(session, false); + upload = uploads == null ? null : uploads.remove(uploadId); + } + if (upload != null) { + upload.deleteStagedFile(); + } + } + + public static void clear(HttpServletRequest request) { + if (request == null || request.getSession(false) == null) { + return; + } + HttpSession session = request.getSession(false); + synchronized (session) { + session.removeAttribute(SESSION_ATTRIBUTE); + } + } + + private static StagedUploads getUploads(HttpSession session, boolean create) { + synchronized (session) { + StagedUploads uploads = (StagedUploads) session.getAttribute(SESSION_ATTRIBUTE); + if (uploads == null && create) { + uploads = new StagedUploads(); + session.setAttribute(SESSION_ATTRIBUTE, uploads); + } + return uploads; + } + } + + private static class StagedUploads extends HashMap + implements HttpSessionBindingListener { + private static final long serialVersionUID = 1L; + + @Override + public void valueBound(HttpSessionBindingEvent event) { + } + + @Override + public void valueUnbound(HttpSessionBindingEvent event) { + for (TemporaryDocumentData upload : values()) { + upload.deleteStagedFile(); + } + clear(); + } + } +} \ No newline at end of file diff --git a/amp/src/main/java/org/digijava/module/contentrepository/helper/TemporaryDocumentData.java b/amp/src/main/java/org/digijava/module/contentrepository/helper/TemporaryDocumentData.java index feb220fd82b..62390a80de1 100644 --- a/amp/src/main/java/org/digijava/module/contentrepository/helper/TemporaryDocumentData.java +++ b/amp/src/main/java/org/digijava/module/contentrepository/helper/TemporaryDocumentData.java @@ -15,6 +15,7 @@ import javax.jcr.Session; import javax.servlet.http.HttpServletRequest; import java.io.UnsupportedEncodingException; +import java.io.File; import java.util.ArrayList; import java.util.Calendar; @@ -26,6 +27,7 @@ public class TemporaryDocumentData extends DocumentData { private boolean errorsFound; private int trueUploadedFileSize; private FormFile formFile; + private File stagedFile; private static Logger logger = Logger.getLogger(TemporaryDocumentData.class); public int getTrueUploadedFileSize() { @@ -46,6 +48,18 @@ public FormFile getFormFile() { public void setFormFile(FormFile formFile) { this.formFile = formFile; } + + public void setStagedFile(File stagedFile) { + this.stagedFile = stagedFile; + } + + public void deleteStagedFile() { + if (stagedFile != null && stagedFile.exists() && !stagedFile.delete()) { + logger.warn("Unable to delete staged document upload " + stagedFile.getName()); + } + stagedFile = null; + formFile = null; + } public TemporaryDocumentData (){ diff --git a/amp/src/main/resources/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.html b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.html index 373e6db6a62..d903df4cbe3 100644 --- a/amp/src/main/resources/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.html +++ b/amp/src/main/resources/org/dgfoundation/amp/onepager/components/fields/AmpNewResourceFieldPanel.html @@ -36,6 +36,7 @@
...* +