From c2d7f3ebc6368a15aabed3d747ec970751961d46 Mon Sep 17 00:00:00 2001 From: Alexander ADAM Date: Sat, 7 Sep 2024 22:27:48 +0200 Subject: [PATCH] feat: add option to disable OCR (#768) fixes #344 refs #767 --- .../docs/core/constant/ConfigType.java | 9 ++- .../sismics/docs/core/util/ConfigUtil.java | 29 +++++-- .../core/util/format/ImageFormatHandler.java | 5 +- .../core/util/format/PdfFormatHandler.java | 4 +- .../com/sismics/util/jpa/DbOpenHelper.java | 24 +++--- .../src/main/resources/config.properties | 2 +- .../resources/db/update/dbupdate-031-0.sql | 7 ++ docs-web/src/dev/resources/config.properties | 2 +- .../docs/rest/resource/AppResource.java | 78 +++++++++++++------ .../controller/settings/SettingsConfig.js | 13 +++- docs-web/src/main/webapp/src/locale/de.json | 4 + docs-web/src/main/webapp/src/locale/en.json | 4 + .../src/partial/docs/settings.config.html | 13 +++- docs-web/src/prod/resources/config.properties | 2 +- .../sismics/docs/rest/TestAppResource.java | 46 +++++++++-- scripts/dev_setup.sh | 41 ++++++++++ 16 files changed, 221 insertions(+), 62 deletions(-) create mode 100644 docs-core/src/main/resources/db/update/dbupdate-031-0.sql create mode 100755 scripts/dev_setup.sh diff --git a/docs-core/src/main/java/com/sismics/docs/core/constant/ConfigType.java b/docs-core/src/main/java/com/sismics/docs/core/constant/ConfigType.java index bf51a703..61319ab5 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/constant/ConfigType.java +++ b/docs-core/src/main/java/com/sismics/docs/core/constant/ConfigType.java @@ -1,9 +1,9 @@ package com.sismics.docs.core.constant; /** - * Configuration parameters. + * Configuration parameters. * - * @author jtremeaux + * @author jtremeaux */ public enum ConfigType { /** @@ -20,6 +20,11 @@ public enum ConfigType { */ GUEST_LOGIN, + /** + * OCR enabled. + */ + OCR_ENABLED, + /** * Default language. */ diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/ConfigUtil.java b/docs-core/src/main/java/com/sismics/docs/core/util/ConfigUtil.java index d9c6754b..6cee2ac4 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/util/ConfigUtil.java +++ b/docs-core/src/main/java/com/sismics/docs/core/util/ConfigUtil.java @@ -8,13 +8,12 @@ import java.util.ResourceBundle; /** * Configuration parameter utilities. - * - * @author jtremeaux + * */ public class ConfigUtil { /** * Returns the textual value of a configuration parameter. - * + * * @param configType Type of the configuration parameter * @return Textual value of the configuration parameter * @throws IllegalStateException Configuration parameter undefined @@ -30,7 +29,7 @@ public class ConfigUtil { /** * Returns the configuration resource bundle. - * + * * @return Resource bundle */ public static ResourceBundle getConfigBundle() { @@ -39,14 +38,14 @@ public class ConfigUtil { /** * Returns the integer value of a configuration parameter. - * + * * @param configType Type of the configuration parameter * @return Integer value of the configuration parameter * @throws IllegalStateException Configuration parameter undefined */ public static int getConfigIntegerValue(ConfigType configType) { String value = getConfigStringValue(configType); - + return Integer.parseInt(value); } @@ -65,14 +64,28 @@ public class ConfigUtil { /** * Returns the boolean value of a configuration parameter. - * + * * @param configType Type of the configuration parameter * @return Boolean value of the configuration parameter * @throws IllegalStateException Configuration parameter undefined */ public static boolean getConfigBooleanValue(ConfigType configType) { String value = getConfigStringValue(configType); - return Boolean.parseBoolean(value); } + + /** + * Returns the boolean value of a configuration parameter with a default value. + * + * @param configType Type of the configuration parameter + * @param defaultValue Default value to return if the configuration parameter is undefined + * @return Boolean value of the configuration parameter + */ + public static boolean getConfigBooleanValue(ConfigType configType, boolean defaultValue) { + try { + return getConfigBooleanValue(configType); + } catch (IllegalStateException e) { + return defaultValue; + } + } } diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/format/ImageFormatHandler.java b/docs-core/src/main/java/com/sismics/docs/core/util/format/ImageFormatHandler.java index 5d3a2459..545d6e28 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/util/format/ImageFormatHandler.java +++ b/docs-core/src/main/java/com/sismics/docs/core/util/format/ImageFormatHandler.java @@ -3,6 +3,8 @@ package com.sismics.docs.core.util.format; import com.google.common.io.Closer; import com.sismics.docs.core.constant.Constants; import com.sismics.docs.core.util.FileUtil; +import com.sismics.docs.core.util.ConfigUtil; +import com.sismics.docs.core.constant.ConfigType; import com.sismics.util.mime.MimeType; import org.apache.pdfbox.io.MemoryUsageSetting; import org.apache.pdfbox.pdmodel.PDDocument; @@ -22,7 +24,6 @@ import java.nio.file.Path; /** * Image format handler. * - * @author bgamard */ public class ImageFormatHandler implements FormatHandler { /** @@ -45,7 +46,7 @@ public class ImageFormatHandler implements FormatHandler { @Override public String extractContent(String language, Path file) throws Exception { - if (language == null) { + if (language == null || !ConfigUtil.getConfigBooleanValue(ConfigType.OCR_ENABLED, true)) { return null; } diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/format/PdfFormatHandler.java b/docs-core/src/main/java/com/sismics/docs/core/util/format/PdfFormatHandler.java index 670358b9..e6417a0c 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/util/format/PdfFormatHandler.java +++ b/docs-core/src/main/java/com/sismics/docs/core/util/format/PdfFormatHandler.java @@ -2,6 +2,8 @@ package com.sismics.docs.core.util.format; import com.google.common.io.Closer; import com.sismics.docs.core.util.FileUtil; +import com.sismics.docs.core.util.ConfigUtil; +import com.sismics.docs.core.constant.ConfigType; import com.sismics.util.mime.MimeType; import org.apache.pdfbox.io.MemoryUsageSetting; import org.apache.pdfbox.multipdf.PDFMergerUtility; @@ -53,7 +55,7 @@ public class PdfFormatHandler implements FormatHandler { } // No text content, try to OCR it - if (language != null && content != null && content.trim().isEmpty()) { + if (language != null && content != null && content.trim().isEmpty() && ConfigUtil.getConfigBooleanValue(ConfigType.OCR_ENABLED, true)) { StringBuilder sb = new StringBuilder(); try (InputStream inputStream = Files.newInputStream(file); PDDocument pdfDocument = PDDocument.load(inputStream)) { diff --git a/docs-core/src/main/java/com/sismics/util/jpa/DbOpenHelper.java b/docs-core/src/main/java/com/sismics/util/jpa/DbOpenHelper.java index c16383d0..cc49c2e6 100644 --- a/docs-core/src/main/java/com/sismics/util/jpa/DbOpenHelper.java +++ b/docs-core/src/main/java/com/sismics/util/jpa/DbOpenHelper.java @@ -39,7 +39,7 @@ abstract class DbOpenHelper { private static final Logger log = LoggerFactory.getLogger(DbOpenHelper.class); private final JdbcConnectionAccess jdbcConnectionAccess; - + private final List exceptions = new ArrayList<>(); private Formatter formatter; @@ -99,7 +99,7 @@ abstract class DbOpenHelper { onCreate(); oldVersion = 0; } - + // Execute update script ResourceBundle configBundle = ConfigUtil.getConfigBundle(); Integer currentVersion = Integer.parseInt(configBundle.getString("db.version")); @@ -126,7 +126,7 @@ abstract class DbOpenHelper { /** * Execute all upgrade scripts in ascending order for a given version. - * + * * @param version Version number * @throws Exception e */ @@ -136,7 +136,7 @@ abstract class DbOpenHelper { return name.matches("dbupdate-" + versionString + "-\\d+\\.sql"); }); Collections.sort(fileNameList); - + for (String fileName : fileNameList) { if (log.isInfoEnabled()) { log.info(MessageFormat.format("Executing script: {0}", fileName)); @@ -145,16 +145,16 @@ abstract class DbOpenHelper { executeScript(is); } } - + /** * Execute a SQL script. All statements must be one line only. - * + * * @param inputScript Script to execute * @throws IOException e */ private void executeScript(InputStream inputScript) throws IOException { List lines = CharStreams.readLines(new InputStreamReader(inputScript)); - + for (String sql : lines) { if (Strings.isNullOrEmpty(sql) || sql.startsWith("--")) { continue; @@ -178,13 +178,13 @@ abstract class DbOpenHelper { } public abstract void onCreate() throws Exception; - + public abstract void onUpgrade(int oldVersion, int newVersion) throws Exception; - + /** - * Returns a List of all Exceptions which occured during the export. + * Returns a List of all Exceptions which occurred during the export. * - * @return A List containig the Exceptions occured during the export + * @return A List containing the Exceptions occurred during the export */ public List getExceptions() { return exceptions; @@ -192,7 +192,7 @@ abstract class DbOpenHelper { /** * Format the output SQL statements. - * + * * @param format True to format */ public void setFormat(boolean format) { diff --git a/docs-core/src/main/resources/config.properties b/docs-core/src/main/resources/config.properties index 435fb302..67df6648 100644 --- a/docs-core/src/main/resources/config.properties +++ b/docs-core/src/main/resources/config.properties @@ -1 +1 @@ -db.version=30 +db.version=31 diff --git a/docs-core/src/main/resources/db/update/dbupdate-031-0.sql b/docs-core/src/main/resources/db/update/dbupdate-031-0.sql new file mode 100644 index 00000000..6c93d5ef --- /dev/null +++ b/docs-core/src/main/resources/db/update/dbupdate-031-0.sql @@ -0,0 +1,7 @@ +-- DBUPDATE-031-0.SQL + +-- Insert a new setting for OCR recognition +insert into T_CONFIG (CFG_ID_C, CFG_VALUE_C) values ('OCR_ENABLED', 'true'); + +-- Update the database version +update T_CONFIG set CFG_VALUE_C = '31' where CFG_ID_C = 'DB_VERSION'; diff --git a/docs-web/src/dev/resources/config.properties b/docs-web/src/dev/resources/config.properties index 37e03ad0..d182751e 100644 --- a/docs-web/src/dev/resources/config.properties +++ b/docs-web/src/dev/resources/config.properties @@ -1,3 +1,3 @@ api.current_version=${project.version} api.min_version=1.0 -db.version=30 +db.version=31 diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java index bb1e8edd..e458bde0 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java @@ -47,7 +47,7 @@ import java.util.*; /** * General app REST resource. - * + * * @author jtremeaux */ @Path("/app") @@ -56,11 +56,11 @@ public class AppResource extends BaseResource { * Logger. */ private static final Logger log = LoggerFactory.getLogger(AppResource.class); - + /** - * Returns informations about the application. + * Returns information about the application. * - * @api {get} /app Get application informations + * @api {get} /app Get application information * @apiName GetApp * @apiGroup App * @apiSuccess {String} current_version API current version @@ -85,6 +85,7 @@ public class AppResource extends BaseResource { String currentVersion = configBundle.getString("api.current_version"); String minVersion = configBundle.getString("api.min_version"); Boolean guestLogin = ConfigUtil.getConfigBooleanValue(ConfigType.GUEST_LOGIN); + Boolean ocrEnabled = ConfigUtil.getConfigBooleanValue(ConfigType.OCR_ENABLED, true); String defaultLanguage = ConfigUtil.getConfigStringValue(ConfigType.DEFAULT_LANGUAGE); UserDao userDao = new UserDao(); DocumentDao documentDao = new DocumentDao(); @@ -98,6 +99,7 @@ public class AppResource extends BaseResource { .add("current_version", currentVersion.replace("-SNAPSHOT", "")) .add("min_version", minVersion) .add("guest_login", guestLogin) + .add("ocr_enabled", ocrEnabled) .add("default_language", defaultLanguage) .add("queued_tasks", AppContext.getInstance().getQueuedTaskCount()) .add("total_memory", Runtime.getRuntime().totalMemory()) @@ -140,6 +142,34 @@ public class AppResource extends BaseResource { return Response.ok().build(); } + /** + * Enable/disable OCR. + * + * @api {post} /app/ocr Enable/disable OCR + * @apiName PostAppOcr + * @apiGroup App + * @apiParam {Boolean} enabled If true, enable OCR + * @apiError (client) ForbiddenError Access denied + * @apiPermission admin + * @apiVersion 1.5.0 + * + * @param enabled If true, enable OCR + * @return Response + */ + @POST + @Path("ocr") + public Response ocr(@FormParam("enabled") Boolean enabled) { + if (!authenticate()) { + throw new ForbiddenClientException(); + } + checkBaseFunction(BaseFunction.ADMIN); + + ConfigDao configDao = new ConfigDao(); + configDao.update(ConfigType.OCR_ENABLED, enabled.toString()); + + return Response.ok().build(); + } + /** * General application configuration. * @@ -240,7 +270,7 @@ public class AppResource extends BaseResource { return Response.ok().entity(response.build()).build(); } - + /** * Configure the SMTP server. * @@ -546,13 +576,13 @@ public class AppResource extends BaseResource { throw new ServerException("ServerError", "MEMORY appender not configured"); } MemoryAppender memoryAppender = (MemoryAppender) appender; - + // Find the logs LogCriteria logCriteria = new LogCriteria() .setMinLevel(Level.toLevel(StringUtils.stripToNull(minLevel))) .setTag(StringUtils.stripToNull(tag)) .setMessage(StringUtils.stripToNull(message)); - + PaginatedList paginatedList = PaginatedLists.create(limit, offset); memoryAppender.find(logCriteria, paginatedList); JsonArrayBuilder logs = Json.createArrayBuilder(); @@ -563,14 +593,14 @@ public class AppResource extends BaseResource { .add("tag", logEntry.getTag()) .add("message", logEntry.getMessage())); } - + JsonObjectBuilder response = Json.createObjectBuilder() .add("total", paginatedList.getResultCount()) .add("logs", logs); - + return Response.ok().entity(response.build()).build(); } - + /** * Destroy and rebuild the search index. * @@ -592,7 +622,7 @@ public class AppResource extends BaseResource { throw new ForbiddenClientException(); } checkBaseFunction(BaseFunction.ADMIN); - + RebuildIndexAsyncEvent rebuildIndexAsyncEvent = new RebuildIndexAsyncEvent(); ThreadLocalContext.get().addAsyncEvent(rebuildIndexAsyncEvent); @@ -601,7 +631,7 @@ public class AppResource extends BaseResource { .add("status", "ok"); return Response.ok().entity(response.build()).build(); } - + /** * Clean storage. * @@ -623,7 +653,7 @@ public class AppResource extends BaseResource { throw new ForbiddenClientException(); } checkBaseFunction(BaseFunction.ADMIN); - + // Get all files FileDao fileDao = new FileDao(); List fileList = fileDao.findAll(0, Integer.MAX_VALUE); @@ -632,7 +662,7 @@ public class AppResource extends BaseResource { fileMap.put(file.getId(), file); } log.info("Checking {} files", fileMap.size()); - + // Check if each stored file is valid try (DirectoryStream storedFileList = Files.newDirectoryStream(DirectoryUtil.getStorageDirectory())) { for (java.nio.file.Path storedFile : storedFileList) { @@ -646,7 +676,7 @@ public class AppResource extends BaseResource { } catch (IOException e) { throw new ServerException("FileError", "Error deleting orphan files", e); } - + // Hard delete orphan audit logs EntityManager em = ThreadLocalContext.get().getEntityManager(); StringBuilder sb = new StringBuilder("delete from T_AUDIT_LOG al where al.LOG_ID_C in (select al.LOG_ID_C from T_AUDIT_LOG al "); @@ -660,7 +690,7 @@ public class AppResource extends BaseResource { sb.append(" where d.DOC_ID_C is null and a.ACL_ID_C is null and c.COM_ID_C is null and f.FIL_ID_C is null and t.TAG_ID_C is null and u.USE_ID_C is null and g.GRP_ID_C is null)"); Query q = em.createNativeQuery(sb.toString()); log.info("Deleting {} orphan audit logs", q.executeUpdate()); - + // Soft delete orphan ACLs sb = new StringBuilder("update T_ACL a set ACL_DELETEDATE_D = :dateNow where a.ACL_ID_C in (select a.ACL_ID_C from T_ACL a "); sb.append(" left join T_SHARE s on s.SHA_ID_C = a.ACL_TARGETID_C "); @@ -672,37 +702,37 @@ public class AppResource extends BaseResource { q = em.createNativeQuery(sb.toString()); q.setParameter("dateNow", new Date()); log.info("Deleting {} orphan ACLs", q.executeUpdate()); - + // Soft delete orphan comments q = em.createNativeQuery("update T_COMMENT set COM_DELETEDATE_D = :dateNow where COM_ID_C in (select c.COM_ID_C from T_COMMENT c left join T_DOCUMENT d on d.DOC_ID_C = c.COM_IDDOC_C and d.DOC_DELETEDATE_D is null where d.DOC_ID_C is null)"); q.setParameter("dateNow", new Date()); log.info("Deleting {} orphan comments", q.executeUpdate()); - + // Soft delete orphan document tag links q = em.createNativeQuery("update T_DOCUMENT_TAG set DOT_DELETEDATE_D = :dateNow where DOT_ID_C in (select dt.DOT_ID_C from T_DOCUMENT_TAG dt left join T_DOCUMENT d on dt.DOT_IDDOCUMENT_C = d.DOC_ID_C and d.DOC_DELETEDATE_D is null left join T_TAG t on t.TAG_ID_C = dt.DOT_IDTAG_C and t.TAG_DELETEDATE_D is null where d.DOC_ID_C is null or t.TAG_ID_C is null)"); q.setParameter("dateNow", new Date()); log.info("Deleting {} orphan document tag links", q.executeUpdate()); - + // Soft delete orphan shares q = em.createNativeQuery("update T_SHARE set SHA_DELETEDATE_D = :dateNow where SHA_ID_C in (select s.SHA_ID_C from T_SHARE s left join T_ACL a on a.ACL_TARGETID_C = s.SHA_ID_C and a.ACL_DELETEDATE_D is null where a.ACL_ID_C is null)"); q.setParameter("dateNow", new Date()); log.info("Deleting {} orphan shares", q.executeUpdate()); - + // Soft delete orphan tags q = em.createNativeQuery("update T_TAG set TAG_DELETEDATE_D = :dateNow where TAG_ID_C in (select t.TAG_ID_C from T_TAG t left join T_USER u on u.USE_ID_C = t.TAG_IDUSER_C and u.USE_DELETEDATE_D is null where u.USE_ID_C is null)"); q.setParameter("dateNow", new Date()); log.info("Deleting {} orphan tags", q.executeUpdate()); - + // Soft delete orphan documents q = em.createNativeQuery("update T_DOCUMENT set DOC_DELETEDATE_D = :dateNow where DOC_ID_C in (select d.DOC_ID_C from T_DOCUMENT d left join T_USER u on u.USE_ID_C = d.DOC_IDUSER_C and u.USE_DELETEDATE_D is null where u.USE_ID_C is null)"); q.setParameter("dateNow", new Date()); log.info("Deleting {} orphan documents", q.executeUpdate()); - + // Soft delete orphan files q = em.createNativeQuery("update T_FILE set FIL_DELETEDATE_D = :dateNow where FIL_ID_C in (select f.FIL_ID_C from T_FILE f left join T_USER u on u.USE_ID_C = f.FIL_IDUSER_C and u.USE_DELETEDATE_D is null where u.USE_ID_C is null)"); q.setParameter("dateNow", new Date()); log.info("Deleting {} orphan files", q.executeUpdate()); - + // Hard delete softly deleted data log.info("Deleting {} soft deleted document tag links", em.createQuery("delete DocumentTag where deleteDate is not null").executeUpdate()); log.info("Deleting {} soft deleted ACLs", em.createQuery("delete Acl where deleteDate is not null").executeUpdate()); @@ -713,7 +743,7 @@ public class AppResource extends BaseResource { log.info("Deleting {} soft deleted documents", em.createQuery("delete Document where deleteDate is not null").executeUpdate()); log.info("Deleting {} soft deleted users", em.createQuery("delete User where deleteDate is not null").executeUpdate()); log.info("Deleting {} soft deleted groups", em.createQuery("delete Group where deleteDate is not null").executeUpdate()); - + // Always return OK JsonObjectBuilder response = Json.createObjectBuilder() .add("status", "ok"); diff --git a/docs-web/src/main/webapp/src/app/docs/controller/settings/SettingsConfig.js b/docs-web/src/main/webapp/src/app/docs/controller/settings/SettingsConfig.js index 2da01396..733c7a28 100644 --- a/docs-web/src/main/webapp/src/app/docs/controller/settings/SettingsConfig.js +++ b/docs-web/src/main/webapp/src/app/docs/controller/settings/SettingsConfig.js @@ -21,6 +21,15 @@ angular.module('docs').controller('SettingsConfig', function($scope, $rootScope, }); }; + // Enable/disable OCR + $scope.changeOcrEnabled = function (enabled) { + Restangular.one('app').post('ocr', { + enabled: enabled + }).then(function () { + $scope.app.ocr_enabled = enabled; + }); + }; + // Fetch the current theme configuration Restangular.one('theme').get().then(function (data) { $scope.theme = data; @@ -36,7 +45,7 @@ angular.module('docs').controller('SettingsConfig', function($scope, $rootScope, $rootScope.appName = $scope.theme.name; }); }; - + // Send an image $scope.sendingImage = false; $scope.sendImage = function (type, image) { @@ -108,4 +117,4 @@ angular.module('docs').controller('SettingsConfig', function($scope, $rootScope, $scope.loadWebhooks(); }); }; -}); \ No newline at end of file +}); diff --git a/docs-web/src/main/webapp/src/locale/de.json b/docs-web/src/main/webapp/src/locale/de.json index c0cf3810..4cd94a66 100644 --- a/docs-web/src/main/webapp/src/locale/de.json +++ b/docs-web/src/main/webapp/src/locale/de.json @@ -421,6 +421,10 @@ "smtp_username": "SMTP-Benutzername", "smtp_password": "SMTP-Passwort", "smtp_updated": "SMTP-Konfiguration erfolgreich aktualisiert", + "title_ocr": "Texterkennung (OCR)", + "message_ocr": "OCR ist eine Technologie, die es ermöglicht, Text aus Bildern zu extrahieren. Es wird verwendet, um den Inhalt von Bildern zu durchsuchen und zu indizieren. Diese Funktionalität setzt voraus, dass die Tesseract auf Ihrem Server installiert ist.", + "enable_ocr": "OCR aktivieren", + "disable_ocr": "OCR deaktivieren", "webhooks": "Webhooks", "webhooks_explain": "Webhooks werden aufgerufen, wenn das angegebene Ereignis eintritt. Die angegebene URL wird mit einer JSON-Payload gepostet, die den Ereignisnamen und die ID der betreffenden Ressource enthält.", "webhook_event": "Ereignisse", diff --git a/docs-web/src/main/webapp/src/locale/en.json b/docs-web/src/main/webapp/src/locale/en.json index a6f98f0e..eb37bbe7 100644 --- a/docs-web/src/main/webapp/src/locale/en.json +++ b/docs-web/src/main/webapp/src/locale/en.json @@ -421,6 +421,10 @@ "smtp_username": "SMTP username", "smtp_password": "SMTP password", "smtp_updated": "SMTP configuration updated successfully", + "title_ocr": "Optical Character Recognition (OCR)", + "message_ocr": "OCR is a feature that extracts text from images and PDF files. This feature requires a working Tesseract installation on the server.", + "enable_ocr": "Enable OCR", + "disable_ocr": "Disable OCR", "webhooks": "Webhooks", "webhooks_explain": "Webhooks will be called when the specified event occur. The given URL will be POST-ed with a JSON payload containing the event name and the ID of the concerned resource.", "webhook_event": "Event", diff --git a/docs-web/src/main/webapp/src/partial/docs/settings.config.html b/docs-web/src/main/webapp/src/partial/docs/settings.config.html index d28a26a8..58d484bb 100644 --- a/docs-web/src/main/webapp/src/partial/docs/settings.config.html +++ b/docs-web/src/main/webapp/src/partial/docs/settings.config.html @@ -31,6 +31,17 @@ +

+ + + {{ app.ocr_enabled ? 'enabled' : 'disabled' | translate }} +

+

+
+ + +
+

@@ -196,4 +207,4 @@ -
\ No newline at end of file + diff --git a/docs-web/src/prod/resources/config.properties b/docs-web/src/prod/resources/config.properties index 37e03ad0..d182751e 100644 --- a/docs-web/src/prod/resources/config.properties +++ b/docs-web/src/prod/resources/config.properties @@ -1,3 +1,3 @@ api.current_version=${project.version} api.min_version=1.0 -db.version=30 +db.version=31 diff --git a/docs-web/src/test/java/com/sismics/docs/rest/TestAppResource.java b/docs-web/src/test/java/com/sismics/docs/rest/TestAppResource.java index 39af5318..7145b063 100644 --- a/docs-web/src/test/java/com/sismics/docs/rest/TestAppResource.java +++ b/docs-web/src/test/java/com/sismics/docs/rest/TestAppResource.java @@ -29,7 +29,7 @@ import org.junit.Test; /** * Test the app resource. - * + * * @author jtremeaux */ public class TestAppResource extends BaseJerseyTest { @@ -46,7 +46,7 @@ public class TestAppResource extends BaseJerseyTest { public void testAppResource() { // Login admin String adminToken = adminToken(); - + // Check the application info JsonObject json = target().path("/app").request() .get(JsonObject.class); @@ -58,6 +58,7 @@ public class TestAppResource extends BaseJerseyTest { Assert.assertTrue(totalMemory > 0 && totalMemory > freeMemory); Assert.assertEquals(0, json.getJsonNumber("queued_tasks").intValue()); Assert.assertFalse(json.getBoolean("guest_login")); + Assert.assertFalse(json.getBoolean("ocr_enabled")); Assert.assertEquals("eng", json.getString("default_language")); Assert.assertTrue(json.containsKey("global_storage_current")); Assert.assertTrue(json.getJsonNumber("active_user_count").longValue() > 0); @@ -67,13 +68,13 @@ public class TestAppResource extends BaseJerseyTest { .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) .post(Entity.form(new Form())); Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); - + // Clean storage response = target().path("/app/batch/clean_storage").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) .post(Entity.form(new Form())); Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); - + // Change the default language response = target().path("/app/config").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) @@ -104,7 +105,7 @@ public class TestAppResource extends BaseJerseyTest { public void testLogResource() { // Login admin String adminToken = adminToken(); - + // Check the logs (page 1) JsonObject json = target().path("/app/log") .queryParam("level", "DEBUG") @@ -116,7 +117,7 @@ public class TestAppResource extends BaseJerseyTest { Long date1 = logs.getJsonObject(0).getJsonNumber("date").longValue(); Long date2 = logs.getJsonObject(9).getJsonNumber("date").longValue(); Assert.assertTrue(date1 >= date2); - + // Check the logs (page 2) json = target().path("/app/log") .queryParam("offset", "10") @@ -194,7 +195,7 @@ public class TestAppResource extends BaseJerseyTest { target().path("/document/list").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, guestToken) .get(JsonObject.class); - + // Disable guest login (clean up state) target().path("/app/guest_login").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) @@ -202,6 +203,37 @@ public class TestAppResource extends BaseJerseyTest { .param("enabled", "false")), JsonObject.class); } + /** + * Test the ocr setting + */ + @Test + public void testOcrSetting() { + // Login admin + String adminToken = adminToken(); + + // Get OCR configuration + JsonObject json = target().path("/app/ocr").request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) + .get(JsonObject.class); + if (!configInboxChanged) { + Assert.assertFalse(json.getBoolean("enabled")); + } + + // Change OCR configuration + target().path("/app/ocr").request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) + .post(Entity.form(new Form() + .param("enabled", "true") + ), JsonObject.class); + configInboxChanged = true; + + // Get OCR configuration + json = target().path("/app/ocr").request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) + .get(JsonObject.class); + Assert.assertTrue(json.getBoolean("enabled")); + } + /** * Test SMTP configuration changes. */ diff --git a/scripts/dev_setup.sh b/scripts/dev_setup.sh new file mode 100755 index 00000000..ae82d458 --- /dev/null +++ b/scripts/dev_setup.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +install_macos_dependencies() { + brew install openjdk@11 + export JAVA_HOME=/usr/local/opt/openjdk@11 + brew install maven + brew install node + npm install -g grunt-cli + brew install tesseract + brew install ffmpeg + brew install mediainfo +} + +install_ubuntu_debian_dependencies() { + sudo apt update + sudo apt install -y openjdk-11-jdk maven nodejs npm tesseract-ocr ffmpeg mediainfo + sudo npm install -g grunt-cli +} + +install_fedora_dependencies() { + sudo dnf install -y java-11-openjdk-devel maven nodejs npm tesseract ffmpeg mediainfo + sudo npm install -g grunt-cli +} + +echo "📥 Installing dependencies" +if [[ "$OSTYPE" == "darwin"* ]]; then + install_macos_dependencies +elif [[ -f /etc/debian_version ]]; then + install_ubuntu_debian_dependencies +elif [[ -f /etc/fedora-release ]]; then + install_fedora_dependencies +else + echo "❔ Unsupported OS. Feel free to contribute!" + exit 1 +fi + +echo "✅ Dependencies installed." + +mvn clean -DskipTests install + +echo "You can start the server with 'mvn jetty:run' and then access it at http://localhost:8080/docs-web/src/"