From 95c81c07f3150a912966d0527803d06c8400d51d Mon Sep 17 00:00:00 2001 From: mpostelnicu Date: Tue, 28 Apr 2020 12:57:03 +0300 Subject: [PATCH 01/15] disabled pentaho reporting --- .../forms/util/MarkupCacheService.java | 33 +- .../wicket/page/edit/AbstractEditPage.java | 11 +- .../page/reports/AbstractReportPage.java | 347 +++++++++--------- .../reports/WicketResourceURLRewriter.java | 22 +- 4 files changed, 196 insertions(+), 217 deletions(-) diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/util/MarkupCacheService.java b/forms/src/main/java/org/devgateway/toolkit/forms/util/MarkupCacheService.java index 34eaedd6..6cb6625b 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/util/MarkupCacheService.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/util/MarkupCacheService.java @@ -11,13 +11,14 @@ *******************************************************************************/ package org.devgateway.toolkit.forms.util; -import net.sf.ehcache.Cache; -import net.sf.ehcache.CacheManager; -import net.sf.ehcache.Element; + import org.apache.wicket.markup.Markup; import org.apache.wicket.markup.MarkupCache; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import javax.cache.Cache; +import javax.cache.CacheManager; import java.util.Collection; /** @@ -30,6 +31,10 @@ */ @Component public class MarkupCacheService { + + @Autowired + private CacheManager cm; + /** * start-key used to identify the reports markup */ @@ -61,12 +66,11 @@ public final void flushMarkupCache() { */ public void addPentahoReportToCache(final String outputType, final String reportName, final String parameters, final byte[] buffer) { - final CacheManager cm = CacheManager.getInstance(); // get the reports cache "reportsCache", declared in ehcache.xml - final Cache cache = cm.getCache("reportsCache"); + final Cache cache = cm.getCache("reportsCache", String.class, byte[].class); - cache.put(new Element(createCacheKey(outputType, reportName, parameters), buffer)); + cache.put(createCacheKey(outputType, reportName, parameters), buffer); } /** @@ -78,15 +82,14 @@ public void addPentahoReportToCache(final String outputType, final String report * @return */ public byte[] getPentahoReportFromCache(final String outputType, final String reportName, final String parameters) { - final CacheManager cm = CacheManager.getInstance(); // get the reports cache "reportsCache", declared in ehcache.xml - final Cache cache = cm.getCache("reportsCache"); + final Cache cache = cm.getCache("reportsCache", String.class, byte[].class); final String key = createCacheKey(outputType, reportName, parameters); - if (cache.isKeyInCache(key)) { - return (byte[]) cache.get(key).getObjectValue(); + if (cache.containsKey(key)) { + return cache.get(key); } return null; @@ -96,10 +99,9 @@ public byte[] getPentahoReportFromCache(final String outputType, final String re * Remove from cache all reports content */ public void clearPentahoReportsCache() { - final CacheManager cm = CacheManager.getInstance(); // get the reports cache "reportsCache", declared in ehcache.xml - final Cache cache = cm.getCache("reportsCache"); + final Cache cache = cm.getCache("reportsCache"); if (cache != null) { cache.removeAll(); @@ -110,22 +112,21 @@ public void clearPentahoReportsCache() { * Remove from cache all APIs/Services content. */ public void clearAllCaches() { - final CacheManager cm = CacheManager.getInstance(); // get the reports cache "reportsApiCache", declared in ehcache.xml - final Cache cache = cm.getCache("reportsApiCache"); + final Cache cache = cm.getCache("reportsApiCache"); if (cache != null) { cache.removeAll(); } // get the reports cache "excelExportCache", declared in ehcache.xml - final Cache excelExportCache = cm.getCache("excelExportCache"); + final Cache excelExportCache = cm.getCache("excelExportCache"); if (excelExportCache != null) { excelExportCache.removeAll(); } // get the reports cache "servicesCache", declared in ehcache.xml - final Cache servicesCache = cm.getCache("servicesCache"); + final Cache servicesCache = cm.getCache("servicesCache"); if (servicesCache != null) { servicesCache.removeAll(); } diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditPage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditPage.java index 19f21c2e..90306e40 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditPage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditPage.java @@ -40,7 +40,6 @@ import org.devgateway.toolkit.forms.wicket.page.BasePage; import org.devgateway.toolkit.persistence.dao.GenericPersistable; import org.devgateway.toolkit.persistence.service.BaseJpaService; -import org.devgateway.toolkit.reporting.spring.util.ReportsCacheService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DataIntegrityViolationException; @@ -108,8 +107,8 @@ private T newInstance() { @SpringBean private EntityManager entityManager; - @SpringBean(required = false) - private ReportsCacheService reportsCacheService; +// @SpringBean(required = false) +// private ReportsCacheService reportsCacheService; @SpringBean(required = false) private MarkupCacheService markupCacheService; @@ -123,9 +122,9 @@ public EntityManager getEntityManager() { } public void flushReportingCaches() { - if (reportsCacheService != null) { - reportsCacheService.flushCache(); - } +// if (reportsCacheService != null) { +// reportsCacheService.flushCache(); +// } if (markupCacheService != null) { markupCacheService.flushMarkupCache(); diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/reports/AbstractReportPage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/reports/AbstractReportPage.java index afd04050..d08ae58d 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/reports/AbstractReportPage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/reports/AbstractReportPage.java @@ -38,27 +38,6 @@ import org.devgateway.toolkit.forms.wicket.page.BasePage; import org.devgateway.toolkit.forms.wicket.styles.BlockUiReportsJavaScript; import org.devgateway.toolkit.forms.wicket.styles.ReportsStyles; -import org.devgateway.toolkit.reporting.ReportUtil; -import org.pentaho.reporting.engine.classic.core.MasterReport; -import org.pentaho.reporting.engine.classic.core.ReportProcessingException; -import org.pentaho.reporting.engine.classic.core.layout.output.AbstractReportProcessor; -import org.pentaho.reporting.engine.classic.core.modules.output.pageable.base.PageableReportProcessor; -import org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.PdfOutputProcessor; -import org.pentaho.reporting.engine.classic.core.modules.output.table.base.FlowReportProcessor; -import org.pentaho.reporting.engine.classic.core.modules.output.table.base.StreamReportProcessor; -import org.pentaho.reporting.engine.classic.core.modules.output.table.html.AllItemsHtmlPrinter; -import org.pentaho.reporting.engine.classic.core.modules.output.table.html.HtmlOutputProcessor; -import org.pentaho.reporting.engine.classic.core.modules.output.table.html.HtmlPrinter; -import org.pentaho.reporting.engine.classic.core.modules.output.table.html.StreamHtmlOutputProcessor; -import org.pentaho.reporting.engine.classic.core.modules.output.table.rtf.FlowRTFOutputProcessor; -import org.pentaho.reporting.engine.classic.core.modules.output.table.xls.FlowExcelOutputProcessor; -import org.pentaho.reporting.libraries.repository.ContentIOException; -import org.pentaho.reporting.libraries.repository.ContentLocation; -import org.pentaho.reporting.libraries.repository.DefaultNameGenerator; -import org.pentaho.reporting.libraries.repository.file.FileRepository; -import org.pentaho.reporting.libraries.resourceloader.Resource; -import org.pentaho.reporting.libraries.resourceloader.ResourceException; -import org.pentaho.reporting.libraries.resourceloader.ResourceManager; import java.io.ByteArrayOutputStream; import java.io.File; @@ -154,33 +133,33 @@ public void onClick() { @Override public void write(final OutputStream output) throws IOException { - try { - if (canRenderReport()) { - // first try to fetch the report from cache, - // otherwise create the report and cache it - byte[] reportContent = markupCacheService.getPentahoReportFromCache(outputType.name(), - FilenameUtils.getName(AbstractReportPage.this.reportResourceName).replace(".prpt", - ""), - getPageParameters().toString()); - if (reportContent == null) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - generateReport(outputType, baos); - reportContent = baos.toByteArray(); - - if (caching) { - markupCacheService - .addPentahoReportToCache(outputType.name(), - FilenameUtils.getName(AbstractReportPage.this.reportResourceName) - .replace(".prpt", ""), - getPageParameters().toString(), reportContent); - } - } - - output.write(reportContent); - } - } catch (IllegalArgumentException | ReportProcessingException e) { - e.printStackTrace(); - } +// try { +// if (canRenderReport()) { +// // first try to fetch the report from cache, +// // otherwise create the report and cache it +// byte[] reportContent = markupCacheService.getPentahoReportFromCache(outputType.name(), +// FilenameUtils.getName(AbstractReportPage.this.reportResourceName).replace(".prpt", +// ""), +// getPageParameters().toString()); +// if (reportContent == null) { +// ByteArrayOutputStream baos = new ByteArrayOutputStream(); +// generateReport(outputType, baos); +// reportContent = baos.toByteArray(); +// +// if (caching) { +// markupCacheService +// .addPentahoReportToCache(outputType.name(), +// FilenameUtils.getName(AbstractReportPage.this.reportResourceName) +// .replace(".prpt", ""), +// getPageParameters().toString(), reportContent); +// } +// } +// +// output.write(reportContent); +// } +// } catch (IllegalArgumentException | ReportProcessingException e) { +// e.printStackTrace(); +// } } }; @@ -219,20 +198,21 @@ public ResourceStreamPanel(final String id, final AbstractReportPage parent) { */ @Override public IResourceStream getMarkupResourceStream(final MarkupContainer container, final Class containerClass) { - StringBuilder panelMarkup = new StringBuilder(); - panelMarkup.append(""); - ByteArrayOutputStream htmlStreamData = new ByteArrayOutputStream(); - try { - if (canRenderReport()) { - generateReport(OutputType.HTML, htmlStreamData); - } - } catch (IllegalArgumentException | ReportProcessingException e) { - e.printStackTrace(); - } - String content = new String(htmlStreamData.toByteArray()); - panelMarkup.append(content); - panelMarkup.append(""); - return new StringResourceStream(panelMarkup.toString()); +// StringBuilder panelMarkup = new StringBuilder(); +// panelMarkup.append(""); +// ByteArrayOutputStream htmlStreamData = new ByteArrayOutputStream(); +// try { +// if (canRenderReport()) { +// generateReport(OutputType.HTML, htmlStreamData); +// } +// } catch (IllegalArgumentException | ReportProcessingException e) { +// e.printStackTrace(); +// } +// String content = new String(htmlStreamData.toByteArray()); +// panelMarkup.append(content); +// panelMarkup.append(""); +// return new StringResourceStream(panelMarkup.toString()); + return null; } @Override @@ -307,22 +287,22 @@ public Component getLazyLoadComponent(final String id) { * * @return the report definition used by thus report generator */ - public MasterReport getReportDefinition() { - try { - // Using the classloader, get the URL to the reportDefinition file - final ClassLoader classloader = this.getClass().getClassLoader(); - final URL reportDefinitionURL = classloader.getResource(reportResourceName); - - // Parse the report file - final ResourceManager resourceManager = new ResourceManager(); - final Resource directly = resourceManager.createDirectly(reportDefinitionURL, MasterReport.class); - - return (MasterReport) directly.getResource(); - } catch (ResourceException e) { - e.printStackTrace(); - } - return null; - } +// public MasterReport getReportDefinition() { +// try { +// // Using the classloader, get the URL to the reportDefinition file +// final ClassLoader classloader = this.getClass().getClassLoader(); +// final URL reportDefinitionURL = classloader.getResource(reportResourceName); +// +// // Parse the report file +// final ResourceManager resourceManager = new ResourceManager(); +// final Resource directly = resourceManager.createDirectly(reportDefinitionURL, MasterReport.class); +// +// return (MasterReport) directly.getResource(); +// } catch (ResourceException e) { +// e.printStackTrace(); +// } +// return null; +// } /** * Returns the set of parameters that will be passed to the report @@ -352,111 +332,112 @@ public MasterReport getReportDefinition() { * indicates an error generating the report */ - public void generateReport(final OutputType outputType, final OutputStream outputStream) - throws IllegalArgumentException, ReportProcessingException { - if (outputStream == null) { - throw new IllegalArgumentException("The output stream was not specified"); - } - - // Get the report and data factory - final MasterReport report = getReportDefinition(); - - // Add any parameters to the report - final Map reportParameters = getReportParameters(); - if (reportParameters == null) { - return; - } - - for (String key : reportParameters.keySet()) { - report.getParameterValues().put(key, reportParameters.get(key)); - } - - // Prepare to generate the report - AbstractReportProcessor reportProcessor = null; - try { - // Greate the report processor for the specified output type - switch (outputType) { - case PDF: - final PdfOutputProcessor targetPdf = new PdfOutputProcessor(report.getConfiguration(), - outputStream, report.getResourceManager()); - reportProcessor = new PageableReportProcessor(report, targetPdf); - reportProcessor.processReport(); - break; - - case EXCEL: - final FlowExcelOutputProcessor targetExcel = new FlowExcelOutputProcessor(report.getConfiguration(), - outputStream, report.getResourceManager()); - reportProcessor = new FlowReportProcessor(report, targetExcel); - reportProcessor.processReport(); - break; - - case RTF: - final FlowRTFOutputProcessor targetRtf = new FlowRTFOutputProcessor(report.getConfiguration(), - outputStream, report.getResourceManager()); - reportProcessor = new FlowReportProcessor(report, targetRtf); - reportProcessor.processReport(); - break; - - case HTML: - ContentLocation targetRoot = null; - File tempDir = null; - try { - - // we manually make the folder to drop all exported html - // files into - tempDir = ReportUtil.createTemporaryDirectory("tmpreport"); - targetRoot = new FileRepository(tempDir).getRoot(); - } catch (ContentIOException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - - // we create a folder content resource for the entire tmpdir. - // This dir will only hold the fields for this export - FolderContentResource fcr = new FolderContentResource(tempDir); - - // we always have an authenticated web app - AuthenticatedWebApplication authApp = (AuthenticatedWebApplication) getApplication(); - - // we add the folder resource as a shared resource - authApp.getSharedResources().add(tempDir.getName(), fcr); - SharedResourceReference folderResourceReference = new SharedResourceReference(tempDir.getName()); - authApp.mountResource(tempDir.getName(), folderResourceReference); - - final HtmlOutputProcessor outputProcessor = - new StreamHtmlOutputProcessor(report.getConfiguration()); - final HtmlPrinter printer = new AllItemsHtmlPrinter(report.getResourceManager()); - printer.setContentWriter(targetRoot, new DefaultNameGenerator(targetRoot, "index", "html")); - - printer.setDataWriter(targetRoot, new DefaultNameGenerator(targetRoot, "content")); //$NON-NLS-1$ - - // we use a special URL Rewriter that knows how to speak Wicket - // :-) - printer.setUrlRewriter(new WicketResourceURLRewriter(folderResourceReference)); - outputProcessor.setPrinter(printer); - reportProcessor = new StreamReportProcessor(report, outputProcessor); - reportProcessor.processReport(); - - // we plug the html file stream into the output stream - FileInputStream indexFileStream = - new FileInputStream(tempDir.getAbsolutePath() + File.separator + "index.html"); - IOUtils.copy(indexFileStream, outputStream); - indexFileStream.close(); - - break; - - default: - throw new RuntimeException("Unknown output type provided!"); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - if (reportProcessor != null) { - reportProcessor.close(); - } - } - } +// public void generateReport(final OutputType outputType, final OutputStream outputStream) +// throws IllegalArgumentException, ReportProcessingException { +// if (outputStream == null) { +// throw new IllegalArgumentException("The output stream was not specified"); +// } +// +// // Get the report and data factory +// final MasterReport report = getReportDefinition(); +// +// // Add any parameters to the report +// final Map reportParameters = getReportParameters(); +// if (reportParameters == null) { +// return; +// } +// +// for (String key : reportParameters.keySet()) { +// report.getParameterValues().put(key, reportParameters.get(key)); +// } +// +// // Prepare to generate the report +// AbstractReportProcessor reportProcessor = null; +// try { +// // Greate the report processor for the specified output type +// switch (outputType) { +// case PDF: +// final PdfOutputProcessor targetPdf = new PdfOutputProcessor(report.getConfiguration(), +// outputStream, report.getResourceManager()); +// reportProcessor = new PageableReportProcessor(report, targetPdf); +// reportProcessor.processReport(); +// break; +// +// case EXCEL: +// final FlowExcelOutputProcessor targetExcel = new FlowExcelOutputProcessor( +// report.getConfiguration(), +// outputStream, report.getResourceManager()); +// reportProcessor = new FlowReportProcessor(report, targetExcel); +// reportProcessor.processReport(); +// break; +// +// case RTF: +// final FlowRTFOutputProcessor targetRtf = new FlowRTFOutputProcessor(report.getConfiguration(), +// outputStream, report.getResourceManager()); +// reportProcessor = new FlowReportProcessor(report, targetRtf); +// reportProcessor.processReport(); +// break; +// +// case HTML: +// ContentLocation targetRoot = null; +// File tempDir = null; +// try { +// +// // we manually make the folder to drop all exported html +// // files into +// tempDir = null; //ReportUtil.createTemporaryDirectory("tmpreport"); +// targetRoot = new FileRepository(tempDir).getRoot(); +// } catch (ContentIOException e) { +// e.printStackTrace(); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// +// // we create a folder content resource for the entire tmpdir. +// // This dir will only hold the fields for this export +// FolderContentResource fcr = new FolderContentResource(tempDir); +// +// // we always have an authenticated web app +// AuthenticatedWebApplication authApp = (AuthenticatedWebApplication) getApplication(); +// +// // we add the folder resource as a shared resource +// authApp.getSharedResources().add(tempDir.getName(), fcr); +// SharedResourceReference folderResourceReference = new SharedResourceReference(tempDir.getName()); +// authApp.mountResource(tempDir.getName(), folderResourceReference); +// +// final HtmlOutputProcessor outputProcessor = +// new StreamHtmlOutputProcessor(report.getConfiguration()); +// final HtmlPrinter printer = new AllItemsHtmlPrinter(report.getResourceManager()); +// printer.setContentWriter(targetRoot, new DefaultNameGenerator(targetRoot, "index", "html")); +// +// printer.setDataWriter(targetRoot, new DefaultNameGenerator(targetRoot, "content")); //$NON-NLS-1$ +// +// // we use a special URL Rewriter that knows how to speak Wicket +// // :-) +// printer.setUrlRewriter(new WicketResourceURLRewriter(folderResourceReference)); +// outputProcessor.setPrinter(printer); +// reportProcessor = new StreamReportProcessor(report, outputProcessor); +// reportProcessor.processReport(); +// +// // we plug the html file stream into the output stream +// FileInputStream indexFileStream = +// new FileInputStream(tempDir.getAbsolutePath() + File.separator + "index.html"); +// IOUtils.copy(indexFileStream, outputStream); +// indexFileStream.close(); +// +// break; +// +// default: +// throw new RuntimeException("Unknown output type provided!"); +// } +// } catch (IOException e) { +// e.printStackTrace(); +// } finally { +// if (reportProcessor != null) { +// reportProcessor.close(); +// } +// } +// } @Override public void renderHead(final IHeaderResponse response) { diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/reports/WicketResourceURLRewriter.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/reports/WicketResourceURLRewriter.java index 1a7e1f35..2c1a6515 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/reports/WicketResourceURLRewriter.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/reports/WicketResourceURLRewriter.java @@ -18,18 +18,16 @@ import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.request.resource.SharedResourceReference; import org.devgateway.toolkit.forms.util.FolderContentResource; -import org.pentaho.reporting.engine.classic.core.modules.output.table.html.URLRewriteException; -import org.pentaho.reporting.engine.classic.core.modules.output.table.html.URLRewriter; -import org.pentaho.reporting.libraries.repository.ContentEntity; /** * @author mpostelnicu This {@link URLRewriter} will translate local folder * resources into wicket encoded resources using the * {@link SharedResourceReference} to {@link FolderContentResource} */ -public class WicketResourceURLRewriter implements URLRewriter { +public class WicketResourceURLRewriter { + //implements URLRewriter - private SharedResourceReference folderResourceReference; + private final SharedResourceReference folderResourceReference; /* * (non-Javadoc) @@ -44,11 +42,11 @@ public WicketResourceURLRewriter(final SharedResourceReference folderResourceRef this.folderResourceReference = folderResourceReference; } - @Override - public String rewrite(final ContentEntity sourceDocument, final ContentEntity dataEntity) - throws URLRewriteException { - PageParameters parameters = new PageParameters(); - parameters.add(FolderContentResource.PARAM_FILE_NAME, dataEntity.getName()); - return RequestCycle.get().urlFor(folderResourceReference, parameters).toString(); - } +// @Override +// public String rewrite(final ContentEntity sourceDocument, final ContentEntity dataEntity) +// throws URLRewriteException { +// PageParameters parameters = new PageParameters(); +// parameters.add(FolderContentResource.PARAM_FILE_NAME, dataEntity.getName()); +// return RequestCycle.get().urlFor(folderResourceReference, parameters).toString(); +// } } From ae2176ddc10b15200ae7893159a0ed7ebb4398c7 Mon Sep 17 00:00:00 2001 From: mpostelnicu Date: Tue, 28 Apr 2020 13:05:48 +0300 Subject: [PATCH 02/15] upgrade to spring boot 2.2.6, wicket 8.8, ehcache 3.8.1 --- forms/pom.xml | 62 +++++--------- .../forms/wicket/page/user/EditUserPage.java | 2 +- .../forms/wicket/page/user/LoginPage.java | 2 +- .../SortableJpaServiceDataProvider.java | 4 +- persistence/pom.xml | 24 +++--- .../persistence/application.properties | 5 +- .../spring/CacheConfiguration.java | 25 ++---- persistence/src/main/resources/ehcache.xml | 84 +++++-------------- .../excel/ExcelFileImportDefaultTest.java | 10 +++ pom.xml | 16 +++- reporting/pom.xml | 6 ++ web/pom.xml | 15 ++-- .../toolkit/web/spring/JMXConfiguration.java | 31 ------- .../toolkit/web/spring/SwaggerConfig.java | 12 +-- .../toolkit/web/spring/WebSecurityConfig.java | 1 + 15 files changed, 116 insertions(+), 183 deletions(-) delete mode 100644 web/src/main/java/org/devgateway/toolkit/web/spring/JMXConfiguration.java diff --git a/forms/pom.xml b/forms/pom.xml index aedfb146..5a61773e 100644 --- a/forms/pom.xml +++ b/forms/pom.xml @@ -30,15 +30,15 @@ UTF-8 1.8 - 8.6.1 - 8.6.0 - 2.0.10 + 8.8.0 + 8.7.0 + 2.0.11 1.13 - 2.0.15 - v20190528 + 2.0.17 + v20200406 3.1 2.4.8 - 1.80.0 + 1.82.0 @@ -60,21 +60,21 @@ 0.0.1-SNAPSHOT - - org.devgateway.toolkit - reporting - 0.0.1-SNAPSHOT - - - bcprov-jdk14 - bouncycastle - - - bcprov-jdk14 - org.bouncycastle - - - + + + + + + + + + + + + + + + org.devgateway.toolkit @@ -114,17 +114,6 @@ - - org.springframework.boot - spring-boot-starter-integration - - - xercesImpl - xerces - - - - org.springframework.boot spring-boot-starter-web @@ -146,11 +135,6 @@ spring-boot-starter-security - - org.springframework.data - spring-data-rest-hal-browser - - org.springframework.boot spring-boot-starter-test @@ -391,10 +375,6 @@ derby ${derby.version} - - org.hibernate - hibernate-ehcache - org.hibernate hibernate-entitymanager diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/user/EditUserPage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/user/EditUserPage.java index 351539a0..9d68a9ee 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/user/EditUserPage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/user/EditUserPage.java @@ -11,7 +11,7 @@ *******************************************************************************/ package org.devgateway.toolkit.forms.wicket.page.user; -import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.wicket.Component; import org.apache.wicket.Page; import org.apache.wicket.ajax.AjaxRequestTarget; diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/user/LoginPage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/user/LoginPage.java index d76bb293..28d37ec8 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/user/LoginPage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/user/LoginPage.java @@ -16,7 +16,7 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel; import de.agilecoders.wicket.core.markup.html.bootstrap.form.BootstrapForm; -import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession; import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton; diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/providers/SortableJpaServiceDataProvider.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/providers/SortableJpaServiceDataProvider.java index 075e6888..578845a1 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/providers/SortableJpaServiceDataProvider.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/providers/SortableJpaServiceDataProvider.java @@ -58,7 +58,9 @@ protected Sort translateSort() { if (getSort() == null) { return null; } - return new Sort(getSort().isAscending() ? Direction.ASC : Direction.DESC, getSort().getProperty()); + + return Sort.by(getSort().isAscending() ? Direction.ASC : Direction.DESC, + getSort().getProperty()); } /** diff --git a/persistence/pom.xml b/persistence/pom.xml index 7983af04..26aed82c 100644 --- a/persistence/pom.xml +++ b/persistence/pom.xml @@ -19,12 +19,12 @@ UTF-8 org.devgateway.toolkit.persistence.spring.PersistenceApplication 1.8 - 2.10.6 + 3.8.1 0.3.8 3.2.1 28.1-jre 1.9.3 - 6.1.0.Final + 6.1.4.Final @@ -73,11 +73,16 @@ - net.sf.ehcache + org.ehcache ehcache ${ehcache.version} + + org.springframework.boot + spring-boot-starter-cache + + org.springframework spring-context-support @@ -106,13 +111,12 @@ org.hibernate - hibernate-ehcache - - - ehcache-core - net.sf.ehcache - - + hibernate-jcache + + + + javax.cache + cache-api diff --git a/persistence/src/main/java/org/devgateway/toolkit/persistence/application.properties b/persistence/src/main/java/org/devgateway/toolkit/persistence/application.properties index f1c602a5..f95731a7 100644 --- a/persistence/src/main/java/org/devgateway/toolkit/persistence/application.properties +++ b/persistence/src/main/java/org/devgateway/toolkit/persistence/application.properties @@ -33,7 +33,8 @@ spring.servlet.multipart.enabled = false spring.jpa.properties.org.hibernate.envers.global_with_modified_flag=true spring.jpa.hibernate.ddl-auto=update -spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory +spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.jcache.JCacheRegionFactory +spring.jpa.properties.hibernate.javax.cache.missing_cache_strategy=create spring.datasource.max-active=3000 spring.datasource.max-idle=8 spring.datasource.min-idle=8 @@ -46,5 +47,7 @@ spring.datasource.transaction-isolation=2 dg-toolkit.datasource.jndi-name=toolkitDS dg-toolkit.derby.port=1527 +spring.cache.jcache.config=classpath:ehcache.xml + spring.data.rest.base-path=/rest spring.profiles.active=integration diff --git a/persistence/src/main/java/org/devgateway/toolkit/persistence/spring/CacheConfiguration.java b/persistence/src/main/java/org/devgateway/toolkit/persistence/spring/CacheConfiguration.java index ac41640a..94ff4d9b 100644 --- a/persistence/src/main/java/org/devgateway/toolkit/persistence/spring/CacheConfiguration.java +++ b/persistence/src/main/java/org/devgateway/toolkit/persistence/spring/CacheConfiguration.java @@ -14,12 +14,14 @@ */ package org.devgateway.toolkit.persistence.spring; -import net.sf.ehcache.management.ManagementService; +import org.hibernate.cache.jcache.ConfigSettings; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.CacheManager; +import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.ehcache.EhCacheCacheManager; import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; +import org.springframework.cache.jcache.JCacheCacheManager; +import org.springframework.cache.jcache.JCacheManagerFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @@ -27,6 +29,7 @@ import org.springframework.data.auditing.DateTimeProvider; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import javax.cache.CacheManager; import javax.management.MBeanServer; import java.time.ZonedDateTime; import java.util.Optional; @@ -49,23 +52,9 @@ public DateTimeProvider dateTimeProvider() { private MBeanServer mbeanServer; @Bean - public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() { - final EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean(); - ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml")); - ehCacheManagerFactoryBean.setShared(true); - return ehCacheManagerFactoryBean; + public HibernatePropertiesCustomizer hibernateSecondLevelCacheCustomizer(final JCacheCacheManager cacheManager) { + return (properties) -> properties.put(ConfigSettings.CACHE_MANAGER, cacheManager.getCacheManager()); } - @Bean - public CacheManager cacheManager(final EhCacheManagerFactoryBean factory) { - return new EhCacheCacheManager(factory.getObject()); - } - @Bean(destroyMethod = "dispose", initMethod = "init") - @Profile("!integration") - public ManagementService ehCacheManagementService(final EhCacheManagerFactoryBean factory) { - final ManagementService managementService = - new ManagementService(factory.getObject(), mbeanServer, true, true, true, true); - return managementService; - } } diff --git a/persistence/src/main/resources/ehcache.xml b/persistence/src/main/resources/ehcache.xml index b7b7f72b..3bcb1235 100644 --- a/persistence/src/main/resources/ehcache.xml +++ b/persistence/src/main/resources/ehcache.xml @@ -1,62 +1,22 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + 100000 + + + + + 100000 + + + + + 100000 + + + + \ No newline at end of file diff --git a/persistence/src/test/java/org/devgateway/toolkit/persistence/excel/ExcelFileImportDefaultTest.java b/persistence/src/test/java/org/devgateway/toolkit/persistence/excel/ExcelFileImportDefaultTest.java index 4c3e2ac1..d5e642a1 100644 --- a/persistence/src/test/java/org/devgateway/toolkit/persistence/excel/ExcelFileImportDefaultTest.java +++ b/persistence/src/test/java/org/devgateway/toolkit/persistence/excel/ExcelFileImportDefaultTest.java @@ -125,6 +125,11 @@ public String[] getBeanNamesForType(ResolvableType resolvableType) { return new String[0]; } + @Override + public String[] getBeanNamesForType(ResolvableType resolvableType, boolean b, boolean b1) { + return new String[0]; + } + @Override public String[] getBeanNamesForType(Class aClass) { return new String[0]; @@ -220,6 +225,11 @@ public Class getType(String s) throws NoSuchBeanDefinitionException { return null; } + @Override + public Class getType(String s, boolean b) throws NoSuchBeanDefinitionException { + return null; + } + @Override public String[] getAliases(String s) { return new String[0]; diff --git a/pom.xml b/pom.xml index d7602d0b..b1a58161 100644 --- a/pom.xml +++ b/pom.xml @@ -23,8 +23,8 @@ UTF-8 1.8 - 3.8.3 - 2.1.11.RELEASE + 3.8.9 + 2.2.6.RELEASE 10.14.2.0 4.0.1 devgateway/toolkit @@ -38,7 +38,7 @@ persistence web ui - reporting + forms persistence-mongodb @@ -76,6 +76,11 @@ + + jcenter-snapshots + jcenter + http://oss.jfrog.org/artifactory/oss-snapshot-local/ + jcenter https://jcenter.bintray.com/ @@ -120,6 +125,11 @@ derby ${derby.version} + + org.springframework.plugin + spring-plugin-core + 2.0.0.RELEASE + diff --git a/reporting/pom.xml b/reporting/pom.xml index e1ac42d5..749837dd 100644 --- a/reporting/pom.xml +++ b/reporting/pom.xml @@ -221,6 +221,12 @@ org.apache.xmlgraphics batik-bridge 1.7 + + + xalan + xalan + + diff --git a/web/pom.xml b/web/pom.xml index 3b726fea..ade369f0 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -18,8 +18,7 @@ org.devgateway.toolkit.web.spring.WebApplication 1.8 1.4 - 2.9.2 - 1.2.0 + 3.0.0-SNAPSHOT @@ -72,12 +71,6 @@ test - - org.jminix - jminix - ${jminix.version} - - de.flapdoodle.embed de.flapdoodle.embed.mongo @@ -130,6 +123,12 @@ ${swagger.version} + + io.springfox + springfox-spring-webmvc + ${swagger.version} + + io.springfox springfox-swagger-ui diff --git a/web/src/main/java/org/devgateway/toolkit/web/spring/JMXConfiguration.java b/web/src/main/java/org/devgateway/toolkit/web/spring/JMXConfiguration.java deleted file mode 100644 index 410f55a8..00000000 --- a/web/src/main/java/org/devgateway/toolkit/web/spring/JMXConfiguration.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.devgateway.toolkit.web.spring; - -import org.jminix.console.application.MiniConsoleApplication; -import org.jminix.console.servlet.SpringMiniConsoleServlet; -import org.jminix.server.WebSpringServerConnectionProvider; -import org.springframework.boot.web.servlet.ServletRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class JMXConfiguration { - - @Bean - public WebSpringServerConnectionProvider jMiniXConnectionProvider() { - return new WebSpringServerConnectionProvider(); - } - - @Bean - public MiniConsoleApplication miniConsoleApplication() { - final MiniConsoleApplication mca = new MiniConsoleApplication(); - mca.setServerConnectionProvider(jMiniXConnectionProvider()); - return mca; - } - - @Bean - public ServletRegistrationBean jminiXServletRegistration(final MiniConsoleApplication miniConsoleApplication) { - final ServletRegistrationBean registration = new ServletRegistrationBean(new SpringMiniConsoleServlet()); - registration.addUrlMappings("/jminix/*"); - return registration; - } -} diff --git a/web/src/main/java/org/devgateway/toolkit/web/spring/SwaggerConfig.java b/web/src/main/java/org/devgateway/toolkit/web/spring/SwaggerConfig.java index 36f6466c..eaeedab5 100644 --- a/web/src/main/java/org/devgateway/toolkit/web/spring/SwaggerConfig.java +++ b/web/src/main/java/org/devgateway/toolkit/web/spring/SwaggerConfig.java @@ -7,16 +7,16 @@ import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; +import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; import static springfox.documentation.builders.PathSelectors.regex; @Configuration -@EnableSwagger2 +@EnableSwagger2WebMvc public class SwaggerConfig { @Bean public Docket yaliApi() { - return new Docket(DocumentationType.SWAGGER_2).groupName("Api").apiInfo(yaliApiInfo()) + return new Docket(DocumentationType.SWAGGER_2).groupName("Api").apiInfo(apiInfo()) .select().apis(RequestHandlerSelectors.any()).paths(regex("/api/.*")).build(); } @@ -26,9 +26,9 @@ public Docket manageApi() { .select().apis(RequestHandlerSelectors.any()).paths(regex("/manage/.*")).build(); } - private ApiInfo yaliApiInfo() { - return new ApiInfoBuilder().title("Application API") - .description("These endpoints are used to feed reports").license("MIT License") + private ApiInfo apiInfo() { + return new ApiInfoBuilder().title("DG-Toolkit Application API") + .description("Endpoints description").license("MIT License") .licenseUrl("https://opensource.org/licenses/MIT").version("1.0").build(); } diff --git a/web/src/main/java/org/devgateway/toolkit/web/spring/WebSecurityConfig.java b/web/src/main/java/org/devgateway/toolkit/web/spring/WebSecurityConfig.java index a892d722..688896fc 100644 --- a/web/src/main/java/org/devgateway/toolkit/web/spring/WebSecurityConfig.java +++ b/web/src/main/java/org/devgateway/toolkit/web/spring/WebSecurityConfig.java @@ -66,6 +66,7 @@ public HttpFirewall allowUrlEncodedSlashHttpFirewall() { final StrictHttpFirewall firewall = new StrictHttpFirewall(); firewall.setAllowUrlEncodedSlash(true); firewall.setAllowSemicolon(true); + firewall.setAllowUrlEncodedDoubleSlash(true); return firewall; } From e66489a66ad26ec438cadd7219997b602d0ffb06 Mon Sep 17 00:00:00 2001 From: mpostelnicu Date: Tue, 28 Apr 2020 13:06:23 +0300 Subject: [PATCH 03/15] removed older apps - minix, hal --- .../toolkit/forms/wicket/page/BasePage.java | 36 ------------------- .../forms/wicket/page/BasePage.properties | 1 - 2 files changed, 37 deletions(-) diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java index b81f4942..6fb9e45f 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java @@ -94,24 +94,6 @@ public Boolean fluidContainer() { return false; } - public static class HALRedirectPage extends RedirectPage { - private static final long serialVersionUID = -750983217518258464L; - - public HALRedirectPage() { - super(WebApplication.get().getServletContext().getContextPath() + "/api/browser/"); - } - - } - - public static class JminixRedirectPage extends RedirectPage { - private static final long serialVersionUID = -750983217518258464L; - - public JminixRedirectPage() { - super(WebApplication.get().getServletContext().getContextPath() + "/jminix/"); - } - - } - public static class UIRedirectPage extends RedirectPage { private static final long serialVersionUID = -750983217518258464L; @@ -288,24 +270,6 @@ JavamelodyPage.class, new StringResourceModel("navbar.javamelody", new StringResourceModel("navbar.springendpoints", this, null)) .setIconType(FontAwesomeIconType.anchor)); - list.add(new MenuBookmarkablePageLink(JminixRedirectPage.class, null, - new StringResourceModel("navbar.jminix", this, null)).setIconType(FontAwesomeIconType.bug)); - - final MenuBookmarkablePageLink halBrowserLink = - new MenuBookmarkablePageLink(HALRedirectPage.class, null, - new StringResourceModel("navbar.halbrowser", this, null)) { - private static final long serialVersionUID = 1L; - - @Override - protected void onComponentTag(final ComponentTag tag) { - super.onComponentTag(tag); - tag.put("target", "_blank"); - } - }; - halBrowserLink.setIconType(FontAwesomeIconType.rss).setEnabled(true); - - list.add(halBrowserLink); - final MenuBookmarkablePageLink uiBrowserLink = new MenuBookmarkablePageLink( UIRedirectPage.class, null, new StringResourceModel("navbar.ui", this, null)) { diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.properties b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.properties index fce66d2b..2d6b1ccb 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.properties +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.properties @@ -20,5 +20,4 @@ navbar.ui=React UI navbar.adminSettings=Settings navbar.springendpoints=Spring Endpoints navbar.lang=Language -navbar.jminix=JMX Console navbar.javamelody=Javamelody From 360b8eeaccaf999ac4a7ecff4441d54b1faaf6a4 Mon Sep 17 00:00:00 2001 From: mpostelnicu Date: Wed, 29 Apr 2020 23:31:13 +0300 Subject: [PATCH 04/15] fixes #283 --- forms/pom.xml | 1 + .../toolkit/forms/wicket/page/BasePage.java | 17 +++++++++++++++++ .../forms/wicket/page/BasePage.properties | 1 + .../toolkit/forms/wicket/styles/BaseStyles.css | 4 ++++ .../assets/img/icons/toolkit-favicon.svg | 1 + .../styles/assets/img/toolkit-logo-0048.png | Bin 0 -> 2376 bytes 6 files changed, 24 insertions(+) create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/assets/img/icons/toolkit-favicon.svg create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/assets/img/toolkit-logo-0048.png diff --git a/forms/pom.xml b/forms/pom.xml index 5a61773e..5ea04ed6 100644 --- a/forms/pom.xml +++ b/forms/pom.xml @@ -270,6 +270,7 @@ **/*.css **/*.js **/*.png + **/*.svg **/*.gif **/*.html **/*.properties diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java index 6fb9e45f..3321c0ae 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java @@ -33,6 +33,7 @@ import org.apache.wicket.markup.head.CssHeaderItem; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.head.JavaScriptHeaderItem; +import org.apache.wicket.markup.head.MetaDataHeaderItem; import org.apache.wicket.markup.head.filter.HeaderResponseContainer; import org.apache.wicket.markup.html.GenericWebPage; import org.apache.wicket.markup.html.TransparentWebMarkupContainer; @@ -46,6 +47,7 @@ import org.apache.wicket.protocol.http.WebSession; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.request.resource.JavaScriptResourceReference; +import org.apache.wicket.request.resource.PackageResourceReference; import org.apache.wicket.resource.JQueryResourceReference; import org.apache.wicket.util.string.StringValue; import org.devgateway.toolkit.forms.WebConstants; @@ -195,6 +197,16 @@ protected List newSubMenuButtons(final String buttonMarkupId) { return languageDropDown; } + protected MetaDataHeaderItem getFavicon() { + PackageResourceReference faviconRef = + new PackageResourceReference(BaseStyles.class, "assets/img/icons/toolkit-favicon.svg"); + MetaDataHeaderItem icon = MetaDataHeaderItem.forLinkTag("icon", + urlFor(faviconRef, null).toString()); + icon.addTagAttribute("type", "image/svg+xml"); + return icon; + + } + protected NavbarButton newLogoutMenu() { // logout menu final NavbarButton logoutMenu = @@ -316,6 +328,8 @@ protected Navbar newNavbar(final String markupId) { * @see org.devgateway.toolkit.forms.wicket.styles.BaseStyles */ navbar.setPosition(Navbar.Position.TOP); + navbar.setBrandImage(new PackageResourceReference(BaseStyles.class, "assets/img/toolkit-logo-0048.png"), + new StringResourceModel("brandImageAltText", this, null)); navbar.setInverted(true); navbar.addComponents(NavbarComponents.transform(Navbar.ComponentPosition.RIGHT, newHomeMenu(), newAdminMenu(), @@ -330,6 +344,9 @@ protected Navbar newNavbar(final String markupId) { public void renderHead(final IHeaderResponse response) { super.renderHead(response); + //favicon + response.render(getFavicon()); + // Load Styles. response.render(CssHeaderItem.forReference(BaseStyles.INSTANCE)); response.render(CssHeaderItem.forReference(BootstrapCssReference.instance())); diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.properties b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.properties index 2d6b1ccb..6e0824b5 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.properties +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.properties @@ -21,3 +21,4 @@ navbar.adminSettings=Settings navbar.springendpoints=Spring Endpoints navbar.lang=Language navbar.javamelody=Javamelody +brandImageAltText=DG-Toolkit \ No newline at end of file diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/BaseStyles.css b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/BaseStyles.css index 8a2bca13..9927dfb0 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/BaseStyles.css +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/BaseStyles.css @@ -42,6 +42,10 @@ body { margin-bottom: 20px; } +.navbar-brand { + padding: 0px; +} + .mainContainer { /* * The main page content should have some height. diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/assets/img/icons/toolkit-favicon.svg b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/assets/img/icons/toolkit-favicon.svg new file mode 100644 index 00000000..cd261bc4 --- /dev/null +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/assets/img/icons/toolkit-favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/assets/img/toolkit-logo-0048.png b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/assets/img/toolkit-logo-0048.png new file mode 100644 index 0000000000000000000000000000000000000000..54a0b2fe91adf9ccc0d59d53edfb11a0b0fa8e8c GIT binary patch literal 2376 zcmV-O3Agr%P)9~a zYyk_RC0HZ`Fck(&P^VPI=>V-$k+HQjfnZVu11K|q6`3lG&=Is2MZuZcnIeM_ZB6A7 zhwOtSn|trCe{8aBlHE;sXr~>%|Lnczch2wk{hr6~oO2g0-~uk-{}cVE_7D8GIrIgR z&JZl~|3j3edAa_LjdhKuz78_lM4&ClDi-uwREDcCTvQ4G^|sj~WdNb>Z88Zs2wV@E#ZA^B6i+LE4l3vGgFbN2qOVc3X(It`A&VR_GtwKru*L#Wi${1lBZJ( z%5Frrxb5hZ=N$)gp{+uWQg$zfTC-}b*~rX zVTG-6-;4oy&Sde=z%+mTMP%5ry%c&@Bm0X@s4{V9r=s`#MMK@ z**>I3zH@SI==Y(u;k&Xt*##<6+9Adgq0Gg67@Y23<(uLg+$;I0H4$U&1GyoESp~|y zK4+DSQw*T0?_~0qUVxF0lHO{9)BNQCCsv;bhw4KwhSrAf@?aOCECEiTlv`P5zrWOf zOFEyeQR_~WO+a4ipt{JL@4NsY$n5~eSbxnVAnh9HKed0rvD%wBy{J@G7l+nGe$~CM zZ;EelR+f22VHyxqk$Xb*q1Te@w6suY9P4j@KTXKh+4)X6#IsQ>+d2xU*XQ&TNuM;* zj=+~Mei;(jn^Yts^2=bEe>KHjGevYm^yta8;WFR>z$2;~0y6@acM0TJPp2aLU;`Di=#^tevab@?oJct zvn4xtA|BoH(;zRMgtht9(g`qk9o=D0W8;30b~Iidn4bg0D$J~@Na;9Y0TLuwZSs~ z^UNS8QK}jQa1md}?-wcBuJ-zzy5xdLgl~XW0uETe?Ts7sGKD(fFZGWwM)!lHPJaNR z`_(mbRn;Ymi?)ch9+;}isvN)bzh5O(A_z6T80a0MH2q9Jd zKFHNTScNG$KIgR*Vb7m{P7Ep;lpo7!83_y!i{p9Tz~0V#!-x@uPfL71NI;dP2M#nW zj76-C2zMo!T!fd^cAm%ya_CHXoV9>Vxbn)vMqo&{;x7&yXc(f3*kR8oS9FLPTC*Kz zj|4cYBRQnFJe&!Burgd=T>Dl;EY$pa`&|D!h$u;OKM4+i2nzOJ&LwU#&Pu?z<)JH7 z+^11@B4XmBD9YHCp{-Qu$wU=lDvJk3vfJicGpc9f1qOvfT##AVB`FfI#}SP2*(Xgm+L z<;A41`V|-ZaT*?JI1@f+v-Zn@&J~T=nRh$ef+dV zMQpQ4U{PlB0O}XfEfiHx=qs;23uuqoPXR*|)@^?<@O-kZc#Y<$MNNW+GHHBmWBk}r zv|=>qcp$2Vn^g2>V1I^CS)#l|(XtWeO~9C%FRw#n1`u||%PBZ z14YV?_X;>P0l=|l6516vTTp{TmY^yBb@@2l5)Bd{Zdp8`Bt)6YqOi^R_@!p6?B z;>GH*ZG8zCnyQTmf86z8L8S9tjVu5Oi?WoWs-EZkgR@U)L({la>q3OmvldXYQN04i zpenK{*?IYM?JH9ShNb*s{ zL1BO~&xIC(%uLgy9k_SZiDL|zgnlZ@qBOOmB682RxxsB| z{e4=oM=j5{M0FKHuEGNG*hi$W`c;amOA|faP`O4$<|Nv=T$`3+CjMhlJ~6nf!IZW!2Xjm-z~lA@M<=6=kOiJHhNWIMWq*SYVCYP}w6@ zXB05LwlU9kd`F32IM$#%)T5Ge6w%)uvx3j=n=S2qCZCK;mJh(ok41GNPz)re&CXBz zyS1URI92nsr%}hrzZW#WgJ?2&8+cbm*SF>d>vzqR)|3wViq9*WfI5Xbu3qdVGVn&= zyCQlk${ihD-2HNtK*|vqTh$a0%kv(98a;;c32;a;e;2!b*S2{Bdi>S%mEhvqJiKj_ u1$k5(DzExlVtysakVY@y0xsZx9RC6NTUI2p=t({R0000 Date: Wed, 29 Apr 2020 23:49:56 +0300 Subject: [PATCH 05/15] #283 allow svg resources --- .../java/org/devgateway/toolkit/forms/FormsSecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/FormsSecurityConfig.java b/forms/src/main/java/org/devgateway/toolkit/forms/FormsSecurityConfig.java index abad38dc..789cca44 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/FormsSecurityConfig.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/FormsSecurityConfig.java @@ -48,7 +48,7 @@ public void configure(final WebSecurity web) throws Exception { web.ignoring().antMatchers("/img/**", "/css*/**", "/js*/**", "/assets*/**", "/wicket/resource/**/*.js", "/wicket/resource/**/*.css", "/wicket/resource/**/*.png", "/wicket/resource/**/*.jpg", "/wicket/resource/**/*.woff", "/wicket/resource/**/*.woff2", "/wicket/resource/**/*.ttf", - "/favicon.ico", + "/favicon.ico", "/wicket/resource/**/*.svg", "/wicket/resource/**/*.gif", "/login/**", "/forgotPassword/**", "/resources/**", "/resources/public/**"); } From 5db70f6b5a4a1784f79fa3f7a68d65e29e9f1c97 Mon Sep 17 00:00:00 2001 From: mpostelnicu Date: Thu, 30 Apr 2020 03:09:33 +0300 Subject: [PATCH 06/15] #283 ordering of headeritems --- .../java/org/devgateway/toolkit/forms/wicket/page/BasePage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java index 3321c0ae..30926811 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java @@ -348,9 +348,9 @@ public void renderHead(final IHeaderResponse response) { response.render(getFavicon()); // Load Styles. - response.render(CssHeaderItem.forReference(BaseStyles.INSTANCE)); response.render(CssHeaderItem.forReference(BootstrapCssReference.instance())); response.render(CssHeaderItem.forReference(FontAwesomeCssReference.instance())); + response.render(CssHeaderItem.forReference(BaseStyles.INSTANCE)); // Load Scripts. response.render(RespondJavaScriptReference.headerItem()); From 6e43a83721c9e81fdaab5ca4a01e24a26243a3e9 Mon Sep 17 00:00:00 2001 From: mpostelnicu Date: Mon, 25 May 2020 12:02:46 +0300 Subject: [PATCH 07/15] fixes #291 --- .../dao/AbstractStatusAuditableEntity.java | 87 +++++++++++++++++++ .../toolkit/persistence/dao/DBConstants.java | 13 +++ .../persistence/dao/StatusChangedComment.java | 42 +++++++++ .../toolkit/persistence/dao/Statusable.java | 15 ++++ 4 files changed, 157 insertions(+) create mode 100644 persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AbstractStatusAuditableEntity.java create mode 100644 persistence/src/main/java/org/devgateway/toolkit/persistence/dao/StatusChangedComment.java create mode 100644 persistence/src/main/java/org/devgateway/toolkit/persistence/dao/Statusable.java diff --git a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AbstractStatusAuditableEntity.java b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AbstractStatusAuditableEntity.java new file mode 100644 index 00000000..01cd0236 --- /dev/null +++ b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AbstractStatusAuditableEntity.java @@ -0,0 +1,87 @@ +package org.devgateway.toolkit.persistence.dao; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.envers.Audited; + +import javax.persistence.CascadeType; +import javax.persistence.FetchType; +import javax.persistence.MappedSuperclass; +import javax.persistence.OneToMany; +import javax.persistence.OrderColumn; +import javax.persistence.Transient; +import javax.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; + +@MappedSuperclass +public abstract class AbstractStatusAuditableEntity extends AbstractAuditableEntity implements Statusable { + @NotNull + @Audited + private String status = DBConstants.Status.DRAFT; + + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) + @OrderColumn(name = "index") + @JsonIgnore + @org.springframework.data.annotation.Transient + protected List statusComments = new ArrayList<>(); + + @Transient + @org.springframework.data.annotation.Transient + @JsonIgnore + private String newStatusComment; + + @Transient + @org.springframework.data.annotation.Transient + @JsonIgnore + private Boolean visibleStatusComments = false; + + @Transient + @org.springframework.data.annotation.Transient + @JsonIgnore + private Boolean visibleStatusLabel = true; + + @Override + public String getStatus() { + return status; + } + + public void setStatus(final String status) { + this.status = status; + } + + public List getStatusComments() { + return statusComments; + } + + public void setStatusComments(final List statusComments) { + this.statusComments = statusComments; + } + + public String getNewStatusComment() { + return newStatusComment; + } + + public void setNewStatusComment(final String newStatusComment) { + this.newStatusComment = newStatusComment; + } + + public Boolean getVisibleStatusComments() { + return visibleStatusComments; + } + + public void setVisibleStatusComments(final Boolean visibleStatusComments) { + this.visibleStatusComments = visibleStatusComments; + } + + public Boolean getVisibleStatusLabel() { + return visibleStatusLabel; + } + + public void setVisibleStatusLabel(final Boolean visibleStatusLabel) { + this.visibleStatusLabel = visibleStatusLabel; + + } +} diff --git a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/DBConstants.java b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/DBConstants.java index 73926da0..47a40fd9 100644 --- a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/DBConstants.java +++ b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/DBConstants.java @@ -11,12 +11,25 @@ *******************************************************************************/ package org.devgateway.toolkit.persistence.dao; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + public final class DBConstants { private DBConstants() { } + public static final class Status { + public static final String DRAFT = "DRAFT"; + public static final String SUBMITTED = "SUBMITTED"; + public static final String APPROVED = "APPROVED"; + + public static final String[] ALL = {DRAFT, SUBMITTED, APPROVED}; + public static final List ALL_LIST = Collections.unmodifiableList(Arrays.asList(ALL)); + } + public static final int MAX_DEFAULT_TEXT_LENGTH = 32000; public static final int STD_DEFAULT_TEXT_LENGTH = 255; public static final int MAX_DEFAULT_TEXT_LENGTH_ONE_LINE = 3000; diff --git a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/StatusChangedComment.java b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/StatusChangedComment.java new file mode 100644 index 00000000..f3cd6a2a --- /dev/null +++ b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/StatusChangedComment.java @@ -0,0 +1,42 @@ +package org.devgateway.toolkit.persistence.dao; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.envers.Audited; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Index; +import javax.persistence.Table; + +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +@Entity +@Audited +@Table(indexes = {@Index(columnList = "status")}) +public class StatusChangedComment extends AbstractAuditableEntity { + private String status; + + @Column(length = DBConstants.MAX_DEFAULT_TEXT_AREA) + private String comment; + + @Override + public AbstractAuditableEntity getParent() { + return null; + } + + public String getStatus() { + return status; + } + + public void setStatus(final String status) { + this.status = status; + } + + public String getComment() { + return comment; + } + + public void setComment(final String comment) { + this.comment = comment; + } +} diff --git a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/Statusable.java b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/Statusable.java new file mode 100644 index 00000000..b8cbdd2d --- /dev/null +++ b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/Statusable.java @@ -0,0 +1,15 @@ +package org.devgateway.toolkit.persistence.dao; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * @author mihai + *

+ * Assigned to objects that provide a status, in our case, objects derived from + * {@link AbstractStatusAuditableEntity} + */ +public interface Statusable { + + String getStatus(); + +} From 83f582ee33ce51deae27213f2b0d2bb2c175dc5a Mon Sep 17 00:00:00 2001 From: mpostelnicu Date: Tue, 26 May 2020 17:02:57 +0300 Subject: [PATCH 08/15] fixes #291 --- checkstyle.xml | 2 +- .../toolkit/forms/WebConstants.java | 7 + .../forms/security/SecurityConstants.java | 6 + ...ionallyRequiredTextAreaFieldComponent.java | 64 ++ .../wicket/components/util/ComponentUtil.java | 18 + .../components/util/FormSecurityUtil.java | 93 +++ .../wicket/page/EditAdminSettingsPage.html | 3 + .../wicket/page/EditAdminSettingsPage.java | 9 + .../page/EditAdminSettingsPage.properties | 1 + .../wicket/page/edit/AbstractEditPage.java | 18 +- .../edit/AbstractEditStatusEntityPage.html | 82 ++ .../edit/AbstractEditStatusEntityPage.java | 737 ++++++++++++++++++ .../AbstractEditStatusEntityPage.properties | 22 + .../edit/DefaultValidatorRoleAssignable.java | 17 + .../wicket/page/edit/EditTestFormPage.java | 2 +- .../edit/EditorValidatorRoleAssignable.java | 21 + .../dao/AbstractStatusAuditableEntity.java | 4 - .../persistence/dao/AdminSettings.java | 10 + .../toolkit/persistence/dao/TestForm.java | 2 +- .../main/resources/liquibase-changelog.xml | 24 + .../toolkit/web/util/SettingsUtils.java | 83 ++ 21 files changed, 1217 insertions(+), 8 deletions(-) create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/form/OptionallyRequiredTextAreaFieldComponent.java create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/util/FormSecurityUtil.java create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.html create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.java create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.properties create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/DefaultValidatorRoleAssignable.java create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/EditorValidatorRoleAssignable.java create mode 100644 web/src/main/java/org/devgateway/toolkit/web/util/SettingsUtils.java diff --git a/checkstyle.xml b/checkstyle.xml index 2e94cee8..b4ac18a1 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -96,7 +96,7 @@ - + diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/WebConstants.java b/forms/src/main/java/org/devgateway/toolkit/forms/WebConstants.java index 40112e28..9dee3c26 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/WebConstants.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/WebConstants.java @@ -30,7 +30,14 @@ private WebConstants() { public static final String PARAM_VIEW_MODE = "viewMode"; + public static final String DISABLE_FORM_LEAVING_JS + = "if(typeof disableFormLeavingConfirmation === 'function') disableFormLeavingConfirmation();"; + + public static final String PARAM_PRINT = "print"; + public static final String PARAM_ID = "id"; + public static final String V_POSITION = "vPosition"; + public static final String MAX_HEIGHT = "maxPosition"; public static final String PARAM_REVISION_ID = "revisionId"; public static final String PARAM_ENTITY_CLASS = "class"; diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/security/SecurityConstants.java b/forms/src/main/java/org/devgateway/toolkit/forms/security/SecurityConstants.java index 3acaedc6..220de14d 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/security/SecurityConstants.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/security/SecurityConstants.java @@ -23,5 +23,11 @@ public final class SecurityConstants { public static final class Roles { public static final String ROLE_ADMIN = "ROLE_ADMIN"; public static final String ROLE_USER = "ROLE_USER"; + public static final String ROLE_VALIDATOR = "ROLE_VALIDATOR"; + } + + public static final class Action { + public static final String EDIT = "EDIT"; + public static final String VIEW = "VIEW"; } } diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/form/OptionallyRequiredTextAreaFieldComponent.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/form/OptionallyRequiredTextAreaFieldComponent.java new file mode 100644 index 00000000..4b629141 --- /dev/null +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/form/OptionallyRequiredTextAreaFieldComponent.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2015 Development Gateway, Inc and others. + *

+ * All rights reserved. This program and the accompanying materials + * are made available under the terms of the MIT License (MIT) + * which accompanies this distribution, and is available at + * https://opensource.org/licenses/MIT + *

+ * Contributors: + * Development Gateway - initial API and implementation + */ +/** + * + */ +package org.devgateway.toolkit.forms.wicket.components.form; + +import org.apache.wicket.markup.html.form.TextArea; +import org.apache.wicket.model.IModel; +import org.apache.wicket.validation.validator.StringValidator; +import org.devgateway.toolkit.forms.WebConstants; + +/** + * @author mpostelnicu + * + * A {@link TextAreaFieldBootstrapFormComponent} that has TextArea{@link #isRequired()} exposed + * + */ +public abstract class OptionallyRequiredTextAreaFieldComponent extends TextAreaFieldBootstrapFormComponent { + private StringValidator validator = WebConstants.StringValidators.MAXIMUM_LENGTH_VALIDATOR_ONE_LINE_TEXTAREA; + + private static final long serialVersionUID = 1L; + + public OptionallyRequiredTextAreaFieldComponent(final String id, final IModel labelModel, + final IModel model) { + super(id, labelModel, model); + } + + public OptionallyRequiredTextAreaFieldComponent(final String id, final IModel labelModel) { + super(id, labelModel, null); + } + + /** + * @param id + */ + public OptionallyRequiredTextAreaFieldComponent(final String id) { + super(id); + } + + public boolean isRequired() { + return false; + } + + @Override + protected TextArea inputField(final String id, final IModel model) { + TextArea textArea = new TextArea(id, initFieldModel()) { + @Override + public boolean isRequired() { + return OptionallyRequiredTextAreaFieldComponent.this.isRequired(); + } + }; + return textArea; + } + +} diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/util/ComponentUtil.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/util/ComponentUtil.java index 4159bdfe..48640689 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/util/ComponentUtil.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/util/ComponentUtil.java @@ -28,6 +28,9 @@ import org.devgateway.toolkit.persistence.service.TextSearchableService; import java.io.Serializable; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Date; /** * @author idobre @@ -49,6 +52,21 @@ public static boolean isViewMode() { .toBoolean(false); } + /** + * Returns true if the {@link WebConstants#PARAM_PRINT} is used as a parameter + * + * @return + */ + public static boolean isPrintMode() { + return RequestCycle.get().getRequest().getRequestParameters().getParameterValue(WebConstants.PARAM_PRINT) + .toBoolean(false); + } + + public static Date getDateFromLocalDate(final LocalDate localDate) { + return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + } + + public static void enableDisableEvent(final Component c, final IEvent event) { if (event.getPayload() instanceof EditingDisabledEvent) { c.setEnabled(false); diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/util/FormSecurityUtil.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/util/FormSecurityUtil.java new file mode 100644 index 00000000..e2122a67 --- /dev/null +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/util/FormSecurityUtil.java @@ -0,0 +1,93 @@ +package org.devgateway.toolkit.forms.wicket.components.util; + +import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession; +import org.devgateway.toolkit.persistence.dao.Person; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.security.Principal; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import static org.devgateway.toolkit.forms.security.SecurityConstants.Roles.ROLE_ADMIN; + +public class FormSecurityUtil { + + protected FormSecurityUtil() { + } + + + public static boolean rolesContainsAny(final Collection roleSet, final String... roles) { + for (final String role : roles) { + if (roleSet.contains(role)) { + return true; + } + } + return false; + } + + public static boolean rolesContainsAny(final String... roles) { + return rolesContainsAny(Objects.requireNonNull(getStringRolesForCurrentPerson()), roles); + } + + public static boolean rolesContainsAll(final Collection roleSet, final String... roles) { + return roleSet.containsAll(Arrays.asList(roles)); + } + + public static boolean rolesContainsAll(final String... roles) { + return rolesContainsAll(Objects.requireNonNull(getStringRolesForCurrentPerson()), roles); + } + + /** + * returns the principal object. In our case the principal should be + * {@link Person} + * + * @return the principal or null + * @see Principal + */ + public static Person getCurrentAuthenticatedPerson() { + if (SecurityContextHolder.getContext().getAuthentication() == null) { + return null; + } + final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + return null; + } + final Object principal = authentication.getPrincipal(); + if (principal instanceof Person) { + return (Person) principal; + } + return null; + } + + public static Set getStringRolesForCurrentPerson() { + if (!AbstractAuthenticatedWebSession.get().isSignedIn()) { + return Collections.emptySet(); + } + return AbstractAuthenticatedWebSession.get().getRoles(); + } + + public static boolean hasAnyUserRoles(String... roles) { + List rList = Arrays.asList(roles); + return getStringRolesForCurrentPerson().stream().anyMatch(rList::contains); + } + + public static boolean hasUserRole(String role) { + return hasAnyUserRoles(role); + } + + public static boolean isCurrentUserAdmin() { + return hasUserRole(ROLE_ADMIN); + } + + public static boolean isCurrentRoleOnlyUser(String userRole, String validatorRole) { + if (hasAnyUserRoles(ROLE_ADMIN, validatorRole)) { + return false; + } + return hasUserRole(userRole); + } +} diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/EditAdminSettingsPage.html b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/EditAdminSettingsPage.html index 8585bf67..bd60a80b 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/EditAdminSettingsPage.html +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/EditAdminSettingsPage.html @@ -13,7 +13,10 @@

+
+ + diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/EditAdminSettingsPage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/EditAdminSettingsPage.java index 8c7988b3..e28bf7a1 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/EditAdminSettingsPage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/EditAdminSettingsPage.java @@ -5,8 +5,10 @@ import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.spring.injection.annot.SpringBean; +import org.apache.wicket.validation.validator.RangeValidator; import org.devgateway.toolkit.forms.security.SecurityConstants; import org.devgateway.toolkit.forms.wicket.components.form.CheckBoxToggleBootstrapFormComponent; +import org.devgateway.toolkit.forms.wicket.components.form.TextFieldBootstrapFormComponent; import org.devgateway.toolkit.forms.wicket.page.edit.AbstractEditPage; import org.devgateway.toolkit.persistence.dao.AdminSettings; import org.devgateway.toolkit.persistence.service.AdminSettingsService; @@ -26,6 +28,8 @@ public class EditAdminSettingsPage extends AbstractEditPage { private CheckBoxToggleBootstrapFormComponent rebootServer; + private TextFieldBootstrapFormComponent autosaveTime; + @SpringBean private AdminSettingsService adminSettingsService; @@ -52,5 +56,10 @@ protected void onInitialize() { rebootServer = new CheckBoxToggleBootstrapFormComponent("rebootServer"); editForm.add(rebootServer); + + autosaveTime = new TextFieldBootstrapFormComponent<>("autosaveTime"); + autosaveTime.integer().required(); + autosaveTime.getField().add(RangeValidator.range(1, 60)); + editForm.add(autosaveTime); } } diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/EditAdminSettingsPage.properties b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/EditAdminSettingsPage.properties index ac8cb69d..e412a67c 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/EditAdminSettingsPage.properties +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/EditAdminSettingsPage.properties @@ -1,3 +1,4 @@ page.title=Admin settings systemTitle=System Settings rebootServer.label=Enable server reboot warning +autosaveTime.label=Autosave Time (minutes) \ No newline at end of file diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditPage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditPage.java index 130c7f0f..8a321760 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditPage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditPage.java @@ -47,6 +47,7 @@ import org.devgateway.toolkit.forms.wicket.page.BasePage; import org.devgateway.toolkit.persistence.dao.GenericPersistable; import org.devgateway.toolkit.persistence.service.BaseJpaService; +import org.devgateway.toolkit.web.util.SettingsUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DataIntegrityViolationException; @@ -118,6 +119,9 @@ private T newInstance() { @SpringBean private EntityManager entityManager; + @SpringBean + protected SettingsUtils settingsUtils; + // @SpringBean(required = false) // private ReportsCacheService reportsCacheService; @@ -197,6 +201,9 @@ protected void onEvent(final AjaxRequestTarget target) { return modal; } + protected void afterSaveEntity(final T saveable) { + } + /** * Traverses all fields and refreshes the ones that are not valid, so that * we can see the errors @@ -318,6 +325,8 @@ protected void onSubmit(final AjaxRequestTarget target) { // save the object and go back to the list page T saveable = editForm.getModelObject(); + beforeSaveEntity(saveable); + // saves the entity and flushes the changes jpaService.saveAndFlush(saveable); @@ -328,6 +337,8 @@ protected void onSubmit(final AjaxRequestTarget target) { // we flush the mondrian/wicket/reports cache to ensure it gets rebuilt flushReportingCaches(); + afterSaveEntity(saveable); + // only redirect if redirect is true if (redirectToSelf) { // we need to close the blockUI if it's opened and enable all @@ -442,7 +453,7 @@ protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) { * * @return */ - public SaveEditPageButton getSaveEditPageButton() { + protected SaveEditPageButton getSaveEditPageButton() { return new SaveEditPageButton("save", new StringResourceModel("saveButton", this, null)); } @@ -536,5 +547,10 @@ protected void onInitialize() { if (model != null) { editForm.setCompoundPropertyModel(model); } + + afterLoad(model); + } + + protected void afterLoad(final IModel model) { } } diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.html b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.html new file mode 100644 index 00000000..bdb88dcd --- /dev/null +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.html @@ -0,0 +1,82 @@ + + + + + + + +
+
+
+ +
+
+
+ +
+ + Status Label +
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + +
#Status changeCommentUserDate & Time
+ + + + + + + + + +
+
+
+
+ + + + +
+
+ AutoSaveLabel +
+
+ + + + + + + + + + + + + + diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.java new file mode 100644 index 00000000..aac3204c --- /dev/null +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.java @@ -0,0 +1,737 @@ +/******************************************************************************* + * Copyright (c) 2015 Development Gateway, Inc and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the MIT License (MIT) + * which accompanies this distribution, and is available at + * https://opensource.org/licenses/MIT + * + * Contributors: + * Development Gateway - initial API and implementation + *******************************************************************************/ +package org.devgateway.toolkit.forms.wicket.page.edit; + +import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons; +import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.TextContentModal; +import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesomeIconType; +import de.agilecoders.wicket.extensions.markup.html.bootstrap.ladda.LaddaAjaxButton; +import org.apache.wicket.AttributeModifier; +import org.apache.wicket.Component; +import org.apache.wicket.ajax.AbstractAjaxTimerBehavior; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy; +import org.apache.wicket.event.Broadcast; +import org.apache.wicket.event.IEvent; +import org.apache.wicket.markup.head.IHeaderResponse; +import org.apache.wicket.markup.head.OnDomReadyHeaderItem; +import org.apache.wicket.markup.html.TransparentWebMarkupContainer; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.form.HiddenField; +import org.apache.wicket.markup.html.form.TextArea; +import org.apache.wicket.markup.html.form.TextField; +import org.apache.wicket.markup.html.list.ListItem; +import org.apache.wicket.markup.html.list.ListView; +import org.apache.wicket.markup.html.panel.Fragment; +import org.apache.wicket.model.CompoundPropertyModel; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.model.StringResourceModel; +import org.apache.wicket.request.mapper.parameter.PageParameters; +import org.apache.wicket.util.string.Strings; +import org.apache.wicket.util.time.Duration; +import org.apache.wicket.util.visit.IVisit; +import org.apache.wicket.util.visit.IVisitor; +import org.devgateway.toolkit.forms.WebConstants; +import org.devgateway.toolkit.forms.security.SecurityConstants; +import org.devgateway.toolkit.forms.wicket.components.form.BootstrapSubmitButton; +import org.devgateway.toolkit.forms.wicket.components.form.CheckBoxYesNoToggleBootstrapFormComponent; +import org.devgateway.toolkit.forms.wicket.components.form.GenericBootstrapFormComponent; +import org.devgateway.toolkit.forms.wicket.components.form.OptionallyRequiredTextAreaFieldComponent; +import org.devgateway.toolkit.forms.wicket.components.form.TextAreaFieldBootstrapFormComponent; +import org.devgateway.toolkit.forms.wicket.components.util.ComponentUtil; +import org.devgateway.toolkit.forms.wicket.components.util.FormSecurityUtil; +import org.devgateway.toolkit.forms.wicket.events.EditingDisabledEvent; +import org.devgateway.toolkit.forms.wicket.page.BasePage; +import org.devgateway.toolkit.persistence.dao.AbstractStatusAuditableEntity; +import org.devgateway.toolkit.persistence.dao.DBConstants; +import org.devgateway.toolkit.persistence.dao.StatusChangedComment; +import org.springframework.util.ObjectUtils; +import org.wicketstuff.datetime.markup.html.basic.DateLabel; +import org.wicketstuff.select2.Select2Choice; + +/** + * @author mpostelnicu + * Page used to make editing easy, extend to get easy access to one entity for editing + */ +public abstract class AbstractEditStatusEntityPage + extends AbstractEditPage implements DefaultValidatorRoleAssignable { + + protected Fragment entityButtonsFragment; + + private SaveEditPageButton saveSubmitButton; + + protected SaveEditPageButton submitAndNext; + + private SaveEditPageButton saveApproveButton; + + private SaveEditPageButton saveDraftContinueButton; + + protected SaveEditPageButton revertToDraftPageButton; + + private CheckBoxYesNoToggleBootstrapFormComponent visibleStatusComments; + + private TransparentWebMarkupContainer statusCommentsWrapper; + + private ListView statusComments; + + protected TextAreaFieldBootstrapFormComponent newStatusComment; + + private Label statusLabel; + + private String previousStatus; + + private Label autoSaveLabel; + + private HiddenField verticalPosition; + + private HiddenField maxHeight; + + protected PageParameters afterSubmitNextParameters; + + protected Fragment extraStatusEntityButtonsFragment; + + public AbstractEditStatusEntityPage(final PageParameters parameters) { + super(parameters); + + } + + + public class ButtonContentModal extends TextContentModal { + private final Buttons.Type buttonType; + private LaddaAjaxButton button; + private IModel buttonModel; + private ModalSaveEditPageButton modalSavePageButton; + + public ButtonContentModal(String markupId, IModel model, IModel buttonModel, + Buttons.Type buttonType) { + super(markupId, model); + addCloseButton(); + this.buttonModel = buttonModel; + this.buttonType = buttonType; + } + + public ButtonContentModal modalSavePageButton(ModalSaveEditPageButton modalSavePageButton) { + this.modalSavePageButton = modalSavePageButton; + return this; + } + + @Override + protected void onInitialize() { + super.onInitialize(); + button = new LaddaAjaxButton("button", buttonType) { + @Override + protected void onSubmit(AjaxRequestTarget target) { + modalSavePageButton.continueSubmit(target); + } + }; + addButton(button); + button.setDefaultFormProcessing(false); + button.setLabel(buttonModel); + } + } + + protected ButtonContentModal createTerminateModal() { + ButtonContentModal buttonContentModal = new ButtonContentModal( + "terminateModal", + Model.of("Are you sure you want to TERMINATE the contracting process?"), + Model.of("TERMINATE"), Buttons.Type.Danger); + return buttonContentModal; + } + + public class ModalSaveEditPageButton extends SaveEditPageButton { + private TextContentModal modal; + + public ModalSaveEditPageButton(String id, IModel model, TextContentModal modal) { + super(id, model); + this.modal = modal; + } + + + @Override + protected String getOnClickScript() { + return WebConstants.DISABLE_FORM_LEAVING_JS; + } + + @Override + protected void onSubmit(AjaxRequestTarget target) { + modal.show(true); + target.add(modal); + } + + public void continueSubmit(AjaxRequestTarget target) { + super.onSubmit(target); + } + } + + @Override + protected void beforeSaveEntity(T saveable) { + super.beforeSaveEntity(saveable); + + afterSubmitNextParameters = parametersAfterSubmitAndNext(); + } + + @Override + protected void onInitialize() { + super.onInitialize(); + + addAutosaveLabel(); + addVerticalMaxPositionFields(); + + statusLabel = addStatusLabel(); + editForm.add(statusLabel); + + visibleStatusComments = getVisibleStatusComments(); + editForm.add(visibleStatusComments); + + statusCommentsWrapper = new TransparentWebMarkupContainer("statusCommentsWrapper"); + statusCommentsWrapper.setOutputMarkupId(true); + statusCommentsWrapper.setOutputMarkupPlaceholderTag(true); + statusCommentsWrapper.setVisibilityAllowed(false); + editForm.add(statusCommentsWrapper); + + statusComments = getStatusCommentsListView(); + newStatusComment = getNewStatusCommentField(); + statusCommentsWrapper.add(statusComments); + editForm.add(newStatusComment); + + entityButtonsFragment = new Fragment("extraButtons", "entityButtons", this); + editForm.replace(entityButtonsFragment); + + Fragment fragment = new Fragment("extraStatusEntityButtons", "noButtons", this); + entityButtonsFragment.add(fragment); + + saveSubmitButton = getSaveSubmitPageButton(); + entityButtonsFragment.add(saveSubmitButton); + + submitAndNext = getSubmitAndNextPageButton(); + submitAndNext.setVisibilityAllowed(false); + entityButtonsFragment.add(submitAndNext); + + saveApproveButton = getSaveApprovePageButton(); + entityButtonsFragment.add(saveApproveButton); + + saveDraftContinueButton = getSaveDraftAndContinueButton(); + entityButtonsFragment.add(saveDraftContinueButton); + + revertToDraftPageButton = getRevertToDraftPageButton(); + entityButtonsFragment.add(revertToDraftPageButton); + + applyDraftSaveBehavior(saveButton); + applyDraftSaveBehavior(saveDraftContinueButton); + applyDraftSaveBehavior(revertToDraftPageButton); + + setButtonsPermissions(); + + enableDisableAutosaveFields(null); + } + + @Override + protected void afterSaveEntity(final T saveable) { + super.afterSaveEntity(saveable); + + getPageParameters().set(WebConstants.V_POSITION, verticalPosition.getValue()) + .set(WebConstants.MAX_HEIGHT, maxHeight.getValue()); + } + + @Override + protected void afterLoad(final IModel entityModel) { + super.afterLoad(entityModel); + previousStatus = entityModel.getObject().getStatus(); + } + + @Override + protected void onBeforeRender() { + super.onBeforeRender(); + + checkAndSendEventForDisableEditing(); + + this.statusLabel.setVisibilityAllowed(editForm.getModelObject().getVisibleStatusLabel()); + } + + protected void checkAndSendEventForDisableEditing() { + if (isDisableEditingEvent()) { + send(getPage(), Broadcast.BREADTH, new EditingDisabledEvent()); + } + } + + public boolean isDisableEditingEvent() { + return !Strings.isEqual(editForm.getModelObject().getStatus(), DBConstants.Status.DRAFT) || isViewMode(); + } + + + protected boolean isViewMode() { + return false; +// return SecurityConstants.Action.VIEW +// .equals(permissionEntityRenderableService.getAllowedAccess(this, editForm.getModelObject())); + } + + private void addAutosaveLabel() { + autoSaveLabel = new Label("autoSaveLabel", + new StringResourceModel("autoSaveLabelMessage", this).setParameters(settingsUtils.getAutosaveTime())); + autoSaveLabel.setVisibilityAllowed(false); + autoSaveLabel.setOutputMarkupPlaceholderTag(true); + autoSaveLabel.setOutputMarkupId(true); + editForm.add(autoSaveLabel); + } + + private void addVerticalMaxPositionFields() { + verticalPosition = new HiddenField<>("verticalPosition", new Model<>(), Double.class); + verticalPosition.setOutputMarkupId(true); + editForm.add(verticalPosition); + + maxHeight = new HiddenField<>("maxHeight", new Model<>(), Double.class); + maxHeight.setOutputMarkupId(true); + editForm.add(maxHeight); + } + + protected void enableDisableAutosaveFields(final AjaxRequestTarget target) { + addAutosaveBehavior(target); + + saveButton.setEnabled(true); + saveDraftContinueButton.setEnabled(true); + submitAndNext.setEnabled(true); + saveSubmitButton.setEnabled(true); + + if (target != null) { + target.add(saveButton, saveSubmitButton, saveDraftContinueButton, submitAndNext); + } + } + + private void addAutosaveBehavior(final AjaxRequestTarget target) { + // enable autosave + if (!ComponentUtil.isPrintMode() + && Strings.isEqual(editForm.getModelObject().getStatus(), DBConstants.Status.DRAFT)) { + saveDraftContinueButton.add(getAutosaveBehavior()); + autoSaveLabel.setVisibilityAllowed(true); + if (target != null) { + target.add(autoSaveLabel); + } + } + } + + private AbstractAjaxTimerBehavior getAutosaveBehavior() { + final AbstractAjaxTimerBehavior ajaxTimerBehavior = new AbstractAjaxTimerBehavior( + Duration.minutes(settingsUtils.getAutosaveTime())) { + @Override + protected void onTimer(final AjaxRequestTarget target) { + // display block UI message until the page is reloaded + target.prependJavaScript(getShowBlockUICode()); + + // disable all fields from js and lose focus (execute this javascript code before components processed) + target.prependJavaScript("$(document.activeElement).blur();"); + + // invoke autosave from js (execute this javascript code before components processed) + target.prependJavaScript("$('#" + maxHeight.getMarkupId() + "').val($(document).height()); " + + "$('#" + verticalPosition.getMarkupId() + "').val($(window).scrollTop()); " + + "$('#" + saveDraftContinueButton.getMarkupId() + "').click();"); + + // disable all buttons from js + target.prependJavaScript("$('#" + editForm.getMarkupId() + " button').prop('disabled', true);"); + } + }; + + return ajaxTimerBehavior; + } + + private Label addStatusLabel() { + statusLabel = new Label("statusLabel", editForm.getModelObject().getStatus()); + statusLabel.add(new AttributeModifier("class", new Model<>("label " + getStatusLabelClass()))); + statusLabel.setVisibilityAllowed(editForm.getModelObject().getVisibleStatusLabel()); + return statusLabel; + } + + private String getStatusLabelClass() { + if (editForm.getModelObject().getStatus() == null) { + return ""; + } + + switch (editForm.getModelObject().getStatus()) { + case DBConstants.Status.APPROVED: + return "label-success"; + case DBConstants.Status.DRAFT: + return "label-danger"; + case DBConstants.Status.SUBMITTED: + return "label-warning"; + default: + return ""; + } + } + + private CheckBoxYesNoToggleBootstrapFormComponent getVisibleStatusComments() { + final CheckBoxYesNoToggleBootstrapFormComponent checkBoxBootstrapFormComponent = + new CheckBoxYesNoToggleBootstrapFormComponent("visibleStatusComments") { + @Override + protected void onUpdate(final AjaxRequestTarget target) { + statusCommentsWrapper.setVisibilityAllowed(editForm.getModelObject() + .getVisibleStatusComments()); + target.add(statusCommentsWrapper); + } + + @Override + public void onEvent(final IEvent event) { + // do nothing - keep this field enabled + } + }; + checkBoxBootstrapFormComponent.setVisibilityAllowed(!isViewMode()); + return checkBoxBootstrapFormComponent; + } + + private TextAreaFieldBootstrapFormComponent getNewStatusCommentField() { + final TextAreaFieldBootstrapFormComponent comment = + new OptionallyRequiredTextAreaFieldComponent("newStatusComment") { + + @Override + public void onEvent(final IEvent event) { + // do nothing - keep this field enabled + } + }; + comment.setShowTooltip(true); + return comment; + } + + private ListView getStatusCommentsListView() { + final ListView statusComments = new ListView("statusComments") { + @Override + protected void populateItem(final ListItem item) { + item.setModel(new CompoundPropertyModel<>(item.getModel())); + item.add(new Label("commentIdx", item.getIndex())); + item.add(new Label("status")); + item.add(new Label("comment")); + item.add(new Label("createdBy", item.getModelObject().getCreatedBy().get())); + item.add(DateLabel.forDateStyle("created", + Model.of(ComponentUtil + .getDateFromLocalDate(item.getModelObject().getCreatedDate().get().toLocalDate())), + "SS")); + } + }; + statusComments.setReuseItems(true); + statusComments.setOutputMarkupId(true); + + return statusComments; + } + + /** + * Use this function to get the block UI message while the form is saved. + */ + private String getShowBlockUICode() { + return "blockUI('" + + new StringResourceModel("autosave_message", AbstractEditStatusEntityPage.this, null).getString() + + "')"; + } + + /******************************************************************************* + * Buttons Behavior + *******************************************************************************/ + private void applyDraftSaveBehavior(final BootstrapSubmitButton button) { + // disable form validation + button.setDefaultFormProcessing(false); + } + + @Override + protected SaveEditPageButton getSaveEditPageButton() { + final SaveEditPageButton button = new SaveEditPageButton("save", + new StringResourceModel("saveButton", this, null)) { + + @Override + protected String getOnClickScript() { + return WebConstants.DISABLE_FORM_LEAVING_JS; + } + + @Override + protected void onSubmit(final AjaxRequestTarget target) { + editForm.visitChildren(GenericBootstrapFormComponent.class, + new AllowNullForCertainInvalidFieldsVisitor()); + setStatusAppendComment(DBConstants.Status.DRAFT); + super.onSubmit(target); + } + }; + + return button; + } + + private SaveEditPageButton getSaveSubmitPageButton() { + final SaveEditPageButton button = new SaveEditPageButton("saveSubmit", + new StringResourceModel("saveSubmit", this, null)) { + @Override + protected String getOnClickScript() { + return WebConstants.DISABLE_FORM_LEAVING_JS; + } + + @Override + protected void onSubmit(final AjaxRequestTarget target) { + setStatusAppendComment(DBConstants.Status.SUBMITTED); + super.onSubmit(target); + } + }; + + button.setIconType(FontAwesomeIconType.send); + return button; + } + + private SaveEditPageButton getSubmitAndNextPageButton() { + final SaveEditPageButton button = new SaveEditPageButton("submitAndNext", + new StringResourceModel("submitAndNext", this, null)) { + @Override + protected String getOnClickScript() { + return WebConstants.DISABLE_FORM_LEAVING_JS; + } + + @Override + protected void onSubmit(final AjaxRequestTarget target) { + setStatusAppendComment(DBConstants.Status.SUBMITTED); + super.onSubmit(target); + } + + @Override + protected Class getResponsePage() { + return pageAfterSubmitAndNext(); + } + + @Override + protected PageParameters getParameterPage() { + return afterSubmitNextParameters; + } + }; + + button.setIconType(FontAwesomeIconType.tasks); + return button; + } + + /** + * Override this function in order to redirect the user to the next page after clicking on submitAndNext button. + */ + protected Class pageAfterSubmitAndNext() { + return (Class) getPage().getClass(); + } + + /** + * Override this function in order to redirect the user to the next page with parameters + * after clicking on submitAndNext button. + */ + protected PageParameters parametersAfterSubmitAndNext() { + return getPageParameters(); + } + + private SaveEditPageButton getSaveDraftAndContinueButton() { + final SaveEditPageButton button = new SaveEditPageButton("saveContinue", + new StringResourceModel("saveContinue", this, null)) { + + @Override + protected String getOnClickScript() { + return WebConstants.DISABLE_FORM_LEAVING_JS; + } + + @Override + protected void onSubmit(final AjaxRequestTarget target) { + editForm.visitChildren(GenericBootstrapFormComponent.class, + new AllowNullForCertainInvalidFieldsVisitor()); + setStatusAppendComment(DBConstants.Status.DRAFT); + super.onSubmit(target); + } + + @Override + protected Class getResponsePage() { + return (Class) getPage().getClass(); + } + + @Override + protected PageParameters getParameterPage() { + return getPageParameters(); + } + }; + + button.setIconType(FontAwesomeIconType.tasks); + return button; + } + + private SaveEditPageButton getSaveApprovePageButton() { + final SaveEditPageButton saveEditPageButton = new SaveEditPageButton("approve", + new StringResourceModel("approve", this, null)) { + + @Override + protected String getOnClickScript() { + return WebConstants.DISABLE_FORM_LEAVING_JS; + } + + @Override + protected void onSubmit(final AjaxRequestTarget target) { + setStatusAppendComment(DBConstants.Status.APPROVED); + super.onSubmit(target); + } + }; + saveEditPageButton.setIconType(FontAwesomeIconType.thumbs_up); + return saveEditPageButton; + } + + protected SaveEditPageButton getRevertToDraftPageButton() { + final SaveEditPageButton saveEditPageButton = new SaveEditPageButton("revertToDraft", + new StringResourceModel("revertToDraft", this, null)) { + @Override + protected String getOnClickScript() { + return WebConstants.DISABLE_FORM_LEAVING_JS; + } + + @Override + protected void onSubmit(final AjaxRequestTarget target) { + setStatusAppendComment(DBConstants.Status.DRAFT); + super.onSubmit(target); + target.add(editForm); + setButtonsPermissions(); + onAfterRevertToDraft(target); + } + }; + saveEditPageButton.setIconType(FontAwesomeIconType.thumbs_down); + return saveEditPageButton; + } + + protected void onAfterRevertToDraft(AjaxRequestTarget target) { + + } + + protected void setStatusAppendComment(final String status) { + final T saveable = editForm.getModelObject(); + + // do not save an empty comment if previous status is same as current status and comment box is empty + if (status.equals(saveable.getStatus()) && ObjectUtils.isEmpty(saveable.getNewStatusComment())) { + saveable.setStatus(status); + return; + } + saveable.setStatus(status); + + final StatusChangedComment comment = new StatusChangedComment(); + comment.setStatus(status); + comment.setComment(editForm.getModelObject().getNewStatusComment()); + saveable.getStatusComments().add(comment); + } + + protected void setButtonsPermissions() { + addSaveButtonsPermissions(saveButton); + addSaveButtonsPermissions(saveDraftContinueButton); + addSaveButtonsPermissions(submitAndNext); + addSaveButtonsPermissions(saveSubmitButton); + addApproveButtonPermissions(saveApproveButton); + addSaveRevertButtonPermissions(revertToDraftPageButton); + addDeleteButtonPermissions(deleteButton); + + // no need to display the buttons on print view so we overwrite the above permissions + if (ComponentUtil.isPrintMode()) { + saveDraftContinueButton.setVisibilityAllowed(false); + submitAndNext.setVisibilityAllowed(false); + saveSubmitButton.setVisibilityAllowed(false); + saveApproveButton.setVisibilityAllowed(false); + revertToDraftPageButton.setVisibilityAllowed(false); + } + } + + protected void addDeleteButtonPermissions(final Component button) { + MetaDataRoleAuthorizationStrategy.authorize(button, Component.RENDER, SecurityConstants.Roles.ROLE_ADMIN); + button.setVisibilityAllowed(entityId != null && !isViewMode()); + MetaDataRoleAuthorizationStrategy.authorize( + button, Component.RENDER, getCommaCombinedRoles()); + } + + protected void addSaveRevertButtonPermissions(final Component button) { + addDefaultAllButtonsPermissions(button); + MetaDataRoleAuthorizationStrategy.authorize(button, Component.RENDER, getValidatorRole()); + MetaDataRoleAuthorizationStrategy.authorize(button, Component.RENDER, getCommaCombinedRoles()); + button.setVisibilityAllowed(button.isVisibilityAllowed() + && !DBConstants.Status.DRAFT.equals(editForm.getModelObject().getStatus())); + + // additionally normal users should not revert anything that was already validated + if (FormSecurityUtil.isCurrentRoleOnlyUser(getUserRole(), getValidatorRole()) + && DBConstants.Status.APPROVED.equals(editForm.getModelObject().getStatus())) { + button.setVisibilityAllowed(false); + } else + + //admins can revert anything + if (FormSecurityUtil.isCurrentUserAdmin() + && DBConstants.Status.APPROVED.equals(editForm.getModelObject().getStatus())) { + button.setVisibilityAllowed(true); + } + } + + protected void addApproveButtonPermissions(final Component button) { + addDefaultAllButtonsPermissions(button); + MetaDataRoleAuthorizationStrategy.authorize( + button, Component.RENDER, getValidatorRole()); + button.setVisibilityAllowed(button.isVisibilityAllowed() + && DBConstants.Status.SUBMITTED.equals(editForm.getModelObject().getStatus())); + } + + protected void addSaveButtonsPermissions(final Component button) { + addDefaultAllButtonsPermissions(button); + MetaDataRoleAuthorizationStrategy.authorize(button, Component.RENDER, getCommaCombinedRoles()); + button.setVisibilityAllowed(button.isVisibilityAllowed() + && DBConstants.Status.DRAFT.equals(editForm.getModelObject().getStatus())); + } + + + protected void addDefaultAllButtonsPermissions(final Component button) { + MetaDataRoleAuthorizationStrategy.authorize(button, Component.RENDER, SecurityConstants.Roles.ROLE_ADMIN); + } + + + private void scrollToPreviousPosition(final IHeaderResponse response) { + response.render(OnDomReadyHeaderItem.forScript(String.format( + "var vPosition= +%s, mHeight = +%s, cmHeight=$(document).height();" + + "if(mHeight!=0) $(window).scrollTop(vPosition*cmHeight/mHeight)", + getPageParameters().get(WebConstants.V_POSITION).toDouble(0), + getPageParameters().get(WebConstants.MAX_HEIGHT).toDouble(0) + ))); + } + + @Override + public void renderHead(final IHeaderResponse response) { + super.renderHead(response); + + scrollToPreviousPosition(response); + } + + /** + * Allow null saving for draft entities even if the field is required. + * Bypass validation for this purpose. + * + * @author mpostelnicu + */ + public class AllowNullForCertainInvalidFieldsVisitor + implements IVisitor, Void> { + @Override + public void component(final GenericBootstrapFormComponent object, final IVisit visit) { + // we found the GenericBootstrapFormComponent, stop doing useless + // things like traversing inside the GenericBootstrapFormComponent itself + visit.dontGoDeeper(); + + // do not process disabled fields + if (!object.isEnabledInHierarchy()) { + return; + } + object.getField().processInput(); + + // we try validate the field + object.getField().validate(); + + // still, if the field is invalid, its input is null, and field is + // of a certain type, we turn + // the input into a null. This helps us to save empty REQUIRED + // fields when saving as draft + if (!object.getField().isValid() && Strings.isEmpty(object.getField().getInput())) { + // for text/select fields we just make the object model null + if (object.getField() instanceof TextField || object.getField() instanceof TextArea + || object.getField() instanceof Select2Choice) { + object.getField().getModel().setObject(null); + } + + } + } + } +} diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.properties b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.properties new file mode 100644 index 00000000..a4ea0877 --- /dev/null +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.properties @@ -0,0 +1,22 @@ +############################################################################### +# Copyright (c) 2015 Development Gateway, Inc and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the MIT License (MIT) +# which accompanies this distribution, and is available at +# https://opensource.org/licenses/MIT +# +# Contributors: +# Development Gateway - initial API and implementation +############################################################################### +approve=Approve +revertToDraft=Reject to draft +saveButton=Save Draft +saveSubmit=Save & Submit +submitAndNext=Save and Next +saveContinue=Save Draft & Continue +newStatusComment.label=Update Comment +newStatusComment.help=Add a comment to describe this update during validation or data entry. This comment can be useful for other people editing or validating this form. \ + PLEASE NOTE, text entered here will be viewable by anyone with access to this page. +visibleStatusComments.label=View comments and form history +autoSaveLabelMessage=Form auto-saved less than {0,number} minutes ago \ No newline at end of file diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/DefaultValidatorRoleAssignable.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/DefaultValidatorRoleAssignable.java new file mode 100644 index 00000000..cd0d4474 --- /dev/null +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/DefaultValidatorRoleAssignable.java @@ -0,0 +1,17 @@ +package org.devgateway.toolkit.forms.wicket.page.edit; + + +import org.devgateway.toolkit.forms.security.SecurityConstants; + +public interface DefaultValidatorRoleAssignable extends EditorValidatorRoleAssignable { + + @Override + default String getUserRole() { + return SecurityConstants.Roles.ROLE_USER; + } + + @Override + default String getValidatorRole() { + return SecurityConstants.Roles.ROLE_VALIDATOR; + } +} diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/EditTestFormPage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/EditTestFormPage.java index 5d4435a3..8e92a3c3 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/EditTestFormPage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/EditTestFormPage.java @@ -47,7 +47,7 @@ @AuthorizeInstantiation(SecurityConstants.Roles.ROLE_USER) @MountPath("/editTestForm") -public class EditTestFormPage extends AbstractEditPage { +public class EditTestFormPage extends AbstractEditStatusEntityPage { private static final long serialVersionUID = 1L; diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/EditorValidatorRoleAssignable.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/EditorValidatorRoleAssignable.java new file mode 100644 index 00000000..966d9b7e --- /dev/null +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/EditorValidatorRoleAssignable.java @@ -0,0 +1,21 @@ +package org.devgateway.toolkit.forms.wicket.page.edit; + +import com.google.common.collect.Sets; +import org.apache.commons.lang3.StringUtils; + +import java.util.Set; + +public interface EditorValidatorRoleAssignable { + + String getUserRole(); + + String getValidatorRole(); + + default Set getCombinedRoles() { + return Sets.newHashSet(getUserRole(), getValidatorRole()); + } + + default String getCommaCombinedRoles() { + return StringUtils.join(getCombinedRoles(), ","); + } +} diff --git a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AbstractStatusAuditableEntity.java b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AbstractStatusAuditableEntity.java index 01cd0236..9857fd02 100644 --- a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AbstractStatusAuditableEntity.java +++ b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AbstractStatusAuditableEntity.java @@ -25,21 +25,17 @@ public abstract class AbstractStatusAuditableEntity extends AbstractAuditableEnt @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) @OrderColumn(name = "index") @JsonIgnore - @org.springframework.data.annotation.Transient protected List statusComments = new ArrayList<>(); @Transient - @org.springframework.data.annotation.Transient @JsonIgnore private String newStatusComment; @Transient - @org.springframework.data.annotation.Transient @JsonIgnore private Boolean visibleStatusComments = false; @Transient - @org.springframework.data.annotation.Transient @JsonIgnore private Boolean visibleStatusLabel = true; diff --git a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AdminSettings.java b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AdminSettings.java index 919bd13b..b43751b8 100644 --- a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AdminSettings.java +++ b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AdminSettings.java @@ -19,6 +19,8 @@ public class AdminSettings extends AbstractAuditableEntity { private static final long serialVersionUID = -1051140524022133178L; private Boolean rebootServer = false; + private Integer autosaveTime; + @Override public AbstractAuditableEntity getParent() { return null; @@ -31,4 +33,12 @@ public Boolean getRebootServer() { public void setRebootServer(final Boolean rebootServer) { this.rebootServer = rebootServer; } + + public Integer getAutosaveTime() { + return autosaveTime; + } + + public void setAutosaveTime(Integer autosaveTime) { + this.autosaveTime = autosaveTime; + } } diff --git a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/TestForm.java b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/TestForm.java index cc2441d5..c37960c4 100644 --- a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/TestForm.java +++ b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/TestForm.java @@ -41,7 +41,7 @@ @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) @Entity @Audited -public class TestForm extends AbstractAuditableEntity { +public class TestForm extends AbstractStatusAuditableEntity { private static final long serialVersionUID = 1L; diff --git a/persistence/src/main/resources/liquibase-changelog.xml b/persistence/src/main/resources/liquibase-changelog.xml index 7ebe70e1..3250e485 100644 --- a/persistence/src/main/resources/liquibase-changelog.xml +++ b/persistence/src/main/resources/liquibase-changelog.xml @@ -60,4 +60,28 @@ + + + + select count(*) > 0 from test_form + + + + + status is null + + + + + + + select autosave_time is null from admin_settings + + + + + autosave_time is null + + + \ No newline at end of file diff --git a/web/src/main/java/org/devgateway/toolkit/web/util/SettingsUtils.java b/web/src/main/java/org/devgateway/toolkit/web/util/SettingsUtils.java new file mode 100644 index 00000000..a2ee7cfe --- /dev/null +++ b/web/src/main/java/org/devgateway/toolkit/web/util/SettingsUtils.java @@ -0,0 +1,83 @@ +package org.devgateway.toolkit.web.util; + +import org.devgateway.toolkit.persistence.dao.AdminSettings; +import org.devgateway.toolkit.persistence.repository.AdminSettingsRepository; +import org.devgateway.toolkit.persistence.service.AdminSettingsService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import java.util.List; + +/** + * @author idobre + * @since 6/22/16 + */ +@Service +public class SettingsUtils { + protected static Logger logger = LoggerFactory.getLogger(SettingsUtils.class); + + public static final int AUTOSAVE_TIME_DEFAULT = 10; + + @Autowired + private AdminSettingsService adminSettingsService; + + private AdminSettings setting; + + @Value("${googleAnalyticsTrackingId:#{null}}") + private String googleAnalyticsTrackingId; + + public String getGoogleAnalyticsTrackingId() { + return googleAnalyticsTrackingId; + } + + public int getAutosaveTime() { + init(); + if (ObjectUtils.isEmpty(setting.getAutosaveTime())) { + return AUTOSAVE_TIME_DEFAULT; + } + return setting.getAutosaveTime(); + } + + public boolean getRebootServer() { + init(); + if (setting.getRebootServer() == null) { + return false; + } + return setting.getRebootServer(); + } + + public AdminSettings getSetting() { + init(); + return setting; + } + + private void init() { + final List list = adminSettingsService.findAll(); + if (list.size() == 0) { + setting = new AdminSettings(); + } else { + setting = list.get(0); + } + } + + + public static final String DEFAULT_LANGUAGE = "en_US"; + + @Autowired + private AdminSettingsRepository adminSettingsRepository; + + public AdminSettings getSettings() { + List list = adminSettingsRepository.findAll(); + if (list.size() == 0) { + return new AdminSettings(); + } else { + return list.get(0); + } + } + + +} From 4f4578da6d8834c700bd32f129602560f87f07fd Mon Sep 17 00:00:00 2001 From: Octavian Ciubotaru Date: Fri, 26 Jun 2020 17:39:02 +0300 Subject: [PATCH 09/15] #297 Do not trigger DozerModel proxy during serialization of GenericPersistable --- .../devgateway/toolkit/persistence/dao/GenericPersistable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/GenericPersistable.java b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/GenericPersistable.java index 03044ffc..e70bcee3 100644 --- a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/GenericPersistable.java +++ b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/GenericPersistable.java @@ -33,7 +33,7 @@ public class GenericPersistable extends AbstractPersistable implements Ser * Serializable. */ private void writeObject(final ObjectOutputStream out) throws IOException { - out.writeObject(getId()); + out.writeObject((this instanceof Proxied) ? null : getId()); out.defaultWriteObject(); } From 0fbec2f64fce4d22a2f2a265594fea326b5b36a3 Mon Sep 17 00:00:00 2001 From: mpostelnicu Date: Wed, 29 Jul 2020 16:13:21 +0300 Subject: [PATCH 10/15] fixes #300 --- ...SelectFilteredBootstrapPropertyColumn.java | 85 ++++++++++++-- ...tMultiFilteredBootstrapPropertyColumn.java | 104 ++++++++++++++++++ .../toolkit/forms/wicket/page/BasePage.java | 2 +- .../edit/AbstractEditStatusEntityPage.html | 10 +- .../edit/AbstractEditStatusEntityPage.java | 44 +++++++- .../AbstractEditStatusEntityPage.properties | 6 +- .../wicket/page/edit/ResourceLockable.java | 68 ++++++++++++ .../wicket/page/lists/AbstractListPage.java | 15 ++- .../lists/AbstractListStatusEntityPage.java | 82 ++++++++++++++ .../wicket/page/lists/ListTestFormPage.java | 4 +- .../forms/wicket/styles/BaseStyles.css | 1 + .../dao/AbstractStatusAuditableEntity.java | 26 +++++ 12 files changed, 424 insertions(+), 23 deletions(-) create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/table/SelectMultiFilteredBootstrapPropertyColumn.java create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/ResourceLockable.java create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/lists/AbstractListStatusEntityPage.java diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/table/SelectFilteredBootstrapPropertyColumn.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/table/SelectFilteredBootstrapPropertyColumn.java index 6b2b957b..c34709b5 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/table/SelectFilteredBootstrapPropertyColumn.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/table/SelectFilteredBootstrapPropertyColumn.java @@ -1,12 +1,15 @@ package org.devgateway.toolkit.forms.wicket.components.table; -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.extensions.markup.html.repeater.data.table.DataTable; import org.apache.wicket.extensions.markup.html.repeater.data.table.filter.ChoiceFilteredPropertyColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.filter.FilterForm; import org.apache.wicket.model.IModel; import org.devgateway.toolkit.forms.wicket.components.form.Select2ChoiceBootstrapFormComponent; import org.devgateway.toolkit.forms.wicket.providers.GenericChoiceProvider; +import org.wicketstuff.select2.ChoiceProvider; import java.util.List; @@ -17,28 +20,92 @@ * @since 12/20/16 */ public class SelectFilteredBootstrapPropertyColumn extends ChoiceFilteredPropertyColumn { + private DataTable dataTable; + private boolean disableFilter = false; + private ChoiceProvider choiceProvider; public SelectFilteredBootstrapPropertyColumn(final IModel displayModel, final S sortProperty, final String propertyExpression, - final IModel> filterChoices) { - super(displayModel, sortProperty, propertyExpression, filterChoices); + final ChoiceProvider choiceProvider, + final DataTable dataTable) { + this(displayModel, sortProperty, propertyExpression, choiceProvider, dataTable, false); + } + + public SelectFilteredBootstrapPropertyColumn(final IModel displayModel, + final S sortProperty, + final String propertyExpression, + final IModel> filterChoices, + final DataTable dataTable) { + this(displayModel, sortProperty, propertyExpression, filterChoices, dataTable, false); } public SelectFilteredBootstrapPropertyColumn(final IModel displayModel, + final S sortProperty, + final String propertyExpression, + final ChoiceProvider choiceProvider, + final DataTable dataTable, + final boolean disableFilter) { + super(displayModel, sortProperty, propertyExpression, null); + this.disableFilter = disableFilter; + this.dataTable = dataTable; + this.choiceProvider = choiceProvider; + } + + + public SelectFilteredBootstrapPropertyColumn(final IModel displayModel, + final S sortProperty, final String propertyExpression, - final IModel> filterChoices) { - super(displayModel, propertyExpression, filterChoices); + final IModel> filterChoices, + final DataTable dataTable, + final boolean disableFilter) { + super(displayModel, sortProperty, propertyExpression, filterChoices); + this.disableFilter = disableFilter; + this.dataTable = dataTable; } @Override public Component getFilter(final String componentId, final FilterForm form) { + ChoiceProvider provider; + + if (choiceProvider != null) { + provider = choiceProvider; + } else { + provider = new GenericChoiceProvider<>((List) getFilterChoices().getObject()); + } final Select2ChoiceBootstrapFormComponent selectorField = - new Select2ChoiceBootstrapFormComponent<>(componentId, - new GenericChoiceProvider<>((List) getFilterChoices().getObject()), - getFilterModel(form)); + new Select2ChoiceBootstrapFormComponent<>(componentId, provider, getFilterModel(form)); selectorField.hideLabel(); - selectorField.getField().add(AttributeModifier.replace("onchange", "this.form.submit();")); + if (disableFilter) { + selectorField.setEnabled(false); + } + + selectorField.getField().add(new AjaxComponentUpdatingBehavior(form, "change")); + return selectorField; } + + private class AjaxComponentUpdatingBehavior extends AjaxFormComponentUpdatingBehavior { + private final FilterForm form; + + AjaxComponentUpdatingBehavior(final FilterForm form, final String event) { + super(event); + this.form = form; + } + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + // update the table component + dataTable.setCurrentPage(0); + target.add(dataTable); + } + } + + @Override + public void detach() { + super.detach(); + if (choiceProvider != null) { + choiceProvider.detach(); + } + } } diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/table/SelectMultiFilteredBootstrapPropertyColumn.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/table/SelectMultiFilteredBootstrapPropertyColumn.java new file mode 100644 index 00000000..f3ac7afb --- /dev/null +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/components/table/SelectMultiFilteredBootstrapPropertyColumn.java @@ -0,0 +1,104 @@ +package org.devgateway.toolkit.forms.wicket.components.table; + +import org.apache.wicket.Component; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; +import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable; +import org.apache.wicket.extensions.markup.html.repeater.data.table.filter.ChoiceFilteredPropertyColumn; +import org.apache.wicket.extensions.markup.html.repeater.data.table.filter.FilterForm; +import org.apache.wicket.model.IModel; +import org.devgateway.toolkit.forms.wicket.components.form.Select2MultiChoiceBootstrapFormComponent; +import org.devgateway.toolkit.forms.wicket.providers.GenericChoiceProvider; +import org.wicketstuff.select2.ChoiceProvider; + +import java.util.Collection; +import java.util.List; + +/** + * @author idobre + * @since 2019-03-11 + * + * A ChoiceFilteredPropertyColumn that uses Select2MultiChoiceBootstrapFormComponent as a filter. + */ +public class SelectMultiFilteredBootstrapPropertyColumn extends ChoiceFilteredPropertyColumn { + private final DataTable dataTable; + private boolean disableFilter = false; + private ChoiceProvider choiceProvider; + + public SelectMultiFilteredBootstrapPropertyColumn(final IModel displayModel, + final S sortProperty, + final String propertyExpression, + final ChoiceProvider choiceProvider, + final DataTable dataTable) { + this(displayModel, sortProperty, propertyExpression, choiceProvider, dataTable, false); + } + + public SelectMultiFilteredBootstrapPropertyColumn(final IModel displayModel, + final S sortProperty, + final String propertyExpression, + final ChoiceProvider choiceProvider, + final DataTable dataTable, + final boolean disableFilter) { + super(displayModel, sortProperty, propertyExpression, null); + this.disableFilter = disableFilter; + this.dataTable = dataTable; + this.choiceProvider = choiceProvider; + } + + public SelectMultiFilteredBootstrapPropertyColumn(final IModel displayModel, + final S sortProperty, + final String propertyExpression, + final IModel> filterChoices, + final DataTable dataTable) { + super(displayModel, sortProperty, propertyExpression, filterChoices); + + this.dataTable = dataTable; + } + + public SelectMultiFilteredBootstrapPropertyColumn(final IModel displayModel, + final String propertyExpression, + final IModel> filterChoices, + final DataTable dataTable) { + super(displayModel, propertyExpression, filterChoices); + + this.dataTable = dataTable; + } + + @Override + public Component getFilter(final String componentId, final FilterForm form) { + ChoiceProvider provider; + + if (choiceProvider != null) { + provider = choiceProvider; + } else { + provider = new GenericChoiceProvider<>((List) getFilterChoices().getObject()); + } + + + final Select2MultiChoiceBootstrapFormComponent selectorField = + new Select2MultiChoiceBootstrapFormComponent<>(componentId, + provider, + (IModel>) getFilterModel(form)); + selectorField.hideLabel(); + + selectorField.getField().add(new AjaxComponentUpdatingBehavior(form, "change")); + + return selectorField; + } + + private class AjaxComponentUpdatingBehavior extends AjaxFormComponentUpdatingBehavior { + private final FilterForm form; + + AjaxComponentUpdatingBehavior(final FilterForm form, final String event) { + super(event); + this.form = form; + } + + @Override + protected void onUpdate(final AjaxRequestTarget target) { + // update the table component + dataTable.setCurrentPage(0); + target.add(dataTable); + } + } +} diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java index 30926811..c24f0f93 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/BasePage.java @@ -307,7 +307,7 @@ protected void onComponentTag(final ComponentTag tag) { }; adminMenu.setIconType(FontAwesomeIconType.cog); - MetaDataRoleAuthorizationStrategy.authorize(adminMenu, Component.RENDER, SecurityConstants.Roles.ROLE_ADMIN); + MetaDataRoleAuthorizationStrategy.authorize(adminMenu, Component.RENDER, SecurityConstants.Roles.ROLE_USER); return adminMenu; } diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.html b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.html index bdb88dcd..efa99fb7 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.html +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.html @@ -61,12 +61,20 @@ +
+
+ AutoSaveLabel + CheckedOutTo +
+
+
- AutoSaveLabel +
+ diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.java index aac3204c..ca42f8c4 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.java @@ -13,6 +13,7 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons; import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.TextContentModal; +import de.agilecoders.wicket.core.markup.html.bootstrap.form.BootstrapCheckbox; import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesomeIconType; import de.agilecoders.wicket.extensions.markup.html.bootstrap.ladda.LaddaAjaxButton; import org.apache.wicket.AttributeModifier; @@ -44,6 +45,7 @@ import org.devgateway.toolkit.forms.WebConstants; import org.devgateway.toolkit.forms.security.SecurityConstants; import org.devgateway.toolkit.forms.wicket.components.form.BootstrapSubmitButton; +import org.devgateway.toolkit.forms.wicket.components.form.CheckBoxBootstrapFormComponent; import org.devgateway.toolkit.forms.wicket.components.form.CheckBoxYesNoToggleBootstrapFormComponent; import org.devgateway.toolkit.forms.wicket.components.form.GenericBootstrapFormComponent; import org.devgateway.toolkit.forms.wicket.components.form.OptionallyRequiredTextAreaFieldComponent; @@ -64,7 +66,7 @@ * Page used to make editing easy, extend to get easy access to one entity for editing */ public abstract class AbstractEditStatusEntityPage - extends AbstractEditPage implements DefaultValidatorRoleAssignable { + extends AbstractEditPage implements DefaultValidatorRoleAssignable, ResourceLockable { protected Fragment entityButtonsFragment; @@ -92,6 +94,8 @@ public abstract class AbstractEditStatusEntityPage verticalPosition; private HiddenField maxHeight; @@ -99,6 +103,7 @@ public abstract class AbstractEditStatusEntityPage object, final IV visit.dontGoDeeper(); // do not process disabled fields - if (!object.isEnabledInHierarchy()) { + if (!object.isEnabledInHierarchy() || object.getField() instanceof BootstrapCheckbox) { return; } object.getField().processInput(); diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.properties b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.properties index a4ea0877..22d1a3cc 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.properties +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/AbstractEditStatusEntityPage.properties @@ -19,4 +19,8 @@ newStatusComment.label=Update Comment newStatusComment.help=Add a comment to describe this update during validation or data entry. This comment can be useful for other people editing or validating this form. \ PLEASE NOTE, text entered here will be viewable by anyone with access to this page. visibleStatusComments.label=View comments and form history -autoSaveLabelMessage=Form auto-saved less than {0,number} minutes ago \ No newline at end of file +autoSaveLabelMessage=Form auto-saved less than {0,number} minutes ago +checkedOutToMessage=Checked out to user {0}. +removeLock.label=Force form check in +removeLock.help=This will check back in a form that has been checked out by another user. Use this only if you know \ + the user is no longer editing the form and you want to allow other users access to the form. diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/ResourceLockable.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/ResourceLockable.java new file mode 100644 index 00000000..7aa17e6d --- /dev/null +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/edit/ResourceLockable.java @@ -0,0 +1,68 @@ +package org.devgateway.toolkit.forms.wicket.page.edit; + + +import org.apache.commons.lang3.BooleanUtils; +import org.devgateway.toolkit.forms.security.SecurityUtil; +import org.devgateway.toolkit.persistence.dao.AbstractStatusAuditableEntity; +import org.devgateway.toolkit.persistence.dao.DBConstants; +import org.devgateway.toolkit.persistence.dao.Person; + +public interface ResourceLockable { + + AbstractStatusAuditableEntity getLockableResource(); + + default boolean checkoutResource() { + if (!SecurityUtil.isCurrentUserAdmin() && isResourceCheckedOut() && !isCurrentUserLockOwner()) { + return false; + } + getLockableResource().setCheckedOutUser(SecurityUtil.getCurrentAuthenticatedPerson()); + return true; + } + + default boolean canEditLockableResource() { + return SecurityUtil.isCurrentUserAdmin() || getLockableResource().getCheckedOutUser() == null + || isCurrentUserLockOwner(); + } + + default String getCheckedOutUsername() { + if (getLockableResource().getCheckedOutUser() == null) { + return null; + } + return getLockableResource().getCheckedOutUser().getUsername(); + } + + default boolean beforeSaveLockableResource() { + if (BooleanUtils.isTrue(getLockableResource().getRemoveLock())) { + return checkinResource(); + } + if (!DBConstants.Status.DRAFT.equals(getLockableResource().getStatus())) { + return checkinResource(); + } + if (DBConstants.Status.DRAFT.equals(getLockableResource().getStatus()) && !isCurrentUserLockOwner()) { + return checkoutResource(); + } + return false; + } + + default boolean isCurrentUserLockOwner() { + Person currentAuthenticatedPerson = SecurityUtil.getCurrentAuthenticatedPerson(); + Person checkedOutUser = getLockableResource().getCheckedOutUser(); + return checkedOutUser != null && checkedOutUser.equals(currentAuthenticatedPerson); + } + + default boolean isResourceCheckedOut() { + return getLockableResource().getCheckedOutUser() != null; + } + + default boolean checkinResource() { + if (!isResourceCheckedOut()) { + return true; + } else { + if (isCurrentUserLockOwner() || SecurityUtil.isCurrentUserAdmin()) { + getLockableResource().setCheckedOutUser(null); + return true; + } + return false; + } + } +} diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/lists/AbstractListPage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/lists/AbstractListPage.java index bee2a7ea..00fac73d 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/lists/AbstractListPage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/lists/AbstractListPage.java @@ -27,7 +27,7 @@ import org.apache.wicket.extensions.markup.html.repeater.data.table.filter.IFilteredColumn; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.form.Form; -import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.markup.html.panel.GenericPanel; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; @@ -74,7 +74,7 @@ public abstract class AbstractListPage> editPageClass; - private AjaxFallbackBootstrapDataTable dataTable; + protected AjaxFallbackBootstrapDataTable dataTable; protected List> columns; @@ -156,8 +156,10 @@ public void populateItem(final Item> cellItem, final String co add(editPageLink); } - public class ActionPanel extends Panel { + public class ActionPanel extends GenericPanel { private static final long serialVersionUID = 5821419128121941939L; + protected PageParameters pageParameters; + protected BootstrapBookmarkablePageLink editItemPageLink; /** * @param id @@ -174,11 +176,12 @@ public ActionPanel(final String id, final IModel model) { pageParameters.set(WebConstants.PARAM_ID, entity.getId()); } - BootstrapBookmarkablePageLink editPageLink = + editItemPageLink = new BootstrapBookmarkablePageLink<>("edit", editPageClass, pageParameters, Buttons.Type.Info); - editPageLink.setIconType(FontAwesomeIconType.edit).setSize(Size.Small) + editItemPageLink.setIconType(FontAwesomeIconType.edit).setSize(Size.Small) .setLabel(new StringResourceModel("edit", AbstractListPage.this, null)); - add(editPageLink); + add(editItemPageLink); + add(getPrintButton(pageParameters)); diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/lists/AbstractListStatusEntityPage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/lists/AbstractListStatusEntityPage.java new file mode 100644 index 00000000..462d5a20 --- /dev/null +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/lists/AbstractListStatusEntityPage.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2015 Development Gateway, Inc and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the MIT License (MIT) + * which accompanies this distribution, and is available at + * https://opensource.org/licenses/MIT + * + * Contributors: + * Development Gateway - initial API and implementation + *******************************************************************************/ +package org.devgateway.toolkit.forms.wicket.page.lists; + +import de.agilecoders.wicket.core.markup.html.bootstrap.components.TooltipBehavior; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.model.util.ListModel; +import org.apache.wicket.request.mapper.parameter.PageParameters; +import org.devgateway.toolkit.forms.wicket.components.table.SelectFilteredBootstrapPropertyColumn; +import org.devgateway.toolkit.forms.wicket.page.edit.ResourceLockable; +import org.devgateway.toolkit.persistence.dao.AbstractStatusAuditableEntity; +import org.devgateway.toolkit.persistence.dao.DBConstants; + +import java.util.List; + +/** + * This class can be use to display a list of Entities that have a status {@link AbstractStatusAuditableEntity}. + * + * @author idobre + * @since 2019-04-01 + */ +public abstract class AbstractListStatusEntityPage + extends AbstractListPage { + + + public AbstractListStatusEntityPage(final PageParameters parameters) { + super(parameters); + } + + + public class LockableResourceActionPanel extends ActionPanel implements ResourceLockable { + + /** + * @param id + * @param model + */ + public LockableResourceActionPanel(final String id, final IModel model) { + super(id, model); + editItemPageLink.setEnabled(canEditLockableResource()); + if (getLockableResource().getCheckedOutUser() != null) { + add(new TooltipBehavior(Model.of("Checked out to user " + getCheckedOutUsername()))); + } + } + + @Override + public AbstractStatusAuditableEntity getLockableResource() { + return getModelObject(); + } + } + + @Override + public ActionPanel getActionPanel(final String id, final IModel model) { + return new LockableResourceActionPanel(id, model); + } + + + @Override + protected void onInitialize() { + addStatusColumn(); + + super.onInitialize(); + } + + private List getStatusDropdownValues() { + return DBConstants.Status.ALL_LIST; + } + + private void addStatusColumn() { + columns.add(1, new SelectFilteredBootstrapPropertyColumn<>(new Model<>("Status"), + "status", "status", new ListModel<>(getStatusDropdownValues()), dataTable)); + } +} diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/lists/ListTestFormPage.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/lists/ListTestFormPage.java index 63c31c91..71fb0863 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/lists/ListTestFormPage.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/page/lists/ListTestFormPage.java @@ -25,9 +25,9 @@ import org.devgateway.toolkit.persistence.service.TestFormService; import org.wicketstuff.annotation.mount.MountPath; -@AuthorizeInstantiation(SecurityConstants.Roles.ROLE_ADMIN) +@AuthorizeInstantiation(SecurityConstants.Roles.ROLE_USER) @MountPath(value = "/listTestForm") -public class ListTestFormPage extends AbstractListPage { +public class ListTestFormPage extends AbstractListStatusEntityPage { private static final long serialVersionUID = -324298525712620234L; @SpringBean diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/BaseStyles.css b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/BaseStyles.css index 9927dfb0..a9286a7b 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/BaseStyles.css +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/styles/BaseStyles.css @@ -89,6 +89,7 @@ body { .autosave { display: block; margin-bottom: 12px; + color: #ADADAD; } hr.edit-separator { diff --git a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AbstractStatusAuditableEntity.java b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AbstractStatusAuditableEntity.java index 9857fd02..a7399f86 100644 --- a/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AbstractStatusAuditableEntity.java +++ b/persistence/src/main/java/org/devgateway/toolkit/persistence/dao/AbstractStatusAuditableEntity.java @@ -7,6 +7,7 @@ import javax.persistence.CascadeType; import javax.persistence.FetchType; +import javax.persistence.ManyToOne; import javax.persistence.MappedSuperclass; import javax.persistence.OneToMany; import javax.persistence.OrderColumn; @@ -31,6 +32,10 @@ public abstract class AbstractStatusAuditableEntity extends AbstractAuditableEnt @JsonIgnore private String newStatusComment; + @JsonIgnore + @Transient + private Boolean removeLock = false; + @Transient @JsonIgnore private Boolean visibleStatusComments = false; @@ -39,6 +44,11 @@ public abstract class AbstractStatusAuditableEntity extends AbstractAuditableEnt @JsonIgnore private Boolean visibleStatusLabel = true; + @JsonIgnore + @ManyToOne(fetch = FetchType.LAZY) + @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) + private Person checkedOutUser; + @Override public String getStatus() { return status; @@ -80,4 +90,20 @@ public void setVisibleStatusLabel(final Boolean visibleStatusLabel) { this.visibleStatusLabel = visibleStatusLabel; } + + public Person getCheckedOutUser() { + return checkedOutUser; + } + + public void setCheckedOutUser(final Person checkedOutUser) { + this.checkedOutUser = checkedOutUser; + } + + public Boolean getRemoveLock() { + return removeLock; + } + + public void setRemoveLock(Boolean removeLock) { + this.removeLock = removeLock; + } } From 0c0c2a9715b071938bfe402ad7420717c42b4d70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2020 11:57:50 +0000 Subject: [PATCH 11/15] Bump commons-beanutils from 1.9.3 to 1.9.4 in /persistence Bumps commons-beanutils from 1.9.3 to 1.9.4. Signed-off-by: dependabot[bot] --- persistence/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistence/pom.xml b/persistence/pom.xml index 26aed82c..e79f290f 100644 --- a/persistence/pom.xml +++ b/persistence/pom.xml @@ -23,7 +23,7 @@ 0.3.8 3.2.1 28.1-jre - 1.9.3 + 1.9.4 6.1.4.Final From 5eaa130c9cc2f7d64fd3cd97b6647bf987c31c33 Mon Sep 17 00:00:00 2001 From: mpostelnicu Date: Mon, 31 Aug 2020 14:51:38 +0300 Subject: [PATCH 12/15] fixes #296 --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index b1a58161..b8e6d866 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,7 @@ 3.0.0 2.22.1 2.5.3 + 3.27.0-GA @@ -125,6 +126,11 @@ derby ${derby.version} + + org.javassist + javassist + ${javassist.version} + org.springframework.plugin spring-plugin-core From d9635006bb442d5015d1e645e7f89d2a7488c2ff Mon Sep 17 00:00:00 2001 From: mpostelnicu Date: Sat, 10 Oct 2020 17:47:39 +0300 Subject: [PATCH 13/15] fixes #315 --- forms/README.md | 15 +++++++++++++++ forms/pom.xml | 6 ++++++ .../resources/META-INF/spring-devtools.properties | 2 ++ web/README.md | 13 +++++++++++++ web/pom.xml | 6 ++++++ 5 files changed, 42 insertions(+) create mode 100644 forms/src/main/resources/META-INF/spring-devtools.properties diff --git a/forms/README.md b/forms/README.md index 1fc62f0a..608c6db2 100644 --- a/forms/README.md +++ b/forms/README.md @@ -62,3 +62,18 @@ This module is packaged as a fat jar. For testing purposes, the default configur `java -Dspring.profiles.active=dev -jar target/forms-0.0.1-SNAPSHOT.jar` This will start everything, including an embedded Tomcat Web server and all the services attached it. + +# Using Spring Boot Developer Tools + +This spring add-on allows automatic context reload of beans when resource changes detected +(classes recompiled, other files in classpath changed). This means you do not have to restart your +application when you recompile classes, nor you need paid tools like jrebel for it. + +Read more about it [here](https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/html/using-boot-devtools.html) +A good howto can be found [here](https://www.baeldung.com/spring-boot-devtools) + +To enable devtools you need to start the application using java startup property +`spring.devtools.restart.enabled=true` + +Wicket integration is done according to section [20.2.6](https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/html/using-boot-devtools.html#using-boot-devtools-customizing-classload) +of the documentation. \ No newline at end of file diff --git a/forms/pom.xml b/forms/pom.xml index 5ea04ed6..5548dc25 100644 --- a/forms/pom.xml +++ b/forms/pom.xml @@ -48,6 +48,12 @@ 0.0.1-SNAPSHOT + + org.springframework.boot + spring-boot-devtools + true + + org.devgateway.toolkit persistence-mongodb diff --git a/forms/src/main/resources/META-INF/spring-devtools.properties b/forms/src/main/resources/META-INF/spring-devtools.properties new file mode 100644 index 00000000..fdaf7593 --- /dev/null +++ b/forms/src/main/resources/META-INF/spring-devtools.properties @@ -0,0 +1,2 @@ +restart.include.wicket-annotation=/wicketstuff-annotation-[\\w-\.]+\.jar +restart.include.wicket-spring=/wicket-spring-[\\w-\.]+\.jar \ No newline at end of file diff --git a/web/README.md b/web/README.md index e24d3abc..7be18a3a 100644 --- a/web/README.md +++ b/web/README.md @@ -16,3 +16,16 @@ Because it gets packaged as a fat jar, starting it is piece of cake: `java -jar target/web-0.0.1-SNAPSHOT.jar` This will start everything, including an embedded Tomcat Web server and all the services attached it. + +# Using Spring Boot Developer Tools + +This spring add-on allows automatic context reload of beans when resource changes detected +(classes recompiled, other files in classpath changed). This means you do not have to restart your +application when you recompile classes, nor you need paid tools like jrebel for it. + +Read more about it [here](https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/html/using-boot-devtools.html) +A good howto can be found [here](https://www.baeldung.com/spring-boot-devtools) + +To enable devtools you need to start the application using java startup property +`spring.devtools.restart.enabled=true` + diff --git a/web/pom.xml b/web/pom.xml index ade369f0..0aa14c9f 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -54,6 +54,12 @@ spring-boot-starter-actuator + + org.springframework.boot + spring-boot-devtools + true + + org.springframework.boot spring-boot-starter-test From c0bdecddeeef3343c88fbbda8a5d38a1b89916c9 Mon Sep 17 00:00:00 2001 From: mpostelnicu Date: Mon, 12 Oct 2020 09:47:05 +0300 Subject: [PATCH 14/15] #315 spring devtools serializer --- .../serializer/SpringDevToolsSerializer.java | 47 +++++++++++++++++++ .../forms/wicket/FormsWebApplication.java | 3 ++ 2 files changed, 50 insertions(+) create mode 100644 forms/src/main/java/org/devgateway/toolkit/forms/serializer/SpringDevToolsSerializer.java diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/serializer/SpringDevToolsSerializer.java b/forms/src/main/java/org/devgateway/toolkit/forms/serializer/SpringDevToolsSerializer.java new file mode 100644 index 00000000..8ec66adc --- /dev/null +++ b/forms/src/main/java/org/devgateway/toolkit/forms/serializer/SpringDevToolsSerializer.java @@ -0,0 +1,47 @@ +package org.devgateway.toolkit.forms.serializer; + +import org.apache.wicket.serialize.ISerializer; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.serializer.DefaultDeserializer; +import org.springframework.core.serializer.support.DeserializingConverter; +import org.springframework.core.serializer.support.SerializingConverter; + +/** + * A custom serializer is needed to support Spring Boot Devtools. Spring Boot Devtools + * has some limitation with support of serialisation/deserialization support. So we have to + * provide a custom Wicket {@link ISerializer}. + * + *

+ * 20.2.6 Known limitations
+ * Restart functionality does not work well with objects that are deserialized using a + * standard ObjectInputStream. If you need to deserialize data, you may need to use Spring’s + * ConfigurableObjectInputStream in combination with Thread.currentThread().getContextClassLoader(). + * Unfortunately, several third-party libraries deserialize without considering the context classloader. + * If you find such a problem, you will need to request a fix with the original authors. + *

+ * + * @see Spring Boot Devtools Serializer Issue + * @author Marc Giffing + * + */ +public class SpringDevToolsSerializer implements ISerializer { + + private Converter serializer = new SerializingConverter(); + private Converter deserializer; + + public SpringDevToolsSerializer(){ + this.deserializer = new DeserializingConverter(new DefaultDeserializer(Thread.currentThread() + .getContextClassLoader())); + } + + @Override + public byte[] serialize(Object object) { + return serializer.convert(object); + } + + @Override + public Object deserialize(byte[] data) { + return deserializer.convert(data); + } + +} \ No newline at end of file diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/FormsWebApplication.java b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/FormsWebApplication.java index c11e4863..93f6ad8b 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/wicket/FormsWebApplication.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/wicket/FormsWebApplication.java @@ -44,6 +44,7 @@ import org.apache.wicket.settings.RequestCycleSettings.RenderStrategy; import org.apache.wicket.spring.injection.annot.SpringComponentInjector; import org.apache.wicket.util.file.Folder; +import org.devgateway.toolkit.forms.serializer.SpringDevToolsSerializer; import org.devgateway.toolkit.forms.service.SessionFinderService; import org.devgateway.toolkit.forms.wicket.components.form.SummernoteJpaStorageService; import org.devgateway.toolkit.forms.wicket.converters.NonNumericFilteredBigDecimalConverter; @@ -209,6 +210,8 @@ protected void init() { guard.addPattern("+*.woff2"); } + getFrameworkSettings().setSerializer(new SpringDevToolsSerializer()); + // this ensures that spring DI works for wicket components and pages // see @SpringBean annotation getComponentInstantiationListeners().add(new SpringComponentInjector(this, applicationContext)); From e2f303766513cac1e7b6018d74dc779c888f34b5 Mon Sep 17 00:00:00 2001 From: mpostelnicu Date: Mon, 12 Oct 2020 09:52:56 +0300 Subject: [PATCH 15/15] #315 spring devtools serializer checkstyle --- .../serializer/SpringDevToolsSerializer.java | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/forms/src/main/java/org/devgateway/toolkit/forms/serializer/SpringDevToolsSerializer.java b/forms/src/main/java/org/devgateway/toolkit/forms/serializer/SpringDevToolsSerializer.java index 8ec66adc..87194197 100644 --- a/forms/src/main/java/org/devgateway/toolkit/forms/serializer/SpringDevToolsSerializer.java +++ b/forms/src/main/java/org/devgateway/toolkit/forms/serializer/SpringDevToolsSerializer.java @@ -10,38 +10,37 @@ * A custom serializer is needed to support Spring Boot Devtools. Spring Boot Devtools * has some limitation with support of serialisation/deserialization support. So we have to * provide a custom Wicket {@link ISerializer}. - * + * *

* 20.2.6 Known limitations
- * Restart functionality does not work well with objects that are deserialized using a - * standard ObjectInputStream. If you need to deserialize data, you may need to use Spring’s + * Restart functionality does not work well with objects that are deserialized using a + * standard ObjectInputStream. If you need to deserialize data, you may need to use Spring’s * ConfigurableObjectInputStream in combination with Thread.currentThread().getContextClassLoader(). - * Unfortunately, several third-party libraries deserialize without considering the context classloader. + * Unfortunately, several third-party libraries deserialize without considering the context classloader. * If you find such a problem, you will need to request a fix with the original authors. *

- * - * @see Spring Boot Devtools Serializer Issue - * @author Marc Giffing * + * @author Marc Giffing + * @see Spring Boot Devtools Serializer Issue */ public class SpringDevToolsSerializer implements ISerializer { - private Converter serializer = new SerializingConverter(); + private Converter serializer = new SerializingConverter(); private Converter deserializer; - - public SpringDevToolsSerializer(){ - this.deserializer = new DeserializingConverter(new DefaultDeserializer(Thread.currentThread() - .getContextClassLoader())); + + public SpringDevToolsSerializer() { + this.deserializer = new DeserializingConverter(new DefaultDeserializer(Thread.currentThread() + .getContextClassLoader())); + } + + @Override + public byte[] serialize(final Object object) { + return serializer.convert(object); } - - @Override - public byte[] serialize(Object object) { - return serializer.convert(object); - } - @Override - public Object deserialize(byte[] data) { - return deserializer.convert(data); - } + @Override + public Object deserialize(final byte[] data) { + return deserializer.convert(data); + } } \ No newline at end of file