1
0
mirror of https://github.com/sismics/docs.git synced 2025-12-14 10:16:21 +00:00

229 Commits
v1.5 ... v1.7

Author SHA1 Message Date
Benjamin Gamard
7aa9fa4646 v1.7 2019-05-21 15:55:41 +02:00
Benjamin Gamard
82d788c8d3 Closes #300: adjust tests 2019-05-21 15:53:54 +02:00
Benjamin Gamard
ab8176efcb #300: custom metadata fields: UI read/write 2019-05-21 15:44:23 +02:00
Benjamin Gamard
b4c3e7a928 update Docker image infos 2019-05-20 15:18:08 +02:00
Benjamin Gamard
2db263fb68 #300: custom metadata fields: API read 2019-05-20 15:08:16 +02:00
Benjamin Gamard
5fd4d37972 #300: custom metadata fields: API write 2019-05-17 16:00:03 +02:00
Benjamin Gamard
9b1dbf351a #300: custom metadata fields: UI admin 2019-05-15 14:15:55 +02:00
Benjamin Gamard
4c7c058e0d Merge remote-tracking branch 'origin/master' 2019-05-15 13:34:21 +02:00
Benjamin Gamard
f8dc08b02b #300: custom metadata fields: API admin 2019-05-15 13:34:01 +02:00
yosef langer
0e6bc3ce54 Hebrew Language Support (#320) 2019-05-11 16:26:01 +02:00
Benjamin Gamard
fcb018406d Merge remote-tracking branch 'origin/master' 2019-05-07 14:38:15 +02:00
Benjamin Gamard
40756a5e4b Fix for https://bugs.openjdk.java.net/browse/JDK-8216039 2019-05-07 14:38:05 +02:00
Benjamin Gamard
61b12bdebd Closes #309: store onboarding status server side 2019-05-06 18:12:44 +02:00
Benjamin Gamard
8b1c41ae1e Closes #305: exclude tags from search 2019-05-03 15:35:47 +02:00
Benjamin Gamard
d654564f6b Closes #318: Sort file list on drag & drop before sending to server 2019-05-03 15:09:43 +02:00
Benjamin Gamard
8bd22ebafa Closes #301: download link in overflow menu 2019-05-03 14:09:35 +02:00
Benjamin Gamard
647e66d57b update api doc 2019-05-03 13:55:18 +02:00
Benjamin Gamard
67c8ac1aa3 Closes #307: log workflow create/delete in document logs 2019-05-03 13:54:10 +02:00
Benjamin Gamard
f336c7ae53 Closes #313: remove administrators from ACL targets search 2019-05-03 13:27:23 +02:00
Benjamin Gamard
9ea1dad62d Closes #296: firefox hack to prevent file open on drag & drop 2019-05-03 13:19:21 +02:00
Benjamin Gamard
58bc374e64 Closes #306: Prevent deleting/renaming users/groups used in route models 2019-05-02 16:19:50 +02:00
Benjamin Gamard
cea0d4887d Closes #302: increase version history modal size 2019-05-02 11:19:08 +02:00
Benjamin Gamard
d5e73ecd8b Closes #308: update user quota when deleting a document 2019-05-02 11:13:16 +02:00
Benjamin Gamard
2235a0498b Merge remote-tracking branch 'origin/master' 2019-05-02 10:54:28 +02:00
Benjamin Gamard
3f9b92831c Closes #312: fix file importer description 2019-05-02 10:54:19 +02:00
Benjamin Gamard
5680750c82 Merge remote-tracking branch 'origin/master' 2019-05-01 17:36:27 +02:00
Benjamin Gamard
298e3efe49 Spanish translation 2019-05-01 17:35:35 +02:00
Burak Sormageç
7b2bd6f9eb Turkish Language Support (#304)
Add turkish language support
2019-03-07 20:34:02 +01:00
Benjamin Gamard
d935e07990 pgsql compatibility 2019-02-12 14:55:12 +01:00
Benjamin Gamard
868a74c184 sismics docs -> teedy 2019-02-12 13:57:54 +01:00
Benjamin Gamard
a86af9736b fix test 2019-02-07 16:55:58 +01:00
Benjamin Gamard
8bd4d27d2f increase h2 lock timeout 2019-02-07 15:17:38 +01:00
Benjamin Gamard
94951c59f3 quick test 2019-02-07 15:08:17 +01:00
Benjamin Gamard
e39c83a5a6 update the file ID on a document with a native query 2019-02-07 14:50:47 +01:00
Benjamin Gamard
516a5f03e3 Merge remote-tracking branch 'origin/sismics_prod' into sismics_prod 2019-02-05 17:46:32 +01:00
Benjamin Gamard
992150804a #236: onboarding localization 2019-02-05 17:45:07 +01:00
Benjamin Gamard
e905aa76a0 fr translations 2019-02-05 17:38:58 +01:00
Benjamin Gamard
940b365447 Closes #236: onboarding wizard 2019-02-05 17:32:47 +01:00
Benjamin Gamard
d4d1c35264 Closes #260: Re-index all if the Lucene directory is unusable 2019-02-05 15:37:46 +01:00
Benjamin Gamard
5b818c8258 Closes #277: display files in list 2019-02-05 14:14:06 +01:00
Benjamin Gamard
dedfae7b33 update french translation 2019-02-04 17:46:21 +01:00
Benjamin Gamard
aa91a7fe24 Closes #291: feedback modal only for admin users 2019-02-04 16:51:29 +01:00
Benjamin Gamard
822a4ae776 Closes #268: test TOTP after activation 2019-02-04 16:47:43 +01:00
Benjamin Gamard
c6eb1c813c #268: endpoint to test TOTP code 2019-02-01 14:47:18 +01:00
Mario Voigt
0d50676586 Added translations strings for german lang, docs-android and docs-web (#294) 2019-02-01 11:44:05 +01:00
Benjamin Gamard
a2a3297986 Closes #256: display version history 2019-01-31 15:44:58 +01:00
Benjamin Gamard
8bdab73ae9 #256: list versions of a file (API) 2019-01-30 21:14:07 +01:00
Benjamin Gamard
4469bb7bee #256: upload a new version UI 2019-01-30 18:15:00 +01:00
Benjamin Gamard
b2dc460b4b Closes #269: translate audit log 2019-01-30 17:38:30 +01:00
Benjamin Gamard
3b281a8c3f Closes #270: add notification channel for android > 26 2019-01-30 16:53:07 +01:00
Benjamin Gamard
fe40a0a677 Closes #168: UI for disabling TOTP as admin 2019-01-24 20:20:03 +01:00
Benjamin Gamard
b8c2bd3564 Closes #267: spaces not allowed in group names 2019-01-24 20:03:10 +01:00
Benjamin Gamard
be236313e9 Merge branch 'master' into sismics_prod 2019-01-24 17:38:00 +01:00
Benjamin Gamard
7a285d11a5 Closes #270: missing permission for android 28 2019-01-24 17:34:44 +01:00
Benjamin Gamard
6e56a0f568 #289: better search parsing (including wildcard and fuzzy) 2019-01-24 17:26:46 +01:00
Mario Voigt
10d5c4334b Fixed some spelling mistakes + added translated properties for german… (#266)
german fixes
2019-01-24 11:53:45 +01:00
Freekers
8a5e90e562 Added OCR support for Dutch (Nederlands) (#286)
Dutch support for OCR
2019-01-24 11:52:44 +01:00
Benjamin Gamard
c7c7badaf0 fix travis build for PR 2019-01-24 11:49:45 +01:00
Benjamin Gamard
183e86aad6 fix travis build for PR 2019-01-24 11:48:42 +01:00
Mario Voigt
5fbbcfc888 Open jdk11 (#287)
Java 11 compat
2019-01-24 11:38:17 +01:00
Benjamin Gamard
5169deb005 fix workflow form
(cherry picked from commit db4f5f9011)
2018-12-05 17:24:07 +01:00
Benjamin Gamard
98fa89bd80 css fix
(cherry picked from commit 33aaf8afd2)
2018-12-05 17:24:02 +01:00
Benjamin Gamard
db4f5f9011 fix workflow form 2018-12-05 17:23:19 +01:00
Benjamin Gamard
33aaf8afd2 css fix 2018-11-28 13:09:20 +01:00
Benjamin Gamard
3902d6361e #256: versioning API 2018-11-23 14:54:11 +01:00
Benjamin Gamard
d8d5249a23 Closes #257: admin users can see all logs 2018-11-09 14:49:34 +01:00
Benjamin Gamard
42828efa19 #168: disable TOTP as admin for a specific user 2018-11-07 13:42:43 +01:00
Benjamin Gamard
a75b40bbfb fix tests 2018-11-01 16:40:55 +01:00
Benjamin Gamard
4ebf621e11 #254: no 404 if no main file for a document 2018-11-01 16:29:22 +01:00
Benjamin Gamard
cee82f39c2 #254: display documents in grid + concept of main file 2018-11-01 16:27:35 +01:00
Benjamin Gamard
eb9e0e0543 Closes #219: button to force full reindexing 2018-10-29 19:01:32 +01:00
Benjamin Gamard
35c3ee023b Closes #222: full reindexing scalable + concurrent Lucene merges 2018-10-29 18:32:39 +01:00
Benjamin Gamard
2134f116da #252: fix route test 2018-10-28 17:26:34 +01:00
Benjamin Gamard
feb3d15968 #252: info when no route model available 2018-10-28 17:18:50 +01:00
Benjamin Gamard
4910dfd527 Closes #252: route model permissions 2018-10-28 17:03:21 +01:00
Benjamin Gamard
dc5a157dad fix dockerfile 2018-10-21 14:29:26 +02:00
Benjamin Gamard
bf8411cc24 fix dockerfile 2018-10-21 13:04:59 +02:00
Benjamin Gamard
a9648f803c #247: upgrade dependencies 2018-10-21 11:54:19 +02:00
Benjamin Gamard
b2b4eed4fa #241: update README.md 2018-10-21 01:24:59 +02:00
Benjamin Gamard
7665eb6bf2 #241: update API doc 2018-10-21 01:23:45 +02:00
Benjamin Gamard
40c1ff0e1a #241: test highlights and suggestions 2018-10-21 01:22:31 +02:00
Benjamin Gamard
6ceb1f6c02 Closes #241: search highlighting 2018-10-20 21:16:06 +02:00
Benjamin Gamard
8c37af6207 #241: search suggestions 2018-10-19 19:13:39 +02:00
Benjamin Gamard
7baf5e44fd Closes #237: prevent tag circular reference 2018-10-19 17:04:49 +02:00
Benjamin Gamard
8fff672d2f Closes #244: feedback when saving inbox scanning configuration 2018-10-19 16:10:39 +02:00
Benjamin Gamard
d4fe719a2c hide toolbar and fab on doc list scroll 2018-10-19 13:34:45 +02:00
Benjamin Gamard
108d5ae830 Android: target api 28 2018-10-18 23:57:08 +02:00
Benjamin Gamard
867c3207c5 #246: CURL examples for API authentication 2018-10-18 22:22:40 +02:00
Benjamin Gamard
0c257b763d Closes #245: admin group undeletable + admin can see all 2018-10-18 18:57:06 +02:00
Benjamin Gamard
58af2b00cb fix api doc 2018-10-18 11:32:42 +02:00
Benjamin Gamard
1d78ce4b72 Merge remote-tracking branch 'origin/master' 2018-10-17 16:51:48 +02:00
Benjamin Gamard
26d9d826a3 fix permission query 2018-10-17 16:51:07 +02:00
Benjamin Gamard
bb9957be24 Update README.md 2018-10-17 11:40:42 +02:00
Benjamin Gamard
0dce279fd0 Closes #243: webhooks UI 2018-10-17 11:31:49 +02:00
Benjamin Gamard
884239bc26 #243: call webhooks 2018-10-16 22:49:29 +02:00
Benjamin Gamard
dfdd5f8d20 #243: webhook CRUD 2018-10-16 19:04:04 +02:00
Benjamin Gamard
b5f0612e78 add a new MP4 magic number 2018-07-25 13:11:51 +02:00
James Smith
dc044e684d Added required environment variable to startup (#240)
Added DOCS_BASE_URL to the environment variables in the Docker documentation
2018-07-18 11:35:17 +02:00
Benjamin Gamard
8f1dddb8a3 update fr/de translations 2018-05-30 13:53:04 +02:00
Benjamin Gamard
763f91fd4c Closes #220: background tasks count 2018-04-22 11:12:09 +02:00
Benjamin Gamard
dd1c06013b #230: Add a check all/none button on "quick upload" 2018-04-11 16:17:37 +02:00
Benjamin Gamard
748659e78e create a single index writer for Lucene + Closes #231 2018-04-11 12:38:03 +02:00
Benjamin Gamard
b265b8b1e0 process events concurrently 2018-04-11 11:27:55 +02:00
Benjamin Gamard
7b3c0915d8 fix tests 2018-04-09 13:11:33 +02:00
Benjamin Gamard
d0335b6b16 Closes #206: action to process all files 2018-04-09 13:02:39 +02:00
Benjamin Gamard
6d35020840 Closes #227: fix searching by tag 2018-04-06 23:37:54 +02:00
Benjamin Gamard
cedd4b47b3 #227: revert waiting for better solution 2018-04-06 13:18:47 +02:00
Benjamin Gamard
45a672ab0d Closes #227: AND tags search criteria 2018-04-06 12:58:18 +02:00
Benjamin Gamard
6798e01f49 Closes #224: scan for indexing handler 2018-04-05 19:19:22 +02:00
Benjamin Gamard
c0678e9a90 #221: increase ThreadPoolExecutor size to 8 threads 2018-04-05 12:45:04 +02:00
Benjamin Gamard
156e67bc52 upgrade opensagres.xdocreport 2018-04-03 20:40:24 +02:00
Benjamin Gamard
9b1456e1a7 Closes #223: open document after creation 2018-04-02 16:39:38 +02:00
Benjamin Gamard
f9b61546ab richer acl event 2018-04-02 11:09:21 +02:00
Benjamin Gamard
1b3a33104a richer acl event + upgrade json lib 2018-04-02 10:47:43 +02:00
Benjamin Gamard
d819c05669 Fire some more indexing events 2018-04-01 23:06:45 +02:00
Benjamin Gamard
68729e3b54 more tests + 1M tested 2018-04-01 21:00:26 +02:00
Benjamin Gamard
90a3d7aa68 add missing index 2018-03-31 22:53:49 +02:00
Benjamin Gamard
b3349176d9 fix reindexing on application startup 2018-03-31 13:24:25 +02:00
Benjamin Gamard
e72dab2a6e indexing in transactional context 2018-03-31 13:22:59 +02:00
Benjamin Gamard
b54debe2e5 handle index rebuilding 2018-03-31 11:01:27 +02:00
Benjamin Gamard
95ce4f0dc0 indexing in transactional context 2018-03-30 21:36:44 +02:00
Benjamin Gamard
c9ba182a2e search acl without type 2018-03-30 21:22:28 +02:00
Benjamin Gamard
716954aa9a indexing in transactional context 2018-03-30 21:16:13 +02:00
Benjamin Gamard
5c5a3f614f indexing in transactional context 2018-03-30 14:45:25 +02:00
Benjamin Gamard
229d845a42 missing document updated events 2018-03-30 12:37:10 +02:00
Benjamin Gamard
d1a8fa38b0 Refactor documents and files indexing 2018-03-29 17:59:48 +02:00
Benjamin Gamard
899f13cb35 Closes #201: reprocess file manually 2018-03-29 11:34:25 +02:00
Benjamin Gamard
0409c2ef79 Closes #210: choose a tag applied on documents 2018-03-28 16:48:16 +02:00
Benjamin Gamard
83b065180f typo 2018-03-27 23:11:06 +02:00
Benjamin Gamard
34a003d0e5 typo 2018-03-27 19:20:12 +02:00
Benjamin Gamard
7b89bb449d set the admin email by env var 2018-03-27 18:58:08 +02:00
Benjamin Gamard
4aca4ad495 cleanup user creation 2018-03-26 22:28:22 +02:00
Benjamin Gamard
99d44f2a92 extensible authentication system 2018-03-26 22:07:26 +02:00
Benjamin Gamard
c9606f98d3 scan classpath for format handlers 2018-03-26 20:51:53 +02:00
Benjamin Gamard
af217b4831 fix fulltext search with advanced form 2018-03-26 19:10:28 +02:00
Benjamin Gamard
f45f2d5df9 fix content disposition header 2018-03-26 17:15:36 +02:00
Benjamin Gamard
c8c4208bb4 dead code 2018-03-26 17:00:31 +02:00
Benjamin Gamard
dc8b8ae6c4 #210: create documents with file importer 2018-03-26 17:00:14 +02:00
Benjamin Gamard
7aa4a1bf82 fix ui for readonly documents 2018-03-24 21:46:23 +01:00
Benjamin Gamard
e2548ef6b1 Closes #190: lightweight text editor on description field 2018-03-24 21:42:34 +01:00
Benjamin Gamard
abde9b7897 Closes #200: use tesseract command line 2018-03-23 22:12:55 +01:00
Benjamin Gamard
be1c2a7b90 Closes #208: display file content + fix filename encoding 2018-03-23 16:41:02 +01:00
Benjamin Gamard
55a4bb7621 #208: rename files 2018-03-23 12:52:42 +01:00
Benjamin Gamard
37ca5ff84e #208: overflow menu on files 2018-03-23 11:29:10 +01:00
Benjamin Gamard
d2256eabfa Closes #216: redirect to login if not connected 2018-03-23 09:53:18 +01:00
Benjamin Gamard
cbb8d4e1b6 Closes #216: redirect to login if not connected 2018-03-22 16:11:25 +01:00
Benjamin Gamard
785ad7f3a1 Closes #216: redirect to login if not connected 2018-03-22 15:05:24 +01:00
Benjamin Gamard
5d9b87dace Closes #114: fix query for h2 2018-03-21 22:44:23 +01:00
Benjamin Gamard
feb5484cf6 Closes #114: Better PostgreSQL performance 2018-03-21 21:48:19 +01:00
Benjamin Gamard
3821a15f9d Closes #114: PostgreSQL compatibility 2018-03-21 18:58:50 +01:00
Benjamin Gamard
e5f85c931c stop polling for errors 2018-03-21 12:19:44 +01:00
Benjamin Gamard
6c99de1e89 fix tests 2018-03-20 23:15:46 +01:00
Benjamin Gamard
3613f6f8de Closes #215: handle pptx files 2018-03-20 22:46:56 +01:00
Benjamin Gamard
945e619d55 java 8 minimum 2018-03-18 22:23:29 +01:00
Benjamin Gamard
5220b13e7d Java 8, Tesseract 4, Ubuntu (#214)
Java 8, Tesseract 4, Ubuntu 18.04
2018-03-18 22:21:31 +01:00
Benjamin Gamard
7ea8d0c0f7 Closes #182: format handling refactoring 2018-03-18 16:16:32 +01:00
Benjamin Gamard
996585d7ac Closes #212: ui issue with parent tags 2018-03-18 11:59:33 +01:00
tedstriker
ce115eadbb Fixed typos, grammar and meaning in German translation (#211)
Fix german translation
2018-03-18 11:37:59 +01:00
Benjamin Gamard
aebdc9e208 Order tags in navigation 2018-03-15 12:43:44 +01:00
Benjamin Gamard
b0bceefc0e Closes #174, closes #176: add a tag from the navigation 2018-03-15 12:28:55 +01:00
Benjamin Gamard
bf37c5cb51 #176: Default tag when creating a document with a tag opened 2018-03-15 11:55:03 +01:00
Benjamin Gamard
16215dde3b #207: new temporary thumbnail 2018-03-14 20:39:32 +01:00
Benjamin Gamard
2ac10e8127 #182: do not cache the temporary thumbnail 2018-03-14 19:01:28 +01:00
Benjamin Gamard
dcb924abac #182: fix tests 2018-03-14 18:31:06 +01:00
Benjamin Gamard
94e18146fd #182: fix thumbnail for orphan files 2018-03-14 18:12:19 +01:00
Benjamin Gamard
2a619849f4 #182: thumbnail generation asynchronous 2018-03-14 15:13:09 +01:00
Benjamin Gamard
1e57ee5fb3 clean up 2018-03-14 14:53:41 +01:00
Benjamin Gamard
db721a9d10 Closes #198: show hierarchy in tag screen 2018-03-14 13:45:37 +01:00
Benjamin Gamard
40951e8da0 fix tests 2018-03-13 23:37:26 +01:00
Benjamin Gamard
f44b4bb0e0 #201: file processing indicator 2018-03-13 23:32:05 +01:00
Benjamin Gamard
b330d54ca2 Closes #199: manifest.json 2018-03-13 20:07:36 +01:00
Benjamin Gamard
2678ff4477 Closes #205: action: remote tag 2018-03-13 14:09:39 +01:00
Benjamin Gamard
995e45d28f #202 Actions on route transitions (#204)
#202 Actions on route transition
2018-03-12 22:55:04 +01:00
Benjamin Gamard
9ca27d7c12 Merge pull request #203 from schemen/master
Update some German translations
2018-03-12 16:53:09 +01:00
Schemen
4183dae458 Update some German translations 2018-03-12 16:12:29 +01:00
Benjamin Gamard
5426be9fa0 Closes #193: last updated date (db + search + ui) 2018-03-12 14:15:00 +01:00
Benjamin Gamard
9d8034e010 clickable labels 2018-03-12 11:43:11 +01:00
Benjamin Gamard
647ad841df #186: ocr pdf if it contains no text 2018-03-12 11:12:48 +01:00
Benjamin Gamard
a66a1e6f8e i18n cache killer 2018-03-11 00:02:00 +01:00
Benjamin Gamard
ebfd860458 more tag tests 2018-03-10 17:58:37 +01:00
Benjamin Gamard
740476460f #176: show children tag 2018-03-10 17:50:24 +01:00
Benjamin Gamard
2b6a14a348 remove parent tag after tag deletion 2018-03-10 17:04:20 +01:00
Benjamin Gamard
77311f42cd #176: navigation by tag 2018-03-10 16:36:14 +01:00
Benjamin Gamard
a0e89103af cleanup duplicate code 2018-03-10 11:24:06 +01:00
Benjamin Gamard
5cdbe9338b non crashing pdf font 2018-03-10 10:44:40 +01:00
Benjamin Gamard
f7b84238df update german translation 2018-03-10 10:30:15 +01:00
Benjamin Gamard
8b039c61ed trim crappy characters 2018-03-09 22:01:12 +01:00
Benjamin Gamard
f227335e14 update German translation 2018-03-09 21:54:33 +01:00
Benjamin Gamard
ce7a8590db cache kill + hack to disable firefox autofill 2018-03-09 21:47:11 +01:00
Benjamin Gamard
d497fa8ed7 recreate a new imap session for each sync 2018-03-09 19:55:26 +01:00
Benjamin Gamard
6b940c4366 fix angular scope issue + disable LastPass in some forms 2018-03-09 19:20:20 +01:00
Benjamin Gamard
f167e8ea0a fix zip export 2018-03-09 17:28:08 +01:00
Benjamin Gamard
de703531f6 fix for pdf generation and \r\n in description 2018-03-09 17:11:50 +01:00
Benjamin Gamard
c72f9fbdb1 #176: search by tags and all associated children 2018-03-09 16:54:30 +01:00
Benjamin Gamard
09eaf18632 fix German translation (plural) 2018-03-09 16:49:32 +01:00
Benjamin Gamard
5cee20163d update German translation 2018-03-09 16:44:50 +01:00
Benjamin Gamard
63c7e9710b Closes #194: PDF viewer 2018-03-09 11:18:19 +01:00
Benjamin Gamard
6f27b9c13f update german translation 2018-03-09 00:01:08 +01:00
Benjamin Gamard
76e68c09d8 fix icons 2018-03-08 20:00:47 +01:00
Benjamin Gamard
aea389cd3d add jpeg 2000 imageIO plugin 2018-03-08 17:25:58 +01:00
Benjamin Gamard
9ce18b128e german translation 2018-03-08 14:15:19 +01:00
Benjamin Gamard
a7c954c6bc Workflow assigned to me on home + Closes #170 + glyphicon -> fa 2018-03-07 16:31:37 +01:00
Benjamin Gamard
a0880c4a16 api doc badge 2018-03-06 13:34:13 +01:00
Benjamin Gamard
2c90df2c2d Closes #192: workflow active info + search criteria 2018-03-06 12:27:45 +01:00
Benjamin Gamard
71f15e1736 cleanup auditlog css 2018-03-05 15:59:00 +01:00
Benjamin Gamard
0b14ab5032 #185: prepare home design for widgets 2018-03-05 15:44:14 +01:00
Benjamin Gamard
2771e56357 Closes #188: Quiet mode for the file importer 2018-03-05 14:36:30 +01:00
Benjamin Gamard
1479b818ea Closes #189: Ignore folderclosedexception on imap scanning 2018-03-05 14:11:32 +01:00
Benjamin Gamard
4f6de892b5 #189: fire async event after transactionutil.handle 2018-03-05 11:57:56 +01:00
Benjamin Gamard
e540260377 Closes #187: requirements on README.md 2018-03-05 11:06:26 +01:00
Benjamin Gamard
f3f21bbf73 inbox service: fix javax.mail duplicate jar with jetty 2018-03-04 16:43:40 +01:00
Benjamin Gamard
44a1389dd7 inbox service: catch all errors to avoid service crash 2018-03-04 16:10:32 +01:00
Benjamin Gamard
1b05261a97 inbox service imap(s) timeouts 2018-03-03 17:33:57 +01:00
Benjamin Gamard
d55334739c Merge remote-tracking branch 'origin/master' 2018-03-03 14:39:33 +01:00
Benjamin Gamard
c511ed380e inbox service logs 2018-03-03 14:39:12 +01:00
Benjamin Gamard
b59ecf0370 Create CODE_OF_CONDUCT.md 2018-03-02 22:25:48 +01:00
Benjamin Gamard
65b038afcd ffmpeg less chatty 2018-03-02 20:36:39 +01:00
Benjamin Gamard
5fbde0dca0 fix docker image 2018-03-02 20:13:55 +01:00
Benjamin Gamard
ab69e502da fix travis build 2018-03-02 19:43:28 +01:00
Benjamin Gamard
52065dda6b fix travis build 2018-03-02 19:28:59 +01:00
Benjamin Gamard
7cd8e48145 fix travis build 2018-03-02 19:26:34 +01:00
Benjamin Gamard
2a7f143bbf fix travis build 2018-03-02 19:22:11 +01:00
Benjamin Gamard
4a676b01e1 Closes #140: video file support 2018-03-02 19:05:20 +01:00
Benjamin Gamard
18c9833104 Cache clear 2018-03-01 17:35:29 +01:00
Benjamin Gamard
7e2787704b responsive fix 2018-03-01 17:13:46 +01:00
Benjamin Gamard
b13c2ccd32 login design refresh 2018-03-01 17:10:48 +01:00
Benjamin Gamard
4795d8f48c add logs to inbox scanning 2018-03-01 16:28:57 +01:00
Benjamin Gamard
808a06b0af bulk file importer infos 2018-03-01 15:14:46 +01:00
Benjamin Gamard
70a42afab8 next dev iteration 2018-03-01 14:34:09 +01:00
381 changed files with 21808 additions and 6017 deletions

3
.gitignore vendored
View File

@@ -11,3 +11,6 @@
*.iml
node_modules
import_test
docs-importer-linux
docs-importer-macos
docs-importer-win.exe

View File

@@ -2,22 +2,24 @@ sudo: required
dist: trusty
language: java
before_install:
- sudo add-apt-repository -y ppa:mc3man/trusty-media
- sudo apt-get -qq update
- sudo apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-ita tesseract-ocr-kor tesseract-ocr-rus tesseract-ocr-ukr tesseract-ocr-spa tesseract-ocr-ara tesseract-ocr-hin tesseract-ocr-deu tesseract-ocr-pol tesseract-ocr-jpn tesseract-ocr-por tesseract-ocr-tha tesseract-ocr-jpn tesseract-ocr-chi-sim tesseract-ocr-chi-tra
- sudo apt-get -y -q install ffmpeg mediainfo tesseract-ocr tesseract-ocr-fra tesseract-ocr-ita tesseract-ocr-kor tesseract-ocr-rus tesseract-ocr-ukr tesseract-ocr-spa tesseract-ocr-ara tesseract-ocr-hin tesseract-ocr-deu tesseract-ocr-pol tesseract-ocr-jpn tesseract-ocr-por tesseract-ocr-tha tesseract-ocr-jpn tesseract-ocr-chi-sim tesseract-ocr-chi-tra tesseract-ocr-nld tesseract-ocr-tur tesseract-ocr-heb
- sudo apt-get -y -q install haveged && sudo service haveged start
after_success:
- mvn -Pprod -DskipTests clean install
- docker login -u $DOCKER_USER -p $DOCKER_PASS
- export REPO=sismics/docs
- export TAG=`if [ "$TRAVIS_BRANCH" == "master" ]; then echo "latest"; else echo $TRAVIS_BRANCH ; fi`
- docker build -f Dockerfile -t $REPO:$COMMIT .
- docker tag $REPO:$COMMIT $REPO:$TAG
- docker tag $REPO:$COMMIT $REPO:travis-$TRAVIS_BUILD_NUMBER
- docker push $REPO
- |
if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
mvn -Pprod -DskipTests clean install
docker login -u $DOCKER_USER -p $DOCKER_PASS
export REPO=sismics/docs
export TAG=`if [ "$TRAVIS_BRANCH" == "master" ]; then echo "latest"; else echo $TRAVIS_BRANCH ; fi`
docker build -f Dockerfile -t $REPO:$COMMIT .
docker tag $REPO:$COMMIT $REPO:$TAG
docker tag $REPO:$COMMIT $REPO:travis-$TRAVIS_BUILD_NUMBER
docker push $REPO
fi
env:
global:
- TESSDATA_PREFIX=/usr/share/tesseract-ocr
- LC_NUMERIC=C
- secure: LRGpjWORb0qy6VuypZjTAfA8uRHlFUMTwb77cenS9PPRBxuSnctC531asS9Xg3DqC5nsRxBBprgfCKotn5S8nBSD1ceHh84NASyzLSBft3xSMbg7f/2i7MQ+pGVwLncusBU6E/drnMFwZBleo+9M8Tf96axY5zuUp90MUTpSgt0=
- secure: bCDDR6+I7PmSkuTYZv1HF/z98ANX/SFEESUCqxVmV5Gs0zFC0vQXaPJQ2xaJNRop1HZBFMZLeMMPleb0iOs985smpvK2F6Rbop9Tu+Vyo0uKqv9tbZ7F8Nfgnv9suHKZlL84FNeUQZJX6vsFIYPEJ/r7K5P/M0PdUy++fEwxEhU=
- secure: ewXnzbkgCIHpDWtaWGMa1OYZJ/ki99zcIl4jcDPIC0eB3njX/WgfcC6i0Ke9mLqDqwXarWJ6helm22sNh+xtQiz6isfBtBX+novfRt9AANrBe3koCMUemMDy7oh5VflBaFNP0DVb8LSCnwf6dx6ZB5E9EB8knvk40quc/cXpGjY=

46
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@sismicsdocs.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -1,11 +1,11 @@
FROM sismics/jetty:9.2.20-jdk7
FROM sismics/ubuntu-jetty:9.4.12
MAINTAINER b.gamard@sismics.com
RUN apt-get update && apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-ita tesseract-ocr-kor tesseract-ocr-rus tesseract-ocr-ukr tesseract-ocr-spa tesseract-ocr-ara tesseract-ocr-hin tesseract-ocr-deu tesseract-ocr-pol tesseract-ocr-jpn tesseract-ocr-por tesseract-ocr-tha tesseract-ocr-jpn tesseract-ocr-chi-sim tesseract-ocr-chi-tra && \
RUN apt-get update && apt-get -y -q install ffmpeg mediainfo tesseract-ocr tesseract-ocr-fra tesseract-ocr-ita tesseract-ocr-kor tesseract-ocr-rus tesseract-ocr-ukr tesseract-ocr-spa tesseract-ocr-ara tesseract-ocr-hin tesseract-ocr-deu tesseract-ocr-pol tesseract-ocr-jpn tesseract-ocr-por tesseract-ocr-tha tesseract-ocr-jpn tesseract-ocr-chi-sim tesseract-ocr-chi-tra tesseract-ocr-nld tesseract-ocr-tur tesseract-ocr-heb && \
apt-get clean && rm -rf /var/lib/apt/lists/*
ENV TESSDATA_PREFIX /usr/share/tesseract-ocr
ENV LC_NUMERIC C
# Remove the embedded javax.mail jar from Jetty
RUN rm -f /opt/jetty/lib/mail/javax.mail.glassfish-*.jar
ADD docs.xml /opt/jetty/webapps/docs.xml
ADD docs-web/target/docs-web-*.war /opt/jetty/webapps/docs.war

View File

@@ -1,25 +1,27 @@
<h3 align="center">
<img src="https://www.sismicsdocs.com/img/github-title.png" alt="Sismics Docs" width=500 />
<img src="https://teedy.io/img/github-title.png" alt="Teedy" width=500 />
</h3>
[![Twitter: @sismicsdocs](https://img.shields.io/badge/contact-@sismicsdocs-blue.svg?style=flat)](https://twitter.com/sismicsdocs)
[![Twitter: @teedyio](https://img.shields.io/badge/contact-@teedyio-blue.svg?style=flat)](https://twitter.com/teedyio)
[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-blue.svg)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
[![Build Status](https://secure.travis-ci.org/sismics/docs.png)](http://travis-ci.org/sismics/docs)
Docs is an open source, lightweight document management system for individuals and businesses.
Teedy is an open source, lightweight document management system for individuals and businesses.
**Discuss it on [Product Hunt](https://www.producthunt.com/posts/sismics-docs) 🦄**
<hr />
<h2 align="center">
We just launched a Cloud version of Sismics Docs! Head to <a href="https://www.sismicsdocs.com/">sismicsdocs.com</a> for more informations
Sismics Docs is now called Teedy! You can still find our cloud and support offer on <a href="https://teedy.io">teedy.io</a>
</h2>
<hr />
![New!](https://www.sismicsdocs.com/img/laptop-demo.png)
![New!](https://teedy.io/img/laptop-demo.png?20180301)
Demo
----
A demo is available at [demo.sismicsdocs.com](https://demo.sismicsdocs.com)
A demo is available at [demo.teedy.io](https://demo.teedy.io)
- Guest login is enabled with read access on all documents
- "admin" login with "admin" password
- "demo" login with "password" password
@@ -29,15 +31,18 @@ Features
- Responsive user interface
- Optical character recognition
- Support image, PDF, ODT and DOCX files
- Flexible search engine
- Support image, PDF, ODT, DOCX, PPTX files
- Video file support
- Flexible search engine with suggestions and highlighting
- Full text search in all supported files
- All [Dublin Core](http://dublincore.org/) metadata
- Custom user-defined metadata ![New!](https://www.sismics.com/public/img/new.png)
- Workflow system ![New!](https://www.sismics.com/public/img/new.png)
- 256-bit AES encryption of stored files
- File versioning ![New!](https://www.sismics.com/public/img/new.png)
- Tag system with nesting
- Import document from email (EML format) ![New!](https://www.sismics.com/public/img/new.png)
- Automatic inbox scanning and importing ![New!](https://www.sismics.com/public/img/new.png)
- Import document from email (EML format)
- Automatic inbox scanning and importing
- User/group permission system
- 2-factor authentication
- Hierarchical groups
@@ -46,31 +51,44 @@ Features
- Storage quota per user
- Document sharing by URL
- RESTful Web API
- Webhooks to trigger external service
- Fully featured Android client
- [Mass files importer](https://github.com/sismics/docs/tree/master/docs-importer) (single or scan mode) ![New!](https://www.sismics.com/public/img/new.png)
- Tested to 100k documents
Download
--------
The latest release is downloadable here: <https://github.com/sismics/docs/releases> in WAR format.
You will need a Java webapp server to run it, like [Jetty](http://eclipse.org/jetty/) or [Tomcat](http://tomcat.apache.org/).
The default admin password is "admin". Don't forget to change it before going to production.
- [Bulk files importer](https://github.com/sismics/docs/tree/master/docs-importer) (single or scan mode)
- Tested to one million documents
Install with Docker
-------------------
From a Docker host, run this command to download and install Sismics Docs. The server will run on <http://[your-docker-host-ip]:8100>.
The default admin password is "admin". Don't forget to change it before going to production.
A preconfigured Docker image is available, including OCR and media conversion tools, listening on port 8080. The database is an embedded H2 database but PostgreSQL is also supported for more performance.
docker run --rm --name sismics_docs_latest -d -p 8100:8080 -v sismics_docs_latest:/data sismics/docs:latest
**The default admin password is "admin". Don't forget to change it before going to production.**
- Master branch, can be unstable. Not recommended for production use: `sismics/docs:latest`
- Latest stable version: `sismics/docs:v1.7`
How to build Docs from the sources
The data directory is `/data`. Don't forget to mount a volume on it.
To build external URL, the server is expecting a `DOCS_BASE_URL` environment variable (for example https://teedy.mycompany.com)
Manual installation
-------------------
#### Requirements
- Java 8 with the [Java Cryptography Extension](http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html)
- Tesseract 3 or 4 for OCR
- ffmpeg for video thumbnails
- mediainfo for video metadata extraction
- A webapp server like [Jetty](http://eclipse.org/jetty/) or [Tomcat](http://tomcat.apache.org/)
#### Download
The latest release is downloadable here: <https://github.com/sismics/docs/releases> in WAR format.
**The default admin password is "admin". Don't forget to change it before going to production.**
How to build Teedy from the sources
----------------------------------
Prerequisites: JDK 7 with JCE, Maven 3, Tesseract 3.02
Prerequisites: JDK 8 with JCE, Maven 3, Tesseract 3 or 4
Docs is organized in several Maven modules:
Teedy is organized in several Maven modules:
- docs-core
- docs-web
@@ -99,8 +117,26 @@ From the `docs-web` directory:
You will get your deployable WAR in the `docs-web/target` directory.
Contributing
------------
All contributions are more than welcomed. Contributions may close an issue, fix a bug (reported or not reported), improve the existing code, add new feature, and so on.
The `master` branch is the default and base branch for the project. It is used for development and all Pull Requests should go there.
Community
---------
Get updates on Teedy's development and chat with the project maintainers:
- Follow [@teedyio on Twitter](https://twitter.com/teedyio)
- Read and subscribe to [The Official Teedy Blog](https://blog.teedy.io/)
- Check the [Official Website](https://teedy.io)
- Join us [on Facebook](https://www.facebook.com/teedyio)
License
-------
Docs is released under the terms of the GPL license. See `COPYING` for more
Teedy is released under the terms of the GPL license. See `COPYING` for more
information or see <http://opensource.org/licenses/GPL-2.0>.

View File

@@ -4,7 +4,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.android.tools.build:gradle:3.4.0'
}
}
apply plugin: 'com.android.application'
@@ -15,11 +15,11 @@ repositories {
}
android {
compileSdkVersion 26
compileSdkVersion 28
defaultConfig {
minSdkVersion 14
targetSdkVersion 26
targetSdkVersion 28
versionCode 1
versionName '1.0'
}
@@ -30,14 +30,14 @@ android {
}
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
compile 'com.android.support:appcompat-v7:26.1.0'
compile 'com.android.support:recyclerview-v7:26.1.0'
compile 'com.android.support:design:26.1.0'
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
compile 'org.greenrobot:eventbus:3.0.0'
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.squareup.okhttp3:okhttp:3.7.0'
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.4.0'
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
implementation fileTree(dir: 'libs', include: '*.jar')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
implementation 'org.greenrobot:eventbus:3.1.1'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.10.0'
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
}

View File

@@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:name=".MainApplication"
@@ -28,6 +29,7 @@
android:name=".activity.MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar"
android:windowSoftInputMode="adjustNothing">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
@@ -43,6 +45,9 @@
<activity
android:name=".activity.DocumentViewActivity"
android:label="">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
</intent-filter>
</activity>
<activity
android:name=".activity.DocumentEditActivity"

View File

@@ -1,6 +1,7 @@
package com.sismics.docs;
import android.app.Application;
import android.support.v7.app.AppCompatDelegate;
import com.sismics.docs.model.application.ApplicationContext;
import com.sismics.docs.util.PreferenceUtil;
@@ -22,5 +23,7 @@ public class MainApplication extends Application {
// TODO Provide documents to intent action get content
super.onCreate();
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
}

View File

@@ -52,7 +52,7 @@ public class AuditLogActivity extends AppCompatActivity {
}
// Configure the swipe refresh layout
SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout);
swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
@@ -65,7 +65,7 @@ public class AuditLogActivity extends AppCompatActivity {
});
// Navigate to user profile on click
final ListView auditLogListView = (ListView) findViewById(R.id.auditLogListView);
final ListView auditLogListView = findViewById(R.id.auditLogListView);
auditLogListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -88,15 +88,15 @@ public class AuditLogActivity extends AppCompatActivity {
* Refresh the view.
*/
private void refreshView(String documentId) {
final SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
final ListView auditLogListView = (ListView) findViewById(R.id.auditLogListView);
final SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout);
final ProgressBar progressBar = findViewById(R.id.progressBar);
final ListView auditLogListView = findViewById(R.id.auditLogListView);
progressBar.setVisibility(View.VISIBLE);
auditLogListView.setVisibility(View.GONE);
AuditLogResource.list(this, documentId, new HttpCallback() {
@Override
public void onSuccess(JSONObject response) {
auditLogListView.setAdapter(new AuditLogListAdapter(response.optJSONArray("logs")));
auditLogListView.setAdapter(new AuditLogListAdapter(AuditLogActivity.this, response.optJSONArray("logs")));
}
@Override

View File

@@ -9,11 +9,13 @@ import android.provider.SearchRecentSuggestions;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.TextView;
@@ -61,7 +63,10 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.main_activity);
// Enable ActionBar app icon to behave as action to toggle nav drawer
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
drawerLayout = findViewById(R.id.drawer_layout);
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setTitleTextColor(getResources().getColor(android.R.color.white));
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
@@ -75,15 +80,15 @@ public class MainActivity extends AppCompatActivity {
// Fill the drawer user info
JSONObject userInfo = ApplicationContext.getInstance().getUserInfo();
TextView usernameTextView = (TextView) findViewById(R.id.usernameTextView);
TextView usernameTextView = findViewById(R.id.usernameTextView);
usernameTextView.setText(userInfo.optString("username"));
TextView emailTextView = (TextView) findViewById(R.id.emailTextView);
TextView emailTextView = findViewById(R.id.emailTextView);
emailTextView.setText(userInfo.optString("email"));
// Get tag list to fill the drawer
final ListView tagListView = (ListView) findViewById(R.id.tagListView);
final ListView tagListView = findViewById(R.id.tagListView);
final View tagProgressView = findViewById(R.id.tagProgressView);
final TextView tagEmptyView = (TextView) findViewById(R.id.tagEmptyView);
final TextView tagEmptyView = findViewById(R.id.tagEmptyView);
tagListView.setEmptyView(tagProgressView);
JSONObject cacheTags = PreferenceUtil.getCachedJson(this, PreferenceUtil.PREF_CACHED_TAGS_JSON);
if (cacheTags != null) {
@@ -145,6 +150,15 @@ public class MainActivity extends AppCompatActivity {
}
});
// Add document button
ImageButton addDocumentButton = findViewById(R.id.addDocumentButton);
addDocumentButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, DocumentEditActivity.class));
}
});
handleIntent(getIntent());
EventBus.getDefault().register(this);

View File

@@ -1,8 +1,6 @@
package com.sismics.docs.adapter;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
@@ -30,12 +28,19 @@ public class AuditLogListAdapter extends BaseAdapter {
*/
private List<JSONObject> logList;
/**
* Context.
*/
private Context context;
/**
* Audit log list adapter.
*
* @param context Context
* @param logs Logs
*/
public AuditLogListAdapter(JSONArray logs) {
public AuditLogListAdapter(Context context, JSONArray logs) {
this.context = context;
this.logList = new ArrayList<>();
for (int i = 0; i < logs.length(); i++) {
@@ -67,11 +72,21 @@ public class AuditLogListAdapter extends BaseAdapter {
// Build message
final JSONObject log = getItem(position);
StringBuilder message = new StringBuilder(log.optString("class"));
StringBuilder message = new StringBuilder();
// Translate entity name
int stringId = context.getResources().getIdentifier("auditlog_" + log.optString("class"), "string", context.getPackageName());
if (stringId == 0) {
message.append(log.optString("class"));
} else {
message.append(context.getResources().getString(stringId));
}
message.append(" ");
switch (log.optString("type")) {
case "CREATE": message.append(" created"); break;
case "UPDATE": message.append(" updated"); break;
case "DELETE": message.append(" deleted"); break;
case "CREATE": message.append(context.getResources().getString(R.string.auditlog_created)); break;
case "UPDATE": message.append(context.getResources().getString(R.string.auditlog_updated)); break;
case "DELETE": message.append(context.getResources().getString(R.string.auditlog_deleted)); break;
}
switch (log.optString("class")) {
case "Document":
@@ -85,9 +100,9 @@ public class AuditLogListAdapter extends BaseAdapter {
}
// Fill the view
TextView usernameTextView = (TextView) view.findViewById(R.id.usernameTextView);
TextView messageTextView = (TextView) view.findViewById(R.id.messageTextView);
TextView dateTextView = (TextView) view.findViewById(R.id.dateTextView);
TextView usernameTextView = view.findViewById(R.id.usernameTextView);
TextView messageTextView = view.findViewById(R.id.messageTextView);
TextView dateTextView = view.findViewById(R.id.dateTextView);
usernameTextView.setText(log.optString("username"));
messageTextView.setText(message);
String date = DateFormat.getDateFormat(parent.getContext()).format(new Date(log.optLong("create_date")));

View File

@@ -33,6 +33,7 @@ public class LanguageAdapter extends BaseAdapter {
}
languageList.add(new Language("fra", R.string.language_french, R.drawable.fra));
languageList.add(new Language("eng", R.string.language_english, R.drawable.eng));
languageList.add(new Language("deu", R.string.language_german, R.drawable.deu));
}
@Override

View File

@@ -2,6 +2,7 @@ package com.sismics.docs.fragment;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
@@ -9,12 +10,10 @@ import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import com.sismics.docs.R;
import com.sismics.docs.activity.DocumentEditActivity;
import com.sismics.docs.activity.DocumentViewActivity;
import com.sismics.docs.adapter.DocListAdapter;
import com.sismics.docs.event.DocumentAddEvent;
@@ -46,11 +45,6 @@ public class DocListFragment extends Fragment {
*/
private String query;
/**
* Request code of adding document.
*/
private static final int REQUEST_CODE_ADD_DOCUMENT = 1;
// View cache
private EmptyRecyclerView recyclerView;
private SwipeRefreshLayout swipeRefreshLayout;
@@ -60,23 +54,22 @@ public class DocListFragment extends Fragment {
private int previousTotal = 0;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.doc_list_fragment, container, false);
// Configure the RecyclerView
recyclerView = (EmptyRecyclerView) view.findViewById(R.id.docList);
recyclerView = view.findViewById(R.id.docList);
adapter = new DocListAdapter();
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
recyclerView.setLongClickable(true);
recyclerView.addItemDecoration(new DividerItemDecoration(getResources().getDrawable(R.drawable.abc_list_divider_mtrl_alpha)));
// Configure the LayoutManager
final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
// Configure the swipe refresh layout
swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipeRefreshLayout);
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout);
swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
@@ -122,16 +115,6 @@ public class DocListFragment extends Fragment {
}
});
// Add document button
ImageButton addDocumentButton = (ImageButton) view.findViewById(R.id.addDocumentButton);
addDocumentButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), DocumentEditActivity.class);
startActivityForResult(intent, REQUEST_CODE_ADD_DOCUMENT);
}
});
// Grab the documents
loadDocuments(view, true);
@@ -210,7 +193,7 @@ public class DocListFragment extends Fragment {
private void loadDocuments(final View view, final boolean reset) {
if (view == null) return;
final View progressBar = view.findViewById(R.id.progressBar);
final TextView documentsEmptyView = (TextView) view.findViewById(R.id.documentsEmptyView);
final TextView documentsEmptyView = view.findViewById(R.id.documentsEmptyView);
if (reset) {
loading = true;

View File

@@ -1,10 +1,12 @@
package com.sismics.docs.service;
import android.app.IntentService;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.PowerManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
@@ -29,7 +31,8 @@ import okhttp3.internal.Util;
* @author bgamard
*/
public class FileUploadService extends IntentService {
private static final String TAG = "FileUploadService";
private static final String TAG = "sismicsdocs:fileupload";
private static final String CHANNEL_ID = "FileUploadService";
private static final int UPLOAD_NOTIFICATION_ID = 1;
private static final int UPLOAD_NOTIFICATION_ID_DONE = 2;
@@ -49,18 +52,30 @@ public class FileUploadService extends IntentService {
super.onCreate();
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notification = new NotificationCompat.Builder(this);
initChannels();
notification = new NotificationCompat.Builder(this, CHANNEL_ID);
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
}
private void initChannels() {
if (Build.VERSION.SDK_INT < 26) {
return;
}
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
"File Upload", NotificationManager.IMPORTANCE_HIGH);
channel.setDescription("Used to show file upload progress");
notificationManager.createNotificationChannel(channel);
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent == null) {
return;
}
wakeLock.acquire();
wakeLock.acquire(60_000 * 30); // 30 minutes upload time maximum
try {
onStart();
handleFileUpload(intent.getStringExtra(PARAM_DOCUMENT_ID), (Uri) intent.getParcelableExtra(PARAM_URI));
@@ -77,7 +92,7 @@ public class FileUploadService extends IntentService {
*
* @param documentId Document ID
* @param uri Data URI
* @throws IOException
* @throws IOException e
*/
private void handleFileUpload(final String documentId, final Uri uri) throws Exception {
final InputStream is = getContentResolver().openInputStream(uri);

View File

@@ -0,0 +1,47 @@
package com.sismics.docs.ui;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.NonNull;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.util.AttributeSet;
import android.view.View;
import com.sismics.docs.R;
public class ScrollingFABBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
private int toolbarHeight;
public ScrollingFABBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
this.toolbarHeight = getToolbarHeight(context);
}
@Override
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull FloatingActionButton fab, @NonNull View dependency) {
return dependency instanceof AppBarLayout;
}
@Override
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull FloatingActionButton fab, @NonNull View dependency) {
if (dependency instanceof AppBarLayout) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
int fabBottomMargin = lp.bottomMargin;
int distanceToScroll = fab.getHeight() + fabBottomMargin;
float ratio = dependency.getY() /(float) toolbarHeight;
fab.setTranslationY(- distanceToScroll * ratio);
}
return true;
}
private int getToolbarHeight(Context context) {
final TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(
new int[] { R.attr.actionBarSize });
int toolbarHeight = (int) styledAttributes.getDimension(0, 0);
styledAttributes.recycle();
return toolbarHeight;
}
}

View File

@@ -156,7 +156,7 @@ public class OkHttpUtil {
public static OkHttpClient buildClient(final Context context) {
// One-time header computation
if (userAgent == null) {
userAgent = "Sismics Docs Android " + ApplicationUtil.getVersionName(context) + "/Android " + Build.VERSION.RELEASE + "/" + Build.MODEL;
userAgent = "Teedy Android " + ApplicationUtil.getVersionName(context) + "/Android " + Build.VERSION.RELEASE + "/" + Build.MODEL;
}
if (acceptLanguage == null) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -37,17 +36,4 @@
android:textSize="16sp"
android:layout_centerInParent="true"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/addDocumentButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="20dp"
android:src="@drawable/ic_add_white_24dp"
app:fabSize="normal"/>
</RelativeLayout>

View File

@@ -29,7 +29,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textColor="#212121"
android:textColor="?android:attr/textColorPrimary"
android:text="Test"
android:textSize="16sp"
android:ellipsize="end"
@@ -46,7 +46,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textColor="#777777"
android:textColor="?android:attr/textColorPrimary"
android:text="test2"
android:textSize="16sp"
android:maxLines="1"
@@ -69,7 +69,7 @@
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:textColor="#777777"
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="sans-serif-light"/>
</RelativeLayout>

View File

@@ -6,12 +6,47 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.CoordinatorLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/overview_coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/AppTheme"
app:layout_scrollFlags="enterAlways|scroll|snap" />
</android.support.design.widget.AppBarLayout>
<fragment
android:id="@+id/main_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
class="com.sismics.docs.fragment.DocListFragment"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/addDocumentButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:src="@drawable/ic_add_white_24dp"
app:layout_anchor="@id/main_fragment"
app:layout_behavior="com.sismics.docs.ui.ScrollingFABBehavior"
app:layout_anchorGravity="bottom|right|end"
app:fabSize="normal"/>
</android.support.design.widget.CoordinatorLayout>
<LinearLayout
android:id="@+id/left_drawer"
android:layout_width="240dp"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Validation -->
<string name="validate_error_email">Ungültige E-Mail</string>
<string name="validate_error_length_min">Zu kurz (min. %d)</string>
<string name="validate_error_length_max">Zu lang (max. %d)</string>
<string name="validate_error_required">Erforderlich</string>
<string name="validate_error_alphanumeric">Nur Buchstaben und Zahlen</string>
<!-- App -->
<string name="drawer_open">Navigationsleiste öffnen</string>
<string name="drawer_close">Navigationsleiste schließen</string>
<string name="login_explain"><![CDATA[Um zu beginnen, müssen Sie Teedy Server herunterladen und installieren <a href="https://github.com/sismics/docs">github.com/sismics/docs</a>, sowie die Login-Daten unten eingeben]]></string>
<string name="server">Server</string>
<string name="username">Username</string>
<string name="password">Password</string>
<string name="login">Login</string>
<string name="ok">OK</string>
<string name="cancel">Abbrechen</string>
<string name="login_fail_title">Login gescheitert</string>
<string name="login_fail">Benutzername oder Passwort falsch</string>
<string name="network_error_title">Netzwerkfehler</string>
<string name="network_error">Netzwerkfehler, überprüfen Sie die Internetverbindung und die Server-URL</string>
<string name="invalid_url_title">Ungültige URL</string>
<string name="invalid_url">Bitte überprüfen Sie die Server-URL und versuchen Sie es erneut</string>
<string name="crash_toast_text">Ein Absturz ist aufgetreten, ein Bericht wurde gesendet, um dieses Problem zu beheben</string>
<string name="created_date">Erstellungsdatum</string>
<string name="download_file">Aktuelle Datei herunterladen</string>
<string name="download_document">Herunterladen</string>
<string name="action_search">Dokumente durchsuchen</string>
<string name="all_documents">Alle Dokumente</string>
<string name="shared_documents">Geteilte Dokumente</string>
<string name="all_tags">Alle Tags</string>
<string name="no_tags">Keine Tags</string>
<string name="error_loading_tags">Fehler beim Laden von Tags</string>
<string name="no_documents">Keine Dokumente</string>
<string name="error_loading_documents">Fehler beim Laden von Dokumenten</string>
<string name="no_files">Keine Dateien</string>
<string name="error_loading_files">Fehler beim Laden von Dateien</string>
<string name="new_document">Neues Dokument</string>
<string name="share">Teilen</string>
<string name="close">Schließen</string>
<string name="add">Hinzufügen</string>
<string name="add_share_hint">Freigabename (optional)</string>
<string name="document_not_shared">Dieses Dokument wird derzeit nicht freigegeben</string>
<string name="delete_share">Diese Freigabe löschen</string>
<string name="send_share">Send this share link</string>
<string name="error_loading_shares">Fehler beim Laden von Freigaben</string>
<string name="error_adding_share">Fehler beim Hinzufügen der Freigabe</string>
<string name="share_default_name">Freigabe Link</string>
<string name="error_deleting_share">Fehler beim Löschen der Freigabe</string>
<string name="send_share_to">Freigabe senden an</string>
<string name="upload_file">Datei hinzufügen</string>
<string name="upload_from">Datei hochladen von</string>
<string name="settings">Einstellungen</string>
<string name="logout">Ausloggen</string>
<string name="version">Version</string>
<string name="build">Build</string>
<string name="pref_advanced_category">Erweiterte Einstellungen</string>
<string name="pref_about_category">Über</string>
<string name="pref_github">GitHub</string>
<string name="pref_issue">Fehler berichten</string>
<string name="pref_clear_cache_title">Cache leeren</string>
<string name="pref_clear_cache_summary">Zwischengespeicherte Dateien löschen</string>
<string name="pref_clear_cache_success">Cache wurde geleert</string>
<string name="pref_clear_history_title">Suchhistorie löschen</string>
<string name="pref_clear_history_summary">Leert die aktuellen Suchvorschläge</string>
<string name="pref_clear_history_success">Suchvorschläge wurden gelöscht</string>
<string name="pref_cache_size">Cache Größe</string>
<string name="save">Speichern</string>
<string name="edit_document">Bearbeiten</string>
<string name="error_editing_document">Netzwerkfehler, bitte versuchen Sie es erneut</string>
<string name="please_wait">Bitte warten</string>
<string name="document_editing_message">Daten werden gesendet</string>
<string name="delete_document">Löschen</string>
<string name="delete_document_title">Dokument löschen</string>
<string name="delete_document_message">Dieses Dokument und alle zugehörigen Dateien wirklich löschen?</string>
<string name="document_delete_failure">Netzwerkfehler beim Löschen des Dokuments</string>
<string name="document_deleting_message">Lösche Dokument</string>
<string name="delete_file_title">Datei löschen</string>
<string name="delete_file_message">Die aktuelle Datei wirklich löschen?</string>
<string name="file_delete_failure">Netzwerkfehler beim Löschen der Datei</string>
<string name="file_deleting_message">Lösche Datei</string>
<string name="error_reading_file">Fehler beim Lesen der Datei</string>
<string name="upload_notification_title">Teedy</string>
<string name="upload_notification_message">Neue Datei in das Dokument hochladen</string>
<string name="upload_notification_error">Fehler beim Hochladen der neuen Datei</string>
<string name="delete_file">Aktuelle Datei löschen</string>
<string name="advanced_search">Erweiterte Suche</string>
<string name="search">Suche</string>
<string name="add_tags">Tags hinzufügen</string>
<string name="creation_date">Erstellungsdatum</string>
<string name="description">Beschreibung</string>
<string name="title">Titel</string>
<string name="simple_search">Einfache Suche</string>
<string name="fulltext_search">Volltextsuche</string>
<string name="creator">Ersteller</string>
<string name="after_date">Nach Datum</string>
<string name="before_date">Vor Datum</string>
<string name="search_tags">Tags durchsuchen</string>
<string name="all_languages">Alle Sprachen</string>
<string name="toggle_informations">Informationen anzeigen</string>
<string name="who_can_access">Wer kann darauf zugreifen?</string>
<string name="comments">Kommentare</string>
<string name="no_comments">Keine Kommentare</string>
<string name="error_loading_comments">Fehler beim Laden von Kommentaren</string>
<string name="send">Senden</string>
<string name="add_comment">Kommentar hinzufügen</string>
<string name="comment_add_failure">Fehler beim Hinzufügen des Kommentars</string>
<string name="adding_comment">Füge Kommentar hinzu</string>
<string name="comment_delete">Kommentar löschen</string>
<string name="deleting_comment">Lösche Kommentar</string>
<string name="error_deleting_comment">Fehler beim Löschen des Kommentars</string>
<string name="export_pdf">PDF</string>
<string name="download">Download</string>
<string name="margin">Rand</string>
<string name="fit_image_to_page">Bild an Seite anpassen</string>
<string name="export_comments">Kommentare exportieren</string>
<string name="export_metadata">Metadaten exportieren</string>
<string name="mm">mm</string>
<string name="download_file_title">Teedy Datei Export</string>
<string name="download_document_title">Teedy Dokumentenexport</string>
<string name="download_pdf_title">Teedy PDF Export</string>
<string name="latest_activity">Letzte Aktivität</string>
<string name="activity">Aktivitäten</string>
<string name="email">E-Mail</string>
<string name="storage_quota">Speicherbegrenzung</string>
<string name="storage_display">%1$d/%2$d MB</string>
<string name="validation_code">Validierungscode</string>
<string name="shared">Geteilt</string>
<string name="language">Sprache</string>
<string name="coverage">Geltungsbereich</string>
<string name="type">Typ</string>
<string name="source">Quelle</string>
<string name="format">Format</string>
<string name="publisher">Verleger</string>
<string name="identifier">Identifikator</string>
<string name="subject">Thema</string>
<string name="rights">Rechte</string>
<string name="contributors">Mitwirkende</string>
<string name="relations">Beziehungen</string>
<!-- Audit log -->
<string name="auditlog_Acl">ACL</string>
<string name="auditlog_Comment">Kommentar</string>
<string name="auditlog_Document">Dokument</string>
<string name="auditlog_File">Datei</string>
<string name="auditlog_Group">Gruppe</string>
<string name="auditlog_Route">Workflow</string>
<string name="auditlog_RouteModel">Workflow-Muster</string>
<string name="auditlog_Tag">Tag</string>
<string name="auditlog_User">Benutzer</string>
<string name="auditlog_Webhook">Webhook</string>
<string name="auditlog_created">erstellt</string>
<string name="auditlog_updated">aktualisiert</string>
<string name="auditlog_deleted">gelöscht</string>
</resources>

View File

@@ -11,7 +11,7 @@
<!-- App -->
<string name="drawer_open">Ouvrir le menu de navigation</string>
<string name="drawer_close">Fermer le menu de navigation</string>
<string name="login_explain"><![CDATA[Pour commencer, vous devez télécharger et installer le serveur Sismics Docs sur <a href="https://github.com/sismics/docs">github.com/sismics/docs</a> et entrer son URL ci-dessous]]></string>
<string name="login_explain"><![CDATA[Pour commencer, vous devez télécharger et installer le serveur Teedy sur <a href="https://github.com/sismics/docs">github.com/sismics/docs</a> et entrer son URL ci-dessous]]></string>
<string name="server">Serveur</string>
<string name="username">Nom d\'utilisateur</string>
<string name="password">Mot de passe</string>
@@ -83,7 +83,7 @@
<string name="file_delete_failure">Erreur réseau lors de la suppression du fichier</string>
<string name="file_deleting_message">Suppression du fichier</string>
<string name="error_reading_file">Erreur lors de la lecture du fichier</string>
<string name="upload_notification_title">Sismics Docs</string>
<string name="upload_notification_title">Teedy</string>
<string name="upload_notification_message">Envoi du nouveau fichier</string>
<string name="upload_notification_error">Erreur lors de l\'envoi du nouveau fichier</string>
<string name="delete_file">Supprimer ce fichier</string>
@@ -119,9 +119,9 @@
<string name="export_comments">Exporter les commentaires</string>
<string name="export_metadata">Exporter les métadonnées</string>
<string name="mm">mm</string>
<string name="download_file_title">Export de fichier Sismics Docs</string>
<string name="download_document_title">Export de document Sismics Docs</string>
<string name="download_pdf_title">Export PDF Sismics Docs</string>
<string name="download_file_title">Export de fichier Teedy</string>
<string name="download_document_title">Export de document Teedy</string>
<string name="download_pdf_title">Export PDF Teedy</string>
<string name="latest_activity">Activité récente</string>
<string name="activity">Activité</string>
<string name="email">E-mail</string>
@@ -141,4 +141,19 @@
<string name="contributors">Contributeurs</string>
<string name="relations">Relations</string>
<!-- Audit log -->
<string name="auditlog_Acl">ACL</string>
<string name="auditlog_Comment">Commentaire</string>
<string name="auditlog_Document">Document</string>
<string name="auditlog_File">Fichier</string>
<string name="auditlog_Group">Groupe</string>
<string name="auditlog_Route">Workflow</string>
<string name="auditlog_RouteModel">Modèle de workflow</string>
<string name="auditlog_Tag">Tag</string>
<string name="auditlog_User">Utilisateur</string>
<string name="auditlog_Webhook">Webhook</string>
<string name="auditlog_created">créé</string>
<string name="auditlog_updated">mis à jour</string>
<string name="auditlog_deleted">supprimé</string>
</resources>

View File

@@ -9,10 +9,10 @@
<string name="validate_error_alphanumeric">Only letters and numbers</string>
<!-- App -->
<string name="app_name" translatable="false">Sismics Docs</string>
<string name="app_name" translatable="false">Teedy</string>
<string name="drawer_open">Open navigation drawer</string>
<string name="drawer_close">Close navigation drawer</string>
<string name="login_explain"><![CDATA[To start, you must download and install Sismics Docs Server on <a href="https://github.com/sismics/docs">github.com/sismics/docs</a> and enter its below]]></string>
<string name="login_explain"><![CDATA[To start, you must download and install Teedy Server on <a href="https://github.com/sismics/docs">github.com/sismics/docs</a> and enter its below]]></string>
<string name="server">Server</string>
<string name="username">Username</string>
<string name="password">Password</string>
@@ -71,6 +71,7 @@
<string name="pref_cache_size">Cache size</string>
<string name="language_french" translatable="false">Français</string>
<string name="language_english" translatable="false">English</string>
<string name="language_german" translatable="false">Deutsch</string>
<string name="save">Save</string>
<string name="edit_document">Edit</string>
<string name="error_editing_document">Network error, please try again</string>
@@ -86,7 +87,7 @@
<string name="file_delete_failure">Network error while deleting the current file</string>
<string name="file_deleting_message">Deleting file</string>
<string name="error_reading_file">Error while reading the file</string>
<string name="upload_notification_title">Sismics Docs</string>
<string name="upload_notification_title">Teedy</string>
<string name="upload_notification_message">Uploading the new file to the document</string>
<string name="upload_notification_error">Error uploading the new file</string>
<string name="delete_file">Delete current file</string>
@@ -122,9 +123,9 @@
<string name="export_comments">Export comments</string>
<string name="export_metadata">Export metadata</string>
<string name="mm">mm</string>
<string name="download_file_title">Sismics Docs file export</string>
<string name="download_document_title">Sismics Docs document export</string>
<string name="download_pdf_title">Sismics Docs PDF export</string>
<string name="download_file_title">Teedy file export</string>
<string name="download_document_title">Teedy document export</string>
<string name="download_pdf_title">Teedy PDF export</string>
<string name="latest_activity">Latest activity</string>
<string name="activity">Activity</string>
<string name="email">E-mail</string>
@@ -144,4 +145,19 @@
<string name="contributors">Contributors</string>
<string name="relations">Relations</string>
<!-- Audit log -->
<string name="auditlog_Acl">ACL</string>
<string name="auditlog_Comment">Comment</string>
<string name="auditlog_Document">Document</string>
<string name="auditlog_File">File</string>
<string name="auditlog_Group">Group</string>
<string name="auditlog_Route">Workflow</string>
<string name="auditlog_RouteModel">Workflow model</string>
<string name="auditlog_Tag">Tag</string>
<string name="auditlog_User">User</string>
<string name="auditlog_Webhook">Webhook</string>
<string name="auditlog_created">created</string>
<string name="auditlog_updated">updated</string>
<string name="auditlog_deleted">deleted</string>
</resources>

View File

@@ -1,12 +1,20 @@
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppThemeDark" parent="Theme.AppCompat.NoActionBar">
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppThemeDark" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>

View File

@@ -1,6 +1,6 @@
#Tue Nov 14 23:55:56 CET 2017
#Tue May 07 11:49:13 CEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>com.sismics.docs</groupId>
<artifactId>docs-parent</artifactId>
<version>1.5</version>
<version>1.7</version>
<relativePath>..</relativePath>
</parent>
@@ -112,11 +112,26 @@
<artifactId>lucene-queryparser</artifactId>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-suggest</artifactId>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<!-- Only there to read old index and rebuild them -->
<dependency>
<groupId>org.apache.lucene</groupId>
@@ -140,33 +155,39 @@
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.odftoolkit.odfdom.converter.pdf</artifactId>
<artifactId>fr.opensagres.odfdom.converter.pdf</artifactId>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.apache.poi.xwpf.converter.pdf</artifactId>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<artifactId>fr.opensagres.poi.xwpf.converter.pdf</artifactId>
</dependency>
<!-- ImageIO plugins -->
<dependency>
<groupId>com.levigo.jbig2</groupId>
<artifactId>levigo-jbig2-imageio</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
</dependency>
<dependency>
<groupId>com.github.jai-imageio</groupId>
<artifactId>jai-imageio-core</artifactId>
<artifactId>jai-imageio-jpeg2000</artifactId>
</dependency>
<dependency>
<groupId>com.levigo.jbig2</groupId>
<artifactId>levigo-jbig2-imageio</artifactId>
</dependency>
<!-- Only for connecting to PostgreSQL database -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- Test dependencies -->
@@ -209,20 +230,6 @@
<profile>
<id>prod</id>
</profile>
<!-- Demo profile -->
<profile>
<id>demo</id>
<build>
<resources>
<resource>
<directory>src/demo/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</profile>
</profiles>
<build>

View File

@@ -0,0 +1,23 @@
package com.sismics.docs.core.constant;
/**
* Action types.
*
* @author bgamard
*/
public enum ActionType {
/**
* Add a tag.
*/
ADD_TAG,
/**
* Remove a tag.
*/
REMOVE_TAG,
/**
* Process files.
*/
PROCESS_FILES
}

View File

@@ -21,14 +21,9 @@ public class Constants {
public static final String DEFAULT_ADMIN_PASSWORD = "$2a$05$6Ny3TjrW3aVAL1or2SlcR.fhuDgPKp5jp.P9fBXwVNePgeLqb4i3C";
/**
* RAM Lucene directory storage.
* Administrator's default email.
*/
public static final String LUCENE_DIRECTORY_STORAGE_RAM = "RAM";
/**
* File Lucene directory storage.
*/
public static final String LUCENE_DIRECTORY_STORAGE_FILE = "FILE";
public static final String DEFAULT_ADMIN_EMAIL = "admin@localhost";
/**
* Guest user ID.
@@ -43,7 +38,7 @@ public class Constants {
/**
* Supported document languages.
*/
public static final List<String> SUPPORTED_LANGUAGES = Lists.newArrayList("eng", "fra", "ita", "deu", "spa", "por", "pol", "rus", "ukr", "ara", "hin", "chi_sim", "chi_tra", "jpn", "tha", "kor");
public static final List<String> SUPPORTED_LANGUAGES = Lists.newArrayList("eng", "fra", "ita", "deu", "spa", "por", "pol", "rus", "ukr", "ara", "hin", "chi_sim", "chi_tra", "jpn", "tha", "kor", "nld", "tur", "heb");
/**
* Base URL environment variable.
@@ -73,6 +68,11 @@ public class Constants {
*/
public static final String ADMIN_PASSWORD_INIT_ENV = "DOCS_ADMIN_PASSWORD_INIT";
/**
* Initial admin password environment variable.
*/
public static final String ADMIN_EMAIL_INIT_ENV = "DOCS_ADMIN_EMAIL_INIT";
/**
* Expiration time of the password recovery in hours.
*/
@@ -87,4 +87,9 @@ public class Constants {
* Email template for route step validate.
*/
public static final String EMAIL_TEMPLATE_ROUTE_STEP_VALIDATE = "route_step_validate";
/**
* mm per inch.
*/
public static float MM_PER_INCH = 1 / (10 * 2.54f) * 72f;
}

View File

@@ -0,0 +1,14 @@
package com.sismics.docs.core.constant;
/**
* Metadata type.
*
* @author bgamard
*/
public enum MetadataType {
STRING,
INTEGER,
FLOAT,
DATE,
BOOLEAN
}

View File

@@ -0,0 +1,15 @@
package com.sismics.docs.core.constant;
/**
* Webhook events.
*
* @author bgamard
*/
public enum WebhookEvent {
DOCUMENT_CREATED,
DOCUMENT_UPDATED,
DOCUMENT_DELETED,
FILE_CREATED,
FILE_UPDATED,
FILE_DELETED
}

View File

@@ -1,12 +1,13 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.sismics.docs.core.constant.AclTargetType;
import com.sismics.docs.core.constant.AclType;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.constant.PermType;
import com.sismics.docs.core.dao.jpa.dto.AclDto;
import com.sismics.docs.core.dao.dto.AclDto;
import com.sismics.docs.core.model.jpa.Acl;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.docs.core.util.SecurityUtil;
import com.sismics.util.context.ThreadLocalContext;
import javax.persistence.EntityManager;
@@ -73,12 +74,17 @@ public class AclDao {
.append(" left join T_USER u on u.USE_ID_C = a.ACL_TARGETID_C ")
.append(" left join T_SHARE s on s.SHA_ID_C = a.ACL_TARGETID_C ")
.append(" left join T_GROUP g on g.GRP_ID_C = a.ACL_TARGETID_C ")
.append(" where a.ACL_DELETEDATE_D is null and a.ACL_SOURCEID_C = :sourceId and a.ACL_TYPE_C = :type ");
.append(" where a.ACL_DELETEDATE_D is null and a.ACL_SOURCEID_C = :sourceId ");
if (type != null) {
sb.append(" and a.ACL_TYPE_C = :type");
}
// Perform the query
Query q = em.createNativeQuery(sb.toString());
q.setParameter("sourceId", sourceId);
if (type != null) {
q.setParameter("type", type.name());
}
List<Object[]> l = q.getResultList();
// Assemble results
@@ -119,12 +125,17 @@ public class AclDao {
* @return True if the document is accessible
*/
public boolean checkPermission(String sourceId, PermType perm, List<String> targetIdList) {
if (SecurityUtil.skipAclCheck(targetIdList)) {
return true;
}
EntityManager em = ThreadLocalContext.get().getEntityManager();
StringBuilder sb = new StringBuilder("select a.ACL_ID_C from T_ACL a ");
sb.append(" where a.ACL_TARGETID_C in (:targetIdList) and a.ACL_SOURCEID_C = :sourceId and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null ");
sb.append(" union all ");
sb.append(" select a.ACL_ID_C from T_ACL a, T_DOCUMENT_TAG dt ");
sb.append(" where a.ACL_SOURCEID_C = dt.DOT_IDTAG_C and dt.DOT_IDDOCUMENT_C = :sourceId and dt.DOT_DELETEDATE_D is null ");
sb.append(" select a.ACL_ID_C from T_ACL a, T_DOCUMENT_TAG dt, T_DOCUMENT d ");
sb.append(" where a.ACL_SOURCEID_C = dt.DOT_IDTAG_C and dt.DOT_IDDOCUMENT_C = d.DOC_ID_C and dt.DOT_DELETEDATE_D is null ");
sb.append(" and d.DOC_ID_C = :sourceId and d.DOC_DELETEDATE_D is null ");
sb.append(" and a.ACL_TARGETID_C in (:targetIdList) and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null ");
Query q = em.createNativeQuery(sb.toString());
q.setParameter("sourceId", sourceId);

View File

@@ -1,20 +1,10 @@
package com.sismics.docs.core.dao.jpa;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.persistence.EntityManager;
package com.sismics.docs.core.dao;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.dao.jpa.criteria.AuditLogCriteria;
import com.sismics.docs.core.dao.jpa.dto.AuditLogDto;
import com.sismics.docs.core.dao.criteria.AuditLogCriteria;
import com.sismics.docs.core.dao.dto.AuditLogDto;
import com.sismics.docs.core.model.jpa.AuditLog;
import com.sismics.docs.core.util.jpa.PaginatedList;
import com.sismics.docs.core.util.jpa.PaginatedLists;
@@ -22,6 +12,10 @@ import com.sismics.docs.core.util.jpa.QueryParam;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.util.context.ThreadLocalContext;
import javax.persistence.EntityManager;
import java.sql.Timestamp;
import java.util.*;
/**
* Audit log DAO.
*
@@ -33,7 +27,6 @@ public class AuditLogDao {
*
* @param auditLog Audit log
* @return New ID
* @throws Exception
*/
public String create(AuditLog auditLog) {
// Create the UUID
@@ -53,10 +46,9 @@ public class AuditLogDao {
* @param paginatedList List of audit logs (updated by side effects)
* @param criteria Search criteria
* @param sortCriteria Sort criteria
* @return List of audit logs
*/
public void findByCriteria(PaginatedList<AuditLogDto> paginatedList, AuditLogCriteria criteria, SortCriteria sortCriteria) {
Map<String, Object> parameterMap = new HashMap<String, Object>();
Map<String, Object> parameterMap = new HashMap<>();
StringBuilder baseQuery = new StringBuilder("select l.LOG_ID_C c0, l.LOG_CREATEDATE_D c1, u.USE_USERNAME_C c2, l.LOG_IDENTITY_C c3, l.LOG_CLASSENTITY_C c4, l.LOG_TYPE_C c5, l.LOG_MESSAGE_C c6 from T_AUDIT_LOG l ");
baseQuery.append(" join T_USER u on l.LOG_IDUSER_C = u.USE_ID_C ");
@@ -69,22 +61,28 @@ public class AuditLogDao {
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select f.FIL_ID_C from T_FILE f where f.FIL_IDDOC_C = :documentId) ");
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select c.COM_ID_C from T_COMMENT c where c.COM_IDDOC_C = :documentId) ");
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select a.ACL_ID_C from T_ACL a where a.ACL_SOURCEID_C = :documentId) ");
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select r.RTE_ID_C from T_ROUTE r where r.RTE_IDDOCUMENT_C = :documentId) ");
parameterMap.put("documentId", criteria.getDocumentId());
}
if (criteria.getUserId() != null) {
if (criteria.isAdmin()) {
// For admin users, display all logs except ACL logs
queries.add(baseQuery + " where l.LOG_CLASSENTITY_C != 'Acl' ");
} else {
// Get all logs originating from the user, not necessarly on owned items
// Filter out ACL logs
queries.add(baseQuery + " where l.LOG_IDUSER_C = :userId and l.LOG_CLASSENTITY_C != 'Acl' ");
parameterMap.put("userId", criteria.getUserId());
}
}
// Perform the search
QueryParam queryParam = new QueryParam(Joiner.on(" union ").join(queries), parameterMap);
List<Object[]> l = PaginatedLists.executePaginatedQuery(paginatedList, queryParam, sortCriteria);
// Assemble results
List<AuditLogDto> auditLogDtoList = new ArrayList<AuditLogDto>();
List<AuditLogDto> auditLogDtoList = new ArrayList<>();
for (Object[] o : l) {
int i = 0;
AuditLogDto auditLogDto = new AuditLogDto();

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.sismics.docs.core.model.jpa.AuthenticationToken;
import com.sismics.util.context.ThreadLocalContext;
@@ -86,7 +86,7 @@ public class AuthenticationTokenDao {
*/
public void updateLastConnectionDate(String id) {
StringBuilder sb = new StringBuilder("update T_AUTHENTICATION_TOKEN ato ");
sb.append(" set ato.AUT_LASTCONNECTIONDATE_D = :currentDate ");
sb.append(" set AUT_LASTCONNECTIONDATE_D = :currentDate ");
sb.append(" where ato.AUT_ID_C = :id");
EntityManager em = ThreadLocalContext.get().getEntityManager();

View File

@@ -1,21 +1,20 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.dao.dto.CommentDto;
import com.sismics.docs.core.model.jpa.Comment;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.util.context.ThreadLocalContext;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.dao.jpa.dto.CommentDto;
import com.sismics.docs.core.model.jpa.Comment;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.util.context.ThreadLocalContext;
/**
* Comment DAO.
*
@@ -28,7 +27,6 @@ public class CommentDao {
* @param comment Comment
* @param userId User ID
* @return New ID
* @throws Exception
*/
public String create(Comment comment, String userId) {
// Create the UUID
@@ -100,7 +98,7 @@ public class CommentDao {
@SuppressWarnings("unchecked")
List<Object[]> l = q.getResultList();
List<CommentDto> commentDtoList = new ArrayList<CommentDto>();
List<CommentDto> commentDtoList = new ArrayList<>();
for (Object[] o : l) {
int i = 0;
CommentDto commentDto = new CommentDto();
@@ -108,7 +106,7 @@ public class CommentDao {
commentDto.setContent((String) o[i++]);
commentDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
commentDto.setCreatorName((String) o[i++]);
commentDto.setCreatorEmail((String) o[i++]);
commentDto.setCreatorEmail((String) o[i]);
commentDtoList.add(commentDto);
}
return commentDtoList;

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.sismics.docs.core.constant.ConfigType;
import com.sismics.docs.core.model.jpa.Config;

View File

@@ -1,15 +1,14 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import com.sismics.docs.core.dao.dto.ContributorDto;
import com.sismics.docs.core.model.jpa.Contributor;
import com.sismics.util.context.ThreadLocalContext;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import com.sismics.docs.core.dao.jpa.dto.ContributorDto;
import com.sismics.docs.core.model.jpa.Contributor;
import com.sismics.util.context.ThreadLocalContext;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Contributor DAO.

View File

@@ -1,25 +1,19 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.constant.PermType;
import com.sismics.docs.core.dao.jpa.criteria.DocumentCriteria;
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
import com.sismics.docs.core.dao.lucene.LuceneDao;
import com.sismics.docs.core.dao.dto.DocumentDto;
import com.sismics.docs.core.model.jpa.Document;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.docs.core.util.jpa.PaginatedList;
import com.sismics.docs.core.util.jpa.PaginatedLists;
import com.sismics.docs.core.util.jpa.QueryParam;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.util.context.ThreadLocalContext;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.sql.Timestamp;
import java.util.*;
import java.util.Date;
import java.util.List;
import java.util.UUID;
/**
* Document DAO.
@@ -37,6 +31,7 @@ public class DocumentDao {
public String create(Document document, String userId) {
// Create the UUID
document.setId(UUID.randomUUID().toString());
document.setUpdateDate(new Date());
// Create the document
EntityManager em = ThreadLocalContext.get().getEntityManager();
@@ -51,12 +46,16 @@ public class DocumentDao {
/**
* Returns the list of all active documents.
*
* @param offset Offset
* @param limit Limit
* @return List of documents
*/
@SuppressWarnings("unchecked")
public List<Document> findAll() {
public List<Document> findAll(int offset, int limit) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query q = em.createQuery("select d from Document d where d.deleteDate is null");
q.setFirstResult(offset);
q.setMaxResults(limit);
return q.getResultList();
}
@@ -89,9 +88,9 @@ public class DocumentDao {
}
EntityManager em = ThreadLocalContext.get().getEntityManager();
StringBuilder sb = new StringBuilder("select distinct d.DOC_ID_C, d.DOC_TITLE_C, d.DOC_DESCRIPTION_C, d.DOC_SUBJECT_C, d.DOC_IDENTIFIER_C, d.DOC_PUBLISHER_C, d.DOC_FORMAT_C, d.DOC_SOURCE_C, d.DOC_TYPE_C, d.DOC_COVERAGE_C, d.DOC_RIGHTS_C, d.DOC_CREATEDATE_D, d.DOC_LANGUAGE_C, ");
sb.append(" (select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null), ");
sb.append(" (select count(f.FIL_ID_C) from T_FILE f where f.FIL_DELETEDATE_D is null and f.FIL_IDDOC_C = d.DOC_ID_C), ");
StringBuilder sb = new StringBuilder("select distinct d.DOC_ID_C, d.DOC_TITLE_C, d.DOC_DESCRIPTION_C, d.DOC_SUBJECT_C, d.DOC_IDENTIFIER_C, d.DOC_PUBLISHER_C, d.DOC_FORMAT_C, d.DOC_SOURCE_C, d.DOC_TYPE_C, d.DOC_COVERAGE_C, d.DOC_RIGHTS_C, d.DOC_CREATEDATE_D, d.DOC_UPDATEDATE_D, d.DOC_LANGUAGE_C, ");
sb.append(" (select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null) shareCount, ");
sb.append(" (select count(f.FIL_ID_C) from T_FILE f where f.FIL_DELETEDATE_D is null and f.FIL_IDDOC_C = d.DOC_ID_C) fileCount, ");
sb.append(" u.USE_USERNAME_C ");
sb.append(" from T_DOCUMENT d ");
sb.append(" join T_USER u on d.DOC_IDUSER_C = u.USE_ID_C ");
@@ -121,6 +120,7 @@ public class DocumentDao {
documentDto.setCoverage((String) o[i++]);
documentDto.setRights((String) o[i++]);
documentDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
documentDto.setUpdateTimestamp(((Timestamp) o[i++]).getTime());
documentDto.setLanguage((String) o[i++]);
documentDto.setShared(((Number) o[i++]).intValue() > 0);
documentDto.setFileCount(((Number) o[i++]).intValue());
@@ -189,100 +189,7 @@ public class DocumentDao {
}
/**
* Searches documents by criteria.
*
* @param paginatedList List of documents (updated by side effects)
* @param criteria Search criteria
* @param sortCriteria Sort criteria
* @throws Exception
*/
public void findByCriteria(PaginatedList<DocumentDto> paginatedList, DocumentCriteria criteria, SortCriteria sortCriteria) throws Exception {
Map<String, Object> parameterMap = new HashMap<>();
List<String> criteriaList = new ArrayList<>();
StringBuilder sb = new StringBuilder("select distinct d.DOC_ID_C c0, d.DOC_TITLE_C c1, d.DOC_DESCRIPTION_C c2, d.DOC_CREATEDATE_D c3, d.DOC_LANGUAGE_C c4, ");
sb.append(" (select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null) c5, ");
sb.append(" (select count(f.FIL_ID_C) from T_FILE f where f.FIL_DELETEDATE_D is null and f.FIL_IDDOC_C = d.DOC_ID_C) c6 ");
sb.append(" from T_DOCUMENT d ");
// Add search criterias
if (criteria.getTargetIdList() != null) {
// Read permission is enough for searching
sb.append(" left join T_ACL a on a.ACL_TARGETID_C in (:targetIdList) and a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null ");
sb.append(" left join T_DOCUMENT_TAG dta on dta.DOT_IDDOCUMENT_C = d.DOC_ID_C and dta.DOT_DELETEDATE_D is null ");
sb.append(" left join T_ACL a2 on a2.ACL_TARGETID_C in (:targetIdList) and a2.ACL_SOURCEID_C = dta.DOT_IDTAG_C and a2.ACL_PERM_C = 'READ' and a2.ACL_DELETEDATE_D is null ");
criteriaList.add("(a.ACL_ID_C is not null or a2.ACL_ID_C is not null)");
parameterMap.put("targetIdList", criteria.getTargetIdList());
}
if (!Strings.isNullOrEmpty(criteria.getSearch()) || !Strings.isNullOrEmpty(criteria.getFullSearch())) {
LuceneDao luceneDao = new LuceneDao();
Set<String> documentIdList = luceneDao.search(criteria.getSearch(), criteria.getFullSearch());
if (documentIdList.isEmpty()) {
// If the search doesn't find any document, the request should return nothing
documentIdList.add(UUID.randomUUID().toString());
}
criteriaList.add("d.DOC_ID_C in :documentIdList");
parameterMap.put("documentIdList", documentIdList);
}
if (criteria.getCreateDateMin() != null) {
criteriaList.add("d.DOC_CREATEDATE_D >= :createDateMin");
parameterMap.put("createDateMin", criteria.getCreateDateMin());
}
if (criteria.getCreateDateMax() != null) {
criteriaList.add("d.DOC_CREATEDATE_D <= :createDateMax");
parameterMap.put("createDateMax", criteria.getCreateDateMax());
}
if (criteria.getTagIdList() != null && !criteria.getTagIdList().isEmpty()) {
int index = 0;
for (String tagId : criteria.getTagIdList()) {
sb.append(String.format(" join T_DOCUMENT_TAG dt%d on dt%d.DOT_IDDOCUMENT_C = d.DOC_ID_C and dt%d.DOT_IDTAG_C = :tagId%d and dt%d.DOT_DELETEDATE_D is null ", index, index, index, index, index));
parameterMap.put("tagId" + index, tagId);
index++;
}
}
if (criteria.getShared() != null && criteria.getShared()) {
criteriaList.add("(select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null) > 0");
}
if (criteria.getLanguage() != null) {
criteriaList.add("d.DOC_LANGUAGE_C = :language");
parameterMap.put("language", criteria.getLanguage());
}
if (criteria.getCreatorId() != null) {
criteriaList.add("d.DOC_IDUSER_C = :creatorId");
parameterMap.put("creatorId", criteria.getCreatorId());
}
criteriaList.add("d.DOC_DELETEDATE_D is null");
if (!criteriaList.isEmpty()) {
sb.append(" where ");
sb.append(Joiner.on(" and ").join(criteriaList));
}
// Perform the search
QueryParam queryParam = new QueryParam(sb.toString(), parameterMap);
List<Object[]> l = PaginatedLists.executePaginatedQuery(paginatedList, queryParam, sortCriteria);
// Assemble results
List<DocumentDto> documentDtoList = new ArrayList<>();
for (Object[] o : l) {
int i = 0;
DocumentDto documentDto = new DocumentDto();
documentDto.setId((String) o[i++]);
documentDto.setTitle((String) o[i++]);
documentDto.setDescription((String) o[i++]);
documentDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
documentDto.setLanguage((String) o[i++]);
documentDto.setShared(((Number) o[i++]).intValue() > 0);
documentDto.setFileCount(((Number) o[i]).intValue());
documentDtoList.add(documentDto);
}
paginatedList.setResultList(documentDtoList);
}
/**
* Update a document.
* Update a document and log the action.
*
* @param document Document to update
* @param userId User ID
@@ -309,6 +216,8 @@ public class DocumentDao {
documentDb.setRights(document.getRights());
documentDb.setCreateDate(document.getCreateDate());
documentDb.setLanguage(document.getLanguage());
documentDb.setFileId(document.getFileId());
documentDb.setUpdateDate(new Date());
// Create audit log
AuditLogUtil.create(documentDb, AuditLogType.UPDATE, userId);
@@ -316,6 +225,21 @@ public class DocumentDao {
return documentDb;
}
/**
* Update the file ID on a document.
*
* @param document Document
*/
public void updateFileId(Document document) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query query = em.createNativeQuery("update T_DOCUMENT d set d.DOC_IDFILE_C = :fileId, d.DOC_UPDATEDATE_D = :updateDate where d.DOC_ID_C = :id");
query.setParameter("updateDate", new Date());
query.setParameter("fileId", document.getFileId());
query.setParameter("id", document.getId());
query.executeUpdate();
}
/**
* Returns the number of documents.
*

View File

@@ -0,0 +1,89 @@
package com.sismics.docs.core.dao;
import com.sismics.docs.core.constant.MetadataType;
import com.sismics.docs.core.dao.dto.DocumentMetadataDto;
import com.sismics.docs.core.model.jpa.DocumentMetadata;
import com.sismics.util.context.ThreadLocalContext;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Document metadata DAO.
*
* @author bgamard
*/
public class DocumentMetadataDao {
/**
* Creates a new document metadata.
*
* @param documentMetadata Document metadata
* @return New ID
*/
public String create(DocumentMetadata documentMetadata) {
// Create the UUID
documentMetadata.setId(UUID.randomUUID().toString());
// Create the document metadata
EntityManager em = ThreadLocalContext.get().getEntityManager();
em.persist(documentMetadata);
return documentMetadata.getId();
}
/**
* Updates a document metadata.
*
* @param documentMetadata Document metadata
* @return Updated document metadata
*/
public DocumentMetadata update(DocumentMetadata documentMetadata) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the document metadata
Query q = em.createQuery("select u from DocumentMetadata u where u.id = :id");
q.setParameter("id", documentMetadata.getId());
DocumentMetadata documentMetadataDb = (DocumentMetadata) q.getSingleResult();
// Update the document metadata
documentMetadataDb.setValue(documentMetadata.getValue());
return documentMetadata;
}
/**
* Returns the list of all metadata values on a document.
*
* @param documentId Document ID
* @return List of metadata
*/
@SuppressWarnings("unchecked")
public List<DocumentMetadataDto> getByDocumentId(String documentId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
StringBuilder sb = new StringBuilder("select dm.DME_ID_C, dm.DME_IDDOCUMENT_C, dm.DME_IDMETADATA_C, dm.DME_VALUE_C, m.MET_TYPE_C");
sb.append(" from T_DOCUMENT_METADATA dm, T_METADATA m ");
sb.append(" where dm.DME_IDMETADATA_C = m.MET_ID_C and dm.DME_IDDOCUMENT_C = :documentId and m.MET_DELETEDATE_D is null");
// Perform the search
Query q = em.createNativeQuery(sb.toString());
q.setParameter("documentId", documentId);
List<Object[]> l = q.getResultList();
// Assemble results
List<DocumentMetadataDto> dtoList = new ArrayList<>();
for (Object[] o : l) {
int i = 0;
DocumentMetadataDto dto = new DocumentMetadataDto();
dto.setId((String) o[i++]);
dto.setDocumentId((String) o[i++]);
dto.setMetadataId((String) o[i++]);
dto.setValue((String) o[i++]);
dto.setType(MetadataType.valueOf((String) o[i]));
dtoList.add(dto);
}
return dtoList;
}
}

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.model.jpa.File;
@@ -43,12 +43,16 @@ public class FileDao {
/**
* Returns the list of all files.
*
* @param offset Offset
* @param limit Limit
* @return List of files
*/
@SuppressWarnings("unchecked")
public List<File> findAll() {
public List<File> findAll(int offset, int limit) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query q = em.createQuery("select f from File f where f.deleteDate is null");
q.setFirstResult(offset);
q.setMaxResults(limit);
return q.getResultList();
}
@@ -140,9 +144,12 @@ public class FileDao {
// Update the file
fileDb.setDocumentId(file.getDocumentId());
fileDb.setName(file.getName());
fileDb.setContent(file.getContent());
fileDb.setOrder(file.getOrder());
fileDb.setMimeType(file.getMimeType());
fileDb.setVersionId(file.getVersionId());
fileDb.setLatestVersion(file.isLatestVersion());
return file;
}
@@ -175,12 +182,26 @@ public class FileDao {
public List<File> getByDocumentId(String userId, String documentId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
if (documentId == null) {
Query q = em.createQuery("select f from File f where f.documentId is null and f.deleteDate is null and f.userId = :userId order by f.createDate asc");
Query q = em.createQuery("select f from File f where f.documentId is null and f.deleteDate is null and f.latestVersion = true and f.userId = :userId order by f.createDate asc");
q.setParameter("userId", userId);
return q.getResultList();
}
Query q = em.createQuery("select f from File f where f.documentId = :documentId and f.deleteDate is null order by f.order asc");
Query q = em.createQuery("select f from File f where f.documentId = :documentId and f.latestVersion = true and f.deleteDate is null order by f.order asc");
q.setParameter("documentId", documentId);
return q.getResultList();
}
/**
* Get all files from a version.
*
* @param versionId Version ID
* @return List of files
*/
@SuppressWarnings("unchecked")
public List<File> getByVersionId(String versionId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query q = em.createQuery("select f from File f where f.versionId = :versionId and f.deleteDate is null order by f.order asc");
q.setParameter("versionId", versionId);
return q.getResultList();
}
}

View File

@@ -1,9 +1,9 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.google.common.base.Joiner;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria;
import com.sismics.docs.core.dao.jpa.dto.GroupDto;
import com.sismics.docs.core.dao.criteria.GroupCriteria;
import com.sismics.docs.core.dao.dto.GroupDto;
import com.sismics.docs.core.model.jpa.Group;
import com.sismics.docs.core.model.jpa.UserGroup;
import com.sismics.docs.core.util.AuditLogUtil;

View File

@@ -0,0 +1,148 @@
package com.sismics.docs.core.dao;
import com.google.common.base.Joiner;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.constant.MetadataType;
import com.sismics.docs.core.dao.criteria.MetadataCriteria;
import com.sismics.docs.core.dao.dto.MetadataDto;
import com.sismics.docs.core.model.jpa.Metadata;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.docs.core.util.jpa.QueryParam;
import com.sismics.docs.core.util.jpa.QueryUtil;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.util.context.ThreadLocalContext;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.util.*;
/**
* Metadata DAO.
*
* @author bgamard
*/
public class MetadataDao {
/**
* Creates a new metdata.
*
* @param metadata Metadata
* @param userId User ID
* @return New ID
*/
public String create(Metadata metadata, String userId) {
// Create the UUID
metadata.setId(UUID.randomUUID().toString());
// Create the metadata
EntityManager em = ThreadLocalContext.get().getEntityManager();
em.persist(metadata);
// Create audit log
AuditLogUtil.create(metadata, AuditLogType.CREATE, userId);
return metadata.getId();
}
/**
* Update a metadata.
*
* @param metadata Metadata to update
* @param userId User ID
* @return Updated metadata
*/
public Metadata update(Metadata metadata, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the metadata
Query q = em.createQuery("select r from Metadata r where r.id = :id and r.deleteDate is null");
q.setParameter("id", metadata.getId());
Metadata metadataDb = (Metadata) q.getSingleResult();
// Update the metadata
metadataDb.setName(metadata.getName());
// Create audit log
AuditLogUtil.create(metadataDb, AuditLogType.UPDATE, userId);
return metadataDb;
}
/**
* Gets an active metadata by its ID.
*
* @param id Metadata ID
* @return Metadata
*/
public Metadata getActiveById(String id) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
try {
Query q = em.createQuery("select r from Metadata r where r.id = :id and r.deleteDate is null");
q.setParameter("id", id);
return (Metadata) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* Deletes a metadata.
*
* @param id Metadata ID
* @param userId User ID
*/
public void delete(String id, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the metadata
Query q = em.createQuery("select r from Metadata r where r.id = :id and r.deleteDate is null");
q.setParameter("id", id);
Metadata metadataDb = (Metadata) q.getSingleResult();
// Delete the metadata
Date dateNow = new Date();
metadataDb.setDeleteDate(dateNow);
// Create audit log
AuditLogUtil.create(metadataDb, AuditLogType.DELETE, userId);
}
/**
* Returns the list of all metadata.
*
* @param criteria Search criteria
* @param sortCriteria Sort criteria
* @return List of metadata
*/
public List<MetadataDto> findByCriteria(MetadataCriteria criteria, SortCriteria sortCriteria) {
Map<String, Object> parameterMap = new HashMap<>();
List<String> criteriaList = new ArrayList<>();
StringBuilder sb = new StringBuilder("select m.MET_ID_C c0, m.MET_NAME_C c1, m.MET_TYPE_C c2");
sb.append(" from T_METADATA m ");
criteriaList.add("m.MET_DELETEDATE_D is null");
if (!criteriaList.isEmpty()) {
sb.append(" where ");
sb.append(Joiner.on(" and ").join(criteriaList));
}
// Perform the search
QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria);
@SuppressWarnings("unchecked")
List<Object[]> l = QueryUtil.getNativeQuery(queryParam).getResultList();
// Assemble results
List<MetadataDto> dtoList = new ArrayList<>();
for (Object[] o : l) {
int i = 0;
MetadataDto dto = new MetadataDto();
dto.setId((String) o[i++]);
dto.setName((String) o[i++]);
dto.setType(MetadataType.valueOf((String) o[i]));
dtoList.add(dto);
}
return dtoList;
}
}

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.sismics.docs.core.constant.Constants;
import com.sismics.docs.core.model.jpa.PasswordRecovery;

View File

@@ -1,17 +1,12 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import com.sismics.docs.core.dao.dto.RelationDto;
import com.sismics.docs.core.model.jpa.Relation;
import com.sismics.util.context.ThreadLocalContext;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import com.sismics.docs.core.dao.jpa.dto.RelationDto;
import com.sismics.docs.core.model.jpa.Relation;
import com.sismics.util.context.ThreadLocalContext;
import java.util.*;
/**
* Relation DAO.
@@ -41,13 +36,13 @@ public class RelationDao {
List<Object[]> l = q.getResultList();
// Assemble results
List<RelationDto> relationDtoList = new ArrayList<RelationDto>();
List<RelationDto> relationDtoList = new ArrayList<>();
for (Object[] o : l) {
int i = 0;
RelationDto relationDto = new RelationDto();
relationDto.setId((String) o[i++]);
relationDto.setTitle((String) o[i++]);
String fromDocId = (String) o[i++];
String fromDocId = (String) o[i];
relationDto.setSource(documentId.equals(fromDocId));
relationDtoList.add(relationDto);
}

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.google.common.collect.Sets;
import com.sismics.util.context.ThreadLocalContext;

View File

@@ -1,9 +1,9 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.google.common.base.Joiner;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.dao.jpa.criteria.RouteCriteria;
import com.sismics.docs.core.dao.jpa.dto.RouteDto;
import com.sismics.docs.core.dao.criteria.RouteCriteria;
import com.sismics.docs.core.dao.dto.RouteDto;
import com.sismics.docs.core.model.jpa.Route;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.docs.core.util.jpa.QueryParam;
@@ -91,16 +91,21 @@ public class RouteDao {
* Deletes a route and the associated steps.
*
* @param routeId Route ID
* @param userId User ID
*/
public void deleteRoute(String routeId) {
public void deleteRoute(String routeId, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
em.createNativeQuery("update T_ROUTE_STEP rs set rs.RTP_DELETEDATE_D = :dateNow where rs.RTP_IDROUTE_C = :routeId and rs.RTP_DELETEDATE_D is null")
// Create audit log
Route route = em.find(Route.class, routeId);
AuditLogUtil.create(route, AuditLogType.DELETE, userId);
em.createNativeQuery("update T_ROUTE_STEP rs set RTP_DELETEDATE_D = :dateNow where rs.RTP_IDROUTE_C = :routeId and rs.RTP_DELETEDATE_D is null")
.setParameter("routeId", routeId)
.setParameter("dateNow", new Date())
.executeUpdate();
em.createNativeQuery("update T_ROUTE r set r.RTE_DELETEDATE_D = :dateNow where r.RTE_ID_C = :routeId and r.RTE_DELETEDATE_D is null")
em.createNativeQuery("update T_ROUTE r set RTE_DELETEDATE_D = :dateNow where r.RTE_ID_C = :routeId and r.RTE_DELETEDATE_D is null")
.setParameter("routeId", routeId)
.setParameter("dateNow", new Date())
.executeUpdate();

View File

@@ -1,11 +1,12 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.google.common.base.Joiner;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.dao.jpa.criteria.RouteModelCriteria;
import com.sismics.docs.core.dao.jpa.dto.RouteModelDto;
import com.sismics.docs.core.dao.criteria.RouteModelCriteria;
import com.sismics.docs.core.dao.dto.RouteModelDto;
import com.sismics.docs.core.model.jpa.RouteModel;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.docs.core.util.SecurityUtil;
import com.sismics.docs.core.util.jpa.QueryParam;
import com.sismics.docs.core.util.jpa.QueryUtil;
import com.sismics.docs.core.util.jpa.SortCriteria;
@@ -60,7 +61,7 @@ public class RouteModelDao {
q.setParameter("id", routeModel.getId());
RouteModel routeModelDb = (RouteModel) q.getSingleResult();
// Update the group
// Update the route model
routeModelDb.setName(routeModel.getName());
routeModelDb.setSteps(routeModel.getSteps());
@@ -87,6 +88,18 @@ public class RouteModelDao {
}
}
/**
* Returns the list of all route models.
*
* @return List of route models
*/
@SuppressWarnings("unchecked")
public List<RouteModel> findAll() {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query q = em.createQuery("select r from RouteModel r where r.deleteDate is null");
return q.getResultList();
}
/**
* Deletes a route model.
*
@@ -124,6 +137,12 @@ public class RouteModelDao {
sb.append(" from T_ROUTE_MODEL rm ");
// Add search criterias
if (criteria.getTargetIdList() != null && !SecurityUtil.skipAclCheck(criteria.getTargetIdList())) {
sb.append(" left join T_ACL a on a.ACL_TARGETID_C in (:targetIdList) and a.ACL_SOURCEID_C = rm.RTM_ID_C and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null ");
criteriaList.add("a.ACL_ID_C is not null");
parameterMap.put("targetIdList", criteria.getTargetIdList());
}
criteriaList.add("rm.RTM_DELETEDATE_D is null");
if (!criteriaList.isEmpty()) {

View File

@@ -1,11 +1,11 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.google.common.base.Joiner;
import com.sismics.docs.core.constant.AclTargetType;
import com.sismics.docs.core.constant.RouteStepTransition;
import com.sismics.docs.core.constant.RouteStepType;
import com.sismics.docs.core.dao.jpa.criteria.RouteStepCriteria;
import com.sismics.docs.core.dao.jpa.dto.RouteStepDto;
import com.sismics.docs.core.dao.criteria.RouteStepCriteria;
import com.sismics.docs.core.dao.dto.RouteStepDto;
import com.sismics.docs.core.model.jpa.RouteStep;
import com.sismics.docs.core.util.jpa.QueryParam;
import com.sismics.docs.core.util.jpa.QueryUtil;
@@ -68,7 +68,7 @@ public class RouteStepDao {
Map<String, Object> parameterMap = new HashMap<>();
List<String> criteriaList = new ArrayList<>();
StringBuilder sb = new StringBuilder("select rs.RTP_ID_C, rs.RTP_NAME_C c0, rs.RTP_TYPE_C c1, rs.RTP_TRANSITION_C c2, rs.RTP_COMMENT_C c3, rs.RTP_IDTARGET_C c4, u.USE_USERNAME_C as targetUsername, g.GRP_NAME_C, rs.RTP_ENDDATE_D c5, uv.USE_USERNAME_C as validatorUsername, rs.RTP_IDROUTE_C, rs.RTP_ORDER_N c6")
StringBuilder sb = new StringBuilder("select rs.RTP_ID_C, rs.RTP_NAME_C c0, rs.RTP_TYPE_C c1, rs.RTP_TRANSITION_C c2, rs.RTP_COMMENT_C c3, rs.RTP_IDTARGET_C c4, u.USE_USERNAME_C as targetUsername, g.GRP_NAME_C, rs.RTP_ENDDATE_D c5, uv.USE_USERNAME_C as validatorUsername, rs.RTP_IDROUTE_C, rs.RTP_TRANSITIONS_C, rs.RTP_ORDER_N c6")
.append(" from T_ROUTE_STEP rs ")
.append(" join T_ROUTE r on r.RTE_ID_C = rs.RTP_IDROUTE_C ")
.append(" left join T_USER uv on uv.USE_ID_C = rs.RTP_IDVALIDATORUSER_C ")
@@ -124,7 +124,8 @@ public class RouteStepDao {
Timestamp endDateTimestamp = (Timestamp) o[i++];
dto.setEndDateTimestamp(endDateTimestamp == null ? null : endDateTimestamp.getTime());
dto.setValidatorUserName((String) o[i++]);
dto.setRouteId((String) o[i]);
dto.setRouteId((String) o[i++]);
dto.setTransitions((String) o[i]);
dtoList.add(dto);
}
return dtoList;
@@ -140,7 +141,7 @@ public class RouteStepDao {
*/
public void endRouteStep(String id, RouteStepTransition transition, String comment, String validatorUserId) {
StringBuilder sb = new StringBuilder("update T_ROUTE_STEP r ");
sb.append(" set r.RTP_ENDDATE_D = :endDate, r.RTP_TRANSITION_C = :transition, r.RTP_COMMENT_C = :comment, r.RTP_IDVALIDATORUSER_C = :validatorUserId ");
sb.append(" set RTP_ENDDATE_D = :endDate, RTP_TRANSITION_C = :transition, RTP_COMMENT_C = :comment, RTP_IDVALIDATORUSER_C = :validatorUserId ");
sb.append(" where r.RTP_ID_C = :id");
EntityManager em = ThreadLocalContext.get().getEntityManager();

View File

@@ -1,14 +1,13 @@
package com.sismics.docs.core.dao.jpa;
import java.util.Date;
import java.util.UUID;
import javax.persistence.EntityManager;
import javax.persistence.Query;
package com.sismics.docs.core.dao;
import com.sismics.docs.core.model.jpa.Share;
import com.sismics.util.context.ThreadLocalContext;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.Date;
import java.util.UUID;
/**
* Share DAO.
*

View File

@@ -1,12 +1,13 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.google.common.base.Joiner;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.dao.jpa.criteria.TagCriteria;
import com.sismics.docs.core.dao.jpa.dto.TagDto;
import com.sismics.docs.core.dao.criteria.TagCriteria;
import com.sismics.docs.core.dao.dto.TagDto;
import com.sismics.docs.core.model.jpa.DocumentTag;
import com.sismics.docs.core.model.jpa.Tag;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.docs.core.util.SecurityUtil;
import com.sismics.docs.core.util.jpa.QueryParam;
import com.sismics.docs.core.util.jpa.QueryUtil;
import com.sismics.docs.core.util.jpa.SortCriteria;
@@ -131,6 +132,10 @@ public class TagDao {
q.setParameter("dateNow", dateNow);
q.executeUpdate();
q = em.createQuery("update Tag t set t.parentId = null where t.parentId = :tagId and t.deleteDate is null");
q.setParameter("tagId", tagId);
q.executeUpdate();
// Create audit log
AuditLogUtil.create(tagDb, AuditLogType.DELETE, userId);
}
@@ -181,7 +186,7 @@ public class TagDao {
criteriaList.add("t.TAG_ID_C = :id");
parameterMap.put("id", criteria.getId());
}
if (criteria.getTargetIdList() != null) {
if (criteria.getTargetIdList() != null && !SecurityUtil.skipAclCheck(criteria.getTargetIdList())) {
sb.append(" left join T_ACL a on a.ACL_TARGETID_C in (:targetIdList) and a.ACL_SOURCEID_C = t.TAG_ID_C and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null ");
criteriaList.add("a.ACL_ID_C is not null");
parameterMap.put("targetIdList", criteria.getTargetIdList());
@@ -191,14 +196,6 @@ public class TagDao {
criteriaList.add("dt.DOT_IDDOCUMENT_C = :documentId");
parameterMap.put("documentId", criteria.getDocumentId());
}
if (criteria.getName() != null) {
criteriaList.add("t.TAG_NAME_C = :name");
parameterMap.put("name", criteria.getName());
}
if (criteria.getNameLike() != null) {
criteriaList.add("t.TAG_NAME_C like :nameLike");
parameterMap.put("nameLike", criteria.getNameLike() + "%");
}
criteriaList.add("t.TAG_DELETEDATE_D is null");

View File

@@ -1,11 +1,12 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.google.common.base.Joiner;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
import com.sismics.docs.core.dao.jpa.dto.UserDto;
import com.sismics.docs.core.dao.criteria.UserCriteria;
import com.sismics.docs.core.dao.dto.UserDto;
import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.docs.core.util.EncryptionUtil;
import com.sismics.docs.core.util.jpa.QueryParam;
import com.sismics.docs.core.util.jpa.QueryUtil;
import com.sismics.docs.core.util.jpa.SortCriteria;
@@ -71,6 +72,8 @@ public class UserDao {
// Create the user
user.setCreateDate(new Date());
user.setPassword(hashPassword(user.getPassword()));
user.setPrivateKey(EncryptionUtil.generatePrivateKey());
user.setStorageCurrent(0L);
em.persist(user);
// Create audit log
@@ -168,6 +171,26 @@ public class UserDao {
return user;
}
/**
* Update the onboarding status.
*
* @param user User to update
* @return Updated user
*/
public User updateOnboarding(User user) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the user
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
q.setParameter("id", user.getId());
User userDb = (User) q.getSingleResult();
// Update the user
userDb.setOnboarding(user.isOnboarding());
return user;
}
/**
* Gets a user by its ID.
*

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.sismics.docs.core.model.jpa.Vocabulary;
import com.sismics.util.context.ThreadLocalContext;

View File

@@ -0,0 +1,123 @@
package com.sismics.docs.core.dao;
import com.google.common.base.Joiner;
import com.sismics.docs.core.dao.criteria.WebhookCriteria;
import com.sismics.docs.core.dao.dto.WebhookDto;
import com.sismics.docs.core.model.jpa.Webhook;
import com.sismics.docs.core.util.jpa.QueryParam;
import com.sismics.docs.core.util.jpa.QueryUtil;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.util.context.ThreadLocalContext;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.sql.Timestamp;
import java.util.*;
/**
* Webhook DAO.
*
* @author bgamard
*/
public class WebhookDao {
/**
* Returns the list of all webhooks.
*
* @param criteria Search criteria
* @param sortCriteria Sort criteria
* @return List of webhooks
*/
public List<WebhookDto> findByCriteria(WebhookCriteria criteria, SortCriteria sortCriteria) {
Map<String, Object> parameterMap = new HashMap<>();
List<String> criteriaList = new ArrayList<>();
StringBuilder sb = new StringBuilder("select w.WHK_ID_C as c0, w.WHK_EVENT_C as c1, w.WHK_URL_C as c2, w.WHK_CREATEDATE_D as c3 ");
sb.append(" from T_WEBHOOK w ");
// Add search criterias
if (criteria.getEvent() != null) {
criteriaList.add("w.WHK_EVENT_C = :event");
parameterMap.put("event", criteria.getEvent().name());
}
criteriaList.add("w.WHK_DELETEDATE_D is null");
if (!criteriaList.isEmpty()) {
sb.append(" where ");
sb.append(Joiner.on(" and ").join(criteriaList));
}
// Perform the search
QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria);
@SuppressWarnings("unchecked")
List<Object[]> l = QueryUtil.getNativeQuery(queryParam).getResultList();
// Assemble results
List<WebhookDto> webhookDtoList = new ArrayList<>();
for (Object[] o : l) {
int i = 0;
WebhookDto webhookDto = new WebhookDto()
.setId((String) o[i++])
.setEvent((String) o[i++])
.setUrl((String) o[i++])
.setCreateTimestamp(((Timestamp) o[i]).getTime());
webhookDtoList.add(webhookDto);
}
return webhookDtoList;
}
/**
* Returns a webhook by ID.
*
* @param id Webhook ID
* @return Webhook
*/
public Webhook getActiveById(String id) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query q = em.createQuery("select w from Webhook w where w.id = :id and w.deleteDate is null");
q.setParameter("id", id);
try {
return (Webhook) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* Creates a new webhook.
*
* @param webhook Webhook
* @return New ID
*/
public String create(Webhook webhook) {
// Create the UUID
webhook.setId(UUID.randomUUID().toString());
// Create the webhook
EntityManager em = ThreadLocalContext.get().getEntityManager();
webhook.setCreateDate(new Date());
em.persist(webhook);
return webhook.getId();
}
/**
* Deletes a webhook.
*
* @param webhookId Webhook ID
*/
public void delete(String webhookId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the group
Query q = em.createQuery("select w from Webhook w where w.id = :id and w.deleteDate is null");
q.setParameter("id", webhookId);
Webhook webhookDb = (Webhook) q.getSingleResult();
// Delete the group
Date dateNow = new Date();
webhookDb.setDeleteDate(dateNow);
}
}

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.criteria;
package com.sismics.docs.core.dao.criteria;
/**
@@ -17,6 +17,11 @@ public class AuditLogCriteria {
*/
private String userId;
/**
* The search is done for an admin user.
*/
private boolean isAdmin = false;
public String getDocumentId() {
return documentId;
}
@@ -32,4 +37,13 @@ public class AuditLogCriteria {
public void setUserId(String userId) {
this.userId = userId;
}
public boolean isAdmin() {
return isAdmin;
}
public AuditLogCriteria setAdmin(boolean admin) {
isAdmin = admin;
return this;
}
}

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.criteria;
package com.sismics.docs.core.dao.criteria;
import java.util.Date;
import java.util.List;
@@ -36,9 +36,26 @@ public class DocumentCriteria {
private Date createDateMax;
/**
* Tag IDs.
* Minimum update date.
*/
private List<String> tagIdList;
private Date updateDateMin;
/**
* Maximum update date.
*/
private Date updateDateMax;
/**
* Tag IDs.
* The first level list will be AND'ed and the second level list will be OR'ed.
*/
private List<List<String>> tagIdList;
/**
* Tag IDs to excluded.
* The first and second level list will be excluded.
*/
private List<List<String>> excludedTagIdList;
/**
* Shared status.
@@ -55,6 +72,11 @@ public class DocumentCriteria {
*/
private String creatorId;
/**
* A route is active.
*/
private Boolean activeRoute;
public List<String> getTargetIdList() {
return targetIdList;
}
@@ -95,14 +117,23 @@ public class DocumentCriteria {
this.createDateMax = createDateMax;
}
public List<String> getTagIdList() {
public List<List<String>> getTagIdList() {
return tagIdList;
}
public void setTagIdList(List<String> tagIdList) {
public void setTagIdList(List<List<String>> tagIdList) {
this.tagIdList = tagIdList;
}
public List<List<String>> getExcludedTagIdList() {
return excludedTagIdList;
}
public DocumentCriteria setExcludedTagIdList(List<List<String>> excludedTagIdList) {
this.excludedTagIdList = excludedTagIdList;
return this;
}
public Boolean getShared() {
return shared;
}
@@ -126,4 +157,28 @@ public class DocumentCriteria {
public void setCreatorId(String creatorId) {
this.creatorId = creatorId;
}
public Boolean getActiveRoute() {
return activeRoute;
}
public Date getUpdateDateMin() {
return updateDateMin;
}
public void setUpdateDateMin(Date updateDateMin) {
this.updateDateMin = updateDateMin;
}
public Date getUpdateDateMax() {
return updateDateMax;
}
public void setUpdateDateMax(Date updateDateMax) {
this.updateDateMax = updateDateMax;
}
public void setActiveRoute(Boolean activeRoute) {
this.activeRoute = activeRoute;
}
}

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.criteria;
package com.sismics.docs.core.dao.criteria;
/**
* Group criteria.

View File

@@ -0,0 +1,9 @@
package com.sismics.docs.core.dao.criteria;
/**
* Metadata criteria.
*
* @author bgamard
*/
public class MetadataCriteria {
}

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.criteria;
package com.sismics.docs.core.dao.criteria;
/**

View File

@@ -0,0 +1,25 @@
package com.sismics.docs.core.dao.criteria;
import java.util.List;
/**
* Route model criteria.
*
* @author bgamard
*/
public class RouteModelCriteria {
/**
* ACL target ID list.
*/
private List<String> targetIdList;
public List<String> getTargetIdList() {
return targetIdList;
}
public RouteModelCriteria setTargetIdList(List<String> targetIdList) {
this.targetIdList = targetIdList;
return this;
}
}

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.criteria;
package com.sismics.docs.core.dao.criteria;
/**

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.criteria;
package com.sismics.docs.core.dao.criteria;
import java.util.List;
@@ -23,16 +23,6 @@ public class TagCriteria {
*/
private String documentId;
/**
* Tag name.
*/
private String name;
/**
* Approximate tag name.
*/
private String nameLike;
public String getId() {
return id;
}
@@ -59,22 +49,4 @@ public class TagCriteria {
this.documentId = documentId;
return this;
}
public String getName() {
return name;
}
public TagCriteria setName(String name) {
this.name = name;
return this;
}
public String getNameLike() {
return nameLike;
}
public TagCriteria setNameLike(String nameLike) {
this.nameLike = nameLike;
return this;
}
}

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.criteria;
package com.sismics.docs.core.dao.criteria;
/**
* User criteria.

View File

@@ -0,0 +1,24 @@
package com.sismics.docs.core.dao.criteria;
import com.sismics.docs.core.constant.WebhookEvent;
/**
* Webhook criteria.
*
* @author bgamard
*/
public class WebhookCriteria {
/**
* Webhook event.
*/
private WebhookEvent event;
public WebhookEvent getEvent() {
return event;
}
public WebhookCriteria setEvent(WebhookEvent event) {
this.event = event;
return this;
}
}

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.dto;
package com.sismics.docs.core.dao.dto;
import com.sismics.docs.core.constant.PermType;

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.dto;
package com.sismics.docs.core.dao.dto;
import com.sismics.docs.core.constant.AuditLogType;

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.dto;
package com.sismics.docs.core.dao.dto;
/**
* Comment DTO.

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.dto;
package com.sismics.docs.core.dao.dto;
/**
* Contributor DTO.

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.dto;
package com.sismics.docs.core.dao.dto;
/**
* Document DTO.
@@ -11,6 +11,11 @@ public class DocumentDto {
*/
private String id;
/**
* Main file ID.
*/
private String fileId;
/**
* Title.
*/
@@ -71,6 +76,11 @@ public class DocumentDto {
*/
private Long createTimestamp;
/**
* Update date.
*/
private Long updateTimestamp;
/**
* Shared status.
*/
@@ -86,6 +96,21 @@ public class DocumentDto {
*/
private String creator;
/**
* A route is active.
*/
private boolean activeRoute;
/**
* Current route step name.
*/
private String currentStepName;
/**
* Search highlight.
*/
private String highlight;
public String getId() {
return id;
}
@@ -94,6 +119,15 @@ public class DocumentDto {
this.id = id;
}
public String getFileId() {
return fileId;
}
public DocumentDto setFileId(String fileId) {
this.fileId = fileId;
return this;
}
public String getTitle() {
return title;
}
@@ -213,4 +247,38 @@ public class DocumentDto {
public void setCreator(String creator) {
this.creator = creator;
}
public boolean isActiveRoute() {
return activeRoute;
}
public void setActiveRoute(boolean activeRoute) {
this.activeRoute = activeRoute;
}
public String getCurrentStepName() {
return currentStepName;
}
public Long getUpdateTimestamp() {
return updateTimestamp;
}
public void setUpdateTimestamp(Long updateTimestamp) {
this.updateTimestamp = updateTimestamp;
}
public DocumentDto setCurrentStepName(String currentStepName) {
this.currentStepName = currentStepName;
return this;
}
public String getHighlight() {
return highlight;
}
public DocumentDto setHighlight(String highlight) {
this.highlight = highlight;
return this;
}
}

View File

@@ -0,0 +1,94 @@
package com.sismics.docs.core.dao.dto;
import com.sismics.docs.core.constant.MetadataType;
/**
* Document metadata DTO.
*
* @author bgamard
*/
public class DocumentMetadataDto {
/**
* Document metadata ID.
*/
private String id;
/**
* Document ID.
*/
private String documentId;
/**
* Metadata ID.
*/
private String metadataId;
/**
* Name.
*/
private String name;
/**
* Value.
*/
private String value;
/**
* Type.
*/
private MetadataType type;
public String getId() {
return id;
}
public DocumentMetadataDto setId(String id) {
this.id = id;
return this;
}
public String getName() {
return name;
}
public DocumentMetadataDto setName(String name) {
this.name = name;
return this;
}
public MetadataType getType() {
return type;
}
public DocumentMetadataDto setType(MetadataType type) {
this.type = type;
return this;
}
public String getDocumentId() {
return documentId;
}
public DocumentMetadataDto setDocumentId(String documentId) {
this.documentId = documentId;
return this;
}
public String getMetadataId() {
return metadataId;
}
public DocumentMetadataDto setMetadataId(String metadataId) {
this.metadataId = metadataId;
return this;
}
public String getValue() {
return value;
}
public DocumentMetadataDto setValue(String value) {
this.value = value;
return this;
}
}

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.dto;
package com.sismics.docs.core.dao.dto;
/**
* Group DTO.

View File

@@ -0,0 +1,52 @@
package com.sismics.docs.core.dao.dto;
import com.sismics.docs.core.constant.MetadataType;
/**
* Metadata DTO.
*
* @author bgamard
*/
public class MetadataDto {
/**
* Metadata ID.
*/
private String id;
/**
* Name.
*/
private String name;
/**
* Type.
*/
private MetadataType type;
public String getId() {
return id;
}
public MetadataDto setId(String id) {
this.id = id;
return this;
}
public String getName() {
return name;
}
public MetadataDto setName(String name) {
this.name = name;
return this;
}
public MetadataType getType() {
return type;
}
public MetadataDto setType(MetadataType type) {
this.type = type;
return this;
}
}

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.dto;
package com.sismics.docs.core.dao.dto;
/**
* Tag DTO.

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.dto;
package com.sismics.docs.core.dao.dto;
/**
* Route DTO.

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.dto;
package com.sismics.docs.core.dao.dto;
/**
* Route model DTO.

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.dto;
package com.sismics.docs.core.dao.dto;
import com.sismics.docs.core.constant.RouteStepType;
import com.sismics.util.JsonUtil;
@@ -62,6 +62,11 @@ public class RouteStepDto {
*/
private String validatorUserName;
/**
* Transitions data.
*/
private String transitions;
/**
* Route ID.
*/
@@ -166,6 +171,15 @@ public class RouteStepDto {
return this;
}
public String getTransitions() {
return transitions;
}
public RouteStepDto setTransitions(String transitions) {
this.transitions = transitions;
return this;
}
/**
* Transform in JSON.
*

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.dto;
package com.sismics.docs.core.dao.dto;
/**
* Tag DTO.

View File

@@ -1,4 +1,4 @@
package com.sismics.docs.core.dao.jpa.dto;
package com.sismics.docs.core.dao.dto;
import com.google.common.base.MoreObjects;

View File

@@ -0,0 +1,74 @@
package com.sismics.docs.core.dao.dto;
/**
* Webhook DTO.
*
* @author bgamard
*/
public class WebhookDto {
/**
* Webhook ID.
*/
private String id;
/**
* Event.
*/
private String event;
/**
* URL.
*/
private String url;
/**
* Creation date.
*/
private Long createTimestamp;
public String getId() {
return id;
}
public WebhookDto setId(String id) {
this.id = id;
return this;
}
public String getEvent() {
return event;
}
public WebhookDto setEvent(String event) {
this.event = event;
return this;
}
public String getUrl() {
return url;
}
public WebhookDto setUrl(String url) {
this.url = url;
return this;
}
public Long getCreateTimestamp() {
return createTimestamp;
}
public WebhookDto setCreateTimestamp(Long createTimestamp) {
this.createTimestamp = createTimestamp;
return this;
}
@Override
public boolean equals(Object obj) {
return id.equals(((WebhookDto) obj).getId());
}
@Override
public int hashCode() {
return id.hashCode();
}
}

View File

@@ -1,10 +0,0 @@
package com.sismics.docs.core.dao.jpa.criteria;
/**
* Route model criteria.
*
* @author bgamard
*/
public class RouteModelCriteria {
}

View File

@@ -1,30 +0,0 @@
package com.sismics.docs.core.dao.jpa.dto;
/**
* Tag stat DTO.
*
* @author bgamard
*/
public class TagStatDto extends TagDto {
private int count;
/**
* Getter of count.
*
* @return the count
*/
public int getCount() {
return count;
}
/**
* Setter of count.
*
* @param count count
*/
public void setCount(int count) {
this.count = count;
}
}

View File

@@ -1,238 +0,0 @@
package com.sismics.docs.core.dao.lucene;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.flexible.standard.QueryParserUtil;
import org.apache.lucene.queryparser.flexible.standard.StandardQueryParser;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import com.sismics.docs.core.model.context.AppContext;
import com.sismics.docs.core.model.jpa.Document;
import com.sismics.docs.core.model.jpa.File;
import com.sismics.docs.core.util.LuceneUtil;
import com.sismics.docs.core.util.LuceneUtil.LuceneRunnable;
/**
* Lucene DAO.
*
* @author bgamard
*/
public class LuceneDao {
/**
* Destroy and rebuild index.
*
* @param fileList List of files
*/
public void rebuildIndex(final List<Document> documentList, final List<File> fileList) {
LuceneUtil.handle(new LuceneRunnable() {
@Override
public void run(IndexWriter indexWriter) throws Exception {
// Empty index
indexWriter.deleteAll();
// Add all documents
for (Document document : documentList) {
org.apache.lucene.document.Document luceneDocument = getDocumentFromDocument(document);
indexWriter.addDocument(luceneDocument);
}
// Add all files
for (File file : fileList) {
org.apache.lucene.document.Document luceneDocument = getDocumentFromFile(file);
indexWriter.addDocument(luceneDocument);
}
}
});
}
/**
* Add document to the index.
*
* @param document Document to add
*/
public void createDocument(final Document document) {
LuceneUtil.handle(new LuceneRunnable() {
@Override
public void run(IndexWriter indexWriter) throws Exception {
org.apache.lucene.document.Document luceneDocument = getDocumentFromDocument(document);
indexWriter.addDocument(luceneDocument);
}
});
}
/**
* Add file to the index.
*
* @param file File to add
*/
public void createFile(final File file) {
LuceneUtil.handle(new LuceneRunnable() {
@Override
public void run(IndexWriter indexWriter) throws Exception {
org.apache.lucene.document.Document luceneDocument = getDocumentFromFile(file);
indexWriter.addDocument(luceneDocument);
}
});
}
/**
* Update document index.
*
* @param document Updated document
*/
public void updateDocument(final Document document) {
LuceneUtil.handle(new LuceneRunnable() {
@Override
public void run(IndexWriter indexWriter) throws Exception {
org.apache.lucene.document.Document luceneDocument = getDocumentFromDocument(document);
indexWriter.updateDocument(new Term("id", document.getId()), luceneDocument);
}
});
}
/**
* Delete document from the index.
*
* @param id Document ID to delete
*/
public void deleteDocument(final String id) {
LuceneUtil.handle(new LuceneRunnable() {
@Override
public void run(IndexWriter indexWriter) throws Exception {
indexWriter.deleteDocuments(new Term("id", id));
}
});
}
/**
* Search files.
*
* @param searchQuery Search query on title and description
* @param fullSearchQuery Search query on all fields
* @return List of document IDs
* @throws Exception
*/
public Set<String> search(String searchQuery, String fullSearchQuery) throws Exception {
// Escape query and add quotes so QueryParser generate a PhraseQuery
searchQuery = "\"" + QueryParserUtil.escape(searchQuery + " " + fullSearchQuery) + "\"";
fullSearchQuery = "\"" + QueryParserUtil.escape(fullSearchQuery) + "\"";
// Build search query
StandardQueryParser qpHelper = new StandardQueryParser(new StandardAnalyzer());
qpHelper.setPhraseSlop(100000); // PhraseQuery add terms
// Search on documents and files
BooleanQuery query = new BooleanQuery.Builder()
.add(qpHelper.parse(searchQuery, "title"), Occur.SHOULD)
.add(qpHelper.parse(searchQuery, "description"), Occur.SHOULD)
.add(qpHelper.parse(searchQuery, "subject"), Occur.SHOULD)
.add(qpHelper.parse(searchQuery, "identifier"), Occur.SHOULD)
.add(qpHelper.parse(searchQuery, "publisher"), Occur.SHOULD)
.add(qpHelper.parse(searchQuery, "format"), Occur.SHOULD)
.add(qpHelper.parse(searchQuery, "source"), Occur.SHOULD)
.add(qpHelper.parse(searchQuery, "type"), Occur.SHOULD)
.add(qpHelper.parse(searchQuery, "coverage"), Occur.SHOULD)
.add(qpHelper.parse(searchQuery, "rights"), Occur.SHOULD)
.add(qpHelper.parse(fullSearchQuery, "content"), Occur.SHOULD)
.build();
// Search
DirectoryReader directoryReader = AppContext.getInstance().getIndexingService().getDirectoryReader();
Set<String> documentIdList = new HashSet<>();
if (directoryReader == null) {
// The directory reader is not yet initialized (probably because there is nothing indexed)
return documentIdList;
}
IndexSearcher searcher = new IndexSearcher(directoryReader);
TopDocs topDocs = searcher.search(query, Integer.MAX_VALUE);
ScoreDoc[] docs = topDocs.scoreDocs;
// Extract document IDs
for (ScoreDoc doc : docs) {
org.apache.lucene.document.Document document = searcher.doc(doc.doc);
String type = document.get("doctype");
String documentId = null;
if (type.equals("document")) {
documentId = document.get("id");
} else if (type.equals("file")) {
documentId = document.get("document_id");
}
documentIdList.add(documentId);
}
return documentIdList;
}
/**
* Build Lucene document from database document.
*
* @param document Document
* @return Document
*/
private org.apache.lucene.document.Document getDocumentFromDocument(Document document) {
org.apache.lucene.document.Document luceneDocument = new org.apache.lucene.document.Document();
luceneDocument.add(new StringField("id", document.getId(), Field.Store.YES));
luceneDocument.add(new StringField("doctype", "document", Field.Store.YES));
luceneDocument.add(new TextField("title", document.getTitle(), Field.Store.NO));
if (document.getDescription() != null) {
luceneDocument.add(new TextField("description", document.getDescription(), Field.Store.NO));
}
if (document.getSubject() != null) {
luceneDocument.add(new TextField("subject", document.getSubject(), Field.Store.NO));
}
if (document.getIdentifier() != null) {
luceneDocument.add(new TextField("identifier", document.getIdentifier(), Field.Store.NO));
}
if (document.getPublisher() != null) {
luceneDocument.add(new TextField("publisher", document.getPublisher(), Field.Store.NO));
}
if (document.getFormat() != null) {
luceneDocument.add(new TextField("format", document.getFormat(), Field.Store.NO));
}
if (document.getSource() != null) {
luceneDocument.add(new TextField("source", document.getSource(), Field.Store.NO));
}
if (document.getType() != null) {
luceneDocument.add(new TextField("type", document.getType(), Field.Store.NO));
}
if (document.getCoverage() != null) {
luceneDocument.add(new TextField("coverage", document.getCoverage(), Field.Store.NO));
}
if (document.getRights() != null) {
luceneDocument.add(new TextField("rights", document.getRights(), Field.Store.NO));
}
return luceneDocument;
}
/**
* Build Lucene document from file.
*
* @param file File
* @return Document
*/
private org.apache.lucene.document.Document getDocumentFromFile(File file) {
org.apache.lucene.document.Document luceneDocument = new org.apache.lucene.document.Document();
luceneDocument.add(new StringField("id", file.getId(), Field.Store.YES));
luceneDocument.add(new StringField("doctype", "file", Field.Store.YES));
luceneDocument.add(new StringField("document_id", file.getDocumentId(), Field.Store.YES));
if (file.getContent() != null) {
luceneDocument.add(new TextField("content", file.getContent(), Field.Store.NO));
}
return luceneDocument;
}
}

View File

@@ -0,0 +1,9 @@
package com.sismics.docs.core.event;
/**
* ACL created event.
*
* @author bgamard
*/
public class AclCreatedAsyncEvent extends AclEvent {
}

View File

@@ -0,0 +1,9 @@
package com.sismics.docs.core.event;
/**
* ACL deleted event.
*
* @author bgamard
*/
public class AclDeletedAsyncEvent extends AclEvent {
}

View File

@@ -0,0 +1,62 @@
package com.sismics.docs.core.event;
import com.google.common.base.MoreObjects;
import com.sismics.docs.core.constant.PermType;
/**
* ACL event.
*
* @author bgamard
*/
public abstract class AclEvent extends UserEvent {
/**
* Source ID.
*/
private String sourceId;
/**
* Permission type.
*/
private PermType perm;
/**
* Target ID.
*/
private String targetId;
public String getSourceId() {
return sourceId;
}
public AclEvent setSourceId(String sourceId) {
this.sourceId = sourceId;
return this;
}
public PermType getPerm() {
return perm;
}
public AclEvent setPerm(PermType permType) {
this.perm = permType;
return this;
}
public String getTargetId() {
return targetId;
}
public AclEvent setTargetId(String targetId) {
this.targetId = targetId;
return this;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("sourceId", sourceId)
.add("perm", perm)
.add("targetId", targetId)
.toString();
}
}

View File

@@ -1,78 +1,9 @@
package com.sismics.docs.core.event;
import java.io.InputStream;
import java.nio.file.Path;
import com.google.common.base.MoreObjects;
import com.sismics.docs.core.model.jpa.File;
/**
* New file created event.
*
* @author bgamard
*/
public class FileCreatedAsyncEvent extends UserEvent {
/**
* Created file.
*/
private File file;
/**
* Language of the file.
*/
private String language;
/**
* Unencrypted original file.
*/
private Path unencryptedFile;
/**
* Unencrypted file containing PDF representation
* of the original file. May be null if the PDF conversion is not
* necessary or not possible.
*/
private Path unencryptedPdfFile;
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public Path getUnencryptedFile() {
return unencryptedFile;
}
public FileCreatedAsyncEvent setUnencryptedFile(Path unencryptedFile) {
this.unencryptedFile = unencryptedFile;
return this;
}
public Path getUnencryptedPdfFile() {
return unencryptedPdfFile;
}
public FileCreatedAsyncEvent setUnencryptedPdfFile(Path unencryptedPdfFile) {
this.unencryptedPdfFile = unencryptedPdfFile;
return this;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("file", file)
.add("language", language)
.toString();
}
public class FileCreatedAsyncEvent extends FileEvent {
}

View File

@@ -0,0 +1,61 @@
package com.sismics.docs.core.event;
import com.google.common.base.MoreObjects;
import com.sismics.docs.core.model.jpa.File;
import java.nio.file.Path;
/**
* New file event.
*
* @author bgamard
*/
public abstract class FileEvent extends UserEvent {
/**
* Created file.
*/
private File file;
/**
* Language of the file.
*/
private String language;
/**
* Unencrypted original file.
*/
private Path unencryptedFile;
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public Path getUnencryptedFile() {
return unencryptedFile;
}
public FileEvent setUnencryptedFile(Path unencryptedFile) {
this.unencryptedFile = unencryptedFile;
return this;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("file", file)
.add("language", language)
.toString();
}
}

View File

@@ -0,0 +1,9 @@
package com.sismics.docs.core.event;
/**
* New file created event.
*
* @author bgamard
*/
public class FileUpdatedAsyncEvent extends FileEvent {
}

View File

@@ -1,7 +1,7 @@
package com.sismics.docs.core.event;
import com.google.common.base.MoreObjects;
import com.sismics.docs.core.dao.jpa.dto.UserDto;
import com.sismics.docs.core.dao.dto.UserDto;
import com.sismics.docs.core.model.jpa.PasswordRecovery;
/**

View File

@@ -1,7 +1,7 @@
package com.sismics.docs.core.event;
import com.google.common.base.MoreObjects;
import com.sismics.docs.core.dao.jpa.dto.UserDto;
import com.sismics.docs.core.dao.dto.UserDto;
import com.sismics.docs.core.model.jpa.Document;
/**

View File

@@ -1,35 +0,0 @@
package com.sismics.docs.core.event;
import com.google.common.base.MoreObjects;
import com.sismics.docs.core.model.jpa.File;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.List;
/**
* Cleanup temporary files event.
*
* @author bgamard
*/
public class TemporaryFileCleanupAsyncEvent {
/**
* Temporary files.
*/
private List<Path> fileList;
public TemporaryFileCleanupAsyncEvent(List<Path> fileList) {
this.fileList = fileList;
}
public List<Path> getFileList() {
return fileList;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("files", fileList)
.toString();
}
}

View File

@@ -0,0 +1,37 @@
package com.sismics.docs.core.listener.async;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.sismics.docs.core.event.AclCreatedAsyncEvent;
import com.sismics.docs.core.model.context.AppContext;
import com.sismics.docs.core.util.TransactionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Listener on ACL created.
*
* @author bgamard
*/
public class AclCreatedAsyncListener {
/**
* Logger.
*/
private static final Logger log = LoggerFactory.getLogger(AclCreatedAsyncListener.class);
/**
* ACL created.
*
* @param event ACL created event
*/
@Subscribe
@AllowConcurrentEvents
public void on(final AclCreatedAsyncEvent event) {
if (log.isInfoEnabled()) {
log.info("ACL created event: " + event.toString());
}
TransactionUtil.handle(() -> AppContext.getInstance().getIndexingHandler()
.createAcl(event.getSourceId(), event.getPerm(), event.getTargetId()));
}
}

View File

@@ -0,0 +1,37 @@
package com.sismics.docs.core.listener.async;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.sismics.docs.core.event.AclDeletedAsyncEvent;
import com.sismics.docs.core.model.context.AppContext;
import com.sismics.docs.core.util.TransactionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Listener on ACL deleted.
*
* @author bgamard
*/
public class AclDeletedAsyncListener {
/**
* Logger.
*/
private static final Logger log = LoggerFactory.getLogger(AclDeletedAsyncListener.class);
/**
* ACL deleted.
*
* @param event ACL deleted event
*/
@Subscribe
@AllowConcurrentEvents
public void on(final AclDeletedAsyncEvent event) {
if (log.isInfoEnabled()) {
log.info("ACL deleted event: " + event.toString());
}
TransactionUtil.handle(() -> AppContext.getInstance().getIndexingHandler()
.deleteAcl(event.getSourceId(), event.getPerm(), event.getTargetId()));
}
}

View File

@@ -1,9 +1,10 @@
package com.sismics.docs.core.listener.async;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.sismics.docs.core.dao.jpa.ContributorDao;
import com.sismics.docs.core.dao.lucene.LuceneDao;
import com.sismics.docs.core.dao.ContributorDao;
import com.sismics.docs.core.event.DocumentCreatedAsyncEvent;
import com.sismics.docs.core.model.context.AppContext;
import com.sismics.docs.core.model.jpa.Contributor;
import com.sismics.docs.core.util.TransactionUtil;
import org.slf4j.Logger;
@@ -26,25 +27,22 @@ public class DocumentCreatedAsyncListener {
* @param event Document created event
*/
@Subscribe
@AllowConcurrentEvents
public void on(final DocumentCreatedAsyncEvent event) {
if (log.isInfoEnabled()) {
log.info("Document created event: " + event.toString());
}
TransactionUtil.handle(new Runnable() {
@Override
public void run() {
TransactionUtil.handle(() -> {
// Add the first contributor (the creator of the document)
ContributorDao contributorDao = new ContributorDao();
Contributor contributor = new Contributor();
contributor.setDocumentId(event.getDocument().getId());
contributor.setUserId(event.getUserId());
contributorDao.create(contributor);
}
});
// Update Lucene index
LuceneDao luceneDao = new LuceneDao();
luceneDao.createDocument(event.getDocument());
// Update index
AppContext.getInstance().getIndexingHandler().createDocument(event.getDocument());
});
}
}

View File

@@ -1,12 +1,13 @@
package com.sismics.docs.core.listener.async;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.sismics.docs.core.event.DocumentDeletedAsyncEvent;
import com.sismics.docs.core.model.context.AppContext;
import com.sismics.docs.core.util.TransactionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.eventbus.Subscribe;
import com.sismics.docs.core.dao.lucene.LuceneDao;
import com.sismics.docs.core.event.DocumentDeletedAsyncEvent;
/**
* Listener on document deleted.
*
@@ -21,17 +22,18 @@ public class DocumentDeletedAsyncListener {
/**
* Document deleted.
*
* @param documentDeletedAsyncEvent Document deleted event
* @throws Exception
* @param event Document deleted event
*/
@Subscribe
public void on(final DocumentDeletedAsyncEvent documentDeletedAsyncEvent) throws Exception {
@AllowConcurrentEvents
public void on(final DocumentDeletedAsyncEvent event) {
if (log.isInfoEnabled()) {
log.info("Document deleted event: " + documentDeletedAsyncEvent.toString());
log.info("Document deleted event: " + event.toString());
}
// Update Lucene index
LuceneDao luceneDao = new LuceneDao();
luceneDao.deleteDocument(documentDeletedAsyncEvent.getDocumentId());
TransactionUtil.handle(() -> {
// Update index
AppContext.getInstance().getIndexingHandler().deleteDocument(event.getDocumentId());
});
}
}

View File

@@ -1,11 +1,15 @@
package com.sismics.docs.core.listener.async;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.sismics.docs.core.dao.jpa.ContributorDao;
import com.sismics.docs.core.dao.jpa.DocumentDao;
import com.sismics.docs.core.dao.lucene.LuceneDao;
import com.sismics.docs.core.dao.ContributorDao;
import com.sismics.docs.core.dao.DocumentDao;
import com.sismics.docs.core.dao.FileDao;
import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent;
import com.sismics.docs.core.model.context.AppContext;
import com.sismics.docs.core.model.jpa.Contributor;
import com.sismics.docs.core.model.jpa.Document;
import com.sismics.docs.core.model.jpa.File;
import com.sismics.docs.core.util.TransactionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -27,21 +31,35 @@ public class DocumentUpdatedAsyncListener {
* Document updated.
*
* @param event Document updated event
* @throws Exception
*/
@Subscribe
@AllowConcurrentEvents
public void on(final DocumentUpdatedAsyncEvent event) {
if (log.isInfoEnabled()) {
log.info("Document updated event: " + event.toString());
}
TransactionUtil.handle(new Runnable() {
@Override
public void run() {
// Update Lucene index
TransactionUtil.handle(() -> {
// Get the document
DocumentDao documentDao = new DocumentDao();
LuceneDao luceneDao = new LuceneDao();
luceneDao.updateDocument(documentDao.getById(event.getDocumentId()));
Document document = documentDao.getById(event.getDocumentId());
if (document == null) {
// Document deleted since event fired
return;
}
// Set the main file
FileDao fileDao = new FileDao();
List<File> fileList = fileDao.getByDocumentId(null, event.getDocumentId());
if (fileList.isEmpty()) {
document.setFileId(null);
} else {
document.setFileId(fileList.get(0).getId());
}
// Update database and index
documentDao.updateFileId(document);
AppContext.getInstance().getIndexingHandler().updateDocument(document);
// Update contributors list
ContributorDao contributorDao = new ContributorDao();
@@ -60,7 +78,6 @@ public class DocumentUpdatedAsyncListener {
contributor.setDocumentId(event.getDocumentId());
contributor.setUserId(event.getUserId());
contributorDao.create(contributor);
}
});
}
}

View File

@@ -1,66 +0,0 @@
package com.sismics.docs.core.listener.async;
import com.google.common.eventbus.Subscribe;
import com.sismics.docs.core.dao.jpa.FileDao;
import com.sismics.docs.core.dao.lucene.LuceneDao;
import com.sismics.docs.core.event.FileCreatedAsyncEvent;
import com.sismics.docs.core.model.jpa.File;
import com.sismics.docs.core.util.FileUtil;
import com.sismics.docs.core.util.TransactionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.MessageFormat;
/**
* Listener on file created.
*
* @author bgamard
*/
public class FileCreatedAsyncListener {
/**
* Logger.
*/
private static final Logger log = LoggerFactory.getLogger(FileCreatedAsyncListener.class);
/**
* File created.
*
* @param fileCreatedAsyncEvent File created event
* @throws Exception e
*/
@Subscribe
public void on(final FileCreatedAsyncEvent fileCreatedAsyncEvent) throws Exception {
if (log.isInfoEnabled()) {
log.info("File created event: " + fileCreatedAsyncEvent.toString());
}
// Guess the mime type a second time, for open document format (first detected as simple ZIP file)
final File file = fileCreatedAsyncEvent.getFile();
// Extract text content from the file
long startTime = System.currentTimeMillis();
final String content = FileUtil.extractContent(fileCreatedAsyncEvent.getLanguage(), file,
fileCreatedAsyncEvent.getUnencryptedFile(), fileCreatedAsyncEvent.getUnencryptedPdfFile());
log.info(MessageFormat.format("File content extracted in {0}ms", System.currentTimeMillis() - startTime));
// Store the text content in the database
TransactionUtil.handle(new Runnable() {
@Override
public void run() {
FileDao fileDao = new FileDao();
if (fileDao.getActiveById(file.getId()) == null) {
// The file has been deleted since the text extraction started, ignore the result
return;
}
file.setContent(content);
fileDao.update(file);
}
});
// Update Lucene index
LuceneDao luceneDao = new LuceneDao();
luceneDao.createFile(fileCreatedAsyncEvent.getFile());
}
}

View File

@@ -1,13 +1,14 @@
package com.sismics.docs.core.listener.async;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.sismics.docs.core.dao.lucene.LuceneDao;
import com.sismics.docs.core.event.FileDeletedAsyncEvent;
import com.sismics.docs.core.model.context.AppContext;
import com.sismics.docs.core.model.jpa.File;
import com.sismics.docs.core.util.FileUtil;
import com.sismics.docs.core.util.TransactionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Listener on file deleted.
@@ -23,21 +24,23 @@ public class FileDeletedAsyncListener {
/**
* File deleted.
*
* @param fileDeletedAsyncEvent File deleted event
* @throws Exception
* @param event File deleted event
* @throws Exception e
*/
@Subscribe
public void on(final FileDeletedAsyncEvent fileDeletedAsyncEvent) throws Exception {
@AllowConcurrentEvents
public void on(final FileDeletedAsyncEvent event) throws Exception {
if (log.isInfoEnabled()) {
log.info("File deleted event: " + fileDeletedAsyncEvent.toString());
log.info("File deleted event: " + event.toString());
}
// Delete the file from storage
File file = fileDeletedAsyncEvent.getFile();
File file = event.getFile();
FileUtil.delete(file);
// Update Lucene index
LuceneDao luceneDao = new LuceneDao();
luceneDao.deleteDocument(file.getId());
TransactionUtil.handle(() -> {
// Update index
AppContext.getInstance().getIndexingHandler().deleteDocument(file.getId());
});
}
}

View File

@@ -0,0 +1,159 @@
package com.sismics.docs.core.listener.async;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.sismics.docs.core.dao.FileDao;
import com.sismics.docs.core.dao.UserDao;
import com.sismics.docs.core.event.FileCreatedAsyncEvent;
import com.sismics.docs.core.event.FileEvent;
import com.sismics.docs.core.event.FileUpdatedAsyncEvent;
import com.sismics.docs.core.model.context.AppContext;
import com.sismics.docs.core.model.jpa.File;
import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.DirectoryUtil;
import com.sismics.docs.core.util.EncryptionUtil;
import com.sismics.docs.core.util.FileUtil;
import com.sismics.docs.core.util.TransactionUtil;
import com.sismics.docs.core.util.format.FormatHandler;
import com.sismics.docs.core.util.format.FormatHandlerUtil;
import com.sismics.util.ImageUtil;
import com.sismics.util.Scalr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
/**
* Listener on file processing.
*
* @author bgamard
*/
public class FileProcessingAsyncListener {
/**
* Logger.
*/
private static final Logger log = LoggerFactory.getLogger(FileProcessingAsyncListener.class);
/**
* File created.
*
* @param event File created event
*/
@Subscribe
@AllowConcurrentEvents
public void on(final FileCreatedAsyncEvent event) {
if (log.isInfoEnabled()) {
log.info("File created event: " + event.toString());
}
TransactionUtil.handle(() -> {
// Generate thumbnail, extract content
processFile(event);
// Update index
AppContext.getInstance().getIndexingHandler().createFile(event.getFile());
});
FileUtil.endProcessingFile(event.getFile().getId());
}
/**
* File updated.
*
* @param event File updated event
*/
@Subscribe
@AllowConcurrentEvents
public void on(final FileUpdatedAsyncEvent event) {
if (log.isInfoEnabled()) {
log.info("File updated event: " + event.toString());
}
TransactionUtil.handle(() -> {
// Generate thumbnail, extract content
processFile(event);
// Update index
AppContext.getInstance().getIndexingHandler().updateFile(event.getFile());
});
FileUtil.endProcessingFile(event.getFile().getId());
}
/**
* Process the file (create/update).
*
* @param event File event
*/
private void processFile(FileEvent event) {
// Find a format handler
final File file = event.getFile();
FormatHandler formatHandler = FormatHandlerUtil.find(file.getMimeType());
if (formatHandler == null) {
log.info("Format unhandled: " + file.getMimeType());
FileUtil.endProcessingFile(file.getId());
return;
}
// Get the user from the database
UserDao userDao = new UserDao();
User user = userDao.getById(event.getUserId());
if (user == null) {
// The user has been deleted meanwhile
FileUtil.endProcessingFile(file.getId());
return;
}
// Generate file variations
try {
Cipher cipher = EncryptionUtil.getEncryptionCipher(user.getPrivateKey());
BufferedImage image = formatHandler.generateThumbnail(event.getUnencryptedFile());
if (image != null) {
// Generate thumbnails from image
BufferedImage web = Scalr.resize(image, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.AUTOMATIC, 1280);
BufferedImage thumbnail = Scalr.resize(image, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.AUTOMATIC, 256);
image.flush();
// Write "web" encrypted image
Path outputFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_web");
try (OutputStream outputStream = new CipherOutputStream(Files.newOutputStream(outputFile), cipher)) {
ImageUtil.writeJpeg(web, outputStream);
}
// Write "thumb" encrypted image
outputFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_thumb");
try (OutputStream outputStream = new CipherOutputStream(Files.newOutputStream(outputFile), cipher)) {
ImageUtil.writeJpeg(thumbnail, outputStream);
}
}
} catch (Exception e) {
log.error("Unable to generate thumbnails", e);
}
// Extract text content from the file
long startTime = System.currentTimeMillis();
String content = null;
try {
content = formatHandler.extractContent(event.getLanguage(), event.getUnencryptedFile());
} catch (Exception e) {
log.error("Error extracting content from: " + event.getFile(), e);
}
log.info(MessageFormat.format("File content extracted in {0}ms", System.currentTimeMillis() - startTime));
// Save the file to database
FileDao fileDao = new FileDao();
if (fileDao.getActiveById(file.getId()) == null) {
// The file has been deleted since the text extraction started, ignore the result
return;
}
file.setContent(content);
fileDao.update(file);
}
}

Some files were not shown because too many files have changed in this diff Show More