Compare commits

...

656 Commits
v1.4 ... master

Author SHA1 Message Date
Alexander ADAM
c2d7f3ebc6
feat: add option to disable OCR (#768)
fixes #344
refs #767
2024-09-07 22:27:48 +02:00
Kaiyao Ke
8f1ff56d34
fixed remaining non-idempotent tests (#758) 2024-06-03 10:28:10 +02:00
Kaiyao Ke
11ae0ea7d3
fixed non-idempotent tests (#757) 2024-05-17 15:37:43 +02:00
bgamard
afa78857f9 fix test 2024-04-03 22:03:52 +02:00
bgamard
ae2423b2e9 remove check_username route 2024-04-03 21:53:00 +02:00
bgamard
01d3e746d8 cleanup Dockerfile + upgrade to jetty 11.0.20 2024-03-01 22:06:50 +01:00
Sukalpo Mitra
13cd03a762
Self contained Teedy Dockerfile (#745) 2024-03-01 21:46:17 +01:00
bgamard
ac7b3c4eb9 Merge remote-tracking branch 'origin/master' 2024-02-19 18:34:02 +01:00
bgamard
7effbc8de0 list display mode in the share app 2024-02-19 18:33:56 +01:00
Sukalpo Mitra
8c5f0c78e7
Added support for JWT based authentication (#739) 2023-12-02 17:57:51 +01:00
Erich Mauerböck
45e00ac93d
add explicit binding (#735)
* add explicit binding

* fixup building on windows

* reactivate unit test

---------

Co-authored-by: Enrice <erich.mauerboeck@ergo-versicherung.at>
2023-11-10 20:58:11 +01:00
Erich Mauerböck
80454afc0d
fix unit test (#736)
Co-authored-by: Enrice <erich.mauerboeck@ergo-versicherung.at>
2023-11-10 20:57:33 +01:00
Erich Mauerböck
428e898a7a
allow hyphen in username (#731)
* allow hyphen in username

* remove extra escaping

---------

Co-authored-by: Enrice <erich.mauerboeck@ergo-versicherung.at>
2023-11-03 15:36:02 +01:00
Julien Kirch
13762eb67f
Upgrade pdfbox version to 2.0.29 (#728) 2023-10-20 15:41:45 +02:00
Julien Kirch
04c43ebf7b
Specify document search parameter as HTTP params (#722) 2023-10-19 18:34:04 +02:00
Julien Kirch
f9b5a5212d
Allow to specify a pool size (#727) 2023-10-09 14:05:13 +02:00
Julien Kirch
0351f94761
Upgrade Hibernate version (#726) 2023-10-09 12:36:53 +02:00
Julien Kirch
a89543b555
Make search for documents faster for large dataset (#698) 2023-10-08 22:07:01 +02:00
Benjamin Gamard
ce30b1a6ff
fix build 2023-09-15 22:05:04 +02:00
Orland Karamani
1b382004cb
Albanian Language Support (#719)
Co-authored-by: Orlando Karamani <orlandothemover@gmail.com>
2023-09-14 16:51:11 +02:00
Julien Kirch
ab7ff25929
Store file size in DB (#704) 2023-09-14 16:50:39 +02:00
Julien Kirch
eedf19ad9d
Fix no favicon on shares #580 (#718) 2023-09-08 15:43:35 +02:00
Julien Kirch
941ace99c6
Fix typo in /file/:id/versions description (#717) 2023-09-07 16:46:43 +02:00
bgamard
95e0b870f6 Merge remote-tracking branch 'origin/master' 2023-06-29 21:33:12 +02:00
bgamard
2bdb2dc34f #678: reopen ldap connection for each login 2023-06-29 21:33:05 +02:00
Julien Kirch
22a44d0c8d
Finding several documents by their title in a single query (#696) 2023-06-06 21:31:01 +02:00
Julien Kirch
a9cdbdc03e
Add missing french translations (#694) 2023-06-05 16:02:55 +02:00
Julien Kirch
3fd5470eae
Add mention in the API doc that document endpoint returns the document's metadata (#695) 2023-06-04 21:49:36 +02:00
39f96cbd28
Update config.properties (#693)
fix db version to reflect the most recent
2023-06-04 21:48:55 +02:00
4501f10429
fix comma to make valid language de.json file again 2023-05-07 11:56:34 +02:00
bd0cde7e87
Add support for STARTTLS for Inbox Scanning (#682) 2023-04-25 18:27:46 +02:00
bgamard
dd36e08d7d #680: warning when using H2 database 2023-04-22 00:47:01 +02:00
Jose Luis Montes Jiménez
4634def93e
updating README.md (#681)
H2 database should only be use for testing, setting the docker-compose with postgreSQL as default way.
2023-04-22 00:12:48 +02:00
bgamard
1974a8bb8d #668: cleanup hibernate dependencies 2023-04-12 17:58:51 +02:00
bgamard
e9a6609593 #668: jetty 11 deployment 2023-04-12 13:35:54 +02:00
bgamard
b20577026e Closes #668: upgrade jetty/servlet-api/jersey 2023-04-09 21:31:53 +02:00
bgamard
dae9e137f7 Merge remote-tracking branch 'origin/master' 2023-03-22 10:23:18 +01:00
bgamard
1509d0c5bb revert h2 upgrade 2023-03-22 10:23:11 +01:00
430ebbd1c5
support ldaps (#670) 2023-03-21 21:56:14 +01:00
bgamard
b561eaee6d portuguese translation 2023-03-20 20:20:52 +01:00
bgamard
1aa21c3762 bump dependencies 2023-03-19 14:28:22 +01:00
bgamard
c8a67177d8 next dev iteration 2023-03-12 14:11:52 +01:00
bgamard
59597e962d 1.11 2023-03-12 13:58:03 +01:00
bgamard
c85a951a9e upgrade base image 2023-03-12 13:52:30 +01:00
bgamard
7f47a17633 upgrade jetty 2023-03-12 13:45:36 +01:00
bgamard
690c961a55 Merge remote-tracking branch 'origin/master' 2023-03-12 13:35:51 +01:00
bgamard
21efd1e4a7 Closes #658 2023-03-12 13:35:35 +01:00
@RandyMcMillan
ad27228429
docker-compose.yml: add example config (#665) 2023-02-20 11:51:39 +01:00
@RandyMcMillan
dd4a1667ca
.gitignore: add docs/.gitkeep (#664) 2023-02-20 11:51:30 +01:00
@RandyMcMillan
399d2b7951
minor grammar corrections (#663) 2023-02-19 21:31:30 +01:00
bgamard
d51dfd6636 #647: fix doc 2022-08-26 18:18:06 +02:00
bgamard
ca85c1fa9f #647: always return OK on password lost route 2022-08-26 18:15:49 +02:00
bgamard
5e7f06070e keep filename in temporary file 2022-05-16 19:22:54 +02:00
bgamard
dc0c20cd0c moved tests 2022-05-16 18:53:08 +02:00
bgamard
98aa33341a moved tests 2022-05-16 18:50:19 +02:00
bgamard
1f7c0afc1e Closes #639: rework mime type resolution using java api 2022-05-16 18:44:26 +02:00
bgamard
1ccce3f942 rename 2022-05-05 18:15:24 +02:00
Uli
90d5bc8de7
Allow the . (dot) and @ (at) character in usernames (#637)
Co-authored-by: Uli Koeth <uli@kiot.eu>
2022-05-05 17:48:45 +02:00
bgamard
c6a685d7c0 Closes #620: delete a non-existing document should return 404 2022-04-17 13:35:29 +02:00
bgamard
e6cfd899e5 Closes #632: validate POST /app/config_inbox and update documentation 2022-04-17 13:23:22 +02:00
Julien Kirch
bd23f14792
Add doc for search syntax (#634) 2022-04-17 13:10:01 +02:00
Julien Kirch
46f6b9e537
Download zip of files not in same document (#591) 2022-04-15 10:18:39 +02:00
Julien Kirch
d5832c48e1
Small code cleaning 2022-03-21 11:36:25 +01:00
Julien Kirch
64ec0f63ca
Add parameter to return the files when searching for a document (#582) 2022-03-20 11:36:28 +01:00
Ben Grabham
0b7c42e814
Check if environment variables are not empty strings as well as not null (#623) 2022-02-20 15:48:37 +01:00
bgamard
d8dc63fc98 Merge remote-tracking branch 'origin/master' 2022-02-02 21:18:06 +01:00
bgamard
81a7f154c2 logs only for admin 2022-02-02 21:17:58 +01:00
StaryVena
af3263d471
Add OCR support for Czech language (#613)
Co-authored-by: Vaclav Uher <vaclav.uher@bruker.com>
2022-01-26 15:27:14 +01:00
Dan Schaper
bbe5f19997
Tag latest on master, tag version on github tag. (#612)
Signed-off-by: Dan Schaper <dan.schaper@pi-hole.net>
2022-01-25 10:37:47 +01:00
Benjamin Gamard
f33650c099
fix action 2022-01-21 13:51:16 +01:00
Benjamin Gamard
58f81ec851
fix action 2022-01-21 13:37:31 +01:00
Dan Schaper
c9262eb204
Add build tags and labels (#608)
Fixes Docker images always build as 'latest' #607

Signed-off-by: Dan Schaper <dan.schaper@pi-hole.net>
2022-01-21 13:35:39 +01:00
bgamard
3637b832e5 test the new mime type detection 2022-01-17 14:37:22 +01:00
Joost Timmerman
ee56cfe2b4
Support audio mime (#574) 2022-01-17 14:24:50 +01:00
bgamard
721410c7d0 add test dependencies 2022-01-13 00:15:37 +01:00
bgamard
f0310e3933 add test dependencies 2022-01-13 00:06:29 +01:00
bgamard
302d7cccc4 run tests + fix docker username 2022-01-12 23:59:43 +01:00
Dan Schaper
f9977d5ce6
Actions workflow (#601)
Signed-off-by: Dan Schaper <dan@glacialmagma.com>
2022-01-12 23:49:34 +01:00
bgamard
0a927fd320 add application/x-www-form-urlencoded to delete requests 2022-01-02 16:46:20 +01:00
bgamard
523501a592 consumes application/x-www-form-urlencoded 2022-01-02 16:40:01 +01:00
bgamard
ff8155be6a upgrade docker image to use jetty 9.4.36 2022-01-02 16:06:36 +01:00
bgamard
6c5d697051 Merge remote-tracking branch 'origin/master' 2022-01-02 15:39:11 +01:00
bgamard
b19145160e release 1.10 2022-01-02 15:39:00 +01:00
Roland Illig
c7ada71ef5
proofread German translation (#566)
* plural forms
* spelling of composed words
* spaces between numbers and measurement units
* typographic ellipsis (\u2026) instead of three dots
2021-11-20 20:34:36 +01:00
bgamard
4951229576 escape ngTranslate parameters 2021-11-16 20:01:36 +01:00
Julien Kirch
d98c1bddec
Add custom parameter for exact search by title 2021-10-12 13:50:32 +02:00
Dan Schaper
b0d0e93364
Remove duplicate tesseact language and alphabetize (#579)
Signed-off-by: Dan Schaper <dan@glacialmagma.com>
2021-09-30 13:23:58 +02:00
Benjamin Gamard
f20a562439
remove form url encoded from baseresource 2021-08-20 10:45:08 +02:00
Hung Nguyen
4ae8475f5e
Add Vietnamese language support (#549) 2021-06-21 10:51:31 +02:00
Benjamin Gamard
fd4c627c61
remove travis 2021-05-12 19:38:58 +02:00
Benjamin Gamard
a867d48232
remove travis 2021-05-12 19:38:45 +02:00
Somebodyisnobody
f6bf61fce9
Update de.json (#532)
Fix typo
2021-03-31 19:08:58 +02:00
bgamard
c60c9a8f74 Merge remote-tracking branch 'origin/master' 2021-02-12 21:54:33 +01:00
bgamard
dc021ab71e Closes #520: downgrade H2 to 1.4.199 2021-02-12 21:54:25 +01:00
Pascal Pischel
18b5551f6c
Fix german translation 2021-02-12 21:48:57 +01:00
bgamard
6fcd8771a5 upgrade to java 11 + upgrade libraries 2021-01-25 22:40:58 +01:00
bgamard
1fef4c3d2e next dev iteration + cleanup stress project 2021-01-25 21:31:14 +01:00
bgamard
ee6ed2bf0b v1.9 2021-01-25 21:27:22 +01:00
bgamard
57b67fee09 Closes #458: fix css glitch on mobile 2021-01-21 18:14:39 +01:00
bgamard
a6cbacae72 Closes #509: guest users cannot share and unshare 2021-01-21 17:58:36 +01:00
Vec7or
1e0f8e2484
Closes #472: redirect to previous URL after login 2021-01-21 17:44:48 +01:00
bgamard
bcb4c6d7b0 Merge remote-tracking branch 'origin/master' 2021-01-21 17:39:23 +01:00
bgamard
ea1d5907c1 #497: fix npe in unauthenticated cases 2021-01-21 17:39:01 +01:00
Vegard Hoff Walmsness
05bac38fc3
Norwegian language support 2021-01-14 20:20:16 +01:00
Cornelicorn
69746cd369
#486: Fix importer default file filter 2021-01-06 13:51:29 +01:00
Vec7or
ff3db531e5
Configure bcrypt work 2021-01-05 18:59:18 +01:00
bgamard
558de7ba3f README.md 2020-12-31 07:50:04 +01:00
Vec7or
af15116bf9
Upgrade bcrypt library + explain env variables 2020-12-31 07:46:00 +01:00
Benjamin Gamard
36e5a9747b
Merge pull request #489 from Vec7or/fix-color-bug
Fix tag-colors inside inherited acl
2020-12-30 23:45:57 +01:00
Vec7or
1d66b47f5f Fix tag-colors inside inherited acl 2020-12-30 17:33:24 +01:00
Evil McJerkface
1346dd3616
Add option to specify a particular IMAP folder (aka "label" in Gmail) (#477) 2020-11-22 13:39:39 +01:00
Evil McJerkface
b6ec5e108b
Added support for TLS & STARTTLS for SMTP connections. If port 465 is configured, TLS will be assumed. If port 587 is used, STARTTLS is assumed. (#478)
Closes #353: Added support for TLS & STARTTLS for SMTP connections
2020-11-19 10:15:40 +01:00
bgamard
5b2833350c Closes #391: change english date format to yyyy/mm/dd 2020-10-31 20:20:11 +01:00
bgamard
66acb380ab Closes #451: convert lob content to text for pgsql 2020-10-31 20:14:06 +01:00
bgamard
00c62f2ad4 Closes #467: italian translation 2020-10-31 20:11:55 +01:00
bgamard
7205863d95 Closes #469: make sure the IP sent by the forward proxy is not bigger than 45 chars 2020-10-23 19:31:27 +02:00
Pyrox
2a4274d583
Italian translation (#465) 2020-10-16 13:54:49 +02:00
bgamard
087184b598 Closes #466: remove duplicate translation resources 2020-10-16 13:53:12 +02:00
bgamard
e5600e0be7 add fallback for el language 2020-10-14 22:54:22 +02:00
Benjamin Gamard
964f3128d2
Merge pull request #463 from kazelot/dev-language-pl
Add  polish language
2020-10-14 22:49:57 +02:00
marcin
69905cdc55 Merge with last changes in master branch
Fix polish translation
2020-10-14 22:42:59 +02:00
marcin
bf4e277db7 Add translation to timeago.js
Update translation
2020-10-14 19:07:40 +02:00
bgamard
eaa7cca278 Closes #460: minification error following #455 2020-10-14 18:45:18 +02:00
marcin
0e115bb808 Translate to polish 2020-10-14 11:57:00 +02:00
marcin
1897f5567b Initial commit 2020-10-14 10:49:08 +02:00
bgamard
d647528b3c #455: greek translation of angular-timeago by @gdepountis 2020-10-12 19:36:59 +02:00
bgamard
07d42cdb9c Merge remote-tracking branch 'origin/master' 2020-10-12 10:44:41 +02:00
bgamard
dabb960c94 #455: greek translation by @gdepountis 2020-10-12 10:44:28 +02:00
Benjamin Gamard
c71e794051
Merge pull request #454 from vmario89/patch-3
Update de.json
2020-10-08 12:52:43 +02:00
Mario Voigt
1584c0cbb2
Update de.json
Small lang fix in de.json
2020-10-08 09:52:38 +02:00
bgamard
22f0f1abf4 Closes #453: load gravatar icons in HTTPS 2020-10-06 16:37:52 +02:00
Jean-Marc Tremeaux
205f92d093 Upgrade to JDK 11.0.8 2020-09-24 13:12:45 +02:00
Jean-Marc Tremeaux
7488ac15a7 Merge remote-tracking branch 'origin/master' 2020-09-24 12:45:08 +02:00
bgamard
44f5db993a #451: remove @Lob on file content 2020-09-13 17:58:28 +02:00
bgamard
f76eae23ca Merge remote-tracking branch 'origin/master' 2020-09-10 20:44:20 +02:00
bgamard
5e2a18f819 #451: update the file content with an Hibernate query instead of a native query 2020-09-10 20:42:51 +02:00
Benjamin Gamard
2f6e5d53c2
Merge pull request #449 from muhsinkutay/patch-1
Fix typo in English translation
2020-09-08 02:02:59 +02:00
muhsinkutay
50e6c4d965
Update en.json
Line 6: Misspelling
2020-09-08 02:51:53 +03:00
bgamard
3ad0554a7d use org.apache.directory.api for LDAP instead of apacheds 2020-08-29 19:10:14 +02:00
bgamard
113ec78c67 import less apacheds dependencies 2020-08-28 19:33:58 +02:00
bgamard
f814927eca update README.md 2020-08-28 18:10:28 +02:00
bgamard
a9719feeec LDAP support, courtesy of an anonymous donator 2020-08-28 18:09:54 +02:00
bgamard
6dc4f1b448 #423: fulltext search by default 2020-08-28 17:47:07 +02:00
bgamard
e1fa17691d Merge remote-tracking branch 'origin/master' 2020-08-28 17:34:03 +02:00
bgamard
42e61d6e1f #423: fulltext search by default 2020-08-28 17:33:27 +02:00
Jamie Magee
2bf3e6bd3c
Danish language support (#438) 2020-08-02 23:32:29 +02:00
Benjamin Gamard
608b2f868d
Doc about prebuilt Docker image for bulk importer 2020-06-24 21:54:09 +02:00
Benjamin Gamard
46638bab5b
Build and push docs-importer to Docker Hub 2020-06-24 21:18:37 +02:00
Carl Reid
4607362e46
Add file filter to importer (#426) 2020-06-23 22:31:49 +02:00
Cornelicorn
041b2dfcc1
Fix tag-adding bugs of the importer (#427) 2020-06-21 14:48:13 +02:00
Carl Reid
7ad0dd43e2
Remove COPY node_modules from Dockerfile (#425) 2020-06-21 13:45:23 +02:00
buherman11
35339f7328
Fixed sending workflow emails to previous assignee (#422) 2020-06-10 12:04:09 +02:00
Gabisonfire
e474e7cd75
Added option to copy a file before it is deleted after being imported (#418) 2020-06-07 20:13:04 +02:00
Cornelicorn
612fab2aef
Improve the file importer (#415)
Improve the bulk importer (tags by filename, document language, Docker container)
2020-05-22 15:18:19 +02:00
bgamard
3f67bd471b increase default heap space to 1GB 2020-05-19 15:33:23 +02:00
bgamard
cb29dcd6cc handle all content extraction errors 2020-05-19 15:11:05 +02:00
bgamard
d428e89c30 #412: process files outside of a transaction 2020-05-19 14:44:41 +02:00
bgamard
9b2aeb7480 add temporary logs 2020-05-19 14:05:34 +02:00
bgamard
d9ad69c7ff add more log to debug processing indicator 2020-05-17 21:58:13 +02:00
bgamard
16fc058264 Merge remote-tracking branch 'origin/master' 2020-05-17 21:00:30 +02:00
bgamard
520b143165 #412: better handle concurrent updates and async listeners 2020-05-17 21:00:01 +02:00
cadast
95c37a03f8
Improve Inbox Scanning (#407)
closes #386: delete emails after import + closes #405: auto tag documents imported by email
2020-05-14 13:59:11 +02:00
bgamard
0d058b9c9c at least 2 threads for background work 2020-05-07 11:09:11 +02:00
bgamard
7c72b5e69b #401: importer: truncate document title to allowed size 2020-05-06 13:56:45 +02:00
bgamard
3ec254e908 #400: limit async bus to (cpu cores / 2) threads 2020-05-06 11:35:45 +02:00
bgamard
fda13c004e Merge remote-tracking branch 'origin/master' 2020-04-21 21:13:54 +02:00
bgamard
3af85eeea6 bump importer version 2020-04-21 21:13:20 +02:00
lord-lawnmower
c08616e6df
Latvian Language Support (#390) 2020-04-13 21:04:47 +02:00
Antti Tapio
7faa0f8a54
Finnish and Swedish language support (#389) 2020-04-12 18:27:06 +02:00
bgamard
26c5fe2e69 next dev iteration 2020-03-26 20:06:58 +01:00
bgamard
6bdaa8352b update README.md 2020-03-26 20:00:44 +01:00
bgamard
6367a1fd15 v1.8 2020-03-26 19:57:10 +01:00
bgamard
2c5ff64d42 Closes #387: validation username and group name in UI 2020-03-25 19:02:50 +01:00
bgamard
e614cb41d8 update feedback api url 2020-03-25 18:13:49 +01:00
bgamard
82737e2280 Closes #334: highlight previously opened file 2020-03-07 17:56:01 +01:00
bgamard
3b5c27096b Closes #350: better relations widget 2020-03-07 17:46:40 +01:00
bgamard
8a85830bd3 #381: date fields manually editable 2020-03-07 00:36:03 +01:00
bgamard
19ac90688e upgrade guava 2020-02-19 20:47:26 +01:00
bgamard
5f4a6bc462 #336: fix search by type 2020-02-19 18:00:13 +01:00
bgamard
4c7f3166d4 Closes #332: tag text color legibility 2020-02-15 23:00:35 +01:00
bgamard
4233f4dd88 Closes #317: edit tag color hex code manually 2020-02-15 22:38:06 +01:00
bgamard
bd09312418 Closes #336: search document by file mime type 2020-02-15 22:05:04 +01:00
bgamard
11ab07b238 Closes #333: fix overflow in document table with a lot of tags 2020-02-15 15:44:32 +01:00
bgamard
d2e2f089fb update README for build deps 2020-02-14 21:48:45 +01:00
bgamard
d619f98de7 Closes #379: spaces and colons not allowed in tag name 2020-02-14 21:40:13 +01:00
bgamard
89228a52dc Closes #378: silence useless log from Jersey 2020-02-14 21:33:34 +01:00
bgamard
90a49efa4a Closes #373: high quality PDF to image conversion before OCR 2020-02-13 17:43:07 +01:00
junpet
a7423caeb1
Add Hungarian Language Support (#369)
Add Hungarian language support
2020-01-29 15:44:44 +01:00
bgamard
6f31a2c228 Closes #366: get the private key from the right user when processing files 2020-01-21 12:54:50 +01:00
Benjamin Gamard
fc98b0882f Merge remote-tracking branch 'origin/master' 2019-12-28 13:54:45 +01:00
Benjamin Gamard
dff05967ea Closes #363: pgsql compatibility for table alias in update queries 2019-12-28 13:53:42 +01:00
Benjamin Gamard
ec836a2f9d
Update README.md 2019-10-13 01:24:10 +02:00
Benjamin Gamard
737c85cf00
Update README.md 2019-10-13 01:23:03 +02:00
Benjamin Gamard
ff7b07f464
Create FUNDING.yml 2019-10-12 23:05:35 +02:00
Mario Voigt
19422b5afa Fixed some language issue (#352) 2019-08-26 14:19:19 +02:00
Benjamin Gamard
6b93e413b6 #321: remove duplicate contributors 2019-06-03 11:45:38 +02:00
Mario Voigt
ab72736bcc Update de.json (#322)
Added missing german translation strings
2019-05-25 18:53:02 +02:00
Benjamin Gamard
38939e5d05 #314: force file content in utf8 2019-05-22 15:36:50 +02:00
Benjamin Gamard
1a90a0e0ad next dev iteration 2019-05-21 16:17:54 +02:00
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 db4f5f9011c5222f665b4caecccb99474f2a5d58)
2018-12-05 17:24:07 +01:00
Benjamin Gamard
98fa89bd80 css fix
(cherry picked from commit 33aaf8afd28c73ca4f72dd57da301ac296d3e3f1)
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
Jean-Marc Tremeaux
94252de73f Merge remote-tracking branch 'origin/master' 2018-10-23 18:16:35 +02: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
Benjamin Gamard
ea210d6aac v1.5 2018-03-01 14:18:23 +01:00
Benjamin Gamard
f70cded634 file importer 2018-03-01 12:28:29 +01:00
Benjamin Gamard
cc6e1b4052 init files importer 2018-02-28 22:53:06 +01:00
Benjamin Gamard
1ce5ba4f06 #180: expain that only unread emails will be imported, fix logo display 2018-02-28 13:08:30 +01:00
Benjamin Gamard
2b23a1d048 #180: fix tests 2018-02-27 20:17:09 +01:00
Benjamin Gamard
37f262177d #180: advertise inbox scanning 2018-02-27 20:06:34 +01:00
Benjamin Gamard
7ded510625 Closes #180: IMAP inbox synching (ui) 2018-02-27 20:05:10 +01:00
Benjamin Gamard
797a987e2b refresh ui css + init inbox scanning settings 2018-02-27 19:02:23 +01:00
Benjamin Gamard
062dee987f #180: last inbox sync infos 2018-02-27 17:11:04 +01:00
Benjamin Gamard
1054931e63 #180: fix tests for travis 2018-02-27 15:34:20 +01:00
Benjamin Gamard
3720a881a4 #180: IMAP inbox synching (api) 2018-02-27 14:58:37 +01:00
Benjamin Gamard
f379b4e5ab #179: default language (ui) 2018-02-25 16:55:45 +01:00
Benjamin Gamard
9a9e86829e #179: default language (api) 2018-02-22 12:57:33 +01:00
Benjamin Gamard
66cd4abd8b #177: truncate data to fit the database 2018-02-22 11:42:41 +01:00
Benjamin Gamard
ba4470f155 Closes #177: import document from EML file (UI done) 2018-02-22 11:35:34 +01:00
Benjamin Gamard
b95ec019de #177: import document from EML file (api done) 2018-02-22 10:46:32 +01:00
Benjamin Gamard
d3a40ebca8 #177: import document from EML file (wip) 2018-02-21 21:47:57 +01:00
Benjamin Gamard
5d335049a2 fix api doc + fix translations 2018-02-15 20:38:03 +01:00
Benjamin Gamard
bd51e2ab55 fix password reset button 2018-02-14 12:32:50 +01:00
Benjamin Gamard
f8278bd44e update translations 2018-02-02 18:12:14 +01:00
Benjamin Gamard
4797cd1eca Closes #139: screenshot on README.md 2018-02-02 17:28:23 +01:00
Benjamin Gamard
706d244ff8 Closes #159: cancel routes + email at route step validation 2018-02-02 17:18:34 +01:00
Benjamin Gamard
5b8cd18128 #159: display and validate route steps 2018-02-02 12:37:56 +01:00
Benjamin Gamard
8a854bb37d #159: get routes on a document 2018-02-01 23:26:29 +01:00
Benjamin Gamard
6e6f892cb0 fix 2018-02-01 18:26:33 +01:00
Benjamin Gamard
2b4ddfa072 #159: validate route steps 2018-02-01 18:01:11 +01:00
Benjamin Gamard
c9adff5a25 #159: add temporary READ ACL for route step 2018-02-01 11:48:02 +01:00
Benjamin Gamard
503cfff82e #159: return the active step in GET /document/id 2018-01-31 22:07:38 +01:00
Benjamin Gamard
5e713f0c2a #159: start a route on a document 2018-01-29 23:34:43 +01:00
Benjamin Gamard
e035007070 #159: route model steps validation 2018-01-28 14:52:13 +01:00
Benjamin Gamard
17a94395f3 #159: route model api 2018-01-28 12:44:11 +01:00
Benjamin Gamard
0ab6c8e4b0 #159: workflow steps ui 2018-01-28 12:24:40 +01:00
Benjamin Gamard
8284169923 #159: workflow steps ui 2018-01-27 23:41:57 +01:00
Benjamin Gamard
5f9094c540 #159: workflow ui init 2018-01-27 17:33:46 +01:00
Benjamin Gamard
332ac9c109 #159: workflow db model 2018-01-26 11:26:34 +01:00
Benjamin Gamard
9ba49f35ff enable cors 2018-01-24 15:39:27 +01:00
Benjamin Gamard
d0646f12e6 translations 2018-01-08 12:02:01 +01:00
Benjamin Gamard
a8817c75b0
Update README.md 2018-01-08 11:51:15 +01:00
Benjamin Gamard
7fc50f2629 Closes #164: admin can send a password reset email to users 2018-01-07 19:56:11 +01:00
Benjamin Gamard
b2d9684738 russian translation 2018-01-06 11:50:03 +01:00
Benjamin Gamard
8ab284ff98 russian translation 2018-01-06 11:44:44 +01:00
Benjamin Gamard
a099d29524 Merge remote-tracking branch 'origin/master' 2018-01-04 16:18:24 +01:00
Benjamin Gamard
cf44af0065 Closes #169 feedback for username/group name already taken 2018-01-04 16:18:06 +01:00
Benjamin Gamard
b5bf8b6545
info about Sismics Docs Cloud in README.md 2018-01-04 15:23:55 +01:00
Benjamin Gamard
4cc3fa4d89 fix zh_TW translation 2018-01-02 19:25:41 +01:00
Benjamin Gamard
ed50e202d7 zh_CN/zh_TW translations 2018-01-02 19:24:32 +01:00
Benjamin Gamard
6d865af15a Closes #173: fix pagination default page size 2018-01-01 23:24:24 +01:00
Benjamin Gamard
66d331ddb8 env var for admin password expected hashed 2018-01-01 17:14:12 +01:00
Benjamin Gamard
0903c03a29 new android icon 2017-12-21 12:51:47 +01:00
Benjamin Gamard
5f546b6c6d new logo 2017-12-21 12:28:33 +01:00
Benjamin Gamard
9bb73bb35b Merge remote-tracking branch 'origin/master' 2017-12-11 10:17:19 +01:00
Benjamin Gamard
2ca5b04de2 initial admin password by env variable 2017-12-11 10:17:08 +01:00
bgamard
e883c1e678 Closes #171: strip alpha channel from png 2017-11-23 15:40:53 +01:00
bgamard
4fc434a222 Merge remote-tracking branch 'origin/master' 2017-11-23 15:32:31 +01:00
bgamard
ecfa747a2c Closes #172: smart images caching 2017-11-23 15:32:20 +01:00
Benjamin Gamard
dc28ebfa50 fix file modal + fix file link in audit log + high quality thumbs 2017-11-23 01:16:54 +01:00
Benjamin Gamard
6596eba6ca advertise demo app 2017-11-21 23:56:32 +01:00
Benjamin Gamard
7194f9aac0 update fr translation 2017-11-21 20:23:35 +01:00
Benjamin Gamard
e4fe1cfa90 fix active user count 2017-11-21 19:37:29 +01:00
Benjamin Gamard
5bc73548b3 Closes #162: feedback box 2017-11-21 12:01:53 +01:00
Benjamin Gamard
2156848e4a GET /app returns document count 2017-11-21 09:49:33 +01:00
Benjamin Gamard
3f807b3e51 edit -> save 2017-11-20 21:25:52 +01:00
Benjamin Gamard
d786862a60 Closes #167: disable users 2017-11-20 21:21:50 +01:00
Benjamin Gamard
fb75bafe96 Closes #166: global quota 2017-11-20 20:34:29 +01:00
Benjamin Gamard
66f781b716 cleanup logs for Travis 2017-11-18 20:14:50 +01:00
Benjamin Gamard
b3dc409926 cleanup logs for Travis + new process for each test 2017-11-18 20:01:11 +01:00
Benjamin Gamard
287ed06b6a prevent lastpass autofill in non editable fields 2017-11-18 19:45:08 +01:00
Benjamin Gamard
df1d013b1c Closes #165: smtp hostname/port/username/password configurables with env 2017-11-18 19:34:13 +01:00
Benjamin Gamard
fdb95484c1 fix sending email to an unauthenticated smtp server 2017-11-17 23:47:53 +01:00
Benjamin Gamard
4cf1f29e0a Closes #161: password recovery by email 2017-11-17 23:17:05 +01:00
Benjamin Gamard
332fd9d1f6 fix tests 2017-11-17 22:26:20 +01:00
Benjamin Gamard
039d881a07 #161: password recovery by email (wip, server part done) 2017-11-17 22:03:54 +01:00
bgamard
b8176a9fe9 fix tests 2017-11-17 17:10:05 +01:00
bgamard
65937d6f4c #161: password recovery by email (wip) 2017-11-17 17:01:08 +01:00
bgamard
590bf74e98 display two-factor authentication activation in admin area 2017-11-17 15:18:16 +01:00
bgamard
642a3e10ce fix for mobile 2017-11-17 14:05:19 +01:00
Benjamin Gamard
52a8cf92c8 fix homepage dom 2017-11-15 23:07:34 +01:00
bgamard
fc68ee56d5 fix en translation 2017-11-15 10:49:47 +01:00
Benjamin Gamard
644f4803df Merge remote-tracking branch 'origin/master' 2017-11-15 00:19:36 +01:00
Benjamin Gamard
eda6106de8 upgrade android build 2017-11-15 00:19:12 +01:00
bgamard
3a1691066e Closes #155: localize share app 2017-11-14 15:44:40 +01:00
Benjamin Gamard
b02039bad4 Fix zh_CN translation 2017-11-14 01:14:29 +01:00
Benjamin Gamard
6527a9e9bb Closes #156: Localize Android app 2017-11-14 01:07:16 +01:00
Benjamin Gamard
c59ad4d446 fix pagination 2017-11-13 22:50:21 +01:00
Benjamin Gamard
f475cbc5d8 oops 2017-11-13 22:44:06 +01:00
Benjamin Gamard
00452cc505 Closes #158: advanced search form 2017-11-13 22:37:03 +01:00
Benjamin Gamard
23660961bd #158: advanced search form (wip) 2017-11-13 18:11:54 +01:00
Benjamin Gamard
742ff183bf #158: advanced search form (wip) 2017-11-12 23:24:10 +01:00
Benjamin Gamard
dca8c28b84 Closes #157: Deskew before OCR 2017-11-12 14:49:52 +01:00
Benjamin Gamard
46079393d5 Fix file modal routing 2017-11-12 02:18:02 +01:00
Benjamin Gamard
517e4a4507 Closes #150: Display file name in audit log 2017-11-12 02:06:41 +01:00
Benjamin Gamard
6f3ae6da9d Better thumbnails UI 2017-11-11 23:25:52 +01:00
Benjamin Gamard
273136ab23 Closes #152 closes #154: localize date and time format 2017-11-10 23:43:35 +01:00
Benjamin Gamard
e74f86e118 Closes #153: fix missing localization string 2017-11-10 22:56:42 +01:00
Benjamin Gamard
84d4d3b165 Closes #151: upgrade JS libraries 2017-11-10 22:00:34 +01:00
bgamard
c355cb8bd5 Closes #148: force Qihoo 360 to use webkit rendering 2017-11-09 14:43:37 +01:00
bgamard
2957034286 Closes #147: fix IE file upload 2017-11-09 14:39:25 +01:00
bgamard
36b4fbd303 ie fix 2017-11-09 13:36:41 +01:00
bgamard
f57cf46313 Closes #146: no cache 2017-11-09 13:36:10 +01:00
Benjamin Gamard
244ddc7ce2 Closes #141: Never close full file content in memory 2017-11-06 16:45:47 +01:00
Benjamin Gamard
4d161aea07 Closes #117: fix templates minification 2017-11-06 00:48:55 +01:00
Benjamin Gamard
cf9101d157 Closes #143: Select the default language for new documents from browser language 2017-11-05 22:28:23 +01:00
Benjamin Gamard
614c8a1d13 log to stdout 2017-11-05 21:27:54 +01:00
Benjamin Gamard
879ab7951d fix build 2017-11-05 17:30:45 +01:00
Benjamin Gamard
311b42ad25 fix flash on untranslated content 2017-11-05 16:59:04 +01:00
Benjamin Gamard
0ebbbac9a6 optimize docker image 2017-11-04 20:50:57 +01:00
Benjamin Gamard
d2f9fcdda0 zh_CN translation + footer fix 2017-11-04 20:39:39 +01:00
bgamard
cfe5690a73 Closes #142: design cleanup 2017-11-03 14:53:09 +01:00
bgamard
a055b3ff5c #117: more logs + possible fix 2017-11-03 11:13:50 +01:00
bgamard
18f37ec2a8 Closes #131: validate only dirty forms 2017-11-03 11:05:04 +01:00
Benjamin Gamard
14b4e5aeec design refresh 2017-11-03 00:10:17 +01:00
Benjamin Gamard
a980930e69 Closes #137: upload files without drag & drop 2017-11-02 23:36:38 +01:00
Benjamin Gamard
1b4eb70d8d Closes #136 #138: ui fixes 2017-11-02 23:14:00 +01:00
Benjamin Gamard
1856ccc3aa less padding 2017-11-02 21:16:47 +01:00
Benjamin Gamard
3217c67ff6 flat design 2017-11-02 21:00:32 +01:00
bgamard
54d5f1cb1b #111: french translation 2017-11-02 17:14:34 +01:00
bgamard
e49d002941 #111: translate templates 2017-11-02 15:39:50 +01:00
Benjamin Gamard
4822b8bf23
Update README.md 2017-11-01 19:50:42 +01:00
Benjamin Gamard
198a6d5665 #111: translate templates (wip) 2017-11-01 19:48:50 +01:00
Benjamin Gamard
c7b9ec3a4c #111: translate controllers 2017-11-01 14:34:15 +01:00
Benjamin Gamard
f46e10e11c support more languages 2017-10-31 21:20:22 +01:00
Benjamin Gamard
b9acc4ecf8 support more languages 2017-10-31 21:16:46 +01:00
Benjamin Gamard
403d094a3d support more languages 2017-10-31 21:06:12 +01:00
Benjamin Gamard
3b1f11e5a8 Merge branch 'master' into sismics_prod 2017-10-31 21:01:32 +01:00
Benjamin Gamard
ddba06cca3 support more languages 2017-10-31 21:01:23 +01:00
Benjamin Gamard
bf89af0da9 Merge branch 'master' into sismics_prod 2017-10-31 20:39:10 +01:00
Benjamin Gamard
5a30164848 support more languages 2017-10-31 20:36:55 +01:00
Benjamin Gamard
0c4e200900 support more languages 2017-10-31 20:34:54 +01:00
Benjamin Gamard
fe8e8f041c Merge remote-tracking branch 'origin/sismics_prod' into sismics_prod 2017-07-31 14:49:17 +02:00
Benjamin Gamard
43084d9d86 Merge branch 'master' into sismics_prod 2017-07-31 14:48:59 +02:00
Benjamin Gamard
e1e1b4e278 embed free monospaced font 2017-07-31 14:40:35 +02:00
Benjamin Gamard
14f8239ba3 register fonts 2017-07-31 14:09:56 +02:00
Benjamin Gamard
e660a70d00 travis: install microsoft fonts 2017-07-31 13:52:32 +02:00
Benjamin Gamard
119d30bb16 fix plain text file save 2017-07-31 12:08:42 +02:00
Benjamin Gamard
5686de56e2 Merge branch 'master' into sismics_prod 2017-07-31 01:51:39 +02:00
Benjamin Gamard
e0214a6a9f Closes #118: create pdf from text plain files 2017-07-31 01:51:23 +02:00
Benjamin Gamard
330de495db #118: extract text content from text plain files (WIP) 2017-06-11 11:33:30 +02:00
jendib
dcc7fe55f4 Closes #125: Confirmation before deleting a comment 2017-05-07 01:39:20 +02:00
jendib
3274b4c79a Closes #130: Fix document language icon 2017-05-07 01:34:21 +02:00
jendib
cbfa4b1c41 Closes #127: Edit -> Save 2017-05-07 01:32:55 +02:00
jendib
5f7d2f2a68 Closes #129: bigger checkbox 2017-05-07 01:31:59 +02:00
jendib
e38bdbe508 Closes #128: Delete cursor on comment delete button 2017-05-07 01:27:24 +02:00
jendib
c352b94b38 Closes #126: click to copy 2017-05-07 01:25:20 +02:00
Benjamin Gamard
6b0106e385 update readme with docker instructions 2017-04-25 11:09:18 +02:00
Benjamin Gamard
60021e5123 build prod package before pushing to dockerhub 2017-04-25 10:39:58 +02:00
Benjamin Gamard
76d3157247 travis push to dockerhub 2017-04-25 10:26:48 +02:00
Benjamin Gamard
7c24778460 Merge branch 'master' into sismics_prod 2017-03-21 09:14:19 +01:00
Jean-Marc Tremeaux
d43072663e Merge remote-tracking branch 'origin/master' 2017-03-21 09:04:20 +01:00
Jean-Marc Tremeaux
8231db4a5a Fix dockerfile 2017-03-21 08:56:00 +01:00
Jean-Marc Tremeaux
eb3562567d Fix dockerfile 2017-03-21 08:51:58 +01:00
jendib
fe5dd5e8dc Merge branch 'master' of https://github.com/sismics/docs 2017-01-24 22:00:35 +01:00
jendib
5872928812 Hide filename if not available + upgrade Gradle 2017-01-24 22:00:24 +01:00
Benjamin Gamard
8a8f4bb388 Merge pull request #121 from sismics/master
Update Jetty version
2017-01-03 11:43:54 +01:00
Benjamin Gamard
0e8d5fd5ec Update Jetty version 2017-01-03 11:43:31 +01:00
Benjamin Gamard
b9344149b0 Merge pull request #120 from sismics/master
Update base image
2017-01-03 11:11:44 +01:00
Benjamin Gamard
5053a6852f Update base image 2017-01-03 11:11:12 +01:00
jendib
bb3faca533 Closes #119: Keep and display original file name 2016-12-07 01:28:52 +01:00
jendib
4f7fcbfdf0 Merge branch 'master' into sismics_prod 2016-12-05 19:30:15 +01:00
jendib
87c1cc88be #116: Allow all file types 2016-12-05 19:25:52 +01:00
jendib
1d78551f4c Fix tests, add logs for #117 2016-11-20 18:52:47 +01:00
jendib
b36d08db8e Closes #116: Allow all file types 2016-11-20 18:41:42 +01:00
Benjamin Gamard
c99b1a1867 cleanup private scripts 2016-10-24 16:55:56 +02:00
Benjamin Gamard
fc4380e5cc Merge pull request #115 from sismics/master
Edit production domain
2016-10-06 15:35:11 +02:00
Benjamin Gamard
850ed7f76b Edit production domain 2016-10-06 15:34:38 +02:00
jendib
0f6aa3befb Concatenate Angular templates in minified JS file 2016-08-31 19:34:37 +02:00
jendib
ddd976162c Merge branch 'master' into sismics_prod 2016-08-26 22:00:58 +02:00
jendib
cdd19e182b Closes #113: Fire async events after request transaction commit 2016-08-26 21:22:27 +02:00
jendib
afc22a547e Closes #112: Don't update auth token on each request 2016-08-26 20:34:23 +02:00
jendib
79ca54c5af Android: Ask permission to write files 2016-07-30 02:52:53 +02:00
jendib
0aacf20c16
Android: upgrade to Nougat 2016-07-09 19:35:05 +02:00
jendib
cdfb43dbd8
Cleanup Lucene DAO 2016-06-28 23:31:59 +02:00
jendib
35ec8b951c
Build fails if grunt fails 2016-06-16 22:15:54 +02:00
Benjamin Gamard
05bfaa0035 Merge pull request #110 from sismics/master
Push to production
2016-06-16 21:38:14 +02:00
jendib
f5705b1153
Minor UI tweaks 2016-06-16 20:31:39 +02:00
jendib
ed1353a4eb
Android: upgrade build tools 2016-06-16 20:13:34 +02:00
jendib
a79922d7c9
Android: upgrade okhttp (fix for Android 6) 2016-06-06 20:51:05 +02:00
jendib
7a7cbd570c
Closes #85: UI for login as guest 2016-05-29 18:34:51 +02:00
jendib
d7865cfaf0
#85: Login as guest 2016-05-29 16:37:26 +02:00
jendib
ead01ce1d0
#85: Guest login configuration 2016-05-28 23:09:52 +02:00
jendib
8aca012c99
Reduce log verbosity 2016-05-16 21:21:19 +02:00
jendib
67a4dc63ca
Closes #106: Header base authentication 2016-05-16 21:07:01 +02:00
jendib
ce0678784b
#81: Android: Display dublincore metadata 2016-05-15 19:50:12 +02:00
jendib
cbc4bbb818
API documentation introduction 2016-05-14 23:10:29 +02:00
jendib
1c558a884d
Closes #105: Upgrade grunt dependencies 2016-05-14 14:21:00 +02:00
jendib
d84d1428b2
Android: Better layout for read-only documents 2016-05-14 02:05:03 +02:00
Benjamin Gamard
b870ee8d62 Merge pull request #104 from sismics/master
Push to production
2016-05-13 00:46:18 +02:00
jendib
ef18581e71
#103: API documentation for /document 2016-05-13 00:45:08 +02:00
jendib
177bbceaf4
#103: API documentation for all resources except /document 2016-05-12 01:26:02 +02:00
jendib
a13174ac4d
Android: use GET /tag/list instead of /tag/stats 2016-05-11 00:55:27 +02:00
jendib
e181b7d24b
#103: API documentation for /user and /vocabulary 2016-05-10 23:30:28 +02:00
jendib
e631aa0e8a
Closes #101: Allow export buttons for read-only documents 2016-05-10 21:18:16 +02:00
jendib
575ad75a0a
Closes #102: Android: Clear all auth tokens on logout 2016-05-10 20:00:14 +02:00
jendib
394f667ab0
Prepare 1.5 development cycle 2016-05-09 22:52:34 +02:00
Benjamin Gamard
f80cf43ae8 Merge pull request #100 from sismics/master
Push to production
2016-05-09 22:10:49 +02:00
Benjamin Gamard
6b57d29f51 Merge pull request #96 from sismics/master
Push to production
2016-05-08 23:40:50 +02:00
Benjamin Gamard
f2c4dde56e Merge pull request #95 from sismics/master
Fix batch for ACLs on tags
2016-05-08 23:21:36 +02:00
Benjamin Gamard
3a22132363 Merge pull request #94 from sismics/master
Push to production
2016-05-08 23:07:43 +02:00
Benjamin Gamard
767099b7ea Merge pull request #89 from sismics/master
Push to production
2016-03-24 00:06:52 +01:00
Benjamin Gamard
6aef7246a0 Merge pull request #86 from sismics/master
Push to production
2016-03-21 01:01:14 +01:00
Benjamin Gamard
1f6d9f0211 Merge pull request #76 from sismics/master
Push to production
2016-03-02 00:43:37 +01:00
Benjamin Gamard
3248637e8c Merge pull request #64 from sismics/master
Push to production
2016-02-11 23:41:47 +01:00
Benjamin Gamard
bf8e0827e2 Merge pull request #60 from sismics/master
Push to production
2016-01-24 16:30:50 +01:00
Benjamin Gamard
ad1e57316f Merge pull request #56 from sismics/master
Push to production
2016-01-01 01:58:32 +01:00
Benjamin Gamard
737b3299ff Merge pull request #54 from sismics/master
Push to production
2015-12-11 22:22:44 +01:00
Benjamin Gamard
1934bb71f0 Merge pull request #52 from sismics/master
Push to production
2015-12-01 01:17:36 +01:00
Benjamin Gamard
b3ef9b0476 Merge pull request #50 from sismics/master
Push to production
2015-11-30 00:29:31 +01:00
Benjamin Gamard
8477920475 Merge pull request #47 from sismics/master
Push to production
2015-11-30 00:10:26 +01:00
Benjamin Gamard
f98a12b96f Merge pull request #46 from sismics/master
Push to production
2015-11-21 20:32:16 +01:00
Benjamin Gamard
bee8a4fcdc Merge pull request #43 from sismics/master
Push to production
2015-11-18 23:11:02 +01:00
Benjamin Gamard
f984595b97 Merge pull request #40 from sismics/master
Push to production
2015-11-02 23:55:16 +01:00
Benjamin Gamard
f01d78a9ea Merge pull request #39 from sismics/master
Push to production
2015-11-02 21:36:14 +01:00
Benjamin Gamard
97f25de0dc Merge pull request #31 from sismics/master
Push to production
2015-09-08 21:50:12 +02:00
Benjamin Gamard
3d1b5a7394 Merge pull request #28 from sismics/master
#4: Upgrade to unrelease PDFBox 2
2015-09-05 23:12:29 +02:00
Benjamin Gamard
df1eaf54c8 Merge pull request #27 from sismics/master
Push to production
2015-09-05 21:41:43 +02:00
Benjamin Gamard
3851408100 Merge pull request #25 from sismics/master
Push to production
2015-08-28 01:16:33 +02:00
Benjamin Gamard
794c5012ad Merge pull request #19 from sismics/master
Fix post-ACL system
2015-05-10 14:03:44 +02:00
Benjamin Gamard
1ba9f7d7d9 Merge pull request #17 from sismics/master
#13: Fix performance issue
2015-05-09 18:00:33 +02:00
Benjamin Gamard
9b4330d618 Merge pull request #16 from sismics/master
#13: Disable shared status in GET /document/list (too slow)
2015-05-09 17:32:04 +02:00
Benjamin Gamard
3a32b742e8 Merge pull request #15 from sismics/master
ACL system
2015-05-09 16:37:13 +02:00
Benjamin Gamard
87b3d25c0f Merge pull request #12 from sismics/master
Push to production
2015-05-02 17:19:30 +02:00
Benjamin Gamard
9f28649a3a Merge pull request #9 from sismics/master
Upload drag & dropped files sequentially
2015-03-29 16:07:07 +02:00
Benjamin Gamard
8f9df8961b Merge pull request #8 from sismics/master
Attach orphan files to a new document
2015-03-28 18:05:01 +01:00
Benjamin Gamard
9e9217bfcb Merge pull request #7 from sismics/master
Drag & drop
2015-03-27 23:11:33 +01:00
Benjamin Gamard
ffdd5a3631 hook me 2015-03-23 17:30:15 +01:00
Benjamin Gamard
db4bf8d35a Merge pull request #5 from sismics/master
chmod +x
2015-03-17 00:03:00 +01:00
Jean-Marc Tremeaux
7b2859f96e Deploy script 2015-03-11 22:56:56 +01:00
Jean-Marc Tremeaux
03c5c33ea7 Deploy script 2015-03-11 22:54:42 +01:00
604 changed files with 131907 additions and 25370 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [jendib]

84
.github/workflows/build-deploy.yml vendored Normal file
View File

@ -0,0 +1,84 @@
name: Maven CI/CD
on:
push:
branches: [master]
tags: [v*]
workflow_dispatch:
jobs:
build_and_publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: "11"
distribution: "temurin"
cache: maven
- name: Install test dependencies
run: sudo apt-get update && sudo apt-get -y -q --no-install-recommends install ffmpeg mediainfo tesseract-ocr tesseract-ocr-deu
- name: Build with Maven
run: mvn --batch-mode -Pprod clean install
- name: Upload war artifact
uses: actions/upload-artifact@v2
with:
name: docs-web-ci.war
path: docs-web/target/docs*.war
build_docker_image:
name: Publish to Docker Hub
runs-on: ubuntu-latest
needs: [build_and_publish]
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Download war artifact
uses: actions/download-artifact@v2
with:
name: docs-web-ci.war
path: docs-web/target
-
name: Setup up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Populate Docker metadata
id: metadata
uses: docker/metadata-action@v3
with:
images: sismics/docs
flavor: |
latest=false
tags: |
type=ref,event=tag
type=raw,value=latest,enable=${{ github.ref_type != 'tag' }}
labels: |
org.opencontainers.image.title = Teedy
org.opencontainers.image.description = Teedy is an open source, lightweight document management system for individuals and businesses.
org.opencontainers.image.created = ${{ github.event_created_at }}
org.opencontainers.image.author = Sismics
org.opencontainers.image.url = https://teedy.io/
org.opencontainers.image.vendor = Sismics
org.opencontainers.image.license = GPLv2
org.opencontainers.image.version = ${{ github.event_head_commit.id }}
-
name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}

10
.gitignore vendored
View File

@ -9,3 +9,13 @@
/.idea
/.project
*.iml
node_modules
import_test
teedy-importer-linux
teedy-importer-macos
teedy-importer-win.exe
docs/*
!docs/.gitkeep
#macos
.DS_Store

View File

@ -1,11 +0,0 @@
sudo: required
dist: trusty
language: java
before_install:
- sudo apt-get -qq update
- sudo apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-jpn
- sudo apt-get -y -q install haveged && sudo service haveged start
env:
global:
- TESSDATA_PREFIX=/usr/share/tesseract-ocr
- LC_NUMERIC=C

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,10 +1,75 @@
FROM sismics/debian-java7-jetty9
MAINTAINER benjamin.gam@gmail.com
FROM ubuntu:22.04
LABEL maintainer="b.gamard@sismics.com"
RUN apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-jpn
# Run Debian in non interactive mode
ENV DEBIAN_FRONTEND noninteractive
ENV TESSDATA_PREFIX /usr/share/tesseract-ocr
ENV LC_NUMERIC C
# Configure env
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk-amd64/
ENV JAVA_OPTIONS -Dfile.encoding=UTF-8 -Xmx1g
ENV JETTY_VERSION 11.0.20
ENV JETTY_HOME /opt/jetty
ADD docs-web/target/docs-web-*.war /opt/jetty/webapps/docs.war
ADD docs.xml /opt/jetty/webapps/docs.xml
# Install packages
RUN apt-get update && \
apt-get -y -q --no-install-recommends install \
vim less procps unzip wget tzdata openjdk-11-jdk \
ffmpeg \
mediainfo \
tesseract-ocr \
tesseract-ocr-ara \
tesseract-ocr-ces \
tesseract-ocr-chi-sim \
tesseract-ocr-chi-tra \
tesseract-ocr-dan \
tesseract-ocr-deu \
tesseract-ocr-fin \
tesseract-ocr-fra \
tesseract-ocr-heb \
tesseract-ocr-hin \
tesseract-ocr-hun \
tesseract-ocr-ita \
tesseract-ocr-jpn \
tesseract-ocr-kor \
tesseract-ocr-lav \
tesseract-ocr-nld \
tesseract-ocr-nor \
tesseract-ocr-pol \
tesseract-ocr-por \
tesseract-ocr-rus \
tesseract-ocr-spa \
tesseract-ocr-swe \
tesseract-ocr-tha \
tesseract-ocr-tur \
tesseract-ocr-ukr \
tesseract-ocr-vie \
tesseract-ocr-sqi \
&& apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN dpkg-reconfigure -f noninteractive tzdata
# Install Jetty
RUN wget -nv -O /tmp/jetty.tar.gz \
"https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-home/${JETTY_VERSION}/jetty-home-${JETTY_VERSION}.tar.gz" \
&& tar xzf /tmp/jetty.tar.gz -C /opt \
&& mv /opt/jetty* /opt/jetty \
&& useradd jetty -U -s /bin/false \
&& chown -R jetty:jetty /opt/jetty \
&& mkdir /opt/jetty/webapps \
&& chmod +x /opt/jetty/bin/jetty.sh
EXPOSE 8080
# Install app
RUN mkdir /app && \
cd /app && \
java -jar /opt/jetty/start.jar --add-modules=server,http,webapp,deploy
ADD docs.xml /app/webapps/docs.xml
ADD docs-web/target/docs-web-*.war /app/webapps/docs.war
WORKDIR /app
CMD ["java", "-jar", "/opt/jetty/start.jar"]

230
README.md
View File

@ -1,84 +1,246 @@
Sismics Docs [![Build Status](https://secure.travis-ci.org/sismics/docs.png)](http://travis-ci.org/sismics/docs)
============
<h3 align="center">
<img src="https://teedy.io/img/github-title.png" alt="Teedy" width=500 />
</h3>
_Web interface_
[![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)
[![Maven CI/CD](https://github.com/sismics/docs/actions/workflows/build-deploy.yml/badge.svg)](https://github.com/sismics/docs/actions/workflows/build-deploy.yml)
![Web interface](http://sismics.com/docs/screenshot1.png)
Teedy is an open source, lightweight document management system for individuals and businesses.
_Android application_
<hr />
<h2 align="center">
<a href="https://github.com/users/jendib/sponsorship">Sponsor this project if you use and appreciate it!</a>
</h2>
<hr />
![Android documents list](http://sismics.com/docs/android1.png) ![Android navigation](http://sismics.com/docs/android2.png) ![Android document details](http://sismics.com/docs/android3.png) ![Android document actions](http://sismics.com/docs/android4.png)
![New!](https://teedy.io/img/laptop-demo.png?20180301)
What is Docs?
---------------
# Demo
Docs is an open source, lightweight document management system.
A demo is available at [demo.teedy.io](https://demo.teedy.io)
Docs is written in Java, and may be run on any operating system with Java support.
- Guest login is enabled with read access on all documents
- "admin" login with "admin" password
- "demo" login with "password" password
Features
--------
# Features
- Responsive user interface
- Optical character recognition
- Support image, PDF, ODT and DOCX files
- Flexible search engine
- LDAP authentication ![New!](https://www.sismics.com/public/img/new.png)
- 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)
- Automatic inbox scanning and importing
- User/group permission system
- 2-factor authentication
- Hierarchical groups
- Audit log
- Comments
- Storage quota per user
- Document sharing by URL
- RESTful Web API
- Webhooks to trigger external service
- Fully featured Android client
- Tested to 100k documents
- [Bulk files importer](https://github.com/sismics/docs/tree/master/docs-importer) (single or scan mode)
- Tested to one million documents
Download
--------
# Install with Docker
A preconfigured Docker image is available, including OCR and media conversion tools, listening on port 8080. If no PostgreSQL config is provided, the database is an embedded H2 database. The H2 embedded database should only be used for testing. For production usage use the provided PostgreSQL configuration (check the Docker Compose example)
**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.11`
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)
## Available environment variables
- General
- `DOCS_BASE_URL`: The base url used by the application. Generated url's will be using this as base.
- `DOCS_GLOBAL_QUOTA`: Defines the default quota applying to all users.
- `DOCS_BCRYPT_WORK`: Defines the work factor which is used for password hashing. The default is `10`. This value may be `4...31` including `4` and `31`. The specified value will be used for all new users and users changing their password. Be aware that setting this factor to high can heavily impact login and user creation performance.
- Admin
- `DOCS_ADMIN_EMAIL_INIT`: Defines the e-mail-address the admin user should have upon initialization.
- `DOCS_ADMIN_PASSWORD_INIT`: Defines the password the admin user should have upon initialization. Needs to be a bcrypt hash. **Be aware that `$` within the hash have to be escaped with a second `$`.**
- Database
- `DATABASE_URL`: The jdbc connection string to be used by `hibernate`.
- `DATABASE_USER`: The user which should be used for the database connection.
- `DATABASE_PASSWORD`: The password to be used for the database connection.
- `DATABASE_POOL_SIZE`: The pool size to be used for the database connection.
- Language
- `DOCS_DEFAULT_LANGUAGE`: The language which will be used as default. Currently supported values are:
- `eng`, `fra`, `ita`, `deu`, `spa`, `por`, `pol`, `rus`, `ukr`, `ara`, `hin`, `chi_sim`, `chi_tra`, `jpn`, `tha`, `kor`, `nld`, `tur`, `heb`, `hun`, `fin`, `swe`, `lav`, `dan`
- E-Mail
- `DOCS_SMTP_HOSTNAME`: Hostname of the SMTP-Server to be used by Teedy.
- `DOCS_SMTP_PORT`: The port which should be used.
- `DOCS_SMTP_USERNAME`: The username to be used.
- `DOCS_SMTP_PASSWORD`: The password to be used.
## Examples
In the following examples some passwords are exposed in cleartext. This was done in order to keep the examples simple. We strongly encourage you to use variables with an `.env` file or other means to securely store your passwords.
### Default, using PostgreSQL
```yaml
version: '3'
services:
# Teedy Application
teedy-server:
image: sismics/docs:v1.11
restart: unless-stopped
ports:
# Map internal port to host
- 8080:8080
environment:
# Base url to be used
DOCS_BASE_URL: "https://docs.example.com"
# Set the admin email
DOCS_ADMIN_EMAIL_INIT: "admin@example.com"
# Set the admin password (in this example: "superSecure")
DOCS_ADMIN_PASSWORD_INIT: "$$2a$$05$$PcMNUbJvsk7QHFSfEIDaIOjk1VI9/E7IPjTKx.jkjPxkx2EOKSoPS"
# Setup the database connection. "teedy-db" is the hostname
# and "teedy" is the name of the database the application
# will connect to.
DATABASE_URL: "jdbc:postgresql://teedy-db:5432/teedy"
DATABASE_USER: "teedy_db_user"
DATABASE_PASSWORD: "teedy_db_password"
DATABASE_POOL_SIZE: "10"
volumes:
- ./docs/data:/data
networks:
- docker-internal
- internet
depends_on:
- teedy-db
# DB for Teedy
teedy-db:
image: postgres:13.1-alpine
restart: unless-stopped
expose:
- 5432
environment:
POSTGRES_USER: "teedy_db_user"
POSTGRES_PASSWORD: "teedy_db_password"
POSTGRES_DB: "teedy"
volumes:
- ./docs/db:/var/lib/postgresql/data
networks:
- docker-internal
networks:
# Network without internet access. The db does not need
# access to the host network.
docker-internal:
driver: bridge
internal: true
internet:
driver: bridge
```
### Using the internal database (only for testing)
```yaml
version: '3'
services:
# Teedy Application
teedy-server:
image: sismics/docs:v1.11
restart: unless-stopped
ports:
# Map internal port to host
- 8080:8080
environment:
# Base url to be used
DOCS_BASE_URL: "https://docs.example.com"
# Set the admin email
DOCS_ADMIN_EMAIL_INIT: "admin@example.com"
# Set the admin password (in this example: "superSecure")
DOCS_ADMIN_PASSWORD_INIT: "$$2a$$05$$PcMNUbJvsk7QHFSfEIDaIOjk1VI9/E7IPjTKx.jkjPxkx2EOKSoPS"
volumes:
- ./docs/data:/data
```
# Manual installation
## Requirements
- Java 11
- Tesseract 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.
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.**
How to build Docs from the sources
----------------------------------
## How to build Teedy from the sources
Prerequisites: JDK 7 with JCE, Maven 3, Tesseract 3.02
Prerequisites: JDK 11, Maven 3, NPM, Grunt, Tesseract 4
Docs is organized in several Maven modules:
Teedy is organized in several Maven modules:
- docs-core
- docs-web
- docs-web-common
- docs-core
- docs-web
- docs-web-common
First off, clone the repository: `git clone git://github.com/sismics/docs.git`
or download the sources from GitHub.
#### Launch the build
### Launch the build
From the root directory:
mvn clean -DskipTests install
```console
mvn clean -DskipTests install
```
#### Run a stand-alone version
### Run a stand-alone version
From the `docs-web` directory:
mvn jetty:run
```console
mvn jetty:run
```
#### Build a .war to deploy to your servlet container
### Build a .war to deploy to your servlet container
From the `docs-web` directory:
mvn -Pprod -DskipTests clean install
```console
mvn -Pprod -DskipTests clean install
```
You will get your deployable WAR in the `docs-web/target` directory.
License
-------
# Contributing
Docs is released under the terms of the GPL license. See `COPYING` for more
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.
# License
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

@ -1,2 +0,0 @@
#!/bin/sh
docker build -t sismics/docs .

18
docker-compose.yml Normal file
View File

@ -0,0 +1,18 @@
version: '3'
services:
# Teedy Application
teedy-server:
image: sismics/docs:v1.10
restart: unless-stopped
ports:
# Map internal port to host
- 8080:8080
environment:
# Base url to be used
DOCS_BASE_URL: "https://docs.example.com"
# Set the admin email
DOCS_ADMIN_EMAIL_INIT: "admin@example.com"
# Set the admin password (in this example: "superSecure")
DOCS_ADMIN_PASSWORD_INIT: "$$2a$$05$$PcMNUbJvsk7QHFSfEIDaIOjk1VI9/E7IPjTKx.jkjPxkx2EOKSoPS"
volumes:
- ./docs/data:/data

View File

@ -1,46 +1,27 @@
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0-beta1'
classpath 'com.android.tools.build:gradle:3.4.0'
}
}
apply plugin: 'com.android.application'
repositories {
jcenter()
google()
}
android {
compileSdkVersion 23
buildToolsVersion '23.0.3'
compileSdkVersion 28
defaultConfig {
minSdkVersion 14
targetSdkVersion 23
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
signingConfigs {
release {
storeFile file(System.getenv("TRACKINO_STORE_PATH"))
storePassword System.getenv("TRACKINO_STORE_PASS")
keyAlias System.getenv("TRACKINO_STORE_ALIAS")
keyPassword System.getenv("TRACKINO_STORE_KEYPASS")
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
versionName '1.0'
}
lintOptions {
@ -49,14 +30,14 @@ android {
}
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:recyclerview-v7:23.3.0'
compile 'com.android.support:design:23.3.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.1.1'
compile "com.squareup.okhttp3:okhttp-urlconnection:3.1.1"
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.0.2'
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,13 +8,14 @@
<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"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
android:theme="@style/AppTheme">
<activity
android:name=".activity.LoginActivity"
android:label="@string/app_name"
@ -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

@ -101,7 +101,7 @@ public class DocumentEditActivity extends AppCompatActivity {
finish();
return;
}
JSONArray tagArray = tags.optJSONArray("stats");
JSONArray tagArray = tags.optJSONArray("tags");
List<JSONObject> tagList = new ArrayList<>();
for (int i = 0; i < tagArray.length(); i++) {

View File

@ -16,6 +16,7 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.method.LinkMovementMethod;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuInflater;
@ -51,7 +52,7 @@ import com.sismics.docs.resource.FileResource;
import com.sismics.docs.service.FileUploadService;
import com.sismics.docs.util.NetworkUtil;
import com.sismics.docs.util.PreferenceUtil;
import com.sismics.docs.util.TagUtil;
import com.sismics.docs.util.SpannableUtil;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@ -75,11 +76,6 @@ public class DocumentViewActivity extends AppCompatActivity {
*/
public static final int REQUEST_CODE_ADD_FILE = 1;
/**
* Request code of editing document.
*/
public static final int REQUEST_CODE_EDIT_DOCUMENT = 2;
/**
* File view pager.
*/
@ -176,9 +172,11 @@ public class DocumentViewActivity extends AppCompatActivity {
}
// Fill the layout
// Create date
TextView createdDateTextView = (TextView) findViewById(R.id.createdDateTextView);
createdDateTextView.setText(date);
// Description
TextView descriptionTextView = (TextView) findViewById(R.id.descriptionTextView);
if (description.isEmpty() || document.isNull("description")) {
descriptionTextView.setVisibility(View.GONE);
@ -187,17 +185,20 @@ public class DocumentViewActivity extends AppCompatActivity {
descriptionTextView.setText(description);
}
// Tags
TextView tagTextView = (TextView) findViewById(R.id.tagTextView);
if (tags.length() == 0) {
tagTextView.setVisibility(View.GONE);
} else {
tagTextView.setVisibility(View.VISIBLE);
tagTextView.setText(TagUtil.buildSpannable(tags));
tagTextView.setText(SpannableUtil.buildSpannableTags(tags));
}
// Language
ImageView languageImageView = (ImageView) findViewById(R.id.languageImageView);
languageImageView.setImageResource(getResources().getIdentifier(language, "drawable", getPackageName()));
// Shared status
ImageView sharedImageView = (ImageView) findViewById(R.id.sharedImageView);
sharedImageView.setVisibility(shared ? View.VISIBLE : View.GONE);
@ -208,7 +209,7 @@ public class DocumentViewActivity extends AppCompatActivity {
public void onClick(View view) {
Intent intent = new Intent(DocumentViewActivity.this, DocumentEditActivity.class);
intent.putExtra("document", DocumentViewActivity.this.document.toString());
startActivityForResult(intent, REQUEST_CODE_EDIT_DOCUMENT);
startActivity(intent);
}
});
@ -642,10 +643,10 @@ public class DocumentViewActivity extends AppCompatActivity {
}
// Action only available if the document is writable
findViewById(R.id.actionEditDocument).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
findViewById(R.id.actionUploadFile).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
findViewById(R.id.actionSharing).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
findViewById(R.id.actionDelete).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
findViewById(R.id.actionEditDocument).setVisibility(writable ? View.VISIBLE : View.GONE);
findViewById(R.id.actionUploadFile).setVisibility(writable ? View.VISIBLE : View.GONE);
findViewById(R.id.actionSharing).setVisibility(writable ? View.VISIBLE : View.GONE);
findViewById(R.id.actionDelete).setVisibility(writable ? View.VISIBLE : View.GONE);
// ACLs
ListView aclListView = (ListView) findViewById(R.id.aclListView);
@ -679,10 +680,54 @@ public class DocumentViewActivity extends AppCompatActivity {
startActivity(intent);
}
});
// Contributors
TextView contributorsTextView = (TextView) findViewById(R.id.contributorsTextView);
contributorsTextView.setText(SpannableUtil.buildSpannableContributors(document.optJSONArray("contributors")));
// Relations
JSONArray relations = document.optJSONArray("relations");
if (relations.length() > 0) {
TextView relationsTextView = (TextView) findViewById(R.id.relationsTextView);
relationsTextView.setMovementMethod(LinkMovementMethod.getInstance());
relationsTextView.setText(SpannableUtil.buildSpannableRelations(relations));
} else {
findViewById(R.id.relationsLayout).setVisibility(View.GONE);
}
// Additional dublincore metadata
displayDublincoreMetadata(R.id.subjectTextView, R.id.subjectLayout, "subject");
displayDublincoreMetadata(R.id.identifierTextView, R.id.identifierLayout, "identifier");
displayDublincoreMetadata(R.id.publisherTextView, R.id.publisherLayout, "publisher");
displayDublincoreMetadata(R.id.formatTextView, R.id.formatLayout, "format");
displayDublincoreMetadata(R.id.sourceTextView, R.id.sourceLayout, "source");
displayDublincoreMetadata(R.id.typeTextView, R.id.typeLayout, "type");
displayDublincoreMetadata(R.id.coverageTextView, R.id.coverageLayout, "coverage");
displayDublincoreMetadata(R.id.rightsTextView, R.id.rightsLayout, "rights");
}
});
}
/**
* Display a dublincore metadata.
*
* @param textViewId TextView ID
* @param blockViewId View ID
* @param name Name
*/
private void displayDublincoreMetadata(int textViewId, int blockViewId, String name) {
if (document == null) return;
String value = document.optString(name);
if (document.isNull(name) || value.isEmpty()) {
findViewById(blockViewId).setVisibility(View.GONE);
return;
}
findViewById(blockViewId).setVisibility(View.VISIBLE);
TextView textView = (TextView) findViewById(textViewId);
textView.setText(value);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
switch (view.getId()) {

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,25 +80,25 @@ 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) {
tagListView.setAdapter(new TagListAdapter(cacheTags.optJSONArray("stats")));
tagListView.setAdapter(new TagListAdapter(cacheTags.optJSONArray("tags")));
}
TagResource.stats(this, new HttpCallback() {
TagResource.list(this, new HttpCallback() {
@Override
public void onSuccess(JSONObject response) {
PreferenceUtil.setCachedJson(MainActivity.this, PreferenceUtil.PREF_CACHED_TAGS_JSON, response);
tagListView.setAdapter(new TagListAdapter(response.optJSONArray("stats")));
tagListView.setAdapter(new TagListAdapter(response.optJSONArray("tags")));
tagProgressView.setVisibility(View.GONE);
tagListView.setEmptyView(tagEmptyView);
}
@ -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);
@ -158,6 +172,7 @@ public class MainActivity extends AppCompatActivity {
@Override
public void onFinish() {
// Force logout in all cases, so the user is not stuck in case of network error
PreferenceUtil.clearAuthToken(MainActivity.this);
ApplicationContext.getInstance().setUserInfo(getApplicationContext(), null);
startActivity(new Intent(MainActivity.this, LoginActivity.class));
finish();

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

@ -9,7 +9,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.sismics.docs.R;
import com.sismics.docs.util.TagUtil;
import com.sismics.docs.util.SpannableUtil;
import org.json.JSONArray;
import org.json.JSONObject;
@ -69,7 +69,7 @@ public class DocListAdapter extends RecyclerView.Adapter<DocListAdapter.ViewHold
holder.titleTextView.setText(document.optString("title"));
JSONArray tags = document.optJSONArray("tags");
holder.subtitleTextView.setText(TagUtil.buildSpannable(tags));
holder.subtitleTextView.setText(SpannableUtil.buildSpannableTags(tags));
String date = DateFormat.getDateFormat(holder.dateTextView.getContext()).format(new Date(document.optLong("create_date")));
holder.dateTextView.setText(date);

View File

@ -33,7 +33,8 @@ 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("jpn", R.string.language_japanese, R.drawable.jpn));
languageList.add(new Language("deu", R.string.language_german, R.drawable.deu));
languageList.add(new Language("pol", R.string.language_polish, R.drawable.pol));
}
@Override

View File

@ -46,7 +46,7 @@ public class TagListAdapter extends BaseAdapter {
// Reorder tags by parent/child relation and compute depth
int depth = 0;
initTags(tags, JSONObject.NULL.toString(), depth);
initTags(tags, "", depth);
}
/**
@ -60,11 +60,10 @@ public class TagListAdapter extends BaseAdapter {
// Get all tags with this parent
for (JSONObject tag : tags) {
String tagParentId = tag.optString("parent");
if (tagParentId.equals(parentId)) {
if (parentId.equals(tagParentId)) {
TagItem tagItem = new TagItem();
tagItem.id = tag.optString("id");
tagItem.name = tag.optString("name");
tagItem.count = tag.optInt("count");
tagItem.color = tag.optString("color");
tagItem.depth = depth;
tagItemList.add(tagItem);
@ -99,8 +98,6 @@ public class TagListAdapter extends BaseAdapter {
TagItem tagItem = getItem(position);
TextView tagTextView = (TextView) view.findViewById(R.id.tagTextView);
tagTextView.setText(tagItem.name);
TextView tagCountTextView = (TextView) view.findViewById(R.id.tagCountTextView);
tagCountTextView.setText(String.format(Locale.ENGLISH, "%d", tagItem.count));
// Label color filtering
ImageView labelImageView = (ImageView) view.findViewById(R.id.labelImageView);
@ -125,7 +122,6 @@ public class TagListAdapter extends BaseAdapter {
public static class TagItem {
private String id;
private String name;
private int count;
private String color;
private int depth;

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

@ -73,7 +73,7 @@ public class SearchFragment extends DialogFragment {
dialog.cancel();
return dialog;
}
JSONArray tagArray = tags.optJSONArray("stats");
JSONArray tagArray = tags.optJSONArray("tags");
List<JSONObject> tagList = new ArrayList<>();
for (int i = 0; i < tagArray.length(); i++) {

View File

@ -16,14 +16,14 @@ import okhttp3.Request;
*/
public class TagResource extends BaseResource {
/**
* GET /tag/stats.
* GET /tag/list.
*
* @param context Context
* @param callback Callback
*/
public static void stats(Context context, HttpCallback callback) {
public static void list(Context context, HttpCallback callback) {
Request request = new Request.Builder()
.url(HttpUrl.parse(getApiUrl(context) + "/tag/stats"))
.url(HttpUrl.parse(getApiUrl(context) + "/tag/list"))
.get()
.build();
OkHttpUtil.buildClient(context)

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

@ -0,0 +1,33 @@
package com.sismics.docs.ui.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.ListView;
/**
* Non-scrollable ListView.
* All items are visible from the start.
*
* @author http://stackoverflow.com/questions/18813296/non-scrollable-listview-inside-scrollview/24629341#24629341
*/
public class NonScrollListView extends ListView {
public NonScrollListView(Context context) {
super(context);
}
public NonScrollListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NonScrollListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
}

View File

@ -1,9 +1,14 @@
package com.sismics.docs.util;
import android.Manifest;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
/**
* Utility class for network actions.
@ -19,9 +24,14 @@ public class NetworkUtil {
* @param title Notification title
* @param description Notification description
*/
public static void downloadFile(Context context, String url, String fileName, String title, String description) {
String authToken = PreferenceUtil.getAuthToken(context);
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
public static void downloadFile(Activity activity, String url, String fileName, String title, String description) {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, 0);
return;
}
String authToken = PreferenceUtil.getAuthToken(activity);
DownloadManager downloadManager = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);

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) {

View File

@ -126,6 +126,7 @@ public class PreferenceUtil {
/**
* Returns auth token cookie from shared preferences.
*
* @param context Context
* @return Auth token
*/
public static String getAuthToken(Context context) {
@ -140,6 +141,16 @@ public class PreferenceUtil {
return null;
}
/**
* Clear all auth tokens.
*
* @param context Context
*/
public static void clearAuthToken(Context context) {
PersistentCookieStore cookieStore = new PersistentCookieStore(context);
cookieStore.removeAll();
}
/**
* Returns cleaned server URL.
*

View File

@ -39,7 +39,9 @@ public class SearchQueryBuilder {
*/
public SearchQueryBuilder simpleSearch(String simpleSearch) {
if (isValid(simpleSearch)) {
query.append(SEARCH_SEPARATOR).append(simpleSearch);
query.append(SEARCH_SEPARATOR)
.append("simple:")
.append(simpleSearch);
}
return this;
}

View File

@ -0,0 +1,85 @@
package com.sismics.docs.util;
import android.graphics.Color;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* Utility class for spannable.
*
* @author bgamard.
*/
public class SpannableUtil {
/**
* Create a colored spannable from tags.
*
* @param tags Tags
* @return Colored spannable
*/
public static Spannable buildSpannableTags(JSONArray tags) {
return buildSpannable(tags, "name", "color");
}
/**
* Create a spannable for contributors.
*
* @param contributors Contributors
* @return Spannable
*/
public static Spannable buildSpannableContributors(JSONArray contributors) {
return buildSpannable(contributors, "username", null);
}
/**
* Create a spannable for relations.
*
* @param relations Relations
* @return Spannable
*/
public static Spannable buildSpannableRelations(JSONArray relations) {
return buildSpannable(relations, "title", null);
}
/**
* Create a spannable from a JSONArray.
*
* @param array JSONArray
* @param valueName Name of the value part
* @param colorName Name of the color part (optional)
* @return Spannable
*/
private static Spannable buildSpannable(JSONArray array, String valueName, String colorName) {
SpannableStringBuilder builder = new SpannableStringBuilder();
for (int i = 0; i < array.length(); i++) {
final JSONObject tag = array.optJSONObject(i);
int start = builder.length();
builder.append(" ").append(tag.optString(valueName)).append(" ");
builder.setSpan(new ForegroundColorSpan(Color.WHITE), start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.setSpan(new BackgroundColorSpan(Color.parseColor(tag.optString(colorName, "#5bc0de"))), start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
/*
TODO : Make tags, relations and contributors clickable
builder.setSpan(new ClickableSpan() {
@Override
public void onClick(View widget) {
}
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setColor(Color.WHITE);
ds.setUnderlineText(false);
}
}, start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);*/
builder.append(" ");
}
return builder;
}
}

View File

@ -1,39 +0,0 @@
package com.sismics.docs.util;
import android.graphics.Color;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* Utility class for tags.
*
* @author bgamard.
*/
public class TagUtil {
/**
* Create a colored spannable from tags.
*
* @param tags Tags
* @return Colored spannable
*/
public static Spannable buildSpannable(JSONArray tags) {
SpannableStringBuilder builder = new SpannableStringBuilder();
for (int i = 0; i < tags.length(); i++) {
JSONObject tag = tags.optJSONObject(i);
int start = builder.length();
builder.append(" ").append(tag.optString("name")).append(" ");
builder.setSpan(new ForegroundColorSpan(Color.WHITE), start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.setSpan(new BackgroundColorSpan(Color.parseColor(tag.optString("color"))), start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.append(" ");
}
return builder;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 813 B

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

@ -142,15 +142,19 @@
<!-- Right drawer -->
<LinearLayout
<ScrollView
android:id="@+id/right_drawer"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:orientation="vertical"
android:clickable="true"
android:background="#fff"
android:elevation="5dp">
android:elevation="5dp"
android:layout_gravity="end">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Actions -->
@ -166,28 +170,6 @@
android:orientation="horizontal"
style="?android:buttonBarStyle">
<Button
android:id="@+id/actionEditDocument"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="@drawable/ic_create_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/edit_document"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="8dp"/>
<Button
android:id="@+id/actionUploadFile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="@drawable/ic_file_upload_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/upload_file"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="8dp"/>
<Button
android:id="@+id/actionDownload"
android:layout_width="wrap_content"
@ -199,6 +181,28 @@
android:textAllCaps="false"
android:layout_margin="8dp"/>
<Button
android:id="@+id/actionExportPdf"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="@drawable/ic_description_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/export_pdf"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="8dp"/>
<Button
android:id="@+id/actionAuditLog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="@drawable/ic_assignment_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/activity"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="8dp"/>
</LinearLayout>
<LinearLayout
@ -208,12 +212,23 @@
style="?android:buttonBarStyle">
<Button
android:id="@+id/actionExportPdf"
android:id="@+id/actionEditDocument"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="@drawable/ic_description_grey600_24dp"
android:drawableTop="@drawable/ic_create_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/export_pdf"
android:text="@string/edit_document"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="0dp"/>
<Button
android:id="@+id/actionUploadFile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="@drawable/ic_file_upload_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/upload_file"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="0dp"/>
@ -229,17 +244,6 @@
android:textAllCaps="false"
android:layout_margin="0dp"/>
<Button
android:id="@+id/actionAuditLog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="@drawable/ic_assignment_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/activity"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="0dp"/>
<Button
android:id="@+id/actionDelete"
android:layout_width="wrap_content"
@ -285,6 +289,8 @@
android:id="@+id/createdDateTextView"
android:layout_toRightOf="@id/createdDateLabel"
android:layout_toEndOf="@id/createdDateLabel"
android:layout_toLeftOf="@id/sharedImageView"
android:layout_toStartOf="@id/sharedImageView"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:gravity="center_vertical"
@ -332,6 +338,7 @@
android:fontFamily="sans-serif-light"/>
<ImageView
android:contentDescription="@string/shared"
android:id="@+id/sharedImageView"
android:layout_width="24dp"
android:layout_height="24dp"
@ -343,6 +350,7 @@
android:layout_toStartOf="@+id/languageImageView"/>
<ImageView
android:contentDescription="@string/language"
android:id="@+id/languageImageView"
android:layout_width="24dp"
android:layout_height="24dp"
@ -352,6 +360,278 @@
</RelativeLayout>
<!-- Additional dublincore metadata -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:paddingBottom="12dp">
<!-- Subject -->
<LinearLayout
android:id="@+id/subjectLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_weight="0.33"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@string/subject"/>
<TextView
android:id="@+id/subjectTextView"
android:layout_weight="0.67"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"/>
</LinearLayout>
<!-- Identifier -->
<LinearLayout
android:id="@+id/identifierLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_weight="0.33"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@string/identifier"/>
<TextView
android:id="@+id/identifierTextView"
android:layout_weight="0.67"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"/>
</LinearLayout>
<!-- Publisher -->
<LinearLayout
android:id="@+id/publisherLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_weight="0.33"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@string/publisher"/>
<TextView
android:id="@+id/publisherTextView"
android:layout_weight="0.67"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"/>
</LinearLayout>
<!-- Format -->
<LinearLayout
android:id="@+id/formatLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_weight="0.33"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@string/format"/>
<TextView
android:id="@+id/formatTextView"
android:layout_weight="0.67"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"/>
</LinearLayout>
<!-- Source -->
<LinearLayout
android:id="@+id/sourceLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_weight="0.33"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@string/source"/>
<TextView
android:id="@+id/sourceTextView"
android:layout_weight="0.67"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"/>
</LinearLayout>
<!-- Type -->
<LinearLayout
android:id="@+id/typeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_weight="0.33"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@string/type"/>
<TextView
android:id="@+id/typeTextView"
android:layout_weight="0.67"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"/>
</LinearLayout>
<!-- Coverage -->
<LinearLayout
android:id="@+id/coverageLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_weight="0.33"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@string/coverage"/>
<TextView
android:id="@+id/coverageTextView"
android:layout_weight="0.67"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"/>
</LinearLayout>
<!-- Rights -->
<LinearLayout
android:id="@+id/rightsLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_weight="0.33"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@string/rights"/>
<TextView
android:id="@+id/rightsTextView"
android:layout_weight="0.67"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"/>
</LinearLayout>
<!-- Contributors -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_weight="0.33"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@string/contributors"/>
<TextView
android:id="@+id/contributorsTextView"
android:layout_weight="0.67"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"/>
</LinearLayout>
<!-- Relations -->
<LinearLayout
android:id="@+id/relationsLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="2dp">
<TextView
android:layout_weight="0.33"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@string/relations"/>
<TextView
android:id="@+id/relationsTextView"
android:layout_weight="0.67"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"/>
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
@ -369,14 +649,16 @@
android:text="@string/who_can_access"
android:layout_margin="12dp"/>
<ListView
<com.sismics.docs.ui.view.NonScrollListView
android:id="@+id/aclListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"/>
</LinearLayout>
</ScrollView>
</android.support.v4.widget.DrawerLayout>

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"

View File

@ -29,16 +29,4 @@
android:text="Appartement"
android:textSize="14sp"/>
<TextView
android:id="@+id/tagCountTextView"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:textColor="#888"
android:text="5"
android:textSize="14sp"/>
</RelativeLayout>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 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

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Validation -->
<string name="validate_error_email">Email invalide</string>
<string name="validate_error_length_min">Trop court (min. %d)</string>
<string name="validate_error_length_max">Trop long (max. %d)</string>
<string name="validate_error_required">Requis</string>
<string name="validate_error_alphanumeric">Seuls les lettres et les nombres sont autorisés</string>
<!-- 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 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>
<string name="login">Connexion</string>
<string name="ok">OK</string>
<string name="cancel">Annuler</string>
<string name="login_fail_title">Echec de la connexion</string>
<string name="login_fail">Mauvais nom d\'utilisateur ou de mot de passe</string>
<string name="network_error_title">Erreur réseau</string>
<string name="network_error">Erreur réseau, veuillez vérifier votre connexion internet et l\'URL du serveur</string>
<string name="invalid_url_title">URL invalide</string>
<string name="invalid_url">Veuillez vérifier l\URL du serveur et réessayer</string>
<string name="crash_toast_text">Une erreur s\'est produite, un rapport a été envoyé afin de corriger ce problème</string>
<string name="created_date">Date création</string>
<string name="download_file">Télécharger ce fichier</string>
<string name="download_document">Télécharger</string>
<string name="action_search">Rechercher dans les documents</string>
<string name="all_documents">Tous les documents</string>
<string name="shared_documents">Documents partagés</string>
<string name="all_tags">Tous les tags</string>
<string name="no_tags">Aucun tag</string>
<string name="error_loading_tags">Erreur de chargement des tags</string>
<string name="no_documents">Aucun document</string>
<string name="error_loading_documents">Erreur de chargement des documents</string>
<string name="no_files">Aucun fichier</string>
<string name="error_loading_files">Erreur de chargement des fichiers</string>
<string name="new_document">Nouveau document</string>
<string name="share">Partage</string>
<string name="close">Fermer</string>
<string name="add">Ajouter</string>
<string name="add_share_hint">Nom du partage (facultatif)</string>
<string name="document_not_shared">Ce document n\'est pas partagé</string>
<string name="delete_share">Supprimer ce partage</string>
<string name="send_share">Envoi ce lien de partage</string>
<string name="error_loading_shares">Erreur de chargement des partages</string>
<string name="error_adding_share">Erreur lors de l\'ajout du partage</string>
<string name="share_default_name">Lien de partage</string>
<string name="error_deleting_share">Erreur lors de la suppression de ce partage</string>
<string name="send_share_to">Envoi ce lien de partage à</string>
<string name="upload_file">Aj. fichier</string>
<string name="upload_from">Envoyer un fichier depuis</string>
<string name="settings">Paramètres</string>
<string name="logout">Déconnexion</string>
<string name="version">Version</string>
<string name="build">Build</string>
<string name="pref_advanced_category">Paramètres avancés</string>
<string name="pref_about_category">A propors</string>
<string name="pref_github">GitHub</string>
<string name="pref_issue">Rapporter un bug</string>
<string name="pref_clear_cache_title">Vider le cache</string>
<string name="pref_clear_cache_summary">Nettoyer les fichiers en cache</string>
<string name="pref_clear_cache_success">Cache vidé</string>
<string name="pref_clear_history_title">Vider l\'historique de recherche</string>
<string name="pref_clear_history_summary">Supprimer les recherches récentes</string>
<string name="pref_clear_history_success">Historique de recherche vidé</string>
<string name="pref_cache_size">Taille du cache</string>
<string name="save">Enregistrer</string>
<string name="edit_document">Modifier</string>
<string name="error_editing_document">Erreur réseau, veuillez réessayer</string>
<string name="please_wait">Veuillez patienter</string>
<string name="document_editing_message">Envoi des données</string>
<string name="delete_document">Suppr.</string>
<string name="delete_document_title">Supprimer le document</string>
<string name="delete_document_message">Etes-vous sûr de vouloir supprimer ce document et tous les fichiers associés ?</string>
<string name="document_delete_failure">Erreur réseau lors de la suppression de ce document</string>
<string name="document_deleting_message">Suppression du document</string>
<string name="delete_file_title">Supprimer le fichier</string>
<string name="delete_file_message">Etes-vous sûr de vouloir supprimer ce fichier ?</string>
<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">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>
<string name="advanced_search">Recherche avancée</string>
<string name="search">Rechercher</string>
<string name="add_tags">Ajouter des tags</string>
<string name="creation_date">Date de création</string>
<string name="description">Description</string>
<string name="title">Titre</string>
<string name="simple_search">Recherche simple</string>
<string name="fulltext_search">Recherche texte intégral</string>
<string name="creator">Créateur</string>
<string name="after_date">Après cette date</string>
<string name="before_date">Avant cette date</string>
<string name="search_tags">Rechercher dans les tags</string>
<string name="all_languages">Toutes les langues</string>
<string name="toggle_informations">Afficher/masquer les informations</string>
<string name="who_can_access">Qui a accès</string>
<string name="comments">Commentaires</string>
<string name="no_comments">Aucun commentaire</string>
<string name="error_loading_comments">Erreur de chargement des commentaires</string>
<string name="send">Envoyer</string>
<string name="add_comment">Ajouter un commentaire</string>
<string name="comment_add_failure">Erreur lors de l\'ajout du commentaire</string>
<string name="adding_comment">Ajout du commentaire</string>
<string name="comment_delete">Supprimer le commentaire</string>
<string name="deleting_comment">Suppression du commentaire</string>
<string name="error_deleting_comment">Erreur lors de la suppression du commentaire</string>
<string name="export_pdf">PDF</string>
<string name="download">Télécharger</string>
<string name="margin">Marge</string>
<string name="fit_image_to_page">Ajuster les images à la page</string>
<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 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>
<string name="storage_quota">Quota de stockage</string>
<string name="storage_display">%1$d/%2$d Mo</string>
<string name="validation_code">Code de validation</string>
<string name="shared">Partagé</string>
<string name="language">Langue</string>
<string name="coverage">Couverture</string>
<string name="type">Type</string>
<string name="source">Source</string>
<string name="format">Format</string>
<string name="publisher">Editeur</string>
<string name="identifier">Identifiant</string>
<string name="subject">Sujet</string>
<string name="rights">Droits</string>
<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

@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Validation -->
<string name="validate_error_email">Nieprawidłowy email</string>
<string name="validate_error_length_min">Za krótki (min. %d)</string>
<string name="validate_error_length_max">Za długi (max. %d)</string>
<string name="validate_error_required">Wymagany</string>
<string name="validate_error_alphanumeric">Tylko litery i cyfry</string>
<!-- App -->
<string name="app_name" translatable="false">Teedy</string>
<string name="drawer_open">Otwórz szufladę nawigacji</string>
<string name="drawer_close">Zamknij szufladę nawigacji</string>
<string name="login_explain"><![CDATA[Aby rozpocząć, musisz pobrać i zainstalować serwer Teedy na <a href="https://github.com/sismics/docs">github.com/sismics/docs</a> i poniżej wprowadzić adres]]></string>
<string name="server">Serwer</string>
<string name="username">Użytkownik</string>
<string name="password">Hasło</string>
<string name="login">Zaloguj</string>
<string name="ok">OK</string>
<string name="cancel">Anuluj</string>
<string name="login_fail_title">Błąd logowania</string>
<string name="login_fail">Nieprawidłowa nazwa użytkownika lub hasło</string>
<string name="network_error_title">Błąd sieci</string>
<string name="network_error">Błąd sieci, sprawdź połączenie z interneterm oraz adres URL serwera</string>
<string name="invalid_url_title">Nieprawidłowy adres URL</string>
<string name="invalid_url">Sprawdź adres URL serwera i spróbuj ponownie</string>
<string name="crash_toast_text">Wystąpiła awaria, wysłano raport w celu rozwiązania tego problemu</string>
<string name="created_date">Data utworzenia</string>
<string name="download_file">Pobierz bieżący plik</string>
<string name="download_document">Pobierz</string>
<string name="action_search">Znadź dokumenty</string>
<string name="all_documents">Wszystkie dokumenty</string>
<string name="shared_documents">Udostępnione dokumenty</string>
<string name="all_tags">Wszystkie etykiety</string>
<string name="no_tags">Brak etykiet</string>
<string name="error_loading_tags">Błąd ładowania etykiet</string>
<string name="no_documents">Brak dokumentów</string>
<string name="error_loading_documents">Błąd ładowania dokumentów</string>
<string name="no_files">Brak plików</string>
<string name="error_loading_files">Błąd ładowania plików</string>
<string name="new_document">Nowy dokument</string>
<string name="share">Udostępnij</string>
<string name="close">Zamknij</string>
<string name="add">Dodaj</string>
<string name="add_share_hint">Nazwa udostępnienia (opcjonalnie)</string>
<string name="document_not_shared">Ten dokument nie jest obecnie udostępniony</string>
<string name="delete_share">Usuń udostępnienie</string>
<string name="send_share">Wyślij link udostępnienia</string>
<string name="error_loading_shares">Błąd ładowania udostępnień</string>
<string name="error_adding_share">Błąd dodawania udostępnienia</string>
<string name="share_default_name">Udostępnij link</string>
<string name="error_deleting_share">Błąd usuwania udostępnienia</string>
<string name="send_share_to">Wyślij link udostępnienia do</string>
<string name="upload_file">dodaj plik</string>
<string name="upload_from">Przeslij plik z</string>
<string name="settings">ustawienia</string>
<string name="logout">Wyloguj</string>
<string name="version">Wersja</string>
<string name="build">Kompilacja</string>
<string name="pref_advanced_category">Ustawienia zaawansowane</string>
<string name="pref_about_category">O programie</string>
<string name="pref_github">GitHub</string>
<string name="pref_issue">Zgłoś błąd</string>
<string name="pref_clear_cache_title">Wyczyść cache</string>
<string name="pref_clear_cache_summary">Wyczyść podręczne pliki</string>
<string name="pref_clear_cache_success">Cache wyczyszczony</string>
<string name="pref_clear_history_title">Wyczyść historię wyszukiwania</string>
<string name="pref_clear_history_summary">Opróżnij ostatnie sugestie wyszukiwania</string>
<string name="pref_clear_history_success">Historia wyszukiwania wyczyszczona</string>
<string name="pref_cache_size">Rozmiar cache</string>
<string name="language_french" translatable="false">Francuski</string>
<string name="language_english" translatable="false">Angielski</string>
<string name="language_german" translatable="false">Niemiecki</string>
<string name="language_polish" translatable="false">Polski</string>
<string name="save">Zapisz</string>
<string name="edit_document">Edytuj</string>
<string name="error_editing_document">Błąd sieci, spróbuj ponownie</string>
<string name="please_wait">Proszę czekać</string>
<string name="document_editing_message">Wysyłam twoje dane</string>
<string name="delete_document">Usuń</string>
<string name="delete_document_title">Usuń dokument</string>
<string name="delete_document_message">Naprawdę chcesz usunąć dokument i powiązane z nim pliki?</string>
<string name="document_delete_failure">Błąd sieci w czasie usuwania tego dokumentu</string>
<string name="document_deleting_message">Usuwanie dokumentu</string>
<string name="delete_file_title">Usuń plik</string>
<string name="delete_file_message">Naprawdę chcesz usunąć ten plik?</string>
<string name="file_delete_failure">Błąd sieci w czasie usuwania bieżącego pliku</string>
<string name="file_deleting_message">Usuwanie pliku</string>
<string name="error_reading_file">Błąd podczas odczytu pliku</string>
<string name="upload_notification_title">Teedy</string>
<string name="upload_notification_message">Przesyłanie nowego pliku do dokumentu</string>
<string name="upload_notification_error">Błąd przsyłania nowego pliku</string>
<string name="delete_file">Usuń bieżący plik</string>
<string name="advanced_search">Zaawansowane wyszukiwanie</string>
<string name="search">Znajdź</string>
<string name="add_tags">Dodaj eytkiety</string>
<string name="creation_date">Data utworzenia</string>
<string name="description">Opis</string>
<string name="title">Tytuł</string>
<string name="simple_search">Proste wyszukiwanie</string>
<string name="fulltext_search">Wyszukiwanie pełnotekstowe</string>
<string name="creator">Autor</string>
<string name="after_date">Po dacie</string>
<string name="before_date">Przed datą</string>
<string name="search_tags">Znajdź etykiety</string>
<string name="all_languages">Wszystkie języki</string>
<string name="toggle_informations">Przełącz informacje</string>
<string name="who_can_access">Kto ma dostęp</string>
<string name="comments">Komentarze</string>
<string name="no_comments">Brak komentarzy</string>
<string name="error_loading_comments">Błąd ładowania komentarzy</string>
<string name="send">Wyślij</string>
<string name="add_comment">Dodaj komentarz</string>
<string name="comment_add_failure">Błąd dodawania komentarza</string>
<string name="adding_comment">Dodawanie komentarza</string>
<string name="comment_delete">Usuń komentarz</string>
<string name="deleting_comment">Usuwanie komentarza</string>
<string name="error_deleting_comment">Błąd usuwania komentarza</string>
<string name="export_pdf">PDF</string>
<string name="download">Pobierz</string>
<string name="margin">Margines</string>
<string name="fit_image_to_page">Dostosuj obraz do strony</string>
<string name="export_comments">Eksport komentarzy</string>
<string name="export_metadata">Eksport metadanych</string>
<string name="mm">mm</string>
<string name="download_file_title">Eksport plików Teedy</string>
<string name="download_document_title">Eksport dokumentu Teedy</string>
<string name="download_pdf_title">Eksport Teedy jako PDF</string>
<string name="latest_activity">Ostatnie aktywności</string>
<string name="activity">Aktywności</string>
<string name="email">E-mail</string>
<string name="storage_quota">Limit magazynu</string>
<string name="storage_display">%1$d/%2$d MB</string>
<string name="validation_code">Kod weryfikujący</string>
<string name="shared">Udostępnienie</string>
<string name="language">Język</string>
<string name="coverage">Zakres</string>
<string name="type">Rodzaj</string>
<string name="source">Źródło</string>
<string name="format">Format</string>
<string name="publisher">Udostępniający</string>
<string name="identifier">Identifikator</string>
<string name="subject">temat</string>
<string name="rights">Prawa</string>
<string name="contributors">Współtwórcy</string>
<string name="relations">Powiązania</string>
<!-- Audit log -->
<string name="auditlog_Acl">ACL</string>
<string name="auditlog_Comment">Komentarz</string>
<string name="auditlog_Document">Dokument</string>
<string name="auditlog_File">Plik</string>
<string name="auditlog_Group">Grupa</string>
<string name="auditlog_Route">Przepływ</string>
<string name="auditlog_RouteModel">Model przepływu</string>
<string name="auditlog_Tag">Etykieta</string>
<string name="auditlog_User">Użytkownik</string>
<string name="auditlog_Webhook">Webhook</string>
<string name="auditlog_created">utworzony</string>
<string name="auditlog_updated">zaktualizowany</string>
<string name="auditlog_deleted">usunięty</string>
</resources>

View File

@ -9,10 +9,10 @@
<string name="validate_error_alphanumeric">Only letters and numbers</string>
<!-- App -->
<string name="app_name">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="http://www.sismics.com/docs">www.sismics.com/docs</a> and enter your server URL 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>
@ -46,7 +46,7 @@
<string name="add_share_hint">Name the share (optional)</string>
<string name="document_not_shared">This document is not currently shared</string>
<string name="delete_share">Delete this share</string>
<string name="send_share">Send this share</string>
<string name="send_share">Send this share link</string>
<string name="error_loading_shares">Error loading shares</string>
<string name="error_adding_share">Error adding share</string>
<string name="share_default_name">Share link</string>
@ -69,9 +69,10 @@
<string name="pref_clear_history_summary">Wipe the recent search suggestions</string>
<string name="pref_clear_history_success">Search history cleared</string>
<string name="pref_cache_size">Cache size</string>
<string name="language_french">French</string>
<string name="language_english">English</string>
<string name="language_japanese">Japanese</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="language_polish" translatable="false">Polski</string>
<string name="save">Save</string>
<string name="edit_document">Edit</string>
<string name="error_editing_document">Network error, please try again</string>
@ -87,7 +88,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>
@ -123,14 +124,41 @@
<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>
<string name="storage_quota">Storage quota</string>
<string name="storage_display">%1$d/%2$d MB</string>
<string name="validation_code">Validation code</string>
<string name="shared">Shared</string>
<string name="language">Language</string>
<string name="coverage">Coverage</string>
<string name="type">Type</string>
<string name="source">Source</string>
<string name="format">Format</string>
<string name="publisher">Publisher</string>
<string name="identifier">Identifier</string>
<string name="subject">Subject</string>
<string name="rights">Rights</string>
<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

@ -12,6 +12,8 @@
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx3072m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects

View File

@ -1,6 +1,6 @@
#Sat Jan 16 19:15:13 CET 2016
#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-2.10-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

View File

@ -5,8 +5,8 @@
<parent>
<groupId>com.sismics.docs</groupId>
<artifactId>docs-parent</artifactId>
<version>1.4</version>
<relativePath>..</relativePath>
<version>1.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -17,20 +17,10 @@
<dependencies>
<!-- Persistence layer dependencies -->
<dependency>
<groupId>org.hibernate</groupId>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
</dependency>
<!-- Other external dependencies -->
<dependency>
<groupId>joda-time</groupId>
@ -48,8 +38,28 @@
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
</dependency>
<dependency>
@ -73,8 +83,8 @@
</dependency>
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<groupId>at.favre.lib</groupId>
<artifactId>bcrypt</artifactId>
</dependency>
<dependency>
@ -92,6 +102,31 @@
<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.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.apache.directory.api</groupId>
<artifactId>api-ldap-client-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.directory.api</groupId>
<artifactId>api-ldap-codec-standalone</artifactId>
</dependency>
<!-- Only there to read old index and rebuild them -->
<dependency>
<groupId>org.apache.lucene</groupId>
@ -115,33 +150,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 -->
@ -184,20 +225,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,18 @@
package com.sismics.docs.core.constant;
/**
* ACL type.
*
* @author bgamard
*/
public enum AclType {
/**
* User created ACL.
*/
USER,
/**
* ACL created by the routing module.
*/
ROUTING
}

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

@ -13,5 +13,57 @@ public enum ConfigType {
/**
* Theme configuration.
*/
THEME
THEME,
/**
* Guest login.
*/
GUEST_LOGIN,
/**
* OCR enabled.
*/
OCR_ENABLED,
/**
* Default language.
*/
DEFAULT_LANGUAGE,
/**
* SMTP server configuration.
*/
SMTP_HOSTNAME,
SMTP_PORT,
SMTP_FROM,
SMTP_USERNAME,
SMTP_PASSWORD,
/**
* Inbox scanning configuration.
*/
INBOX_ENABLED,
INBOX_HOSTNAME,
INBOX_PORT,
INBOX_STARTTLS,
INBOX_USERNAME,
INBOX_PASSWORD,
INBOX_FOLDER,
INBOX_TAG,
INBOX_AUTOMATIC_TAGS,
INBOX_DELETE_IMPORTED,
/**
* LDAP connection.
*/
LDAP_ENABLED,
LDAP_HOST,
LDAP_PORT,
LDAP_USESSL,
LDAP_ADMIN_DN,
LDAP_ADMIN_PASSWORD,
LDAP_BASE_DN,
LDAP_FILTER,
LDAP_DEFAULT_EMAIL,
LDAP_DEFAULT_STORAGE
}

View File

@ -1,9 +1,9 @@
package com.sismics.docs.core.constant;
import java.util.List;
import com.google.common.collect.Lists;
import java.util.List;
/**
* Application constants.
*
@ -18,17 +18,22 @@ public class Constants {
/**
* Administrator's default password ("admin").
*/
public static final String DEFAULT_ADMIN_PASSWORD = "$2a$05$6Ny3TjrW3aVAL1or2SlcR.fhuDgPKp5jp.P9fBXwVNePgeLqb4i3C";
public static final String DEFAULT_ADMIN_PASSWORD = "$2y$10$xg0EEKVUehutDI1m6qQhVeFz7SMQMl1jQzjf2KkVsR2c7aV2vyyjK";
/**
* RAM Lucene directory storage.
* Administrator's default email.
*/
public static final String LUCENE_DIRECTORY_STORAGE_RAM = "RAM";
public static final String DEFAULT_ADMIN_EMAIL = "admin@localhost";
/**
* File Lucene directory storage.
* Bcrypt default work factor
*/
public static final String LUCENE_DIRECTORY_STORAGE_FILE = "FILE";
public static final int DEFAULT_BCRYPT_WORK = 10;
/**
* Guest user ID.
*/
public static final String GUEST_USER_ID = "guest";
/**
* Default generic user role.
@ -38,5 +43,63 @@ public class Constants {
/**
* Supported document languages.
*/
public static final List<String> SUPPORTED_LANGUAGES = Lists.newArrayList("eng", "fra", "jpn");
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", "hun", "fin", "swe", "lav", "dan", "nor", "vie", "ces", "sqi");
/**
* Base URL environment variable.
*/
public static final String BASE_URL_ENV = "DOCS_BASE_URL";
/**
* Default language environment variable.
*/
public static final String DEFAULT_LANGUAGE_ENV = "DOCS_DEFAULT_LANGUAGE";
/**
* SMTP configuration environment variables.
*/
public static final String SMTP_HOSTNAME_ENV = "DOCS_SMTP_HOSTNAME";
public static final String SMTP_PORT_ENV = "DOCS_SMTP_PORT";
public static final String SMTP_USERNAME_ENV = "DOCS_SMTP_USERNAME";
public static final String SMTP_PASSWORD_ENV = "DOCS_SMTP_PASSWORD";
/**
* Global quota environment variable.
*/
public static final String GLOBAL_QUOTA_ENV = "DOCS_GLOBAL_QUOTA";
/**
* Initial admin password environment variable.
*/
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";
/**
* Work factor to be used by Bcrypt
*/
public static final String BCRYPT_WORK_ENV = "DOCS_BCRYPT_WORK";
/**
* Expiration time of the password recovery in hours.
*/
public static final int PASSWORD_RECOVERY_EXPIRATION_HOUR = 2;
/**
* Email template for password recovery.
*/
public static final String EMAIL_TEMPLATE_PASSWORD_RECOVERY = "password_recovery";
/**
* 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,23 @@
package com.sismics.docs.core.constant;
/**
* Route step transitions.
*
* @author bgamard
*/
public enum RouteStepTransition {
/**
* Route step approved.
*/
APPROVED,
/**
* Route step rejected.
*/
REJECTED,
/**
* Route step validated.
*/
VALIDATED
}

View File

@ -0,0 +1,18 @@
package com.sismics.docs.core.constant;
/**
* Route step types.
*
* @author bgamard
*/
public enum RouteStepType {
/**
* Approval step with 2 choices.
*/
APPROVE,
/**
* Simple validation step, no possible choice.
*/
VALIDATE
}

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,21 +1,22 @@
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.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 jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import com.sismics.docs.core.constant.AclTargetType;
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.model.jpa.Acl;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.util.context.ThreadLocalContext;
/**
* ACL DAO.
*
@ -65,19 +66,25 @@ public class AclDao {
* @return ACL DTO list
*/
@SuppressWarnings("unchecked")
public List<AclDto> getBySourceId(String sourceId) {
public List<AclDto> getBySourceId(String sourceId, AclType type) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
StringBuilder sb = new StringBuilder("select a.ACL_ID_C, a.ACL_PERM_C, a.ACL_TARGETID_C, ");
sb.append(" u.USE_USERNAME_C, s.SHA_ID_C, s.SHA_NAME_C, g.GRP_NAME_C ");
sb.append(" from T_ACL a ");
sb.append(" left join T_USER u on u.USE_ID_C = a.ACL_TARGETID_C ");
sb.append(" left join T_SHARE s on s.SHA_ID_C = a.ACL_TARGETID_C ");
sb.append(" left join T_GROUP g on g.GRP_ID_C = a.ACL_TARGETID_C ");
sb.append(" where a.ACL_DELETEDATE_D is null and a.ACL_SOURCEID_C = :sourceId ");
StringBuilder sb = new StringBuilder("select a.ACL_ID_C, a.ACL_PERM_C, a.ACL_TARGETID_C, ")
.append(" u.USE_USERNAME_C, s.SHA_ID_C, s.SHA_NAME_C, g.GRP_NAME_C ")
.append(" from T_ACL a ")
.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 ");
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
@ -118,12 +125,20 @@ 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;
}
if (targetIdList.isEmpty()) {
return false;
}
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);
@ -141,26 +156,29 @@ public class AclDao {
* @param perm Permission
* @param targetId Target ID
* @param userId User ID
* @param type Type
*/
@SuppressWarnings("unchecked")
public void delete(String sourceId, PermType perm, String targetId, String userId) {
public void delete(String sourceId, PermType perm, String targetId, String userId, AclType type) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Create audit log
Query q = em.createQuery("from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId");
Query q = em.createQuery("from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.type = :type and a.deleteDate is null");
q.setParameter("sourceId", sourceId);
q.setParameter("perm", perm);
q.setParameter("targetId", targetId);
q.setParameter("type", type);
List<Acl> aclList = q.getResultList();
for (Acl acl : aclList) {
AuditLogUtil.create(acl, AuditLogType.DELETE, userId);
}
// Soft delete the ACLs
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId");
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.type = :type and a.deleteDate is null");
q.setParameter("sourceId", sourceId);
q.setParameter("perm", perm);
q.setParameter("targetId", targetId);
q.setParameter("type", type);
q.setParameter("dateNow", new Date());
q.executeUpdate();
}

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 jakarta.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,11 +1,11 @@
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;
import org.joda.time.DateTime;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@ -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 jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.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,11 +1,11 @@
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;
import com.sismics.util.context.ThreadLocalContext;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
/**
* Configuration parameter DAO.

View File

@ -1,16 +1,15 @@
package com.sismics.docs.core.dao.jpa;
package com.sismics.docs.core.dao;
import com.sismics.docs.core.dao.dto.ContributorDto;
import com.sismics.docs.core.model.jpa.Contributor;
import com.sismics.util.context.ThreadLocalContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
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;
/**
* Contributor DAO.
*
@ -57,7 +56,7 @@ public class ContributorDao {
@SuppressWarnings("unchecked")
public List<ContributorDto> getByDocumentId(String documentId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
StringBuilder sb = new StringBuilder("select u.USE_USERNAME_C, u.USE_EMAIL_C from T_CONTRIBUTOR c ");
StringBuilder sb = new StringBuilder("select distinct u.USE_USERNAME_C, u.USE_EMAIL_C from T_CONTRIBUTOR c ");
sb.append(" join T_USER u on u.USE_ID_C = c.CTR_IDUSER_C ");
sb.append(" where c.CTR_IDDOC_C = :documentId ");
Query q = em.createNativeQuery(sb.toString());

View File

@ -0,0 +1,252 @@
package com.sismics.docs.core.dao;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.constant.PermType;
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.util.context.ThreadLocalContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.Query;
import jakarta.persistence.TypedQuery;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.UUID;
/**
* Document DAO.
*
* @author bgamard
*/
public class DocumentDao {
/**
* Creates a new document.
*
* @param document Document
* @param userId User ID
* @return New ID
*/
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();
em.persist(document);
// Create audit log
AuditLogUtil.create(document, AuditLogType.CREATE, userId);
return document.getId();
}
/**
* Returns the list of all active documents.
*
* @param offset Offset
* @param limit Limit
* @return List of documents
*/
public List<Document> findAll(int offset, int limit) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
TypedQuery<Document> q = em.createQuery("select d from Document d where d.deleteDate is null", Document.class);
q.setFirstResult(offset);
q.setMaxResults(limit);
return q.getResultList();
}
/**
* Returns the list of all active documents from a user.
*
* @param userId User ID
* @return List of documents
*/
public List<Document> findByUserId(String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
TypedQuery<Document> q = em.createQuery("select d from Document d where d.userId = :userId and d.deleteDate is null", Document.class);
q.setParameter("userId", userId);
return q.getResultList();
}
/**
* Returns an active document with permission checking.
*
* @param id Document ID
* @param perm Permission needed
* @param targetIdList List of targets
* @return Document
*/
public DocumentDto getDocument(String id, PermType perm, List<String> targetIdList) {
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(id, perm, targetIdList)) {
return null;
}
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_UPDATEDATE_D, d.DOC_LANGUAGE_C, d.DOC_IDFILE_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 ");
sb.append(" where d.DOC_ID_C = :id and d.DOC_DELETEDATE_D is null ");
Query q = em.createNativeQuery(sb.toString());
q.setParameter("id", id);
Object[] o;
try {
o = (Object[]) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
DocumentDto documentDto = new DocumentDto();
int i = 0;
documentDto.setId((String) o[i++]);
documentDto.setTitle((String) o[i++]);
documentDto.setDescription((String) o[i++]);
documentDto.setSubject((String) o[i++]);
documentDto.setIdentifier((String) o[i++]);
documentDto.setPublisher((String) o[i++]);
documentDto.setFormat((String) o[i++]);
documentDto.setSource((String) o[i++]);
documentDto.setType((String) o[i++]);
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.setFileId((String) o[i++]);
documentDto.setShared(((Number) o[i++]).intValue() > 0);
documentDto.setFileCount(((Number) o[i++]).intValue());
documentDto.setCreator((String) o[i]);
return documentDto;
}
/**
* Deletes a document.
*
* @param id Document ID
* @param userId User ID
*/
public void delete(String id, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the document
TypedQuery<Document> dq = em.createQuery("select d from Document d where d.id = :id and d.deleteDate is null", Document.class);
dq.setParameter("id", id);
Document documentDb = dq.getSingleResult();
// Delete the document
Date dateNow = new Date();
documentDb.setDeleteDate(dateNow);
// Delete linked data
Query q = em.createQuery("update File f set f.deleteDate = :dateNow where f.documentId = :documentId and f.deleteDate is null");
q.setParameter("documentId", id);
q.setParameter("dateNow", dateNow);
q.executeUpdate();
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :documentId and a.deleteDate is null");
q.setParameter("documentId", id);
q.setParameter("dateNow", dateNow);
q.executeUpdate();
q = em.createQuery("update DocumentTag dt set dt.deleteDate = :dateNow where dt.documentId = :documentId and dt.deleteDate is not null");
q.setParameter("documentId", id);
q.setParameter("dateNow", dateNow);
q.executeUpdate();
q = em.createQuery("update Relation r set r.deleteDate = :dateNow where (r.fromDocumentId = :documentId or r.toDocumentId = :documentId) and r.deleteDate is not null");
q.setParameter("documentId", id);
q.setParameter("dateNow", dateNow);
q.executeUpdate();
// Create audit log
AuditLogUtil.create(documentDb, AuditLogType.DELETE, userId);
}
/**
* Gets an active document by its ID.
*
* @param id Document ID
* @return Document
*/
public Document getById(String id) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
TypedQuery<Document> q = em.createQuery("select d from Document d where d.id = :id and d.deleteDate is null", Document.class);
q.setParameter("id", id);
try {
return q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* Update a document and log the action.
*
* @param document Document to update
* @param userId User ID
* @return Updated document
*/
public Document update(Document document, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the document
TypedQuery<Document> q = em.createQuery("select d from Document d where d.id = :id and d.deleteDate is null", Document.class);
q.setParameter("id", document.getId());
Document documentDb = q.getSingleResult();
// Update the document
documentDb.setTitle(document.getTitle());
documentDb.setDescription(document.getDescription());
documentDb.setSubject(document.getSubject());
documentDb.setIdentifier(document.getIdentifier());
documentDb.setPublisher(document.getPublisher());
documentDb.setFormat(document.getFormat());
documentDb.setSource(document.getSource());
documentDb.setType(document.getType());
documentDb.setCoverage(document.getCoverage());
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);
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 DOC_IDFILE_C = :fileId, 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.
*
* @return Number of documents
*/
public long getDocumentCount() {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query query = em.createNativeQuery("select count(d.DOC_ID_C) from T_DOCUMENT d where d.DOC_DELETEDATE_D is null");
return ((Number) query.getSingleResult()).longValue();
}
}

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 jakarta.persistence.EntityManager;
import jakarta.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

@ -0,0 +1,257 @@
package com.sismics.docs.core.dao;
import com.sismics.docs.core.constant.AuditLogType;
import com.sismics.docs.core.model.jpa.File;
import com.sismics.docs.core.util.AuditLogUtil;
import com.sismics.util.context.ThreadLocalContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.Query;
import jakarta.persistence.TypedQuery;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* File DAO.
*
* @author bgamard
*/
public class FileDao {
/**
* Creates a new file.
*
* @param file File
* @param userId User ID
* @return New ID
*/
public String create(File file, String userId) {
// Create the UUID
file.setId(UUID.randomUUID().toString());
// Create the file
EntityManager em = ThreadLocalContext.get().getEntityManager();
file.setCreateDate(new Date());
em.persist(file);
// Create audit log
AuditLogUtil.create(file, AuditLogType.CREATE, userId);
return file.getId();
}
/**
* Returns the list of all files.
*
* @param offset Offset
* @param limit Limit
* @return List of files
*/
public List<File> findAll(int offset, int limit) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
TypedQuery<File> q = em.createQuery("select f from File f where f.deleteDate is null", File.class);
q.setFirstResult(offset);
q.setMaxResults(limit);
return q.getResultList();
}
/**
* Returns the list of all files from a user.
*
* @param userId User ID
* @return List of files
*/
public List<File> findByUserId(String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
TypedQuery<File> q = em.createQuery("select f from File f where f.userId = :userId and f.deleteDate is null", File.class);
q.setParameter("userId", userId);
return q.getResultList();
}
/**
* Returns a list of active files.
*
* @param ids Files IDs
* @return List of files
*/
public List<File> getFiles(List<String> ids) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
TypedQuery<File> q = em.createQuery("select f from File f where f.id in :ids and f.deleteDate is null", File.class);
q.setParameter("ids", ids);
return q.getResultList();
}
/**
* Returns an active file or null.
*
* @param id File ID
* @return File
*/
public File getFile(String id) {
List<File> files = getFiles(List.of(id));
if (files.isEmpty()) {
return null;
} else {
return files.get(0);
}
}
/**
* Returns an active file.
*
* @param id File ID
* @param userId User ID
* @return File
*/
public File getFile(String id, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
TypedQuery<File> q = em.createQuery("select f from File f where f.id = :id and f.userId = :userId and f.deleteDate is null", File.class);
q.setParameter("id", id);
q.setParameter("userId", userId);
try {
return q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* Deletes a file.
*
* @param id File ID
* @param userId User ID
*/
public void delete(String id, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the file
TypedQuery<File> q = em.createQuery("select f from File f where f.id = :id and f.deleteDate is null", File.class);
q.setParameter("id", id);
File fileDb = q.getSingleResult();
// Delete the file
Date dateNow = new Date();
fileDb.setDeleteDate(dateNow);
// Create audit log
AuditLogUtil.create(fileDb, AuditLogType.DELETE, userId);
}
/**
* Update a file.
*
* @param file File to update
* @return Updated file
*/
public File update(File file) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the file
TypedQuery<File> q = em.createQuery("select f from File f where f.id = :id and f.deleteDate is null", File.class);
q.setParameter("id", file.getId());
File fileDb = q.getSingleResult();
// 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());
fileDb.setSize(file.getSize());
return file;
}
/**
* Gets a file by its ID.
*
* @param id File ID
* @return File
*/
public File getActiveById(String id) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
TypedQuery<File> q = em.createQuery("select f from File f where f.id = :id and f.deleteDate is null", File.class);
q.setParameter("id", id);
try {
return q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* Get files by document ID or all orphan files of a user.
*
* @param userId User ID
* @param documentId Document ID
* @return List of files
*/
public List<File> getByDocumentId(String userId, String documentId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
if (documentId == null) {
TypedQuery<File> 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", File.class);
q.setParameter("userId", userId);
return q.getResultList();
} else {
return getByDocumentsIds(Collections.singleton(documentId));
}
}
/**
* Get files by documents IDs.
*
* @param documentIds Documents IDs
* @return List of files
*/
public List<File> getByDocumentsIds(Iterable<String> documentIds) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
TypedQuery<File> q = em.createQuery("select f from File f where f.documentId in :documentIds and f.latestVersion = true and f.deleteDate is null order by f.order asc", File.class);
q.setParameter("documentIds", documentIds);
return q.getResultList();
}
/**
* Get files count by documents IDs.
*
* @param documentIds Documents IDs
* @return the number of files per document id
*/
public Map<String, Long> countByDocumentsIds(Iterable<String> documentIds) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query q = em.createQuery("select f.documentId, count(*) from File f where f.documentId in :documentIds and f.latestVersion = true and f.deleteDate is null group by (f.documentId)");
q.setParameter("documentIds", documentIds);
Map<String, Long> result = new HashMap<>();
q.getResultList().forEach(o -> {
Object[] resultLine = (Object[]) o;
result.put((String) resultLine[0], (Long) resultLine[1]);
});
return result;
}
/**
* Get all files from a version.
*
* @param versionId Version ID
* @return List of files
*/
public List<File> getByVersionId(String versionId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
TypedQuery<File> q = em.createQuery("select f from File f where f.versionId = :versionId and f.deleteDate is null order by f.order asc", File.class);
q.setParameter("versionId", versionId);
return q.getResultList();
}
public List<File> getFilesWithUnknownSize(int limit) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
TypedQuery<File> q = em.createQuery("select f from File f where f.size = :size and f.deleteDate is null order by f.order asc", File.class);
q.setParameter("size", File.UNKNOWN_SIZE);
q.setMaxResults(limit);
return q.getResultList();
}
}

View File

@ -1,22 +1,9 @@
package com.sismics.docs.core.dao.jpa;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
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;
@ -25,6 +12,11 @@ import com.sismics.docs.core.util.jpa.QueryUtil;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.util.context.ThreadLocalContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.Query;
import java.util.*;
/**
* Group DAO.
*
@ -192,10 +184,8 @@ public class GroupDao {
criteriaList.add("g.GRP_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);
@ -266,16 +256,16 @@ public class GroupDao {
// Get the group
Query q = em.createQuery("select g from Group g where g.id = :id and g.deleteDate is null");
q.setParameter("id", group.getId());
Group groupFromDb = (Group) q.getSingleResult();
Group groupDb = (Group) q.getSingleResult();
// Update the group
groupFromDb.setName(group.getName());
groupFromDb.setParentId(group.getParentId());
groupDb.setName(group.getName());
groupDb.setParentId(group.getParentId());
// Create audit log
AuditLogUtil.create(groupFromDb, AuditLogType.UPDATE, userId);
AuditLogUtil.create(groupDb, AuditLogType.UPDATE, userId);
return groupFromDb;
return groupDb;
}
}

View File

@ -0,0 +1,146 @@
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 jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.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");
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

@ -0,0 +1,68 @@
package com.sismics.docs.core.dao;
import com.sismics.docs.core.constant.Constants;
import com.sismics.docs.core.model.jpa.PasswordRecovery;
import com.sismics.util.context.ThreadLocalContext;
import org.joda.time.DateTime;
import org.joda.time.DurationFieldType;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.Query;
import java.util.Date;
import java.util.UUID;
/**
* Password recovery DAO.
*
* @author jtremeaux
*/
public class PasswordRecoveryDao {
/**
* Create a new password recovery request.
*
* @param passwordRecovery Password recovery
* @return Unique identifier
*/
public String create(PasswordRecovery passwordRecovery) {
passwordRecovery.setId(UUID.randomUUID().toString());
passwordRecovery.setCreateDate(new Date());
EntityManager em = ThreadLocalContext.get().getEntityManager();
em.persist(passwordRecovery);
return passwordRecovery.getId();
}
/**
* Search an active password recovery by unique identifier.
*
* @param id Unique identifier
* @return Password recovery
*/
public PasswordRecovery getActiveById(String id) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
try {
Query q = em.createQuery("select r from PasswordRecovery r where r.id = :id and r.createDate > :createDateMin and r.deleteDate is null");
q.setParameter("id", id);
q.setParameter("createDateMin", new DateTime().withFieldAdded(DurationFieldType.hours(), -1 * Constants.PASSWORD_RECOVERY_EXPIRATION_HOUR).toDate());
return (PasswordRecovery) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* Deletes active password recovery by username.
*
* @param username Username
*/
public void deleteActiveByLogin(String username) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query q = em.createQuery("update PasswordRecovery r set r.deleteDate = :deleteDate where r.username = :username and r.createDate > :createDateMin and r.deleteDate is null");
q.setParameter("username", username);
q.setParameter("deleteDate", new Date());
q.setParameter("createDateMin", new DateTime().withFieldAdded(DurationFieldType.hours(), -1 * Constants.PASSWORD_RECOVERY_EXPIRATION_HOUR).toDate());
q.executeUpdate();
}
}

View File

@ -1,18 +1,13 @@
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 javax.persistence.EntityManager;
import javax.persistence.Query;
import com.sismics.docs.core.dao.jpa.dto.RelationDto;
import com.sismics.docs.core.dao.dto.RelationDto;
import com.sismics.docs.core.model.jpa.Relation;
import com.sismics.util.context.ThreadLocalContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
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,10 +1,10 @@
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;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import java.util.Set;
/**

View File

@ -0,0 +1,111 @@
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.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;
import com.sismics.docs.core.util.jpa.QueryUtil;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.util.context.ThreadLocalContext;
import jakarta.persistence.EntityManager;
import java.sql.Timestamp;
import java.util.*;
/**
* Route DAO.
*
* @author bgamard
*/
public class RouteDao {
/**
* Creates a new route.
*
* @param route Route
* @param userId User ID
* @return New ID
*/
public String create(Route route, String userId) {
// Create the UUID
route.setId(UUID.randomUUID().toString());
// Create the route
EntityManager em = ThreadLocalContext.get().getEntityManager();
route.setCreateDate(new Date());
em.persist(route);
// Create audit log
AuditLogUtil.create(route, AuditLogType.CREATE, userId);
return route.getId();
}
/**
* Returns the list of all routes.
*
* @param criteria Search criteria
* @param sortCriteria Sort criteria
* @return List of routes
*/
public List<RouteDto> findByCriteria(RouteCriteria criteria, SortCriteria sortCriteria) {
Map<String, Object> parameterMap = new HashMap<String, Object>();
List<String> criteriaList = new ArrayList<>();
StringBuilder sb = new StringBuilder("select r.RTE_ID_C c0, r.RTE_NAME_C c1, r.RTE_CREATEDATE_D c2");
sb.append(" from T_ROUTE r ");
// Add search criterias
if (criteria.getDocumentId() != null) {
criteriaList.add("r.RTE_IDDOCUMENT_C = :documentId");
parameterMap.put("documentId", criteria.getDocumentId());
}
criteriaList.add("r.RTE_DELETEDATE_D is null");
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<RouteDto> dtoList = new ArrayList<>();
for (Object[] o : l) {
int i = 0;
RouteDto dto = new RouteDto();
dto.setId((String) o[i++]);
dto.setName((String) o[i++]);
dto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
dtoList.add(dto);
}
return dtoList;
}
/**
* Deletes a route and the associated steps.
*
* @param routeId Route ID
* @param userId User ID
*/
public void deleteRoute(String routeId, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// 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 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

@ -0,0 +1,168 @@
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.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;
import com.sismics.util.context.ThreadLocalContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.Query;
import java.sql.Timestamp;
import java.util.*;
/**
* Route model DAO.
*
* @author bgamard
*/
public class RouteModelDao {
/**
* Creates a new route model.
*
* @param routeModel Route model
* @param userId User ID
* @return New ID
*/
public String create(RouteModel routeModel, String userId) {
// Create the UUID
routeModel.setId(UUID.randomUUID().toString());
// Create the route model
EntityManager em = ThreadLocalContext.get().getEntityManager();
routeModel.setCreateDate(new Date());
em.persist(routeModel);
// Create audit log
AuditLogUtil.create(routeModel, AuditLogType.CREATE, userId);
return routeModel.getId();
}
/**
* Update a route model.
*
* @param routeModel Route model to update
* @param userId User ID
* @return Updated route model
*/
public RouteModel update(RouteModel routeModel, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the route model
Query q = em.createQuery("select r from RouteModel r where r.id = :id and r.deleteDate is null");
q.setParameter("id", routeModel.getId());
RouteModel routeModelDb = (RouteModel) q.getSingleResult();
// Update the route model
routeModelDb.setName(routeModel.getName());
routeModelDb.setSteps(routeModel.getSteps());
// Create audit log
AuditLogUtil.create(routeModelDb, AuditLogType.UPDATE, userId);
return routeModelDb;
}
/**
* Gets an active route model by its ID.
*
* @param id Route model ID
* @return Route model
*/
public RouteModel getActiveById(String id) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
try {
Query q = em.createQuery("select r from RouteModel r where r.id = :id and r.deleteDate is null");
q.setParameter("id", id);
return (RouteModel) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* 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.
*
* @param id Route model ID
* @param userId User ID
*/
public void delete(String id, String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
// Get the route model
Query q = em.createQuery("select r from RouteModel r where r.id = :id and r.deleteDate is null");
q.setParameter("id", id);
RouteModel routeModelDb = (RouteModel) q.getSingleResult();
// Delete the route model
Date dateNow = new Date();
routeModelDb.setDeleteDate(dateNow);
// Create audit log
AuditLogUtil.create(routeModelDb, AuditLogType.DELETE, userId);
}
/**
* Returns the list of all route models.
*
* @param criteria Search criteria
* @param sortCriteria Sort criteria
* @return List of route models
*/
public List<RouteModelDto> findByCriteria(RouteModelCriteria criteria, SortCriteria sortCriteria) {
Map<String, Object> parameterMap = new HashMap<String, Object>();
List<String> criteriaList = new ArrayList<>();
StringBuilder sb = new StringBuilder("select rm.RTM_ID_C c0, rm.RTM_NAME_C c1, rm.RTM_CREATEDATE_D c2");
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");
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<RouteModelDto> dtoList = new ArrayList<>();
for (Object[] o : l) {
int i = 0;
RouteModelDto dto = new RouteModelDto();
dto.setId((String) o[i++]);
dto.setName((String) o[i++]);
dto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
dtoList.add(dto);
}
return dtoList;
}
}

View File

@ -0,0 +1,154 @@
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.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;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.util.context.ThreadLocalContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import java.sql.Timestamp;
import java.util.*;
/**
* Route step DAO.
*
* @author bgamard
*/
public class RouteStepDao {
/**
* Creates a new route step.
*
* @param routeStep Route step
* @return New ID
*/
public String create(RouteStep routeStep) {
// Create the UUID
routeStep.setId(UUID.randomUUID().toString());
// Create the route step
EntityManager em = ThreadLocalContext.get().getEntityManager();
routeStep.setCreateDate(new Date());
em.persist(routeStep);
return routeStep.getId();
}
/**
* Get the current route step from a document.
*
* @param documentId Document ID
* @return Current route step
*/
public RouteStepDto getCurrentStep(String documentId) {
List<RouteStepDto> routeStepDtoList = findByCriteria(new RouteStepCriteria()
.setDocumentId(documentId)
.setEndDateIsNull(true), new SortCriteria(6, true));
if (routeStepDtoList.isEmpty()) {
return null;
}
return routeStepDtoList.get(0);
}
/**
* Returns the list of all route steps.
*
* @param criteria Search criteria
* @param sortCriteria Sort criteria
* @return List of route steps
*/
public List<RouteStepDto> findByCriteria(RouteStepCriteria criteria, SortCriteria sortCriteria) {
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_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 ")
.append(" left join T_USER u on u.USE_ID_C = rs.RTP_IDTARGET_C ")
.append(" left join T_SHARE s on s.SHA_ID_C = rs.RTP_IDTARGET_C ")
.append(" left join T_GROUP g on g.GRP_ID_C = rs.RTP_IDTARGET_C ");
// Add search criterias
if (criteria.getDocumentId() != null) {
criteriaList.add("r.RTE_IDDOCUMENT_C = :documentId");
parameterMap.put("documentId", criteria.getDocumentId());
}
if (criteria.getRouteId() != null) {
criteriaList.add("rs.RTP_IDROUTE_C = :routeId");
parameterMap.put("routeId", criteria.getRouteId());
}
if (criteria.getEndDateIsNull() != null) {
criteriaList.add("RTP_ENDDATE_D is " + (criteria.getEndDateIsNull() ? "" : "not") + " null");
}
criteriaList.add("rs.RTP_DELETEDATE_D is null");
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<RouteStepDto> dtoList = new ArrayList<>();
for (Object[] o : l) {
int i = 0;
RouteStepDto dto = new RouteStepDto();
dto.setId((String) o[i++]);
dto.setName((String) o[i++]);
dto.setType(RouteStepType.valueOf((String) o[i++]));
dto.setTransition((String) o[i++]);
dto.setComment((String) o[i++]);
dto.setTargetId((String) o[i++]);
String userName = (String) o[i++];
String groupName = (String) o[i++];
if (userName != null) {
dto.setTargetName(userName);
dto.setTargetType(AclTargetType.USER.name());
}
if (groupName != null) {
dto.setTargetName(groupName);
dto.setTargetType(AclTargetType.GROUP.name());
}
Timestamp endDateTimestamp = (Timestamp) o[i++];
dto.setEndDateTimestamp(endDateTimestamp == null ? null : endDateTimestamp.getTime());
dto.setValidatorUserName((String) o[i++]);
dto.setRouteId((String) o[i++]);
dto.setTransitions((String) o[i]);
dtoList.add(dto);
}
return dtoList;
}
/**
* End a route step.
*
* @param id Route step ID
* @param transition Transition
* @param comment Comment
* @param validatorUserId Validator user ID
*/
public void endRouteStep(String id, RouteStepTransition transition, String comment, String validatorUserId) {
StringBuilder sb = new StringBuilder("update T_ROUTE_STEP r ");
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();
Query q = em.createNativeQuery(sb.toString());
q.setParameter("endDate", new Date());
q.setParameter("transition", transition.name());
q.setParameter("comment", comment);
q.setParameter("validatorUserId", validatorUserId);
q.setParameter("id", id);
q.executeUpdate();
}
}

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 jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import java.util.Date;
import java.util.UUID;
/**
* Share DAO.
*
@ -20,7 +19,6 @@ public class ShareDao {
*
* @param share Share
* @return New ID
* @throws Exception
*/
public String create(Share share) {
// Create the UUID

View File

@ -1,29 +1,23 @@
package com.sismics.docs.core.dao.jpa;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
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;
import com.sismics.util.context.ThreadLocalContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.Query;
import java.util.*;
/**
* Tag DAO.
*
@ -138,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);
}
@ -155,17 +153,17 @@ public class TagDao {
// Get the tag
Query q = em.createQuery("select t from Tag t where t.id = :id and t.deleteDate is null");
q.setParameter("id", tag.getId());
Tag tagFromDb = (Tag) q.getSingleResult();
Tag tagDb = (Tag) q.getSingleResult();
// Update the tag
tagFromDb.setName(tag.getName());
tagFromDb.setColor(tag.getColor());
tagFromDb.setParentId(tag.getParentId());
tagDb.setName(tag.getName());
tagDb.setColor(tag.getColor());
tagDb.setParentId(tag.getParentId());
// Create audit log
AuditLogUtil.create(tagFromDb, AuditLogType.UPDATE, userId);
AuditLogUtil.create(tagDb, AuditLogType.UPDATE, userId);
return tagFromDb;
return tagDb;
}
/**
@ -188,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());
@ -198,21 +196,11 @@ 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");
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);

View File

@ -1,36 +1,41 @@
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;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import org.mindrot.jbcrypt.BCrypt;
package com.sismics.docs.core.dao;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import at.favre.lib.crypto.bcrypt.BCrypt;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.constant.Constants;
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;
import com.sismics.util.context.ThreadLocalContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.Query;
import java.sql.Timestamp;
import java.util.*;
/**
* User DAO.
*
* @author jtremeaux
*/
public class UserDao {
/**
* Logger.
*/
private static final Logger log = LoggerFactory.getLogger(UserDao.class);
/**
* Authenticates an user.
*
@ -44,7 +49,8 @@ public class UserDao {
q.setParameter("username", username);
try {
User user = (User) q.getSingleResult();
if (!BCrypt.checkpw(password, user.getPassword())) {
BCrypt.Result result = BCrypt.verifyer().verify(password.toCharArray(), user.getPassword());
if (!result.verified || user.getDisableDate() != null) {
return null;
}
return user;
@ -59,7 +65,7 @@ public class UserDao {
* @param user User to create
* @param userId User ID
* @return User ID
* @throws Exception
* @throws Exception e
*/
public String create(User user, String userId) throws Exception {
// Create the user UUID
@ -77,6 +83,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
@ -98,16 +106,17 @@ public class UserDao {
// 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 userFromDb = (User) q.getSingleResult();
User userDb = (User) q.getSingleResult();
// Update the user (except password)
userFromDb.setEmail(user.getEmail());
userFromDb.setStorageQuota(user.getStorageQuota());
userFromDb.setStorageCurrent(user.getStorageCurrent());
userFromDb.setTotpKey(user.getTotpKey());
userDb.setEmail(user.getEmail());
userDb.setStorageQuota(user.getStorageQuota());
userDb.setStorageCurrent(user.getStorageCurrent());
userDb.setTotpKey(user.getTotpKey());
userDb.setDisableDate(user.getDisableDate());
// Create audit log
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE, userId);
AuditLogUtil.create(userDb, AuditLogType.UPDATE, userId);
return user;
}
@ -116,20 +125,17 @@ public class UserDao {
* Updates a user's quota.
*
* @param user User to update
* @return Updated user
*/
public User updateQuota(User user) {
public void updateQuota(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 userFromDb = (User) q.getSingleResult();
User userDb = (User) q.getSingleResult();
// Update the user
userFromDb.setStorageQuota(user.getStorageQuota());
return user;
userDb.setStorageCurrent(user.getStorageCurrent());
}
/**
@ -145,13 +151,53 @@ public class UserDao {
// 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 userFromDb = (User) q.getSingleResult();
User userDb = (User) q.getSingleResult();
// Update the user
userFromDb.setPassword(hashPassword(user.getPassword()));
userDb.setPassword(hashPassword(user.getPassword()));
// Create audit log
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE, userId);
AuditLogUtil.create(userDb, AuditLogType.UPDATE, userId);
return user;
}
/**
* Update the hashed password silently.
*
* @param user User to update
* @return Updated user
*/
public User updateHashedPassword(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.setPassword(user.getPassword());
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;
}
@ -200,39 +246,39 @@ public class UserDao {
// Get the user
Query q = em.createQuery("select u from User u where u.username = :username and u.deleteDate is null");
q.setParameter("username", username);
User userFromDb = (User) q.getSingleResult();
User userDb = (User) q.getSingleResult();
// Delete the user
Date dateNow = new Date();
userFromDb.setDeleteDate(dateNow);
userDb.setDeleteDate(dateNow);
// Delete linked data
q = em.createQuery("delete from AuthenticationToken at where at.userId = :userId");
q.setParameter("userId", userFromDb.getId());
q.setParameter("userId", userDb.getId());
q.executeUpdate();
q = em.createQuery("update Document d set d.deleteDate = :dateNow where d.userId = :userId and d.deleteDate is null");
q.setParameter("userId", userFromDb.getId());
q.setParameter("userId", userDb.getId());
q.setParameter("dateNow", dateNow);
q.executeUpdate();
q = em.createQuery("update File f set f.deleteDate = :dateNow where f.userId = :userId and f.deleteDate is null");
q.setParameter("userId", userFromDb.getId());
q.setParameter("userId", userDb.getId());
q.setParameter("dateNow", dateNow);
q.executeUpdate();
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.targetId = :userId and a.deleteDate is null");
q.setParameter("userId", userFromDb.getId());
q.setParameter("userId", userDb.getId());
q.setParameter("dateNow", dateNow);
q.executeUpdate();
q = em.createQuery("update Comment c set c.deleteDate = :dateNow where c.userId = :userId and c.deleteDate is null");
q.setParameter("userId", userFromDb.getId());
q.setParameter("userId", userDb.getId());
q.setParameter("dateNow", dateNow);
q.executeUpdate();
// Create audit log
AuditLogUtil.create(userFromDb, AuditLogType.DELETE, userId);
AuditLogUtil.create(userDb, AuditLogType.DELETE, userId);
}
/**
@ -242,7 +288,21 @@ public class UserDao {
* @return Hashed password
*/
private String hashPassword(String password) {
return BCrypt.hashpw(password, BCrypt.gensalt());
int bcryptWork = Constants.DEFAULT_BCRYPT_WORK;
String envBcryptWork = System.getenv(Constants.BCRYPT_WORK_ENV);
if (!Strings.isNullOrEmpty(envBcryptWork)) {
try {
int envBcryptWorkInt = Integer.parseInt(envBcryptWork);
if (envBcryptWorkInt >= 4 && envBcryptWorkInt <= 31) {
bcryptWork = envBcryptWorkInt;
} else {
log.warn(Constants.BCRYPT_WORK_ENV + " needs to be in range 4...31. Falling back to " + Constants.DEFAULT_BCRYPT_WORK + ".");
}
} catch (NumberFormatException e) {
log.warn(Constants.BCRYPT_WORK_ENV + " needs to be a number in range 4...31. Falling back to " + Constants.DEFAULT_BCRYPT_WORK + ".");
}
}
return BCrypt.withDefaults().hashToString(bcryptWork, password.toCharArray());
}
/**
@ -256,7 +316,7 @@ public class UserDao {
Map<String, Object> parameterMap = new HashMap<>();
List<String> criteriaList = new ArrayList<>();
StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3, u.USE_STORAGECURRENT_N as c4, u.USE_STORAGEQUOTA_N as c5");
StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3, u.USE_STORAGECURRENT_N as c4, u.USE_STORAGEQUOTA_N as c5, u.USE_TOTPKEY_C as c6, u.USE_DISABLEDATE_D as c7");
sb.append(" from T_USER u ");
// Add search criterias
@ -264,7 +324,14 @@ public class UserDao {
criteriaList.add("lower(u.USE_USERNAME_C) like lower(:search)");
parameterMap.put("search", "%" + criteria.getSearch() + "%");
}
if (criteria.getUserId() != null) {
criteriaList.add("u.USE_ID_C = :userId");
parameterMap.put("userId", criteria.getUserId());
}
if (criteria.getUserName() != null) {
criteriaList.add("u.USE_USERNAME_C = :userName");
parameterMap.put("userName", criteria.getUserName());
}
if (criteria.getGroupId() != null) {
sb.append(" join T_USER_GROUP ug on ug.UGP_IDUSER_C = u.USE_ID_C and ug.UGP_IDGROUP_C = :groupId and ug.UGP_DELETEDATE_D is null ");
parameterMap.put("groupId", criteria.getGroupId());
@ -292,9 +359,39 @@ public class UserDao {
userDto.setEmail((String) o[i++]);
userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
userDto.setStorageCurrent(((Number) o[i++]).longValue());
userDto.setStorageQuota(((Number) o[i]).longValue());
userDto.setStorageQuota(((Number) o[i++]).longValue());
userDto.setTotpKey((String) o[i++]);
if (o[i] != null) {
userDto.setDisableTimestamp(((Timestamp) o[i]).getTime());
}
userDtoList.add(userDto);
}
return userDtoList;
}
/**
* Returns the global storage used by all users.
*
* @return Current global storage
*/
public long getGlobalStorageCurrent() {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query query = em.createNativeQuery("select sum(u.USE_STORAGECURRENT_N) from T_USER u where u.USE_DELETEDATE_D is null");
return ((Number) query.getSingleResult()).longValue();
}
/**
* Returns the number of active users.
*
* @return Number of active users
*/
public long getActiveUserCount() {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query query = em.createNativeQuery("select count(u.USE_ID_C) from T_USER u where u.USE_DELETEDATE_D is null and (u.USE_DISABLEDATE_D is null or u.USE_DISABLEDATE_D >= :fromDate and u.USE_DISABLEDATE_D < :toDate)");
DateTime fromDate = DateTime.now().minusMonths(1).dayOfMonth().withMinimumValue().withTimeAtStartOfDay();
DateTime toDate = fromDate.plusMonths(1);
query.setParameter("fromDate", fromDate.toDate());
query.setParameter("toDate", toDate.toDate());
return ((Number) query.getSingleResult()).longValue();
}
}

View File

@ -1,15 +1,14 @@
package com.sismics.docs.core.dao.jpa;
import java.util.List;
import java.util.UUID;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
package com.sismics.docs.core.dao;
import com.sismics.docs.core.model.jpa.Vocabulary;
import com.sismics.util.context.ThreadLocalContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.Query;
import java.util.List;
import java.util.UUID;
/**
* Vocabulary DAO.
*
@ -21,7 +20,6 @@ public class VocabularyDao {
*
* @param vocabulary Vocabulary
* @return New ID
* @throws Exception
*/
public String create(Vocabulary vocabulary) {
// Create the UUID
@ -76,14 +74,14 @@ public class VocabularyDao {
// Get the vocabulary entry
Query q = em.createQuery("select v from Vocabulary v where v.id = :id");
q.setParameter("id", vocabulary.getId());
Vocabulary vocabularyFromDb = (Vocabulary) q.getSingleResult();
Vocabulary vocabularyDb = (Vocabulary) q.getSingleResult();
// Update the vocabulary entry
vocabularyFromDb.setName(vocabulary.getName());
vocabularyFromDb.setValue(vocabulary.getValue());
vocabularyFromDb.setOrder(vocabulary.getOrder());
vocabularyDb.setName(vocabulary.getName());
vocabularyDb.setValue(vocabulary.getValue());
vocabularyDb.setOrder(vocabulary.getOrder());
return vocabularyFromDb;
return vocabularyDb;
}
/**

View File

@ -0,0 +1,121 @@
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 jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.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");
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,5 +1,4 @@
package com.sismics.docs.core.dao.jpa.criteria;
package com.sismics.docs.core.dao.criteria;
/**
@ -18,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;
}
@ -33,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,5 +1,6 @@
package com.sismics.docs.core.dao.jpa.criteria;
package com.sismics.docs.core.dao.criteria;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@ -18,7 +19,7 @@ public class DocumentCriteria {
/**
* Search query.
*/
private String search;
private String simpleSearch;
/**
* Full content search query.
@ -36,9 +37,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 = new ArrayList<>();
/**
* Tag IDs to exclude.
* The first and second level list will be excluded.
*/
private List<List<String>> excludedTagIdList = new ArrayList<>();
/**
* Shared status.
@ -55,6 +73,21 @@ public class DocumentCriteria {
*/
private String creatorId;
/**
* A route is active.
*/
private Boolean activeRoute;
/**
* MIME type of a file.
*/
private String mimeType;
/**
* Titles to include.
*/
private List<String> titleList = new ArrayList<>();
public List<String> getTargetIdList() {
return targetIdList;
}
@ -63,12 +96,12 @@ public class DocumentCriteria {
this.targetIdList = targetIdList;
}
public String getSearch() {
return search;
public String getSimpleSearch() {
return simpleSearch;
}
public void setSearch(String search) {
this.search = search;
public void setSimpleSearch(String search) {
this.simpleSearch = search;
}
public String getFullSearch() {
@ -95,12 +128,12 @@ public class DocumentCriteria {
this.createDateMax = createDateMax;
}
public List<String> getTagIdList() {
public List<List<String>> getTagIdList() {
return tagIdList;
}
public void setTagIdList(List<String> tagIdList) {
this.tagIdList = tagIdList;
public List<List<String>> getExcludedTagIdList() {
return excludedTagIdList;
}
public Boolean getShared() {
@ -126,4 +159,40 @@ public class DocumentCriteria {
public void setCreatorId(String creatorId) {
this.creatorId = creatorId;
}
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 Boolean getActiveRoute() {
return activeRoute;
}
public void setActiveRoute(Boolean activeRoute) {
this.activeRoute = activeRoute;
}
public String getMimeType() {
return mimeType;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
public List<String> getTitleList() {
return titleList;
}
}

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

@ -0,0 +1,23 @@
package com.sismics.docs.core.dao.criteria;
/**
* Route criteria.
*
* @author bgamard
*/
public class RouteCriteria {
/**
* Document ID.
*/
private String documentId;
public String getDocumentId() {
return documentId;
}
public RouteCriteria setDocumentId(String documentId) {
this.documentId = documentId;
return this;
}
}

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

@ -0,0 +1,51 @@
package com.sismics.docs.core.dao.criteria;
/**
* Route step criteria.
*
* @author bgamard
*/
public class RouteStepCriteria {
/**
* Document ID.
*/
private String documentId;
/**
* Route ID.
*/
private String routeId;
/**
* End date is null.
*/
private Boolean endDateIsNull;
public String getDocumentId() {
return documentId;
}
public RouteStepCriteria setDocumentId(String documentId) {
this.documentId = documentId;
return this;
}
public String getRouteId() {
return routeId;
}
public RouteStepCriteria setRouteId(String routeId) {
this.routeId = routeId;
return this;
}
public Boolean getEndDateIsNull() {
return endDateIsNull;
}
public RouteStepCriteria setEndDateIsNull(Boolean endDateIsNull) {
this.endDateIsNull = endDateIsNull;
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.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.
@ -16,6 +16,16 @@ public class UserCriteria {
*/
private String groupId;
/**
* User ID.
*/
private String userId;
/**
* Username.
*/
private String userName;
public String getSearch() {
return search;
}
@ -33,4 +43,22 @@ public class UserCriteria {
this.groupId = groupId;
return this;
}
public String getUserId() {
return userId;
}
public UserCriteria setUserId(String userId) {
this.userId = userId;
return this;
}
public String getUserName() {
return userName;
}
public UserCriteria setUserName(String userName) {
this.userName = userName;
return this;
}
}

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;
}
}

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