mirror of
https://github.com/sismics/docs.git
synced 2025-12-16 11:15:07 +00:00
Compare commits
225 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea210d6aac | ||
|
|
f70cded634 | ||
|
|
cc6e1b4052 | ||
|
|
1ce5ba4f06 | ||
|
|
2b23a1d048 | ||
|
|
37f262177d | ||
|
|
7ded510625 | ||
|
|
797a987e2b | ||
|
|
062dee987f | ||
|
|
1054931e63 | ||
|
|
3720a881a4 | ||
|
|
f379b4e5ab | ||
|
|
9a9e86829e | ||
|
|
66cd4abd8b | ||
|
|
ba4470f155 | ||
|
|
b95ec019de | ||
|
|
d3a40ebca8 | ||
|
|
5d335049a2 | ||
|
|
bd51e2ab55 | ||
|
|
f8278bd44e | ||
|
|
4797cd1eca | ||
|
|
706d244ff8 | ||
|
|
5b8cd18128 | ||
|
|
8a854bb37d | ||
|
|
6e6f892cb0 | ||
|
|
2b4ddfa072 | ||
|
|
c9adff5a25 | ||
|
|
503cfff82e | ||
|
|
5e713f0c2a | ||
|
|
e035007070 | ||
|
|
17a94395f3 | ||
|
|
0ab6c8e4b0 | ||
|
|
8284169923 | ||
|
|
5f9094c540 | ||
|
|
332ac9c109 | ||
|
|
9ba49f35ff | ||
|
|
d0646f12e6 | ||
|
|
a8817c75b0 | ||
|
|
7fc50f2629 | ||
|
|
b2d9684738 | ||
|
|
8ab284ff98 | ||
|
|
a099d29524 | ||
|
|
cf44af0065 | ||
|
|
b5bf8b6545 | ||
|
|
4cc3fa4d89 | ||
|
|
ed50e202d7 | ||
|
|
6d865af15a | ||
|
|
66d331ddb8 | ||
|
|
0903c03a29 | ||
|
|
5f546b6c6d | ||
|
|
9bb73bb35b | ||
|
|
2ca5b04de2 | ||
|
|
e883c1e678 | ||
|
|
4fc434a222 | ||
|
|
ecfa747a2c | ||
|
|
dc28ebfa50 | ||
|
|
6596eba6ca | ||
|
|
7194f9aac0 | ||
|
|
e4fe1cfa90 | ||
|
|
5bc73548b3 | ||
|
|
2156848e4a | ||
|
|
3f807b3e51 | ||
|
|
d786862a60 | ||
|
|
fb75bafe96 | ||
|
|
66f781b716 | ||
|
|
b3dc409926 | ||
|
|
287ed06b6a | ||
|
|
df1d013b1c | ||
|
|
fdb95484c1 | ||
|
|
4cf1f29e0a | ||
|
|
332fd9d1f6 | ||
|
|
039d881a07 | ||
|
|
b8176a9fe9 | ||
|
|
65937d6f4c | ||
|
|
590bf74e98 | ||
|
|
642a3e10ce | ||
|
|
52a8cf92c8 | ||
|
|
fc68ee56d5 | ||
|
|
644f4803df | ||
|
|
eda6106de8 | ||
|
|
3a1691066e | ||
|
|
b02039bad4 | ||
|
|
6527a9e9bb | ||
|
|
c59ad4d446 | ||
|
|
f475cbc5d8 | ||
|
|
00452cc505 | ||
|
|
23660961bd | ||
|
|
742ff183bf | ||
|
|
dca8c28b84 | ||
|
|
46079393d5 | ||
|
|
517e4a4507 | ||
|
|
6f3ae6da9d | ||
|
|
273136ab23 | ||
|
|
e74f86e118 | ||
|
|
84d4d3b165 | ||
|
|
c355cb8bd5 | ||
|
|
2957034286 | ||
|
|
36b4fbd303 | ||
|
|
f57cf46313 | ||
|
|
244ddc7ce2 | ||
|
|
4d161aea07 | ||
|
|
cf9101d157 | ||
|
|
614c8a1d13 | ||
|
|
879ab7951d | ||
|
|
311b42ad25 | ||
|
|
0ebbbac9a6 | ||
|
|
d2f9fcdda0 | ||
|
|
cfe5690a73 | ||
|
|
a055b3ff5c | ||
|
|
18f37ec2a8 | ||
|
|
14b4e5aeec | ||
|
|
a980930e69 | ||
|
|
1b4eb70d8d | ||
|
|
1856ccc3aa | ||
|
|
3217c67ff6 | ||
|
|
54d5f1cb1b | ||
|
|
e49d002941 | ||
|
|
4822b8bf23 | ||
|
|
198a6d5665 | ||
|
|
c7b9ec3a4c | ||
|
|
f46e10e11c | ||
|
|
b9acc4ecf8 | ||
|
|
403d094a3d | ||
|
|
3b1f11e5a8 | ||
|
|
ddba06cca3 | ||
|
|
bf89af0da9 | ||
|
|
5a30164848 | ||
|
|
0c4e200900 | ||
|
|
fe8e8f041c | ||
|
|
43084d9d86 | ||
|
|
e1e1b4e278 | ||
|
|
14f8239ba3 | ||
|
|
e660a70d00 | ||
|
|
119d30bb16 | ||
|
|
5686de56e2 | ||
|
|
e0214a6a9f | ||
|
|
330de495db | ||
|
|
dcc7fe55f4 | ||
|
|
3274b4c79a | ||
|
|
cbfa4b1c41 | ||
|
|
5f7d2f2a68 | ||
|
|
e38bdbe508 | ||
|
|
c352b94b38 | ||
|
|
6b0106e385 | ||
|
|
60021e5123 | ||
|
|
76d3157247 | ||
|
|
7c24778460 | ||
|
|
8231db4a5a | ||
|
|
fe5dd5e8dc | ||
|
|
5872928812 | ||
|
|
8a8f4bb388 | ||
|
|
0e8d5fd5ec | ||
|
|
b9344149b0 | ||
|
|
5053a6852f | ||
|
|
bb3faca533 | ||
|
|
4f7fcbfdf0 | ||
|
|
87c1cc88be | ||
|
|
1d78551f4c | ||
|
|
b36d08db8e | ||
|
|
c99b1a1867 | ||
|
|
fc4380e5cc | ||
|
|
850ed7f76b | ||
|
|
0f6aa3befb | ||
|
|
ddd976162c | ||
|
|
cdd19e182b | ||
|
|
afc22a547e | ||
|
|
79ca54c5af | ||
|
|
0aacf20c16 | ||
|
|
cdfb43dbd8 | ||
|
|
35ec8b951c | ||
|
|
05bfaa0035 | ||
|
|
f5705b1153 | ||
|
|
ed1353a4eb | ||
|
|
a79922d7c9 | ||
|
|
7a7cbd570c | ||
|
|
d7865cfaf0 | ||
|
|
ead01ce1d0 | ||
|
|
8aca012c99 | ||
|
|
67a4dc63ca | ||
|
|
ce0678784b | ||
|
|
cbc4bbb818 | ||
|
|
1c558a884d | ||
|
|
d84d1428b2 | ||
|
|
b870ee8d62 | ||
|
|
ef18581e71 | ||
|
|
177bbceaf4 | ||
|
|
a13174ac4d | ||
|
|
e181b7d24b | ||
|
|
e631aa0e8a | ||
|
|
575ad75a0a | ||
|
|
394f667ab0 | ||
|
|
f80cf43ae8 | ||
|
|
6b57d29f51 | ||
|
|
f2c4dde56e | ||
|
|
3a22132363 | ||
|
|
767099b7ea | ||
|
|
6aef7246a0 | ||
|
|
1f6d9f0211 | ||
|
|
3248637e8c | ||
|
|
bf8e0827e2 | ||
|
|
ad1e57316f | ||
|
|
737b3299ff | ||
|
|
1934bb71f0 | ||
|
|
b3ef9b0476 | ||
|
|
8477920475 | ||
|
|
f98a12b96f | ||
|
|
bee8a4fcdc | ||
|
|
f984595b97 | ||
|
|
f01d78a9ea | ||
|
|
97f25de0dc | ||
|
|
3d1b5a7394 | ||
|
|
df1eaf54c8 | ||
|
|
3851408100 | ||
|
|
794c5012ad | ||
|
|
1ba9f7d7d9 | ||
|
|
9b4330d618 | ||
|
|
3a32b742e8 | ||
|
|
87b3d25c0f | ||
|
|
9f28649a3a | ||
|
|
8f9df8961b | ||
|
|
9e9217bfcb | ||
|
|
ffdd5a3631 | ||
|
|
db4bf8d35a | ||
|
|
7b2859f96e | ||
|
|
03c5c33ea7 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,3 +9,5 @@
|
|||||||
/.idea
|
/.idea
|
||||||
/.project
|
/.project
|
||||||
*.iml
|
*.iml
|
||||||
|
node_modules
|
||||||
|
import_test
|
||||||
|
|||||||
15
.travis.yml
15
.travis.yml
@@ -3,9 +3,22 @@ dist: trusty
|
|||||||
language: java
|
language: java
|
||||||
before_install:
|
before_install:
|
||||||
- sudo apt-get -qq update
|
- 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 tesseract-ocr tesseract-ocr-fra tesseract-ocr-ita tesseract-ocr-kor tesseract-ocr-rus tesseract-ocr-ukr tesseract-ocr-spa tesseract-ocr-ara tesseract-ocr-hin tesseract-ocr-deu tesseract-ocr-pol tesseract-ocr-jpn tesseract-ocr-por tesseract-ocr-tha tesseract-ocr-jpn tesseract-ocr-chi-sim tesseract-ocr-chi-tra
|
||||||
- sudo apt-get -y -q install haveged && sudo service haveged start
|
- sudo apt-get -y -q install haveged && sudo service haveged start
|
||||||
|
after_success:
|
||||||
|
- mvn -Pprod -DskipTests clean install
|
||||||
|
- docker login -u $DOCKER_USER -p $DOCKER_PASS
|
||||||
|
- export REPO=sismics/docs
|
||||||
|
- export TAG=`if [ "$TRAVIS_BRANCH" == "master" ]; then echo "latest"; else echo $TRAVIS_BRANCH ; fi`
|
||||||
|
- docker build -f Dockerfile -t $REPO:$COMMIT .
|
||||||
|
- docker tag $REPO:$COMMIT $REPO:$TAG
|
||||||
|
- docker tag $REPO:$COMMIT $REPO:travis-$TRAVIS_BUILD_NUMBER
|
||||||
|
- docker push $REPO
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- TESSDATA_PREFIX=/usr/share/tesseract-ocr
|
- TESSDATA_PREFIX=/usr/share/tesseract-ocr
|
||||||
- LC_NUMERIC=C
|
- LC_NUMERIC=C
|
||||||
|
- secure: LRGpjWORb0qy6VuypZjTAfA8uRHlFUMTwb77cenS9PPRBxuSnctC531asS9Xg3DqC5nsRxBBprgfCKotn5S8nBSD1ceHh84NASyzLSBft3xSMbg7f/2i7MQ+pGVwLncusBU6E/drnMFwZBleo+9M8Tf96axY5zuUp90MUTpSgt0=
|
||||||
|
- secure: bCDDR6+I7PmSkuTYZv1HF/z98ANX/SFEESUCqxVmV5Gs0zFC0vQXaPJQ2xaJNRop1HZBFMZLeMMPleb0iOs985smpvK2F6Rbop9Tu+Vyo0uKqv9tbZ7F8Nfgnv9suHKZlL84FNeUQZJX6vsFIYPEJ/r7K5P/M0PdUy++fEwxEhU=
|
||||||
|
- secure: ewXnzbkgCIHpDWtaWGMa1OYZJ/ki99zcIl4jcDPIC0eB3njX/WgfcC6i0Ke9mLqDqwXarWJ6helm22sNh+xtQiz6isfBtBX+novfRt9AANrBe3koCMUemMDy7oh5VflBaFNP0DVb8LSCnwf6dx6ZB5E9EB8knvk40quc/cXpGjY=
|
||||||
|
- COMMIT=${TRAVIS_COMMIT::8}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
FROM sismics/debian-java7-jetty9
|
FROM sismics/jetty:9.2.20-jdk7
|
||||||
MAINTAINER benjamin.gam@gmail.com
|
MAINTAINER b.gamard@sismics.com
|
||||||
|
|
||||||
RUN apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-jpn
|
RUN apt-get update && apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-ita tesseract-ocr-kor tesseract-ocr-rus tesseract-ocr-ukr tesseract-ocr-spa tesseract-ocr-ara tesseract-ocr-hin tesseract-ocr-deu tesseract-ocr-pol tesseract-ocr-jpn tesseract-ocr-por tesseract-ocr-tha tesseract-ocr-jpn tesseract-ocr-chi-sim tesseract-ocr-chi-tra && \
|
||||||
|
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV TESSDATA_PREFIX /usr/share/tesseract-ocr
|
ENV TESSDATA_PREFIX /usr/share/tesseract-ocr
|
||||||
ENV LC_NUMERIC C
|
ENV LC_NUMERIC C
|
||||||
|
|
||||||
ADD docs-web/target/docs-web-*.war /opt/jetty/webapps/docs.war
|
|
||||||
ADD docs.xml /opt/jetty/webapps/docs.xml
|
ADD docs.xml /opt/jetty/webapps/docs.xml
|
||||||
|
ADD docs-web/target/docs-web-*.war /opt/jetty/webapps/docs.war
|
||||||
|
|||||||
46
README.md
46
README.md
@@ -1,20 +1,28 @@
|
|||||||
Sismics Docs [](http://travis-ci.org/sismics/docs)
|
<h3 align="center">
|
||||||
============
|
<img src="https://www.sismicsdocs.com/img/github-title.png" alt="Sismics Docs" width=500 />
|
||||||
|
</h3>
|
||||||
|
|
||||||
_Web interface_
|
[](https://twitter.com/sismicsdocs)
|
||||||
|
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||||
|
[](http://travis-ci.org/sismics/docs)
|
||||||
|
|
||||||

|
Docs is an open source, lightweight document management system for individuals and businesses.
|
||||||
|
|
||||||
_Android application_
|
<hr />
|
||||||
|
<h2 align="center">
|
||||||
|
✨ We just launched a Cloud version of Sismics Docs! Head to <a href="https://www.sismicsdocs.com/">sismicsdocs.com</a> for more informations ✨
|
||||||
|
</h2>
|
||||||
|
<hr />
|
||||||
|
|
||||||
   
|

|
||||||
|
|
||||||
What is Docs?
|
Demo
|
||||||
---------------
|
----
|
||||||
|
|
||||||
Docs is an open source, lightweight document management system.
|
A demo is available at [demo.sismicsdocs.com](https://demo.sismicsdocs.com)
|
||||||
|
- Guest login is enabled with read access on all documents
|
||||||
Docs is written in Java, and may be run on any operating system with Java support.
|
- "admin" login with "admin" password
|
||||||
|
- "demo" login with "password" password
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
@@ -25,9 +33,13 @@ Features
|
|||||||
- Flexible search engine
|
- Flexible search engine
|
||||||
- Full text search in all supported files
|
- Full text search in all supported files
|
||||||
- All [Dublin Core](http://dublincore.org/) metadata
|
- All [Dublin Core](http://dublincore.org/) metadata
|
||||||
|
- Workflow system 
|
||||||
- 256-bit AES encryption of stored files
|
- 256-bit AES encryption of stored files
|
||||||
- Tag system with nesting
|
- Tag system with nesting
|
||||||
|
- Import document from email (EML format) 
|
||||||
|
- Automatic inbox scanning and importing 
|
||||||
- User/group permission system
|
- User/group permission system
|
||||||
|
- 2-factor authentication
|
||||||
- Hierarchical groups
|
- Hierarchical groups
|
||||||
- Audit log
|
- Audit log
|
||||||
- Comments
|
- Comments
|
||||||
@@ -35,13 +47,23 @@ Features
|
|||||||
- Document sharing by URL
|
- Document sharing by URL
|
||||||
- RESTful Web API
|
- RESTful Web API
|
||||||
- Fully featured Android client
|
- Fully featured Android client
|
||||||
|
- [Mass files importer](https://github.com/sismics/docs/tree/master/docs-importer) (single or scan mode) 
|
||||||
- Tested to 100k documents
|
- Tested to 100k documents
|
||||||
|
|
||||||
Download
|
Download
|
||||||
--------
|
--------
|
||||||
|
|
||||||
The latest release is downloadable here: <https://github.com/sismics/docs/releases> in WAR format.
|
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/)
|
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.
|
||||||
|
|
||||||
|
Install with Docker
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
From a Docker host, run this command to download and install Sismics Docs. The server will run on <http://[your-docker-host-ip]:8100>.
|
||||||
|
The default admin password is "admin". Don't forget to change it before going to production.
|
||||||
|
|
||||||
|
docker run --rm --name sismics_docs_latest -d -p 8100:8080 -v sismics_docs_latest:/data sismics/docs:latest
|
||||||
|
|
||||||
How to build Docs from the sources
|
How to build Docs from the sources
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|||||||
@@ -1,46 +1,27 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:2.1.0-beta1'
|
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
|
google()
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 23
|
compileSdkVersion 26
|
||||||
buildToolsVersion '23.0.3'
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 23
|
targetSdkVersion 26
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lintOptions {
|
lintOptions {
|
||||||
@@ -50,13 +31,13 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile fileTree(dir: 'libs', include: '*.jar')
|
compile fileTree(dir: 'libs', include: '*.jar')
|
||||||
compile 'com.android.support:appcompat-v7:23.3.0'
|
compile 'com.android.support:appcompat-v7:26.1.0'
|
||||||
compile 'com.android.support:recyclerview-v7:23.3.0'
|
compile 'com.android.support:recyclerview-v7:26.1.0'
|
||||||
compile 'com.android.support:design:23.3.0'
|
compile 'com.android.support:design:26.1.0'
|
||||||
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
|
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
|
||||||
compile 'org.greenrobot:eventbus:3.0.0'
|
compile 'org.greenrobot:eventbus:3.0.0'
|
||||||
compile 'com.squareup.picasso:picasso:2.5.2'
|
compile 'com.squareup.picasso:picasso:2.5.2'
|
||||||
compile 'com.squareup.okhttp3:okhttp:3.1.1'
|
compile 'com.squareup.okhttp3:okhttp:3.7.0'
|
||||||
compile "com.squareup.okhttp3:okhttp-urlconnection:3.1.1"
|
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.4.0'
|
||||||
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.0.2'
|
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public class DocumentEditActivity extends AppCompatActivity {
|
|||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
JSONArray tagArray = tags.optJSONArray("stats");
|
JSONArray tagArray = tags.optJSONArray("tags");
|
||||||
|
|
||||||
List<JSONObject> tagList = new ArrayList<>();
|
List<JSONObject> tagList = new ArrayList<>();
|
||||||
for (int i = 0; i < tagArray.length(); i++) {
|
for (int i = 0; i < tagArray.length(); i++) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import android.support.v7.app.AppCompatActivity;
|
|||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -51,7 +52,7 @@ import com.sismics.docs.resource.FileResource;
|
|||||||
import com.sismics.docs.service.FileUploadService;
|
import com.sismics.docs.service.FileUploadService;
|
||||||
import com.sismics.docs.util.NetworkUtil;
|
import com.sismics.docs.util.NetworkUtil;
|
||||||
import com.sismics.docs.util.PreferenceUtil;
|
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.EventBus;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
@@ -75,11 +76,6 @@ public class DocumentViewActivity extends AppCompatActivity {
|
|||||||
*/
|
*/
|
||||||
public static final int REQUEST_CODE_ADD_FILE = 1;
|
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.
|
* File view pager.
|
||||||
*/
|
*/
|
||||||
@@ -176,9 +172,11 @@ public class DocumentViewActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fill the layout
|
// Fill the layout
|
||||||
|
// Create date
|
||||||
TextView createdDateTextView = (TextView) findViewById(R.id.createdDateTextView);
|
TextView createdDateTextView = (TextView) findViewById(R.id.createdDateTextView);
|
||||||
createdDateTextView.setText(date);
|
createdDateTextView.setText(date);
|
||||||
|
|
||||||
|
// Description
|
||||||
TextView descriptionTextView = (TextView) findViewById(R.id.descriptionTextView);
|
TextView descriptionTextView = (TextView) findViewById(R.id.descriptionTextView);
|
||||||
if (description.isEmpty() || document.isNull("description")) {
|
if (description.isEmpty() || document.isNull("description")) {
|
||||||
descriptionTextView.setVisibility(View.GONE);
|
descriptionTextView.setVisibility(View.GONE);
|
||||||
@@ -187,17 +185,20 @@ public class DocumentViewActivity extends AppCompatActivity {
|
|||||||
descriptionTextView.setText(description);
|
descriptionTextView.setText(description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tags
|
||||||
TextView tagTextView = (TextView) findViewById(R.id.tagTextView);
|
TextView tagTextView = (TextView) findViewById(R.id.tagTextView);
|
||||||
if (tags.length() == 0) {
|
if (tags.length() == 0) {
|
||||||
tagTextView.setVisibility(View.GONE);
|
tagTextView.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
tagTextView.setVisibility(View.VISIBLE);
|
tagTextView.setVisibility(View.VISIBLE);
|
||||||
tagTextView.setText(TagUtil.buildSpannable(tags));
|
tagTextView.setText(SpannableUtil.buildSpannableTags(tags));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Language
|
||||||
ImageView languageImageView = (ImageView) findViewById(R.id.languageImageView);
|
ImageView languageImageView = (ImageView) findViewById(R.id.languageImageView);
|
||||||
languageImageView.setImageResource(getResources().getIdentifier(language, "drawable", getPackageName()));
|
languageImageView.setImageResource(getResources().getIdentifier(language, "drawable", getPackageName()));
|
||||||
|
|
||||||
|
// Shared status
|
||||||
ImageView sharedImageView = (ImageView) findViewById(R.id.sharedImageView);
|
ImageView sharedImageView = (ImageView) findViewById(R.id.sharedImageView);
|
||||||
sharedImageView.setVisibility(shared ? View.VISIBLE : View.GONE);
|
sharedImageView.setVisibility(shared ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
@@ -208,7 +209,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
|||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
Intent intent = new Intent(DocumentViewActivity.this, DocumentEditActivity.class);
|
Intent intent = new Intent(DocumentViewActivity.this, DocumentEditActivity.class);
|
||||||
intent.putExtra("document", DocumentViewActivity.this.document.toString());
|
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
|
// Action only available if the document is writable
|
||||||
findViewById(R.id.actionEditDocument).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.INVISIBLE);
|
findViewById(R.id.actionUploadFile).setVisibility(writable ? View.VISIBLE : View.GONE);
|
||||||
findViewById(R.id.actionSharing).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
findViewById(R.id.actionSharing).setVisibility(writable ? View.VISIBLE : View.GONE);
|
||||||
findViewById(R.id.actionDelete).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
findViewById(R.id.actionDelete).setVisibility(writable ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
// ACLs
|
// ACLs
|
||||||
ListView aclListView = (ListView) findViewById(R.id.aclListView);
|
ListView aclListView = (ListView) findViewById(R.id.aclListView);
|
||||||
@@ -679,10 +680,54 @@ public class DocumentViewActivity extends AppCompatActivity {
|
|||||||
startActivity(intent);
|
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
|
@Override
|
||||||
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
|
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
|
||||||
switch (view.getId()) {
|
switch (view.getId()) {
|
||||||
|
|||||||
@@ -87,13 +87,13 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
tagListView.setEmptyView(tagProgressView);
|
tagListView.setEmptyView(tagProgressView);
|
||||||
JSONObject cacheTags = PreferenceUtil.getCachedJson(this, PreferenceUtil.PREF_CACHED_TAGS_JSON);
|
JSONObject cacheTags = PreferenceUtil.getCachedJson(this, PreferenceUtil.PREF_CACHED_TAGS_JSON);
|
||||||
if (cacheTags != null) {
|
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
|
@Override
|
||||||
public void onSuccess(JSONObject response) {
|
public void onSuccess(JSONObject response) {
|
||||||
PreferenceUtil.setCachedJson(MainActivity.this, PreferenceUtil.PREF_CACHED_TAGS_JSON, 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);
|
tagProgressView.setVisibility(View.GONE);
|
||||||
tagListView.setEmptyView(tagEmptyView);
|
tagListView.setEmptyView(tagEmptyView);
|
||||||
}
|
}
|
||||||
@@ -158,6 +158,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onFinish() {
|
public void onFinish() {
|
||||||
// Force logout in all cases, so the user is not stuck in case of network error
|
// 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);
|
ApplicationContext.getInstance().setUserInfo(getApplicationContext(), null);
|
||||||
startActivity(new Intent(MainActivity.this, LoginActivity.class));
|
startActivity(new Intent(MainActivity.this, LoginActivity.class));
|
||||||
finish();
|
finish();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import android.widget.ImageView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.sismics.docs.R;
|
import com.sismics.docs.R;
|
||||||
import com.sismics.docs.util.TagUtil;
|
import com.sismics.docs.util.SpannableUtil;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -69,7 +69,7 @@ public class DocListAdapter extends RecyclerView.Adapter<DocListAdapter.ViewHold
|
|||||||
holder.titleTextView.setText(document.optString("title"));
|
holder.titleTextView.setText(document.optString("title"));
|
||||||
|
|
||||||
JSONArray tags = document.optJSONArray("tags");
|
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")));
|
String date = DateFormat.getDateFormat(holder.dateTextView.getContext()).format(new Date(document.optLong("create_date")));
|
||||||
holder.dateTextView.setText(date);
|
holder.dateTextView.setText(date);
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ public class LanguageAdapter extends BaseAdapter {
|
|||||||
}
|
}
|
||||||
languageList.add(new Language("fra", R.string.language_french, R.drawable.fra));
|
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("eng", R.string.language_english, R.drawable.eng));
|
||||||
languageList.add(new Language("jpn", R.string.language_japanese, R.drawable.jpn));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class TagListAdapter extends BaseAdapter {
|
|||||||
|
|
||||||
// Reorder tags by parent/child relation and compute depth
|
// Reorder tags by parent/child relation and compute depth
|
||||||
int depth = 0;
|
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
|
// Get all tags with this parent
|
||||||
for (JSONObject tag : tags) {
|
for (JSONObject tag : tags) {
|
||||||
String tagParentId = tag.optString("parent");
|
String tagParentId = tag.optString("parent");
|
||||||
if (tagParentId.equals(parentId)) {
|
if (parentId.equals(tagParentId)) {
|
||||||
TagItem tagItem = new TagItem();
|
TagItem tagItem = new TagItem();
|
||||||
tagItem.id = tag.optString("id");
|
tagItem.id = tag.optString("id");
|
||||||
tagItem.name = tag.optString("name");
|
tagItem.name = tag.optString("name");
|
||||||
tagItem.count = tag.optInt("count");
|
|
||||||
tagItem.color = tag.optString("color");
|
tagItem.color = tag.optString("color");
|
||||||
tagItem.depth = depth;
|
tagItem.depth = depth;
|
||||||
tagItemList.add(tagItem);
|
tagItemList.add(tagItem);
|
||||||
@@ -99,8 +98,6 @@ public class TagListAdapter extends BaseAdapter {
|
|||||||
TagItem tagItem = getItem(position);
|
TagItem tagItem = getItem(position);
|
||||||
TextView tagTextView = (TextView) view.findViewById(R.id.tagTextView);
|
TextView tagTextView = (TextView) view.findViewById(R.id.tagTextView);
|
||||||
tagTextView.setText(tagItem.name);
|
tagTextView.setText(tagItem.name);
|
||||||
TextView tagCountTextView = (TextView) view.findViewById(R.id.tagCountTextView);
|
|
||||||
tagCountTextView.setText(String.format(Locale.ENGLISH, "%d", tagItem.count));
|
|
||||||
|
|
||||||
// Label color filtering
|
// Label color filtering
|
||||||
ImageView labelImageView = (ImageView) view.findViewById(R.id.labelImageView);
|
ImageView labelImageView = (ImageView) view.findViewById(R.id.labelImageView);
|
||||||
@@ -125,7 +122,6 @@ public class TagListAdapter extends BaseAdapter {
|
|||||||
public static class TagItem {
|
public static class TagItem {
|
||||||
private String id;
|
private String id;
|
||||||
private String name;
|
private String name;
|
||||||
private int count;
|
|
||||||
private String color;
|
private String color;
|
||||||
private int depth;
|
private int depth;
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class SearchFragment extends DialogFragment {
|
|||||||
dialog.cancel();
|
dialog.cancel();
|
||||||
return dialog;
|
return dialog;
|
||||||
}
|
}
|
||||||
JSONArray tagArray = tags.optJSONArray("stats");
|
JSONArray tagArray = tags.optJSONArray("tags");
|
||||||
|
|
||||||
List<JSONObject> tagList = new ArrayList<>();
|
List<JSONObject> tagList = new ArrayList<>();
|
||||||
for (int i = 0; i < tagArray.length(); i++) {
|
for (int i = 0; i < tagArray.length(); i++) {
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ import okhttp3.Request;
|
|||||||
*/
|
*/
|
||||||
public class TagResource extends BaseResource {
|
public class TagResource extends BaseResource {
|
||||||
/**
|
/**
|
||||||
* GET /tag/stats.
|
* GET /tag/list.
|
||||||
*
|
*
|
||||||
* @param context Context
|
* @param context Context
|
||||||
* @param callback Callback
|
* @param callback Callback
|
||||||
*/
|
*/
|
||||||
public static void stats(Context context, HttpCallback callback) {
|
public static void list(Context context, HttpCallback callback) {
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(HttpUrl.parse(getApiUrl(context) + "/tag/stats"))
|
.url(HttpUrl.parse(getApiUrl(context) + "/tag/list"))
|
||||||
.get()
|
.get()
|
||||||
.build();
|
.build();
|
||||||
OkHttpUtil.buildClient(context)
|
OkHttpUtil.buildClient(context)
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
package com.sismics.docs.util;
|
package com.sismics.docs.util;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.DownloadManager;
|
import android.app.DownloadManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class for network actions.
|
* Utility class for network actions.
|
||||||
@@ -19,9 +24,14 @@ public class NetworkUtil {
|
|||||||
* @param title Notification title
|
* @param title Notification title
|
||||||
* @param description Notification description
|
* @param description Notification description
|
||||||
*/
|
*/
|
||||||
public static void downloadFile(Context context, String url, String fileName, String title, String description) {
|
public static void downloadFile(Activity activity, String url, String fileName, String title, String description) {
|
||||||
String authToken = PreferenceUtil.getAuthToken(context);
|
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||||
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
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));
|
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
|
||||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
|
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ public class PreferenceUtil {
|
|||||||
/**
|
/**
|
||||||
* Returns auth token cookie from shared preferences.
|
* Returns auth token cookie from shared preferences.
|
||||||
*
|
*
|
||||||
|
* @param context Context
|
||||||
* @return Auth token
|
* @return Auth token
|
||||||
*/
|
*/
|
||||||
public static String getAuthToken(Context context) {
|
public static String getAuthToken(Context context) {
|
||||||
@@ -140,6 +141,16 @@ public class PreferenceUtil {
|
|||||||
return null;
|
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.
|
* Returns cleaned server URL.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
Before Width: | Height: | Size: 558 B |
Binary file not shown.
|
Before Width: | Height: | Size: 813 B |
@@ -142,15 +142,19 @@
|
|||||||
|
|
||||||
<!-- Right drawer -->
|
<!-- Right drawer -->
|
||||||
|
|
||||||
<LinearLayout
|
<ScrollView
|
||||||
android:id="@+id/right_drawer"
|
android:id="@+id/right_drawer"
|
||||||
android:layout_width="300dp"
|
android:layout_width="300dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="end"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:background="#fff"
|
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 -->
|
<!-- Actions -->
|
||||||
|
|
||||||
@@ -166,28 +170,6 @@
|
|||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
style="?android:buttonBarStyle">
|
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
|
<Button
|
||||||
android:id="@+id/actionDownload"
|
android:id="@+id/actionDownload"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -199,6 +181,28 @@
|
|||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:layout_margin="8dp"/>
|
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>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -208,12 +212,23 @@
|
|||||||
style="?android:buttonBarStyle">
|
style="?android:buttonBarStyle">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/actionExportPdf"
|
android:id="@+id/actionEditDocument"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:drawableTop="@drawable/ic_description_grey600_24dp"
|
android:drawableTop="@drawable/ic_create_grey600_24dp"
|
||||||
style="?android:buttonBarButtonStyle"
|
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:textColor="#ff5a595b"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:layout_margin="0dp"/>
|
android:layout_margin="0dp"/>
|
||||||
@@ -229,17 +244,6 @@
|
|||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:layout_margin="0dp"/>
|
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
|
<Button
|
||||||
android:id="@+id/actionDelete"
|
android:id="@+id/actionDelete"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -285,6 +289,8 @@
|
|||||||
android:id="@+id/createdDateTextView"
|
android:id="@+id/createdDateTextView"
|
||||||
android:layout_toRightOf="@id/createdDateLabel"
|
android:layout_toRightOf="@id/createdDateLabel"
|
||||||
android:layout_toEndOf="@id/createdDateLabel"
|
android:layout_toEndOf="@id/createdDateLabel"
|
||||||
|
android:layout_toLeftOf="@id/sharedImageView"
|
||||||
|
android:layout_toStartOf="@id/sharedImageView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
@@ -332,6 +338,7 @@
|
|||||||
android:fontFamily="sans-serif-light"/>
|
android:fontFamily="sans-serif-light"/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
android:contentDescription="@string/shared"
|
||||||
android:id="@+id/sharedImageView"
|
android:id="@+id/sharedImageView"
|
||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
@@ -343,6 +350,7 @@
|
|||||||
android:layout_toStartOf="@+id/languageImageView"/>
|
android:layout_toStartOf="@+id/languageImageView"/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
android:contentDescription="@string/language"
|
||||||
android:id="@+id/languageImageView"
|
android:id="@+id/languageImageView"
|
||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
@@ -352,6 +360,278 @@
|
|||||||
|
|
||||||
</RelativeLayout>
|
</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
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
@@ -369,14 +649,16 @@
|
|||||||
android:text="@string/who_can_access"
|
android:text="@string/who_can_access"
|
||||||
android:layout_margin="12dp"/>
|
android:layout_margin="12dp"/>
|
||||||
|
|
||||||
<ListView
|
<com.sismics.docs.ui.view.NonScrollListView
|
||||||
android:id="@+id/aclListView"
|
android:id="@+id/aclListView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:choiceMode="singleChoice"
|
android:choiceMode="singleChoice"
|
||||||
android:divider="@android:color/transparent"
|
android:divider="@android:color/transparent"
|
||||||
android:dividerHeight="0dp"/>
|
android:dividerHeight="0dp"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
</android.support.v4.widget.DrawerLayout>
|
</android.support.v4.widget.DrawerLayout>
|
||||||
@@ -29,16 +29,4 @@
|
|||||||
android:text="Appartement"
|
android:text="Appartement"
|
||||||
android:textSize="14sp"/>
|
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>
|
</RelativeLayout>
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 5.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 11 KiB |
144
docs-android/app/src/main/res/values-fr/strings.xml
Normal file
144
docs-android/app/src/main/res/values-fr/strings.xml
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<?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 Sismics Docs 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">Sismics Docs</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 Sismics Docs</string>
|
||||||
|
<string name="download_document_title">Export de document Sismics Docs</string>
|
||||||
|
<string name="download_pdf_title">Export PDF Sismics Docs</string>
|
||||||
|
<string name="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>
|
||||||
|
|
||||||
|
</resources>
|
||||||
@@ -9,10 +9,10 @@
|
|||||||
<string name="validate_error_alphanumeric">Only letters and numbers</string>
|
<string name="validate_error_alphanumeric">Only letters and numbers</string>
|
||||||
|
|
||||||
<!-- App -->
|
<!-- App -->
|
||||||
<string name="app_name">Sismics Docs</string>
|
<string name="app_name" translatable="false">Sismics Docs</string>
|
||||||
<string name="drawer_open">Open navigation drawer</string>
|
<string name="drawer_open">Open navigation drawer</string>
|
||||||
<string name="drawer_close">Close 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 Sismics Docs 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="server">Server</string>
|
||||||
<string name="username">Username</string>
|
<string name="username">Username</string>
|
||||||
<string name="password">Password</string>
|
<string name="password">Password</string>
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
<string name="add_share_hint">Name the share (optional)</string>
|
<string name="add_share_hint">Name the share (optional)</string>
|
||||||
<string name="document_not_shared">This document is not currently shared</string>
|
<string name="document_not_shared">This document is not currently shared</string>
|
||||||
<string name="delete_share">Delete this share</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_loading_shares">Error loading shares</string>
|
||||||
<string name="error_adding_share">Error adding share</string>
|
<string name="error_adding_share">Error adding share</string>
|
||||||
<string name="share_default_name">Share link</string>
|
<string name="share_default_name">Share link</string>
|
||||||
@@ -69,9 +69,8 @@
|
|||||||
<string name="pref_clear_history_summary">Wipe the recent search suggestions</string>
|
<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_clear_history_success">Search history cleared</string>
|
||||||
<string name="pref_cache_size">Cache size</string>
|
<string name="pref_cache_size">Cache size</string>
|
||||||
<string name="language_french">French</string>
|
<string name="language_french" translatable="false">Français</string>
|
||||||
<string name="language_english">English</string>
|
<string name="language_english" translatable="false">English</string>
|
||||||
<string name="language_japanese">Japanese</string>
|
|
||||||
<string name="save">Save</string>
|
<string name="save">Save</string>
|
||||||
<string name="edit_document">Edit</string>
|
<string name="edit_document">Edit</string>
|
||||||
<string name="error_editing_document">Network error, please try again</string>
|
<string name="error_editing_document">Network error, please try again</string>
|
||||||
@@ -132,5 +131,17 @@
|
|||||||
<string name="storage_quota">Storage quota</string>
|
<string name="storage_quota">Storage quota</string>
|
||||||
<string name="storage_display">%1$d/%2$d MB</string>
|
<string name="storage_display">%1$d/%2$d MB</string>
|
||||||
<string name="validation_code">Validation code</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>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
# 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.
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
# 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
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#Sat Jan 16 19:15:13 CET 2016
|
#Tue Nov 14 23:55:56 CET 2017
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.sismics.docs</groupId>
|
<groupId>com.sismics.docs</groupId>
|
||||||
<artifactId>docs-parent</artifactId>
|
<artifactId>docs-parent</artifactId>
|
||||||
<version>1.4</version>
|
<version>1.5</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
@@ -52,6 +52,26 @@
|
|||||||
<artifactId>commons-lang</artifactId>
|
<artifactId>commons-lang</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-email</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.freemarker</groupId>
|
||||||
|
<artifactId>freemarker</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish</groupId>
|
||||||
|
<artifactId>javax.json</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>log4j</groupId>
|
<groupId>log4j</groupId>
|
||||||
<artifactId>log4j</artifactId>
|
<artifactId>log4j</artifactId>
|
||||||
@@ -92,6 +112,11 @@
|
|||||||
<artifactId>lucene-queryparser</artifactId>
|
<artifactId>lucene-queryparser</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.sun.mail</groupId>
|
||||||
|
<artifactId>javax.mail</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Only there to read old index and rebuild them -->
|
<!-- Only there to read old index and rebuild them -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.lucene</groupId>
|
<groupId>org.apache.lucene</groupId>
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -13,5 +13,34 @@ public enum ConfigType {
|
|||||||
/**
|
/**
|
||||||
* Theme configuration.
|
* Theme configuration.
|
||||||
*/
|
*/
|
||||||
THEME
|
THEME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guest login.
|
||||||
|
*/
|
||||||
|
GUEST_LOGIN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_USERNAME,
|
||||||
|
INBOX_PASSWORD,
|
||||||
|
INBOX_TAG
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.sismics.docs.core.constant;
|
package com.sismics.docs.core.constant;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Application constants.
|
* Application constants.
|
||||||
*
|
*
|
||||||
@@ -30,6 +30,11 @@ public class Constants {
|
|||||||
*/
|
*/
|
||||||
public static final String LUCENE_DIRECTORY_STORAGE_FILE = "FILE";
|
public static final String LUCENE_DIRECTORY_STORAGE_FILE = "FILE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guest user ID.
|
||||||
|
*/
|
||||||
|
public static final String GUEST_USER_ID = "guest";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default generic user role.
|
* Default generic user role.
|
||||||
*/
|
*/
|
||||||
@@ -38,5 +43,48 @@ public class Constants {
|
|||||||
/**
|
/**
|
||||||
* Supported document languages.
|
* 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");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -1,14 +1,7 @@
|
|||||||
package com.sismics.docs.core.dao.jpa;
|
package com.sismics.docs.core.dao.jpa;
|
||||||
|
|
||||||
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.AclTargetType;
|
||||||
|
import com.sismics.docs.core.constant.AclType;
|
||||||
import com.sismics.docs.core.constant.AuditLogType;
|
import com.sismics.docs.core.constant.AuditLogType;
|
||||||
import com.sismics.docs.core.constant.PermType;
|
import com.sismics.docs.core.constant.PermType;
|
||||||
import com.sismics.docs.core.dao.jpa.dto.AclDto;
|
import com.sismics.docs.core.dao.jpa.dto.AclDto;
|
||||||
@@ -16,6 +9,13 @@ import com.sismics.docs.core.model.jpa.Acl;
|
|||||||
import com.sismics.docs.core.util.AuditLogUtil;
|
import com.sismics.docs.core.util.AuditLogUtil;
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ACL DAO.
|
* ACL DAO.
|
||||||
*
|
*
|
||||||
@@ -65,19 +65,20 @@ public class AclDao {
|
|||||||
* @return ACL DTO list
|
* @return ACL DTO list
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public List<AclDto> getBySourceId(String sourceId) {
|
public List<AclDto> getBySourceId(String sourceId, AclType type) {
|
||||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
StringBuilder sb = new StringBuilder("select a.ACL_ID_C, a.ACL_PERM_C, a.ACL_TARGETID_C, ");
|
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 ");
|
.append(" u.USE_USERNAME_C, s.SHA_ID_C, s.SHA_NAME_C, g.GRP_NAME_C ")
|
||||||
sb.append(" from T_ACL a ");
|
.append(" from T_ACL a ")
|
||||||
sb.append(" left join T_USER u on u.USE_ID_C = a.ACL_TARGETID_C ");
|
.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 ");
|
.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 ");
|
.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 ");
|
.append(" where a.ACL_DELETEDATE_D is null and a.ACL_SOURCEID_C = :sourceId and a.ACL_TYPE_C = :type ");
|
||||||
|
|
||||||
// Perform the query
|
// Perform the query
|
||||||
Query q = em.createNativeQuery(sb.toString());
|
Query q = em.createNativeQuery(sb.toString());
|
||||||
q.setParameter("sourceId", sourceId);
|
q.setParameter("sourceId", sourceId);
|
||||||
|
q.setParameter("type", type.name());
|
||||||
List<Object[]> l = q.getResultList();
|
List<Object[]> l = q.getResultList();
|
||||||
|
|
||||||
// Assemble results
|
// Assemble results
|
||||||
@@ -141,26 +142,29 @@ public class AclDao {
|
|||||||
* @param perm Permission
|
* @param perm Permission
|
||||||
* @param targetId Target ID
|
* @param targetId Target ID
|
||||||
* @param userId User ID
|
* @param userId User ID
|
||||||
|
* @param type Type
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@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();
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
|
|
||||||
// Create audit log
|
// 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("sourceId", sourceId);
|
||||||
q.setParameter("perm", perm);
|
q.setParameter("perm", perm);
|
||||||
q.setParameter("targetId", targetId);
|
q.setParameter("targetId", targetId);
|
||||||
|
q.setParameter("type", type);
|
||||||
List<Acl> aclList = q.getResultList();
|
List<Acl> aclList = q.getResultList();
|
||||||
for (Acl acl : aclList) {
|
for (Acl acl : aclList) {
|
||||||
AuditLogUtil.create(acl, AuditLogType.DELETE, userId);
|
AuditLogUtil.create(acl, AuditLogType.DELETE, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Soft delete the ACLs
|
// 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("sourceId", sourceId);
|
||||||
q.setParameter("perm", perm);
|
q.setParameter("perm", perm);
|
||||||
q.setParameter("targetId", targetId);
|
q.setParameter("targetId", targetId);
|
||||||
|
q.setParameter("type", type);
|
||||||
q.setParameter("dateNow", new Date());
|
q.setParameter("dateNow", new Date());
|
||||||
q.executeUpdate();
|
q.executeUpdate();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
package com.sismics.docs.core.dao.jpa;
|
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.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
|
||||||
import javax.persistence.NoResultException;
|
|
||||||
import javax.persistence.Query;
|
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.sismics.docs.core.constant.AuditLogType;
|
import com.sismics.docs.core.constant.AuditLogType;
|
||||||
@@ -28,6 +15,12 @@ import com.sismics.docs.core.util.jpa.QueryParam;
|
|||||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Document DAO.
|
* Document DAO.
|
||||||
*
|
*
|
||||||
@@ -224,7 +217,7 @@ public class DocumentDao {
|
|||||||
if (!Strings.isNullOrEmpty(criteria.getSearch()) || !Strings.isNullOrEmpty(criteria.getFullSearch())) {
|
if (!Strings.isNullOrEmpty(criteria.getSearch()) || !Strings.isNullOrEmpty(criteria.getFullSearch())) {
|
||||||
LuceneDao luceneDao = new LuceneDao();
|
LuceneDao luceneDao = new LuceneDao();
|
||||||
Set<String> documentIdList = luceneDao.search(criteria.getSearch(), criteria.getFullSearch());
|
Set<String> documentIdList = luceneDao.search(criteria.getSearch(), criteria.getFullSearch());
|
||||||
if (documentIdList.size() == 0) {
|
if (documentIdList.isEmpty()) {
|
||||||
// If the search doesn't find any document, the request should return nothing
|
// If the search doesn't find any document, the request should return nothing
|
||||||
documentIdList.add(UUID.randomUUID().toString());
|
documentIdList.add(UUID.randomUUID().toString());
|
||||||
}
|
}
|
||||||
@@ -301,25 +294,36 @@ public class DocumentDao {
|
|||||||
// Get the document
|
// Get the document
|
||||||
Query q = em.createQuery("select d from Document d where d.id = :id and d.deleteDate is null");
|
Query q = em.createQuery("select d from Document d where d.id = :id and d.deleteDate is null");
|
||||||
q.setParameter("id", document.getId());
|
q.setParameter("id", document.getId());
|
||||||
Document documentFromDb = (Document) q.getSingleResult();
|
Document documentDb = (Document) q.getSingleResult();
|
||||||
|
|
||||||
// Update the document
|
// Update the document
|
||||||
documentFromDb.setTitle(document.getTitle());
|
documentDb.setTitle(document.getTitle());
|
||||||
documentFromDb.setDescription(document.getDescription());
|
documentDb.setDescription(document.getDescription());
|
||||||
documentFromDb.setSubject(document.getSubject());
|
documentDb.setSubject(document.getSubject());
|
||||||
documentFromDb.setIdentifier(document.getIdentifier());
|
documentDb.setIdentifier(document.getIdentifier());
|
||||||
documentFromDb.setPublisher(document.getPublisher());
|
documentDb.setPublisher(document.getPublisher());
|
||||||
documentFromDb.setFormat(document.getFormat());
|
documentDb.setFormat(document.getFormat());
|
||||||
documentFromDb.setSource(document.getSource());
|
documentDb.setSource(document.getSource());
|
||||||
documentFromDb.setType(document.getType());
|
documentDb.setType(document.getType());
|
||||||
documentFromDb.setCoverage(document.getCoverage());
|
documentDb.setCoverage(document.getCoverage());
|
||||||
documentFromDb.setRights(document.getRights());
|
documentDb.setRights(document.getRights());
|
||||||
documentFromDb.setCreateDate(document.getCreateDate());
|
documentDb.setCreateDate(document.getCreateDate());
|
||||||
documentFromDb.setLanguage(document.getLanguage());
|
documentDb.setLanguage(document.getLanguage());
|
||||||
|
|
||||||
// Create audit log
|
// Create audit log
|
||||||
AuditLogUtil.create(documentFromDb, AuditLogType.UPDATE, userId);
|
AuditLogUtil.create(documentDb, AuditLogType.UPDATE, userId);
|
||||||
|
|
||||||
return documentFromDb;
|
return documentDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package com.sismics.docs.core.dao.jpa;
|
package com.sismics.docs.core.dao.jpa;
|
||||||
|
|
||||||
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.constant.AuditLogType;
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
import com.sismics.docs.core.util.AuditLogUtil;
|
import com.sismics.docs.core.util.AuditLogUtil;
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File DAO.
|
* File DAO.
|
||||||
*
|
*
|
||||||
@@ -137,13 +136,13 @@ public class FileDao {
|
|||||||
// Get the file
|
// Get the file
|
||||||
Query q = em.createQuery("select f from File f where f.id = :id and f.deleteDate is null");
|
Query q = em.createQuery("select f from File f where f.id = :id and f.deleteDate is null");
|
||||||
q.setParameter("id", file.getId());
|
q.setParameter("id", file.getId());
|
||||||
File fileFromDb = (File) q.getSingleResult();
|
File fileDb = (File) q.getSingleResult();
|
||||||
|
|
||||||
// Update the file
|
// Update the file
|
||||||
fileFromDb.setDocumentId(file.getDocumentId());
|
fileDb.setDocumentId(file.getDocumentId());
|
||||||
fileFromDb.setContent(file.getContent());
|
fileDb.setContent(file.getContent());
|
||||||
fileFromDb.setOrder(file.getOrder());
|
fileDb.setOrder(file.getOrder());
|
||||||
fileFromDb.setMimeType(file.getMimeType());
|
fileDb.setMimeType(file.getMimeType());
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
package com.sismics.docs.core.dao.jpa;
|
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;
|
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.sismics.docs.core.constant.AuditLogType;
|
import com.sismics.docs.core.constant.AuditLogType;
|
||||||
import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria;
|
import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria;
|
||||||
@@ -25,6 +12,11 @@ import com.sismics.docs.core.util.jpa.QueryUtil;
|
|||||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Group DAO.
|
* Group DAO.
|
||||||
*
|
*
|
||||||
@@ -266,16 +258,16 @@ public class GroupDao {
|
|||||||
// Get the group
|
// Get the group
|
||||||
Query q = em.createQuery("select g from Group g where g.id = :id and g.deleteDate is null");
|
Query q = em.createQuery("select g from Group g where g.id = :id and g.deleteDate is null");
|
||||||
q.setParameter("id", group.getId());
|
q.setParameter("id", group.getId());
|
||||||
Group groupFromDb = (Group) q.getSingleResult();
|
Group groupDb = (Group) q.getSingleResult();
|
||||||
|
|
||||||
// Update the group
|
// Update the group
|
||||||
groupFromDb.setName(group.getName());
|
groupDb.setName(group.getName());
|
||||||
groupFromDb.setParentId(group.getParentId());
|
groupDb.setParentId(group.getParentId());
|
||||||
|
|
||||||
// Create audit log
|
// Create audit log
|
||||||
AuditLogUtil.create(groupFromDb, AuditLogType.UPDATE, userId);
|
AuditLogUtil.create(groupDb, AuditLogType.UPDATE, userId);
|
||||||
|
|
||||||
return groupFromDb;
|
return groupDb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.sismics.docs.core.dao.jpa;
|
||||||
|
|
||||||
|
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 javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
|
import javax.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package com.sismics.docs.core.dao.jpa;
|
||||||
|
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import com.sismics.docs.core.constant.AuditLogType;
|
||||||
|
import com.sismics.docs.core.dao.jpa.criteria.RouteCriteria;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.RouteDto;
|
||||||
|
import com.sismics.docs.core.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 javax.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");
|
||||||
|
|
||||||
|
if (!criteriaList.isEmpty()) {
|
||||||
|
sb.append(" where ");
|
||||||
|
sb.append(Joiner.on(" and ").join(criteriaList));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the search
|
||||||
|
QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Object[]> l = QueryUtil.getNativeQuery(queryParam).getResultList();
|
||||||
|
|
||||||
|
// Assemble results
|
||||||
|
List<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
|
||||||
|
*/
|
||||||
|
public void deleteRoute(String routeId) {
|
||||||
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
|
|
||||||
|
em.createNativeQuery("update T_ROUTE_STEP rs set rs.RTP_DELETEDATE_D = :dateNow where rs.RTP_IDROUTE_C = :routeId and rs.RTP_DELETEDATE_D is null")
|
||||||
|
.setParameter("routeId", routeId)
|
||||||
|
.setParameter("dateNow", new Date())
|
||||||
|
.executeUpdate();
|
||||||
|
|
||||||
|
em.createNativeQuery("update T_ROUTE r set r.RTE_DELETEDATE_D = :dateNow where r.RTE_ID_C = :routeId and r.RTE_DELETEDATE_D is null")
|
||||||
|
.setParameter("routeId", routeId)
|
||||||
|
.setParameter("dateNow", new Date())
|
||||||
|
.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
package com.sismics.docs.core.dao.jpa;
|
||||||
|
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import com.sismics.docs.core.constant.AuditLogType;
|
||||||
|
import com.sismics.docs.core.dao.jpa.criteria.RouteModelCriteria;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.RouteModelDto;
|
||||||
|
import com.sismics.docs.core.model.jpa.RouteModel;
|
||||||
|
import com.sismics.docs.core.util.AuditLogUtil;
|
||||||
|
import com.sismics.docs.core.util.jpa.QueryParam;
|
||||||
|
import com.sismics.docs.core.util.jpa.QueryUtil;
|
||||||
|
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||||
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
import java.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 group
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
criteriaList.add("rm.RTM_DELETEDATE_D is null");
|
||||||
|
|
||||||
|
if (!criteriaList.isEmpty()) {
|
||||||
|
sb.append(" where ");
|
||||||
|
sb.append(Joiner.on(" and ").join(criteriaList));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the search
|
||||||
|
QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Object[]> l = QueryUtil.getNativeQuery(queryParam).getResultList();
|
||||||
|
|
||||||
|
// Assemble results
|
||||||
|
List<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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package com.sismics.docs.core.dao.jpa;
|
||||||
|
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import com.sismics.docs.core.constant.AclTargetType;
|
||||||
|
import com.sismics.docs.core.constant.RouteStepTransition;
|
||||||
|
import com.sismics.docs.core.constant.RouteStepType;
|
||||||
|
import com.sismics.docs.core.dao.jpa.criteria.RouteStepCriteria;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.RouteStepDto;
|
||||||
|
import com.sismics.docs.core.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 javax.persistence.EntityManager;
|
||||||
|
import javax.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_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");
|
||||||
|
|
||||||
|
if (!criteriaList.isEmpty()) {
|
||||||
|
sb.append(" where ");
|
||||||
|
sb.append(Joiner.on(" and ").join(criteriaList));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the search
|
||||||
|
QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Object[]> l = QueryUtil.getNativeQuery(queryParam).getResultList();
|
||||||
|
|
||||||
|
// Assemble results
|
||||||
|
List<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]);
|
||||||
|
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 r.RTP_ENDDATE_D = :endDate, r.RTP_TRANSITION_C = :transition, r.RTP_COMMENT_C = :comment, r.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,5 @@
|
|||||||
package com.sismics.docs.core.dao.jpa;
|
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;
|
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.sismics.docs.core.constant.AuditLogType;
|
import com.sismics.docs.core.constant.AuditLogType;
|
||||||
import com.sismics.docs.core.dao.jpa.criteria.TagCriteria;
|
import com.sismics.docs.core.dao.jpa.criteria.TagCriteria;
|
||||||
@@ -24,6 +12,11 @@ import com.sismics.docs.core.util.jpa.QueryUtil;
|
|||||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag DAO.
|
* Tag DAO.
|
||||||
*
|
*
|
||||||
@@ -155,17 +148,17 @@ public class TagDao {
|
|||||||
// Get the tag
|
// Get the tag
|
||||||
Query q = em.createQuery("select t from Tag t where t.id = :id and t.deleteDate is null");
|
Query q = em.createQuery("select t from Tag t where t.id = :id and t.deleteDate is null");
|
||||||
q.setParameter("id", tag.getId());
|
q.setParameter("id", tag.getId());
|
||||||
Tag tagFromDb = (Tag) q.getSingleResult();
|
Tag tagDb = (Tag) q.getSingleResult();
|
||||||
|
|
||||||
// Update the tag
|
// Update the tag
|
||||||
tagFromDb.setName(tag.getName());
|
tagDb.setName(tag.getName());
|
||||||
tagFromDb.setColor(tag.getColor());
|
tagDb.setColor(tag.getColor());
|
||||||
tagFromDb.setParentId(tag.getParentId());
|
tagDb.setParentId(tag.getParentId());
|
||||||
|
|
||||||
// Create audit log
|
// Create audit log
|
||||||
AuditLogUtil.create(tagFromDb, AuditLogType.UPDATE, userId);
|
AuditLogUtil.create(tagDb, AuditLogType.UPDATE, userId);
|
||||||
|
|
||||||
return tagFromDb;
|
return tagDb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -204,7 +197,7 @@ public class TagDao {
|
|||||||
}
|
}
|
||||||
if (criteria.getNameLike() != null) {
|
if (criteria.getNameLike() != null) {
|
||||||
criteriaList.add("t.TAG_NAME_C like :nameLike");
|
criteriaList.add("t.TAG_NAME_C like :nameLike");
|
||||||
parameterMap.put("nameLike", "%" + criteria.getNameLike() + "%");
|
parameterMap.put("nameLike", criteria.getNameLike() + "%");
|
||||||
}
|
}
|
||||||
|
|
||||||
criteriaList.add("t.TAG_DELETEDATE_D is null");
|
criteriaList.add("t.TAG_DELETEDATE_D is null");
|
||||||
|
|||||||
@@ -1,19 +1,5 @@
|
|||||||
package com.sismics.docs.core.dao.jpa;
|
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;
|
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.sismics.docs.core.constant.AuditLogType;
|
import com.sismics.docs.core.constant.AuditLogType;
|
||||||
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
|
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
|
||||||
@@ -24,6 +10,14 @@ import com.sismics.docs.core.util.jpa.QueryParam;
|
|||||||
import com.sismics.docs.core.util.jpa.QueryUtil;
|
import com.sismics.docs.core.util.jpa.QueryUtil;
|
||||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.mindrot.jbcrypt.BCrypt;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User DAO.
|
* User DAO.
|
||||||
@@ -44,7 +38,7 @@ public class UserDao {
|
|||||||
q.setParameter("username", username);
|
q.setParameter("username", username);
|
||||||
try {
|
try {
|
||||||
User user = (User) q.getSingleResult();
|
User user = (User) q.getSingleResult();
|
||||||
if (!BCrypt.checkpw(password, user.getPassword())) {
|
if (!BCrypt.checkpw(password, user.getPassword()) || user.getDisableDate() != null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
@@ -59,7 +53,7 @@ public class UserDao {
|
|||||||
* @param user User to create
|
* @param user User to create
|
||||||
* @param userId User ID
|
* @param userId User ID
|
||||||
* @return User ID
|
* @return User ID
|
||||||
* @throws Exception
|
* @throws Exception e
|
||||||
*/
|
*/
|
||||||
public String create(User user, String userId) throws Exception {
|
public String create(User user, String userId) throws Exception {
|
||||||
// Create the user UUID
|
// Create the user UUID
|
||||||
@@ -98,16 +92,17 @@ public class UserDao {
|
|||||||
// Get the user
|
// Get the user
|
||||||
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
|
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
|
||||||
q.setParameter("id", user.getId());
|
q.setParameter("id", user.getId());
|
||||||
User userFromDb = (User) q.getSingleResult();
|
User userDb = (User) q.getSingleResult();
|
||||||
|
|
||||||
// Update the user (except password)
|
// Update the user (except password)
|
||||||
userFromDb.setEmail(user.getEmail());
|
userDb.setEmail(user.getEmail());
|
||||||
userFromDb.setStorageQuota(user.getStorageQuota());
|
userDb.setStorageQuota(user.getStorageQuota());
|
||||||
userFromDb.setStorageCurrent(user.getStorageCurrent());
|
userDb.setStorageCurrent(user.getStorageCurrent());
|
||||||
userFromDb.setTotpKey(user.getTotpKey());
|
userDb.setTotpKey(user.getTotpKey());
|
||||||
|
userDb.setDisableDate(user.getDisableDate());
|
||||||
|
|
||||||
// Create audit log
|
// Create audit log
|
||||||
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE, userId);
|
AuditLogUtil.create(userDb, AuditLogType.UPDATE, userId);
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
@@ -116,20 +111,17 @@ public class UserDao {
|
|||||||
* Updates a user's quota.
|
* Updates a user's quota.
|
||||||
*
|
*
|
||||||
* @param user User to update
|
* @param user User to update
|
||||||
* @return Updated user
|
|
||||||
*/
|
*/
|
||||||
public User updateQuota(User user) {
|
public void updateQuota(User user) {
|
||||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
|
|
||||||
// Get the user
|
// Get the user
|
||||||
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
|
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
|
||||||
q.setParameter("id", user.getId());
|
q.setParameter("id", user.getId());
|
||||||
User userFromDb = (User) q.getSingleResult();
|
User userDb = (User) q.getSingleResult();
|
||||||
|
|
||||||
// Update the user
|
// Update the user
|
||||||
userFromDb.setStorageQuota(user.getStorageQuota());
|
userDb.setStorageCurrent(user.getStorageCurrent());
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,13 +137,33 @@ public class UserDao {
|
|||||||
// Get the user
|
// Get the user
|
||||||
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
|
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
|
||||||
q.setParameter("id", user.getId());
|
q.setParameter("id", user.getId());
|
||||||
User userFromDb = (User) q.getSingleResult();
|
User userDb = (User) q.getSingleResult();
|
||||||
|
|
||||||
// Update the user
|
// Update the user
|
||||||
userFromDb.setPassword(hashPassword(user.getPassword()));
|
userDb.setPassword(hashPassword(user.getPassword()));
|
||||||
|
|
||||||
// Create audit log
|
// 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;
|
return user;
|
||||||
}
|
}
|
||||||
@@ -200,39 +212,39 @@ public class UserDao {
|
|||||||
// Get the user
|
// Get the user
|
||||||
Query q = em.createQuery("select u from User u where u.username = :username and u.deleteDate is null");
|
Query q = em.createQuery("select u from User u where u.username = :username and u.deleteDate is null");
|
||||||
q.setParameter("username", username);
|
q.setParameter("username", username);
|
||||||
User userFromDb = (User) q.getSingleResult();
|
User userDb = (User) q.getSingleResult();
|
||||||
|
|
||||||
// Delete the user
|
// Delete the user
|
||||||
Date dateNow = new Date();
|
Date dateNow = new Date();
|
||||||
userFromDb.setDeleteDate(dateNow);
|
userDb.setDeleteDate(dateNow);
|
||||||
|
|
||||||
// Delete linked data
|
// Delete linked data
|
||||||
q = em.createQuery("delete from AuthenticationToken at where at.userId = :userId");
|
q = em.createQuery("delete from AuthenticationToken at where at.userId = :userId");
|
||||||
q.setParameter("userId", userFromDb.getId());
|
q.setParameter("userId", userDb.getId());
|
||||||
q.executeUpdate();
|
q.executeUpdate();
|
||||||
|
|
||||||
q = em.createQuery("update Document d set d.deleteDate = :dateNow where d.userId = :userId and d.deleteDate is null");
|
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.setParameter("dateNow", dateNow);
|
||||||
q.executeUpdate();
|
q.executeUpdate();
|
||||||
|
|
||||||
q = em.createQuery("update File f set f.deleteDate = :dateNow where f.userId = :userId and f.deleteDate is null");
|
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.setParameter("dateNow", dateNow);
|
||||||
q.executeUpdate();
|
q.executeUpdate();
|
||||||
|
|
||||||
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.targetId = :userId and a.deleteDate is null");
|
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.setParameter("dateNow", dateNow);
|
||||||
q.executeUpdate();
|
q.executeUpdate();
|
||||||
|
|
||||||
q = em.createQuery("update Comment c set c.deleteDate = :dateNow where c.userId = :userId and c.deleteDate is null");
|
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.setParameter("dateNow", dateNow);
|
||||||
q.executeUpdate();
|
q.executeUpdate();
|
||||||
|
|
||||||
// Create audit log
|
// Create audit log
|
||||||
AuditLogUtil.create(userFromDb, AuditLogType.DELETE, userId);
|
AuditLogUtil.create(userDb, AuditLogType.DELETE, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -256,7 +268,7 @@ public class UserDao {
|
|||||||
Map<String, Object> parameterMap = new HashMap<>();
|
Map<String, Object> parameterMap = new HashMap<>();
|
||||||
List<String> criteriaList = new ArrayList<>();
|
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 ");
|
sb.append(" from T_USER u ");
|
||||||
|
|
||||||
// Add search criterias
|
// Add search criterias
|
||||||
@@ -264,7 +276,14 @@ public class UserDao {
|
|||||||
criteriaList.add("lower(u.USE_USERNAME_C) like lower(:search)");
|
criteriaList.add("lower(u.USE_USERNAME_C) like lower(:search)");
|
||||||
parameterMap.put("search", "%" + criteria.getSearch() + "%");
|
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) {
|
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 ");
|
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());
|
parameterMap.put("groupId", criteria.getGroupId());
|
||||||
@@ -292,9 +311,39 @@ public class UserDao {
|
|||||||
userDto.setEmail((String) o[i++]);
|
userDto.setEmail((String) o[i++]);
|
||||||
userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
||||||
userDto.setStorageCurrent(((Number) o[i++]).longValue());
|
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);
|
userDtoList.add(userDto);
|
||||||
}
|
}
|
||||||
return userDtoList;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
package com.sismics.docs.core.dao.jpa;
|
package com.sismics.docs.core.dao.jpa;
|
||||||
|
|
||||||
import java.util.List;
|
import com.sismics.docs.core.model.jpa.Vocabulary;
|
||||||
import java.util.UUID;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.NoResultException;
|
import javax.persistence.NoResultException;
|
||||||
import javax.persistence.Query;
|
import javax.persistence.Query;
|
||||||
|
import java.util.List;
|
||||||
import com.sismics.docs.core.model.jpa.Vocabulary;
|
import java.util.UUID;
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vocabulary DAO.
|
* Vocabulary DAO.
|
||||||
@@ -76,14 +75,14 @@ public class VocabularyDao {
|
|||||||
// Get the vocabulary entry
|
// Get the vocabulary entry
|
||||||
Query q = em.createQuery("select v from Vocabulary v where v.id = :id");
|
Query q = em.createQuery("select v from Vocabulary v where v.id = :id");
|
||||||
q.setParameter("id", vocabulary.getId());
|
q.setParameter("id", vocabulary.getId());
|
||||||
Vocabulary vocabularyFromDb = (Vocabulary) q.getSingleResult();
|
Vocabulary vocabularyDb = (Vocabulary) q.getSingleResult();
|
||||||
|
|
||||||
// Update the vocabulary entry
|
// Update the vocabulary entry
|
||||||
vocabularyFromDb.setName(vocabulary.getName());
|
vocabularyDb.setName(vocabulary.getName());
|
||||||
vocabularyFromDb.setValue(vocabulary.getValue());
|
vocabularyDb.setValue(vocabulary.getValue());
|
||||||
vocabularyFromDb.setOrder(vocabulary.getOrder());
|
vocabularyDb.setOrder(vocabulary.getOrder());
|
||||||
|
|
||||||
return vocabularyFromDb;
|
return vocabularyDb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.sismics.docs.core.dao.jpa.criteria;
|
package com.sismics.docs.core.dao.jpa.criteria;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Audit log criteria.
|
* Audit log criteria.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.sismics.docs.core.dao.jpa.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.sismics.docs.core.dao.jpa.criteria;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route model criteria.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class RouteModelCriteria {
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.sismics.docs.core.dao.jpa.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,16 @@ public class UserCriteria {
|
|||||||
*/
|
*/
|
||||||
private String groupId;
|
private String groupId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User ID.
|
||||||
|
*/
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Username.
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
public String getSearch() {
|
public String getSearch() {
|
||||||
return search;
|
return search;
|
||||||
}
|
}
|
||||||
@@ -33,4 +43,22 @@ public class UserCriteria {
|
|||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
return this;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.sismics.docs.core.dao.jpa.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route DTO.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class RouteDto {
|
||||||
|
/**
|
||||||
|
* Route ID.
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name.
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creation date.
|
||||||
|
*/
|
||||||
|
private Long createTimestamp;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteDto setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteDto setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getCreateTimestamp() {
|
||||||
|
return createTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteDto setCreateTimestamp(Long createTimestamp) {
|
||||||
|
this.createTimestamp = createTimestamp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.sismics.docs.core.dao.jpa.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route model DTO.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class RouteModelDto {
|
||||||
|
/**
|
||||||
|
* Route model ID.
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name.
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creation date.
|
||||||
|
*/
|
||||||
|
private Long createTimestamp;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteModelDto setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteModelDto setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getCreateTimestamp() {
|
||||||
|
return createTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteModelDto setCreateTimestamp(Long createTimestamp) {
|
||||||
|
this.createTimestamp = createTimestamp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
package com.sismics.docs.core.dao.jpa.dto;
|
||||||
|
|
||||||
|
import com.sismics.docs.core.constant.RouteStepType;
|
||||||
|
import com.sismics.util.JsonUtil;
|
||||||
|
|
||||||
|
import javax.json.Json;
|
||||||
|
import javax.json.JsonObjectBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route step DTO.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class RouteStepDto {
|
||||||
|
/**
|
||||||
|
* Route step ID.
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name.
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type.
|
||||||
|
*/
|
||||||
|
private RouteStepType type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transition.
|
||||||
|
*/
|
||||||
|
private String transition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment.
|
||||||
|
*/
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target ID (user or group).
|
||||||
|
*/
|
||||||
|
private String targetId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target name.
|
||||||
|
*/
|
||||||
|
private String targetName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target type.
|
||||||
|
*/
|
||||||
|
private String targetType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End date.
|
||||||
|
*/
|
||||||
|
private Long endDateTimestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator's username.
|
||||||
|
*/
|
||||||
|
private String validatorUserName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route ID.
|
||||||
|
*/
|
||||||
|
private String routeId;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepDto setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepDto setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepDto setType(RouteStepType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTransition() {
|
||||||
|
return transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepDto setTransition(String transition) {
|
||||||
|
this.transition = transition;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getComment() {
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepDto setComment(String comment) {
|
||||||
|
this.comment = comment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetId() {
|
||||||
|
return targetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepDto setTargetId(String targetId) {
|
||||||
|
this.targetId = targetId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetName() {
|
||||||
|
return targetName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepDto setTargetName(String targetName) {
|
||||||
|
this.targetName = targetName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetType() {
|
||||||
|
return targetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepDto setTargetType(String targetType) {
|
||||||
|
this.targetType = targetType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getEndDateTimestamp() {
|
||||||
|
return endDateTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepDto setEndDateTimestamp(Long endDateTimestamp) {
|
||||||
|
this.endDateTimestamp = endDateTimestamp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValidatorUserName() {
|
||||||
|
return validatorUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepDto setValidatorUserName(String validatorUserName) {
|
||||||
|
this.validatorUserName = validatorUserName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRouteId() {
|
||||||
|
return routeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepDto setRouteId(String routeId) {
|
||||||
|
this.routeId = routeId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform in JSON.
|
||||||
|
*
|
||||||
|
* @return JSON object builder
|
||||||
|
*/
|
||||||
|
public JsonObjectBuilder toJson() {
|
||||||
|
return Json.createObjectBuilder()
|
||||||
|
.add("name", getName())
|
||||||
|
.add("type", getType().name())
|
||||||
|
.add("comment", JsonUtil.nullable(getComment()))
|
||||||
|
.add("end_date", JsonUtil.nullable(getEndDateTimestamp()))
|
||||||
|
.add("validator_username", JsonUtil.nullable(getValidatorUserName()))
|
||||||
|
.add("target", Json.createObjectBuilder()
|
||||||
|
.add("id", getTargetId())
|
||||||
|
.add("name", JsonUtil.nullable(getTargetName()))
|
||||||
|
.add("type", getTargetType()))
|
||||||
|
.add("transition", JsonUtil.nullable(getTransition()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.sismics.docs.core.dao.jpa.dto;
|
package com.sismics.docs.core.dao.jpa.dto;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User DTO.
|
* User DTO.
|
||||||
*
|
*
|
||||||
@@ -26,6 +28,11 @@ public class UserDto {
|
|||||||
*/
|
*/
|
||||||
private Long createTimestamp;
|
private Long createTimestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable date of this user.
|
||||||
|
*/
|
||||||
|
private Long disableTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Storage quota.
|
* Storage quota.
|
||||||
*/
|
*/
|
||||||
@@ -36,6 +43,11 @@ public class UserDto {
|
|||||||
*/
|
*/
|
||||||
private Long storageCurrent;
|
private Long storageCurrent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TOTP key.
|
||||||
|
*/
|
||||||
|
private String totpKey;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -68,6 +80,15 @@ public class UserDto {
|
|||||||
this.createTimestamp = createTimestamp;
|
this.createTimestamp = createTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getDisableTimestamp() {
|
||||||
|
return disableTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDto setDisableTimestamp(Long disableTimestamp) {
|
||||||
|
this.disableTimestamp = disableTimestamp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Long getStorageQuota() {
|
public Long getStorageQuota() {
|
||||||
return storageQuota;
|
return storageQuota;
|
||||||
}
|
}
|
||||||
@@ -83,4 +104,21 @@ public class UserDto {
|
|||||||
public void setStorageCurrent(Long storageCurrent) {
|
public void setStorageCurrent(Long storageCurrent) {
|
||||||
this.storageCurrent = storageCurrent;
|
this.storageCurrent = storageCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getTotpKey() {
|
||||||
|
return totpKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotpKey(String totpKey) {
|
||||||
|
this.totpKey = totpKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("id", id)
|
||||||
|
.add("username", username)
|
||||||
|
.add("email", email)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class LuceneDao {
|
|||||||
/**
|
/**
|
||||||
* Destroy and rebuild index.
|
* Destroy and rebuild index.
|
||||||
*
|
*
|
||||||
* @param fileList
|
* @param fileList List of files
|
||||||
*/
|
*/
|
||||||
public void rebuildIndex(final List<Document> documentList, final List<File> fileList) {
|
public void rebuildIndex(final List<Document> documentList, final List<File> fileList) {
|
||||||
LuceneUtil.handle(new LuceneRunnable() {
|
LuceneUtil.handle(new LuceneRunnable() {
|
||||||
@@ -103,21 +103,6 @@ public class LuceneDao {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update file index.
|
|
||||||
*
|
|
||||||
* @param file Updated file
|
|
||||||
*/
|
|
||||||
public void updateFile(final File file) {
|
|
||||||
LuceneUtil.handle(new LuceneRunnable() {
|
|
||||||
@Override
|
|
||||||
public void run(IndexWriter indexWriter) throws Exception {
|
|
||||||
org.apache.lucene.document.Document luceneDocument = getDocumentFromFile(file);
|
|
||||||
indexWriter.updateDocument(new Term("id", file.getId()), luceneDocument);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete document from the index.
|
* Delete document from the index.
|
||||||
*
|
*
|
||||||
@@ -166,7 +151,7 @@ public class LuceneDao {
|
|||||||
|
|
||||||
// Search
|
// Search
|
||||||
DirectoryReader directoryReader = AppContext.getInstance().getIndexingService().getDirectoryReader();
|
DirectoryReader directoryReader = AppContext.getInstance().getIndexingService().getDirectoryReader();
|
||||||
Set<String> documentIdList = new HashSet<String>();
|
Set<String> documentIdList = new HashSet<>();
|
||||||
if (directoryReader == null) {
|
if (directoryReader == null) {
|
||||||
// The directory reader is not yet initialized (probably because there is nothing indexed)
|
// The directory reader is not yet initialized (probably because there is nothing indexed)
|
||||||
return documentIdList;
|
return documentIdList;
|
||||||
@@ -176,8 +161,8 @@ public class LuceneDao {
|
|||||||
ScoreDoc[] docs = topDocs.scoreDocs;
|
ScoreDoc[] docs = topDocs.scoreDocs;
|
||||||
|
|
||||||
// Extract document IDs
|
// Extract document IDs
|
||||||
for (int i = 0; i < docs.length; i++) {
|
for (ScoreDoc doc : docs) {
|
||||||
org.apache.lucene.document.Document document = searcher.doc(docs[i].doc);
|
org.apache.lucene.document.Document document = searcher.doc(doc.doc);
|
||||||
String type = document.get("doctype");
|
String type = document.get("doctype");
|
||||||
String documentId = null;
|
String documentId = null;
|
||||||
if (type.equals("document")) {
|
if (type.equals("document")) {
|
||||||
@@ -194,7 +179,7 @@ public class LuceneDao {
|
|||||||
/**
|
/**
|
||||||
* Build Lucene document from database document.
|
* Build Lucene document from database document.
|
||||||
*
|
*
|
||||||
* @param documentDto Document
|
* @param document Document
|
||||||
* @return Document
|
* @return Document
|
||||||
*/
|
*/
|
||||||
private org.apache.lucene.document.Document getDocumentFromDocument(Document document) {
|
private org.apache.lucene.document.Document getDocumentFromDocument(Document document) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.sismics.docs.core.event;
|
package com.sismics.docs.core.event;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
@@ -22,16 +23,16 @@ public class FileCreatedAsyncEvent extends UserEvent {
|
|||||||
private String language;
|
private String language;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unencrypted input stream containing the file.
|
* Unencrypted original file.
|
||||||
*/
|
*/
|
||||||
private InputStream inputStream;
|
private Path unencryptedFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unencrypted input stream containing a PDF representation
|
* Unencrypted file containing PDF representation
|
||||||
* of the file. May be null if the PDF conversion is not
|
* of the original file. May be null if the PDF conversion is not
|
||||||
* necessary or not possible.
|
* necessary or not possible.
|
||||||
*/
|
*/
|
||||||
private InputStream pdfInputStream;
|
private Path unencryptedPdfFile;
|
||||||
|
|
||||||
public File getFile() {
|
public File getFile() {
|
||||||
return file;
|
return file;
|
||||||
@@ -49,20 +50,22 @@ public class FileCreatedAsyncEvent extends UserEvent {
|
|||||||
this.language = language;
|
this.language = language;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
public Path getUnencryptedFile() {
|
||||||
return inputStream;
|
return unencryptedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInputStream(InputStream inputStream) {
|
public FileCreatedAsyncEvent setUnencryptedFile(Path unencryptedFile) {
|
||||||
this.inputStream = inputStream;
|
this.unencryptedFile = unencryptedFile;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getPdfInputStream() {
|
public Path getUnencryptedPdfFile() {
|
||||||
return pdfInputStream;
|
return unencryptedPdfFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPdfInputStream(InputStream pdfInputStream) {
|
public FileCreatedAsyncEvent setUnencryptedPdfFile(Path unencryptedPdfFile) {
|
||||||
this.pdfInputStream = pdfInputStream;
|
this.unencryptedPdfFile = unencryptedPdfFile;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.sismics.docs.core.event;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
|
import com.sismics.docs.core.model.jpa.PasswordRecovery;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event fired on user's password lost event.
|
||||||
|
*
|
||||||
|
* @author jtremeaux
|
||||||
|
*/
|
||||||
|
public class PasswordLostEvent {
|
||||||
|
/**
|
||||||
|
* User.
|
||||||
|
*/
|
||||||
|
private UserDto user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password recovery request.
|
||||||
|
*/
|
||||||
|
private PasswordRecovery passwordRecovery;
|
||||||
|
|
||||||
|
public UserDto getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(UserDto user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PasswordRecovery getPasswordRecovery() {
|
||||||
|
return passwordRecovery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPasswordRecovery(PasswordRecovery passwordRecovery) {
|
||||||
|
this.passwordRecovery = passwordRecovery;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("user", user)
|
||||||
|
.add("passwordRecovery", "**hidden**")
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.sismics.docs.core.event;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
|
import com.sismics.docs.core.model.jpa.Document;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event fired on route step validation event.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class RouteStepValidateEvent {
|
||||||
|
/**
|
||||||
|
* User.
|
||||||
|
*/
|
||||||
|
private UserDto user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document linked to the route.
|
||||||
|
*/
|
||||||
|
private Document document;
|
||||||
|
|
||||||
|
public UserDto getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(UserDto user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Document getDocument() {
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepValidateEvent setDocument(Document document) {
|
||||||
|
this.document = document;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("user", user)
|
||||||
|
.add("document", document)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.sismics.docs.core.event;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup temporary files event.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class TemporaryFileCleanupAsyncEvent {
|
||||||
|
/**
|
||||||
|
* Temporary files.
|
||||||
|
*/
|
||||||
|
private List<Path> fileList;
|
||||||
|
|
||||||
|
public TemporaryFileCleanupAsyncEvent(List<Path> fileList) {
|
||||||
|
this.fileList = fileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Path> getFileList() {
|
||||||
|
return fileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("files", fileList)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
package com.sismics.docs.core.listener.async;
|
package com.sismics.docs.core.listener.async;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.sismics.docs.core.dao.jpa.ContributorDao;
|
import com.sismics.docs.core.dao.jpa.ContributorDao;
|
||||||
import com.sismics.docs.core.dao.lucene.LuceneDao;
|
import com.sismics.docs.core.dao.lucene.LuceneDao;
|
||||||
import com.sismics.docs.core.event.DocumentCreatedAsyncEvent;
|
import com.sismics.docs.core.event.DocumentCreatedAsyncEvent;
|
||||||
import com.sismics.docs.core.model.jpa.Contributor;
|
import com.sismics.docs.core.model.jpa.Contributor;
|
||||||
import com.sismics.docs.core.util.TransactionUtil;
|
import com.sismics.docs.core.util.TransactionUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener on document created.
|
* Listener on document created.
|
||||||
@@ -25,10 +24,9 @@ public class DocumentCreatedAsyncListener {
|
|||||||
* Document created.
|
* Document created.
|
||||||
*
|
*
|
||||||
* @param event Document created event
|
* @param event Document created event
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void on(final DocumentCreatedAsyncEvent event) throws Exception {
|
public void on(final DocumentCreatedAsyncEvent event) {
|
||||||
if (log.isInfoEnabled()) {
|
if (log.isInfoEnabled()) {
|
||||||
log.info("Document created event: " + event.toString());
|
log.info("Document created event: " + event.toString());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
package com.sismics.docs.core.listener.async;
|
package com.sismics.docs.core.listener.async;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.sismics.docs.core.dao.jpa.ContributorDao;
|
import com.sismics.docs.core.dao.jpa.ContributorDao;
|
||||||
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||||
@@ -12,6 +7,10 @@ import com.sismics.docs.core.dao.lucene.LuceneDao;
|
|||||||
import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent;
|
import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent;
|
||||||
import com.sismics.docs.core.model.jpa.Contributor;
|
import com.sismics.docs.core.model.jpa.Contributor;
|
||||||
import com.sismics.docs.core.util.TransactionUtil;
|
import com.sismics.docs.core.util.TransactionUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener on document updated.
|
* Listener on document updated.
|
||||||
@@ -31,7 +30,7 @@ public class DocumentUpdatedAsyncListener {
|
|||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void on(final DocumentUpdatedAsyncEvent event) throws Exception {
|
public void on(final DocumentUpdatedAsyncEvent event) {
|
||||||
if (log.isInfoEnabled()) {
|
if (log.isInfoEnabled()) {
|
||||||
log.info("Document updated event: " + event.toString());
|
log.info("Document updated event: " + event.toString());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
package com.sismics.docs.core.listener.async;
|
package com.sismics.docs.core.listener.async;
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.sismics.docs.core.dao.jpa.FileDao;
|
import com.sismics.docs.core.dao.jpa.FileDao;
|
||||||
import com.sismics.docs.core.dao.lucene.LuceneDao;
|
import com.sismics.docs.core.dao.lucene.LuceneDao;
|
||||||
@@ -12,6 +7,10 @@ import com.sismics.docs.core.event.FileCreatedAsyncEvent;
|
|||||||
import com.sismics.docs.core.model.jpa.File;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
import com.sismics.docs.core.util.FileUtil;
|
import com.sismics.docs.core.util.FileUtil;
|
||||||
import com.sismics.docs.core.util.TransactionUtil;
|
import com.sismics.docs.core.util.TransactionUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener on file created.
|
* Listener on file created.
|
||||||
@@ -28,7 +27,7 @@ public class FileCreatedAsyncListener {
|
|||||||
* File created.
|
* File created.
|
||||||
*
|
*
|
||||||
* @param fileCreatedAsyncEvent File created event
|
* @param fileCreatedAsyncEvent File created event
|
||||||
* @throws Exception
|
* @throws Exception e
|
||||||
*/
|
*/
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void on(final FileCreatedAsyncEvent fileCreatedAsyncEvent) throws Exception {
|
public void on(final FileCreatedAsyncEvent fileCreatedAsyncEvent) throws Exception {
|
||||||
@@ -42,11 +41,7 @@ public class FileCreatedAsyncListener {
|
|||||||
// Extract text content from the file
|
// Extract text content from the file
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
final String content = FileUtil.extractContent(fileCreatedAsyncEvent.getLanguage(), file,
|
final String content = FileUtil.extractContent(fileCreatedAsyncEvent.getLanguage(), file,
|
||||||
fileCreatedAsyncEvent.getInputStream(), fileCreatedAsyncEvent.getPdfInputStream());
|
fileCreatedAsyncEvent.getUnencryptedFile(), fileCreatedAsyncEvent.getUnencryptedPdfFile());
|
||||||
fileCreatedAsyncEvent.getInputStream().close();
|
|
||||||
if (fileCreatedAsyncEvent.getPdfInputStream() != null) {
|
|
||||||
fileCreatedAsyncEvent.getPdfInputStream().close();
|
|
||||||
}
|
|
||||||
log.info(MessageFormat.format("File content extracted in {0}ms", System.currentTimeMillis() - startTime));
|
log.info(MessageFormat.format("File content extracted in {0}ms", System.currentTimeMillis() - startTime));
|
||||||
|
|
||||||
// Store the text content in the database
|
// Store the text content in the database
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.sismics.docs.core.listener.async;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
import com.sismics.docs.core.constant.Constants;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
|
import com.sismics.docs.core.event.PasswordLostEvent;
|
||||||
|
import com.sismics.docs.core.model.jpa.PasswordRecovery;
|
||||||
|
import com.sismics.docs.core.util.TransactionUtil;
|
||||||
|
import com.sismics.util.EmailUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for password recovery requests.
|
||||||
|
*
|
||||||
|
* @author jtremeaux
|
||||||
|
*/
|
||||||
|
public class PasswordLostAsyncListener {
|
||||||
|
/**
|
||||||
|
* Logger.
|
||||||
|
*/
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(PasswordLostAsyncListener.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle events.
|
||||||
|
*
|
||||||
|
* @param passwordLostEvent Event
|
||||||
|
*/
|
||||||
|
@Subscribe
|
||||||
|
public void onPasswordLost(final PasswordLostEvent passwordLostEvent) {
|
||||||
|
if (log.isInfoEnabled()) {
|
||||||
|
log.info("Password lost event: " + passwordLostEvent.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
TransactionUtil.handle(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final UserDto user = passwordLostEvent.getUser();
|
||||||
|
final PasswordRecovery passwordRecovery = passwordLostEvent.getPasswordRecovery();
|
||||||
|
|
||||||
|
// Send the password recovery email
|
||||||
|
Map<String, Object> paramRootMap = new HashMap<>();
|
||||||
|
paramRootMap.put("user_name", user.getUsername());
|
||||||
|
paramRootMap.put("password_recovery_key", passwordRecovery.getId());
|
||||||
|
|
||||||
|
EmailUtil.sendEmail(Constants.EMAIL_TEMPLATE_PASSWORD_RECOVERY, user, paramRootMap);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.sismics.docs.core.listener.async;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
import com.sismics.docs.core.constant.Constants;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
|
import com.sismics.docs.core.event.RouteStepValidateEvent;
|
||||||
|
import com.sismics.docs.core.util.TransactionUtil;
|
||||||
|
import com.sismics.util.EmailUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for route step validate.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class RouteStepValidateAsyncListener {
|
||||||
|
/**
|
||||||
|
* Logger.
|
||||||
|
*/
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(RouteStepValidateAsyncListener.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle events.
|
||||||
|
*
|
||||||
|
* @param routeStepValidateEvent Event
|
||||||
|
*/
|
||||||
|
@Subscribe
|
||||||
|
public void onRouteStepValidate(final RouteStepValidateEvent routeStepValidateEvent) {
|
||||||
|
if (log.isInfoEnabled()) {
|
||||||
|
log.info("Route step validate event: " + routeStepValidateEvent.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
TransactionUtil.handle(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final UserDto user = routeStepValidateEvent.getUser();
|
||||||
|
|
||||||
|
// Send the password recovery email
|
||||||
|
Map<String, Object> paramRootMap = new HashMap<>();
|
||||||
|
paramRootMap.put("user_name", user.getUsername());
|
||||||
|
paramRootMap.put("document_id", routeStepValidateEvent.getDocument().getId());
|
||||||
|
paramRootMap.put("document_title", routeStepValidateEvent.getDocument().getTitle());
|
||||||
|
|
||||||
|
EmailUtil.sendEmail(Constants.EMAIL_TEMPLATE_ROUTE_STEP_VALIDATE, user, paramRootMap);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.sismics.docs.core.listener.async;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
import com.sismics.docs.core.event.TemporaryFileCleanupAsyncEvent;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener to cleanup temporary files created during a request.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class TemporaryFileCleanupAsyncListener {
|
||||||
|
/**
|
||||||
|
* Logger.
|
||||||
|
*/
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(FileCreatedAsyncListener.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup temporary files.
|
||||||
|
*
|
||||||
|
* @param event Temporary file cleanup event
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Subscribe
|
||||||
|
public void on(final TemporaryFileCleanupAsyncEvent event) throws Exception {
|
||||||
|
if (log.isInfoEnabled()) {
|
||||||
|
log.info("Cleanup temporary files event: " + event.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Path file : event.getFileList()) {
|
||||||
|
Files.delete(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,20 @@
|
|||||||
package com.sismics.docs.core.model.context;
|
package com.sismics.docs.core.model.context;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.AsyncEventBus;
|
||||||
|
import com.google.common.eventbus.EventBus;
|
||||||
|
import com.sismics.docs.core.constant.ConfigType;
|
||||||
|
import com.sismics.docs.core.constant.Constants;
|
||||||
|
import com.sismics.docs.core.dao.jpa.ConfigDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||||
|
import com.sismics.docs.core.listener.async.*;
|
||||||
|
import com.sismics.docs.core.listener.sync.DeadEventListener;
|
||||||
|
import com.sismics.docs.core.model.jpa.Config;
|
||||||
|
import com.sismics.docs.core.model.jpa.User;
|
||||||
|
import com.sismics.docs.core.service.InboxService;
|
||||||
|
import com.sismics.docs.core.service.IndexingService;
|
||||||
|
import com.sismics.docs.core.util.PdfUtil;
|
||||||
|
import com.sismics.util.EnvironmentUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
@@ -7,21 +22,6 @@ import java.util.concurrent.LinkedBlockingQueue;
|
|||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import com.google.common.eventbus.AsyncEventBus;
|
|
||||||
import com.google.common.eventbus.EventBus;
|
|
||||||
import com.sismics.docs.core.constant.ConfigType;
|
|
||||||
import com.sismics.docs.core.dao.jpa.ConfigDao;
|
|
||||||
import com.sismics.docs.core.listener.async.DocumentCreatedAsyncListener;
|
|
||||||
import com.sismics.docs.core.listener.async.DocumentDeletedAsyncListener;
|
|
||||||
import com.sismics.docs.core.listener.async.DocumentUpdatedAsyncListener;
|
|
||||||
import com.sismics.docs.core.listener.async.FileCreatedAsyncListener;
|
|
||||||
import com.sismics.docs.core.listener.async.FileDeletedAsyncListener;
|
|
||||||
import com.sismics.docs.core.listener.async.RebuildIndexAsyncListener;
|
|
||||||
import com.sismics.docs.core.listener.sync.DeadEventListener;
|
|
||||||
import com.sismics.docs.core.model.jpa.Config;
|
|
||||||
import com.sismics.docs.core.service.IndexingService;
|
|
||||||
import com.sismics.util.EnvironmentUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global application context.
|
* Global application context.
|
||||||
*
|
*
|
||||||
@@ -43,11 +43,21 @@ public class AppContext {
|
|||||||
*/
|
*/
|
||||||
private EventBus asyncEventBus;
|
private EventBus asyncEventBus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronous bus for email sending.
|
||||||
|
*/
|
||||||
|
private EventBus mailEventBus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indexing service.
|
* Indexing service.
|
||||||
*/
|
*/
|
||||||
private IndexingService indexingService;
|
private IndexingService indexingService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inbox scanning service.
|
||||||
|
*/
|
||||||
|
private InboxService inboxService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronous executors.
|
* Asynchronous executors.
|
||||||
*/
|
*/
|
||||||
@@ -59,10 +69,29 @@ public class AppContext {
|
|||||||
private AppContext() {
|
private AppContext() {
|
||||||
resetEventBus();
|
resetEventBus();
|
||||||
|
|
||||||
|
// Start indexing service
|
||||||
ConfigDao configDao = new ConfigDao();
|
ConfigDao configDao = new ConfigDao();
|
||||||
Config luceneStorageConfig = configDao.getById(ConfigType.LUCENE_DIRECTORY_STORAGE);
|
Config luceneStorageConfig = configDao.getById(ConfigType.LUCENE_DIRECTORY_STORAGE);
|
||||||
indexingService = new IndexingService(luceneStorageConfig != null ? luceneStorageConfig.getValue() : null);
|
indexingService = new IndexingService(luceneStorageConfig != null ? luceneStorageConfig.getValue() : null);
|
||||||
indexingService.startAsync();
|
indexingService.startAsync();
|
||||||
|
|
||||||
|
// Start inbox service
|
||||||
|
inboxService = new InboxService();
|
||||||
|
inboxService.startAsync();
|
||||||
|
|
||||||
|
// Register fonts
|
||||||
|
PdfUtil.registerFonts();
|
||||||
|
|
||||||
|
// Change the admin password if needed
|
||||||
|
String envAdminPassword = System.getenv(Constants.ADMIN_PASSWORD_INIT_ENV);
|
||||||
|
if (envAdminPassword != null) {
|
||||||
|
UserDao userDao = new UserDao();
|
||||||
|
User adminUser = userDao.getById("admin");
|
||||||
|
if (Constants.DEFAULT_ADMIN_PASSWORD.equals(adminUser.getPassword())) {
|
||||||
|
adminUser.setPassword(envAdminPassword);
|
||||||
|
userDao.updateHashedPassword(adminUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,6 +110,11 @@ public class AppContext {
|
|||||||
asyncEventBus.register(new DocumentUpdatedAsyncListener());
|
asyncEventBus.register(new DocumentUpdatedAsyncListener());
|
||||||
asyncEventBus.register(new DocumentDeletedAsyncListener());
|
asyncEventBus.register(new DocumentDeletedAsyncListener());
|
||||||
asyncEventBus.register(new RebuildIndexAsyncListener());
|
asyncEventBus.register(new RebuildIndexAsyncListener());
|
||||||
|
asyncEventBus.register(new TemporaryFileCleanupAsyncListener());
|
||||||
|
|
||||||
|
mailEventBus = newAsyncEventBus();
|
||||||
|
mailEventBus.register(new PasswordLostAsyncListener());
|
||||||
|
mailEventBus.register(new RouteStepValidateAsyncListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,6 +161,7 @@ public class AppContext {
|
|||||||
if (EnvironmentUtil.isUnitTest()) {
|
if (EnvironmentUtil.isUnitTest()) {
|
||||||
return new EventBus();
|
return new EventBus();
|
||||||
} else {
|
} else {
|
||||||
|
// /!\ Don't add more threads because a cleanup event is fired at the end of each request
|
||||||
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1,
|
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1,
|
||||||
0L, TimeUnit.MILLISECONDS,
|
0L, TimeUnit.MILLISECONDS,
|
||||||
new LinkedBlockingQueue<Runnable>());
|
new LinkedBlockingQueue<Runnable>());
|
||||||
@@ -135,30 +170,23 @@ public class AppContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter of eventBus.
|
|
||||||
*
|
|
||||||
* @return eventBus
|
|
||||||
*/
|
|
||||||
public EventBus getEventBus() {
|
public EventBus getEventBus() {
|
||||||
return eventBus;
|
return eventBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter of asyncEventBus.
|
|
||||||
*
|
|
||||||
* @return asyncEventBus
|
|
||||||
*/
|
|
||||||
public EventBus getAsyncEventBus() {
|
public EventBus getAsyncEventBus() {
|
||||||
return asyncEventBus;
|
return asyncEventBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public EventBus getMailEventBus() {
|
||||||
* Getter of indexingService.
|
return mailEventBus;
|
||||||
*
|
}
|
||||||
* @return indexingService
|
|
||||||
*/
|
|
||||||
public IndexingService getIndexingService() {
|
public IndexingService getIndexingService() {
|
||||||
return indexingService;
|
return indexingService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InboxService getInboxService() {
|
||||||
|
return inboxService;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
package com.sismics.docs.core.model.jpa;
|
package com.sismics.docs.core.model.jpa;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import javax.persistence.Column;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.EnumType;
|
|
||||||
import javax.persistence.Enumerated;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.sismics.docs.core.constant.AclType;
|
||||||
import com.sismics.docs.core.constant.PermType;
|
import com.sismics.docs.core.constant.PermType;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ACL entity.
|
* ACL entity.
|
||||||
*
|
*
|
||||||
@@ -34,6 +29,13 @@ public class Acl implements Loggable {
|
|||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
private PermType perm;
|
private PermType perm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACL type.
|
||||||
|
*/
|
||||||
|
@Column(name = "ACL_TYPE_C", length = 30, nullable = false)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private AclType type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ACL source ID.
|
* ACL source ID.
|
||||||
*/
|
*/
|
||||||
@@ -84,6 +86,15 @@ public class Acl implements Loggable {
|
|||||||
this.targetId = targetId;
|
this.targetId = targetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AclType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Acl setType(AclType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date getDeleteDate() {
|
public Date getDeleteDate() {
|
||||||
return deleteDate;
|
return deleteDate;
|
||||||
@@ -100,6 +111,7 @@ public class Acl implements Loggable {
|
|||||||
.add("perm", perm)
|
.add("perm", perm)
|
||||||
.add("sourceId", sourceId)
|
.add("sourceId", sourceId)
|
||||||
.add("targetId", targetId)
|
.add("targetId", targetId)
|
||||||
|
.add("type", type)
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
package com.sismics.docs.core.model.jpa;
|
package com.sismics.docs.core.model.jpa;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import javax.persistence.Column;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.Lob;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
import javax.persistence.Transient;
|
|
||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.sismics.util.mime.MimeTypeUtil;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File entity.
|
* File entity.
|
||||||
@@ -38,6 +34,12 @@ public class File implements Loggable {
|
|||||||
@Column(name = "FIL_IDUSER_C", length = 36, nullable = false)
|
@Column(name = "FIL_IDUSER_C", length = 36, nullable = false)
|
||||||
private String userId;
|
private String userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name.
|
||||||
|
*/
|
||||||
|
@Column(name = "FIL_NAME_C", length = 200)
|
||||||
|
private String name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MIME type.
|
* MIME type.
|
||||||
*/
|
*/
|
||||||
@@ -92,6 +94,15 @@ public class File implements Loggable {
|
|||||||
this.documentId = documentId;
|
this.documentId = documentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public String getMimeType() {
|
public String getMimeType() {
|
||||||
return mimeType;
|
return mimeType;
|
||||||
}
|
}
|
||||||
@@ -153,11 +164,26 @@ public class File implements Loggable {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return MoreObjects.toStringHelper(this)
|
return MoreObjects.toStringHelper(this)
|
||||||
.add("id", id)
|
.add("id", id)
|
||||||
|
.add("name", name)
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toMessage() {
|
public String toMessage() {
|
||||||
return documentId;
|
// Attached document ID and name concatenated
|
||||||
|
return (documentId == null ? Strings.repeat(" ", 36) : documentId) + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the full file name.
|
||||||
|
*
|
||||||
|
* @param def Default name if the file doesn't have one.
|
||||||
|
* @return File name
|
||||||
|
*/
|
||||||
|
public String getFullName(String def) {
|
||||||
|
if (Strings.isNullOrEmpty(name)) {
|
||||||
|
return def + "." + MimeTypeUtil.getFileExtension(mimeType);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ public interface Loggable {
|
|||||||
*
|
*
|
||||||
* @return Entity message
|
* @return Entity message
|
||||||
*/
|
*/
|
||||||
public String toMessage();
|
String toMessage();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loggable are soft deletable.
|
* Loggable are soft deletable.
|
||||||
*
|
*
|
||||||
* @return deleteDate
|
* @return deleteDate
|
||||||
*/
|
*/
|
||||||
public Date getDeleteDate();
|
Date getDeleteDate();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package com.sismics.docs.core.model.jpa;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password recovery entity.
|
||||||
|
*
|
||||||
|
* @author jtremeaux
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "T_PASSWORD_RECOVERY")
|
||||||
|
public class PasswordRecovery {
|
||||||
|
/**
|
||||||
|
* Identifier.
|
||||||
|
*/
|
||||||
|
@Id
|
||||||
|
@Column(name = "PWR_ID_C", length = 36)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Username.
|
||||||
|
*/
|
||||||
|
@Column(name = "PWR_USERNAME_C", nullable = false, length = 50)
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creation date.
|
||||||
|
*/
|
||||||
|
@Column(name = "PWR_CREATEDATE_D", nullable = false)
|
||||||
|
private Date createDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete date.
|
||||||
|
*/
|
||||||
|
@Column(name = "PWR_DELETEDATE_D")
|
||||||
|
private Date deleteDate;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreateDate() {
|
||||||
|
return createDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateDate(Date createDate) {
|
||||||
|
this.createDate = createDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDeleteDate() {
|
||||||
|
return deleteDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleteDate(Date deleteDate) {
|
||||||
|
this.deleteDate = deleteDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("id", id)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package com.sismics.docs.core.model.jpa;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "T_ROUTE")
|
||||||
|
public class Route implements Loggable {
|
||||||
|
/**
|
||||||
|
* Route ID.
|
||||||
|
*/
|
||||||
|
@Id
|
||||||
|
@Column(name = "RTE_ID_C", length = 36)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document ID.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTE_IDDOCUMENT_C", nullable = false, length = 36)
|
||||||
|
private String documentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTE_NAME_C", nullable = false, length = 50)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creation date.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTE_CREATEDATE_D", nullable = false)
|
||||||
|
private Date createDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletion date.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTE_DELETEDATE_D")
|
||||||
|
private Date deleteDate;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Route setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDocumentId() {
|
||||||
|
return documentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Route setDocumentId(String documentId) {
|
||||||
|
this.documentId = documentId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Route setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreateDate() {
|
||||||
|
return createDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Route setCreateDate(Date createDate) {
|
||||||
|
this.createDate = createDate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDeleteDate() {
|
||||||
|
return deleteDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Route setDeleteDate(Date deleteDate) {
|
||||||
|
this.deleteDate = deleteDate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toMessage() {
|
||||||
|
return documentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("id", id)
|
||||||
|
.add("name", name)
|
||||||
|
.add("documentId", documentId)
|
||||||
|
.add("createDate", createDate)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package com.sismics.docs.core.model.jpa;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route model.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "T_ROUTE_MODEL")
|
||||||
|
public class RouteModel implements Loggable {
|
||||||
|
/**
|
||||||
|
* Route model ID.
|
||||||
|
*/
|
||||||
|
@Id
|
||||||
|
@Column(name = "RTM_ID_C", length = 36)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTM_NAME_C", nullable = false, length = 50)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTM_STEPS_C", nullable = false, length = 5000)
|
||||||
|
private String steps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creation date.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTM_CREATEDATE_D", nullable = false)
|
||||||
|
private Date createDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletion date.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTM_DELETEDATE_D")
|
||||||
|
private Date deleteDate;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteModel setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteModel setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSteps() {
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteModel setSteps(String steps) {
|
||||||
|
this.steps = steps;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreateDate() {
|
||||||
|
return createDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteModel setCreateDate(Date createDate) {
|
||||||
|
this.createDate = createDate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDeleteDate() {
|
||||||
|
return deleteDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteModel setDeleteDate(Date deleteDate) {
|
||||||
|
this.deleteDate = deleteDate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("id", id)
|
||||||
|
.add("name", name)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toMessage() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
package com.sismics.docs.core.model.jpa;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.sismics.docs.core.constant.RouteStepTransition;
|
||||||
|
import com.sismics.docs.core.constant.RouteStepType;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route step.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "T_ROUTE_STEP")
|
||||||
|
public class RouteStep {
|
||||||
|
/**
|
||||||
|
* Route step ID.
|
||||||
|
*/
|
||||||
|
@Id
|
||||||
|
@Column(name = "RTP_ID_C", length = 36)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route ID.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTP_IDROUTE_C", nullable = false, length = 36)
|
||||||
|
private String routeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTP_NAME_C", nullable = false, length = 200)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTP_TYPE_C", nullable = false, length = 50)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private RouteStepType type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transition.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTP_TRANSITION_C", length = 50)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private RouteStepTransition transition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTP_COMMENT_C", length = 500)
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target ID (user or group).
|
||||||
|
*/
|
||||||
|
@Column(name = "RTP_IDTARGET_C", nullable = false, length = 36)
|
||||||
|
private String targetId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator user ID.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTP_IDVALIDATORUSER_C", length = 36)
|
||||||
|
private String validatorUserId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTP_ORDER_N", nullable = false)
|
||||||
|
private Integer order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creation date.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTP_CREATEDATE_D", nullable = false)
|
||||||
|
private Date createDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End date.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTP_ENDDATE_D")
|
||||||
|
private Date endDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletion date.
|
||||||
|
*/
|
||||||
|
@Column(name = "RTP_DELETEDATE_D")
|
||||||
|
private Date deleteDate;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStep setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRouteId() {
|
||||||
|
return routeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStep setRouteId(String routeId) {
|
||||||
|
this.routeId = routeId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStep setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStep setType(RouteStepType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepTransition getTransition() {
|
||||||
|
return transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStep setTransition(RouteStepTransition transition) {
|
||||||
|
this.transition = transition;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getComment() {
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStep setComment(String comment) {
|
||||||
|
this.comment = comment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetId() {
|
||||||
|
return targetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStep setTargetId(String targetId) {
|
||||||
|
this.targetId = targetId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStep setOrder(Integer order) {
|
||||||
|
this.order = order;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValidatorUserId() {
|
||||||
|
return validatorUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStep setValidatorUserId(String validatorUserId) {
|
||||||
|
this.validatorUserId = validatorUserId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreateDate() {
|
||||||
|
return createDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStep setCreateDate(Date createDate) {
|
||||||
|
this.createDate = createDate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getEndDate() {
|
||||||
|
return endDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStep setEndDate(Date endDate) {
|
||||||
|
this.endDate = endDate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDeleteDate() {
|
||||||
|
return deleteDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStep setDeleteDate(Date deleteDate) {
|
||||||
|
this.deleteDate = deleteDate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("id", id)
|
||||||
|
.add("routeId", routeId)
|
||||||
|
.add("name", name)
|
||||||
|
.add("type", type)
|
||||||
|
.add("transition", transition)
|
||||||
|
.add("comment", comment)
|
||||||
|
.add("targetId", targetId)
|
||||||
|
.add("order", order)
|
||||||
|
.add("createDate", createDate)
|
||||||
|
.add("endDate", endDate)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
package com.sismics.docs.core.model.jpa;
|
package com.sismics.docs.core.model.jpa;
|
||||||
|
|
||||||
import java.util.Date;
|
import com.google.common.base.MoreObjects;
|
||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
import java.util.Date;
|
||||||
import com.google.common.base.MoreObjects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User entity.
|
* User entity.
|
||||||
@@ -84,6 +83,12 @@ public class User implements Loggable {
|
|||||||
@Column(name = "USE_DELETEDATE_D")
|
@Column(name = "USE_DELETEDATE_D")
|
||||||
private Date deleteDate;
|
private Date deleteDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable date.
|
||||||
|
*/
|
||||||
|
@Column(name = "USE_DISABLEDATE_D")
|
||||||
|
private Date disableDate;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -148,6 +153,15 @@ public class User implements Loggable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Date getDisableDate() {
|
||||||
|
return disableDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User setDisableDate(Date disableDate) {
|
||||||
|
this.disableDate = disableDate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public String getPrivateKey() {
|
public String getPrivateKey() {
|
||||||
return privateKey;
|
return privateKey;
|
||||||
}
|
}
|
||||||
@@ -189,6 +203,7 @@ public class User implements Loggable {
|
|||||||
return MoreObjects.toStringHelper(this)
|
return MoreObjects.toStringHelper(this)
|
||||||
.add("id", id)
|
.add("id", id)
|
||||||
.add("username", username)
|
.add("username", username)
|
||||||
|
.add("email", email)
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,236 @@
|
|||||||
|
package com.sismics.docs.core.service;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.common.util.concurrent.AbstractScheduledService;
|
||||||
|
import com.sismics.docs.core.constant.ConfigType;
|
||||||
|
import com.sismics.docs.core.dao.jpa.TagDao;
|
||||||
|
import com.sismics.docs.core.event.DocumentCreatedAsyncEvent;
|
||||||
|
import com.sismics.docs.core.model.jpa.Document;
|
||||||
|
import com.sismics.docs.core.model.jpa.Tag;
|
||||||
|
import com.sismics.docs.core.util.ConfigUtil;
|
||||||
|
import com.sismics.docs.core.util.DocumentUtil;
|
||||||
|
import com.sismics.docs.core.util.FileUtil;
|
||||||
|
import com.sismics.docs.core.util.TransactionUtil;
|
||||||
|
import com.sismics.util.EmailUtil;
|
||||||
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.mail.*;
|
||||||
|
import javax.mail.search.FlagTerm;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inbox scanning service.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class InboxService extends AbstractScheduledService {
|
||||||
|
/**
|
||||||
|
* Logger.
|
||||||
|
*/
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(InboxService.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last synchronization data.
|
||||||
|
*/
|
||||||
|
private Date lastSyncDate;
|
||||||
|
private int lastSyncMessageCount = 0;
|
||||||
|
private String lastSyncError;
|
||||||
|
|
||||||
|
public InboxService() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startUp() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void shutDown() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void runOneIteration() {
|
||||||
|
syncInbox();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronize the inbox.
|
||||||
|
*/
|
||||||
|
public void syncInbox() {
|
||||||
|
TransactionUtil.handle(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Boolean enabled = ConfigUtil.getConfigBooleanValue(ConfigType.INBOX_ENABLED);
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Folder inbox = null;
|
||||||
|
lastSyncError = null;
|
||||||
|
lastSyncDate = new Date();
|
||||||
|
lastSyncMessageCount = 0;
|
||||||
|
try {
|
||||||
|
inbox = openInbox();
|
||||||
|
int count = inbox.getMessageCount();
|
||||||
|
Message[] messages = inbox.getMessages(1, count);
|
||||||
|
|
||||||
|
for (Message message : messages) {
|
||||||
|
if (!message.getFlags().contains(Flags.Flag.SEEN)) {
|
||||||
|
importMessage(message);
|
||||||
|
lastSyncMessageCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error synching the inbox", e);
|
||||||
|
lastSyncError = e.getMessage();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (inbox != null) {
|
||||||
|
inbox.close(false);
|
||||||
|
inbox.getStore().close();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the inbox configuration.
|
||||||
|
*
|
||||||
|
* @return Number of messages currently in the remote inbox
|
||||||
|
*/
|
||||||
|
public int testInbox() {
|
||||||
|
Boolean enabled = ConfigUtil.getConfigBooleanValue(ConfigType.INBOX_ENABLED);
|
||||||
|
if (!enabled) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Folder inbox = null;
|
||||||
|
try {
|
||||||
|
inbox = openInbox();
|
||||||
|
return inbox.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false)).length;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error testing inbox", e);
|
||||||
|
return -1;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (inbox != null) {
|
||||||
|
inbox.close(false);
|
||||||
|
inbox.getStore().close();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Scheduler scheduler() {
|
||||||
|
return Scheduler.newFixedDelaySchedule(0, 15, TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the remote inbox.
|
||||||
|
*
|
||||||
|
* @return Opened inbox folder
|
||||||
|
* @throws Exception e
|
||||||
|
*/
|
||||||
|
private Folder openInbox() throws Exception {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
String port = ConfigUtil.getConfigStringValue(ConfigType.INBOX_PORT);
|
||||||
|
properties.put("mail.imap.host", ConfigUtil.getConfigStringValue(ConfigType.INBOX_HOSTNAME));
|
||||||
|
properties.put("mail.imap.port", port);
|
||||||
|
boolean isSsl = "993".equals(port);
|
||||||
|
properties.put("mail.imap.ssl.enable", String.valueOf(isSsl));
|
||||||
|
properties.setProperty("mail.imap.socketFactory.class",
|
||||||
|
isSsl ? "javax.net.ssl.SSLSocketFactory" : "javax.net.DefaultSocketFactory");
|
||||||
|
properties.setProperty("mail.imap.socketFactory.fallback", "true");
|
||||||
|
properties.setProperty("mail.imap.socketFactory.port", port);
|
||||||
|
|
||||||
|
Session session = Session.getDefaultInstance(properties);
|
||||||
|
|
||||||
|
Store store = session.getStore("imap");
|
||||||
|
store.connect(ConfigUtil.getConfigStringValue(ConfigType.INBOX_USERNAME),
|
||||||
|
ConfigUtil.getConfigStringValue(ConfigType.INBOX_PASSWORD));
|
||||||
|
|
||||||
|
Folder inbox = store.getFolder("INBOX");
|
||||||
|
inbox.open(Folder.READ_WRITE);
|
||||||
|
return inbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import an email.
|
||||||
|
*
|
||||||
|
* @param message Message
|
||||||
|
* @throws Exception e
|
||||||
|
*/
|
||||||
|
private void importMessage(Message message) throws Exception {
|
||||||
|
// Parse the mail
|
||||||
|
EmailUtil.MailContent mailContent = new EmailUtil.MailContent();
|
||||||
|
mailContent.setSubject(message.getSubject());
|
||||||
|
mailContent.setDate(message.getSentDate());
|
||||||
|
EmailUtil.parseMailContent(message, mailContent);
|
||||||
|
|
||||||
|
// Create the document
|
||||||
|
Document document = new Document();
|
||||||
|
document.setUserId("admin");
|
||||||
|
if (mailContent.getSubject() == null) {
|
||||||
|
document.setTitle("Imported email from EML file");
|
||||||
|
} else {
|
||||||
|
document.setTitle(StringUtils.abbreviate(mailContent.getSubject(), 100));
|
||||||
|
}
|
||||||
|
document.setDescription(StringUtils.abbreviate(mailContent.getMessage(), 4000));
|
||||||
|
document.setSubject(StringUtils.abbreviate(mailContent.getSubject(), 500));
|
||||||
|
document.setFormat("EML");
|
||||||
|
document.setSource("Inbox");
|
||||||
|
document.setLanguage(ConfigUtil.getConfigStringValue(ConfigType.DEFAULT_LANGUAGE));
|
||||||
|
if (mailContent.getDate() == null) {
|
||||||
|
document.setCreateDate(new Date());
|
||||||
|
} else {
|
||||||
|
document.setCreateDate(mailContent.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the document, create the base ACLs
|
||||||
|
document = DocumentUtil.createDocument(document, "admin");
|
||||||
|
|
||||||
|
// Add the tag
|
||||||
|
String tagId = ConfigUtil.getConfigStringValue(ConfigType.INBOX_TAG);
|
||||||
|
if (tagId != null) {
|
||||||
|
TagDao tagDao = new TagDao();
|
||||||
|
Tag tag = tagDao.getById(tagId);
|
||||||
|
if (tag != null) {
|
||||||
|
tagDao.updateTagList(document.getId(), Sets.newHashSet(tagId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raise a document created event
|
||||||
|
DocumentCreatedAsyncEvent documentCreatedAsyncEvent = new DocumentCreatedAsyncEvent();
|
||||||
|
documentCreatedAsyncEvent.setUserId("admin");
|
||||||
|
documentCreatedAsyncEvent.setDocument(document);
|
||||||
|
ThreadLocalContext.get().addAsyncEvent(documentCreatedAsyncEvent);
|
||||||
|
|
||||||
|
// Add files to the document
|
||||||
|
for (EmailUtil.FileContent fileContent : mailContent.getFileContentList()) {
|
||||||
|
FileUtil.createFile(fileContent.getName(), fileContent.getFile(), fileContent.getSize(), "eng", "admin", document.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLastSyncDate() {
|
||||||
|
return lastSyncDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLastSyncMessageCount() {
|
||||||
|
return lastSyncMessageCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastSyncError() {
|
||||||
|
return lastSyncError;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
package com.sismics.docs.core.service;
|
package com.sismics.docs.core.service;
|
||||||
|
|
||||||
import java.io.IOException;
|
import com.google.common.util.concurrent.AbstractScheduledService;
|
||||||
import java.nio.file.Path;
|
import com.sismics.docs.core.constant.Constants;
|
||||||
import java.util.concurrent.TimeUnit;
|
import com.sismics.docs.core.event.RebuildIndexAsyncEvent;
|
||||||
|
import com.sismics.docs.core.model.context.AppContext;
|
||||||
|
import com.sismics.docs.core.util.DirectoryUtil;
|
||||||
|
import com.sismics.docs.core.util.TransactionUtil;
|
||||||
import org.apache.lucene.index.CheckIndex;
|
import org.apache.lucene.index.CheckIndex;
|
||||||
import org.apache.lucene.index.CheckIndex.Status;
|
import org.apache.lucene.index.CheckIndex.Status;
|
||||||
import org.apache.lucene.index.CheckIndex.Status.SegmentInfoStatus;
|
import org.apache.lucene.index.CheckIndex.Status.SegmentInfoStatus;
|
||||||
@@ -16,12 +18,9 @@ import org.apache.lucene.util.Version;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.AbstractScheduledService;
|
import java.io.IOException;
|
||||||
import com.sismics.docs.core.constant.Constants;
|
import java.nio.file.Path;
|
||||||
import com.sismics.docs.core.event.RebuildIndexAsyncEvent;
|
import java.util.concurrent.TimeUnit;
|
||||||
import com.sismics.docs.core.model.context.AppContext;
|
|
||||||
import com.sismics.docs.core.util.DirectoryUtil;
|
|
||||||
import com.sismics.docs.core.util.TransactionUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indexing service.
|
* Indexing service.
|
||||||
@@ -115,7 +114,7 @@ public class IndexingService extends AbstractScheduledService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void runOneIteration() throws Exception {
|
protected void runOneIteration() {
|
||||||
TransactionUtil.handle(new Runnable() {
|
TransactionUtil.handle(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -129,16 +128,6 @@ public class IndexingService extends AbstractScheduledService {
|
|||||||
return Scheduler.newFixedDelaySchedule(0, 1, TimeUnit.HOURS);
|
return Scheduler.newFixedDelaySchedule(0, 1, TimeUnit.HOURS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy and rebuild Lucene index.
|
|
||||||
*
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public void rebuildIndex() throws Exception {
|
|
||||||
RebuildIndexAsyncEvent rebuildIndexAsyncEvent = new RebuildIndexAsyncEvent();
|
|
||||||
AppContext.getInstance().getAsyncEventBus().post(rebuildIndexAsyncEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter of directory.
|
* Getter of directory.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.sismics.docs.core.util;
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
|
||||||
|
|
||||||
import com.sismics.docs.core.constant.AuditLogType;
|
import com.sismics.docs.core.constant.AuditLogType;
|
||||||
import com.sismics.docs.core.dao.jpa.AuditLogDao;
|
import com.sismics.docs.core.dao.jpa.AuditLogDao;
|
||||||
import com.sismics.docs.core.model.jpa.AuditLog;
|
import com.sismics.docs.core.model.jpa.AuditLog;
|
||||||
import com.sismics.docs.core.model.jpa.Loggable;
|
import com.sismics.docs.core.model.jpa.Loggable;
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Audit log utilities.
|
* Audit log utilities.
|
||||||
*
|
*
|
||||||
@@ -17,10 +17,15 @@ public class AuditLogUtil {
|
|||||||
/**
|
/**
|
||||||
* Create an audit log.
|
* Create an audit log.
|
||||||
*
|
*
|
||||||
* @param entity Entity
|
* @param loggable Loggable
|
||||||
* @param type Audit log type
|
* @param type Audit log type
|
||||||
|
* @param userId User ID
|
||||||
*/
|
*/
|
||||||
public static void create(Loggable loggable, AuditLogType type, String userId) {
|
public static void create(Loggable loggable, AuditLogType type, String userId) {
|
||||||
|
if (userId == null) {
|
||||||
|
userId = "admin";
|
||||||
|
}
|
||||||
|
|
||||||
// Get the entity ID
|
// Get the entity ID
|
||||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
String entityId = (String) em.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(loggable);
|
String entityId = (String) em.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(loggable);
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
|
import com.sismics.docs.core.constant.AclType;
|
||||||
|
import com.sismics.docs.core.constant.PermType;
|
||||||
|
import com.sismics.docs.core.dao.jpa.AclDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||||
|
import com.sismics.docs.core.model.jpa.Acl;
|
||||||
|
import com.sismics.docs.core.model.jpa.Document;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document utilities.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class DocumentUtil {
|
||||||
|
/**
|
||||||
|
* Create a document and add the base ACLs.
|
||||||
|
*
|
||||||
|
* @param document Document
|
||||||
|
* @param userId User creating the document
|
||||||
|
* @return Created document
|
||||||
|
*/
|
||||||
|
public static Document createDocument(Document document, String userId) {
|
||||||
|
DocumentDao documentDao = new DocumentDao();
|
||||||
|
String documentId = documentDao.create(document, userId);
|
||||||
|
|
||||||
|
// Create read ACL
|
||||||
|
AclDao aclDao = new AclDao();
|
||||||
|
Acl acl = new Acl();
|
||||||
|
acl.setPerm(PermType.READ);
|
||||||
|
acl.setType(AclType.USER);
|
||||||
|
acl.setSourceId(documentId);
|
||||||
|
acl.setTargetId(userId);
|
||||||
|
aclDao.create(acl, userId);
|
||||||
|
|
||||||
|
// Create write ACL
|
||||||
|
acl = new Acl();
|
||||||
|
acl.setPerm(PermType.WRITE);
|
||||||
|
acl.setType(AclType.USER);
|
||||||
|
acl.setSourceId(documentId);
|
||||||
|
acl.setTargetId(userId);
|
||||||
|
aclDao.create(acl, userId);
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
package com.sismics.docs.core.util;
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import com.google.common.base.Strings;
|
||||||
import java.math.BigInteger;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.Security;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.CipherInputStream;
|
import javax.crypto.CipherInputStream;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.SecretKeyFactory;
|
import javax.crypto.SecretKeyFactory;
|
||||||
import javax.crypto.spec.PBEKeySpec;
|
import javax.crypto.spec.PBEKeySpec;
|
||||||
|
import java.io.InputStream;
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import java.math.BigInteger;
|
||||||
|
import java.nio.file.Files;
|
||||||
import com.google.common.base.Strings;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.Security;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encryption utilities.
|
* Encryption utilities.
|
||||||
@@ -22,7 +24,6 @@ import com.google.common.base.Strings;
|
|||||||
* @author bgamard
|
* @author bgamard
|
||||||
*/
|
*/
|
||||||
public class EncryptionUtil {
|
public class EncryptionUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Salt.
|
* Salt.
|
||||||
*/
|
*/
|
||||||
@@ -56,6 +57,27 @@ public class EncryptionUtil {
|
|||||||
return new CipherInputStream(is, getCipher(privateKey, Cipher.DECRYPT_MODE));
|
return new CipherInputStream(is, getCipher(privateKey, Cipher.DECRYPT_MODE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt a file to a temporary file using the specified private key.
|
||||||
|
*
|
||||||
|
* @param file Encrypted file
|
||||||
|
* @param privateKey Private key
|
||||||
|
* @return Decrypted temporary file
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static Path decryptFile(Path file, String privateKey) throws Exception {
|
||||||
|
if (privateKey == null) {
|
||||||
|
// For unit testing
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path tmpFile = ThreadLocalContext.get().createTemporaryFile();
|
||||||
|
try (InputStream is = Files.newInputStream(file)) {
|
||||||
|
Files.copy(new CipherInputStream(is, getCipher(privateKey, Cipher.DECRYPT_MODE)), tmpFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
return tmpFile;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an encryption cipher.
|
* Return an encryption cipher.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package com.sismics.docs.core.util;
|
|
||||||
|
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Entity manager utils.
|
|
||||||
*
|
|
||||||
* @author jtremeaux
|
|
||||||
*/
|
|
||||||
public class EntityManagerUtil {
|
|
||||||
/**
|
|
||||||
* Flush the entity manager session.
|
|
||||||
*/
|
|
||||||
public static void flush() {
|
|
||||||
ThreadLocalContext.get().getEntityManager().flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,27 @@
|
|||||||
package com.sismics.docs.core.util;
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.sismics.docs.core.constant.Constants;
|
||||||
|
import com.sismics.docs.core.dao.jpa.FileDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||||
|
import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent;
|
||||||
|
import com.sismics.docs.core.event.FileCreatedAsyncEvent;
|
||||||
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
|
import com.sismics.docs.core.model.jpa.User;
|
||||||
|
import com.sismics.tess4j.Tesseract;
|
||||||
|
import com.sismics.util.ImageDeskew;
|
||||||
|
import com.sismics.util.ImageUtil;
|
||||||
|
import com.sismics.util.Scalr;
|
||||||
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
import com.sismics.util.mime.MimeTypeUtil;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.CipherInputStream;
|
||||||
|
import javax.crypto.CipherOutputStream;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -7,21 +29,6 @@ import java.io.OutputStream;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.CipherInputStream;
|
|
||||||
import javax.crypto.CipherOutputStream;
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
|
|
||||||
import org.imgscalr.Scalr;
|
|
||||||
import org.imgscalr.Scalr.Method;
|
|
||||||
import org.imgscalr.Scalr.Mode;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
|
||||||
import com.sismics.tess4j.Tesseract;
|
|
||||||
import com.sismics.util.ImageUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File entity utilities.
|
* File entity utilities.
|
||||||
*
|
*
|
||||||
@@ -38,43 +45,47 @@ public class FileUtil {
|
|||||||
*
|
*
|
||||||
* @param language Language to extract
|
* @param language Language to extract
|
||||||
* @param file File to extract
|
* @param file File to extract
|
||||||
* @param inputStream Unencrypted input stream
|
* @param unencryptedFile Unencrypted file
|
||||||
* @param pdfInputStream Unencrypted PDF input stream
|
* @param unencryptedPdfFile Unencrypted PDF file
|
||||||
* @return Content extract
|
* @return Content extract
|
||||||
*/
|
*/
|
||||||
public static String extractContent(String language, File file, InputStream inputStream, InputStream pdfInputStream) {
|
public static String extractContent(String language, File file, Path unencryptedFile, Path unencryptedPdfFile) {
|
||||||
String content = null;
|
String content = null;
|
||||||
|
|
||||||
if (ImageUtil.isImage(file.getMimeType())) {
|
if (ImageUtil.isImage(file.getMimeType())) {
|
||||||
content = ocrFile(inputStream, language);
|
content = ocrFile(unencryptedFile, language);
|
||||||
} else if (pdfInputStream != null) {
|
} else if (unencryptedPdfFile != null) {
|
||||||
content = PdfUtil.extractPdf(pdfInputStream);
|
content = PdfUtil.extractPdf(unencryptedPdfFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optical character recognition on a stream.
|
* Optical character recognition on a file.
|
||||||
*
|
*
|
||||||
* @param inputStream Unencrypted input stream
|
* @param unecryptedFile Unencrypted file
|
||||||
* @param language Language to OCR
|
* @param language Language to OCR
|
||||||
* @return Content extracted
|
* @return Content extracted
|
||||||
*/
|
*/
|
||||||
private static String ocrFile(InputStream inputStream, String language) {
|
private static String ocrFile(Path unecryptedFile, String language) {
|
||||||
Tesseract instance = Tesseract.getInstance();
|
Tesseract instance = Tesseract.getInstance();
|
||||||
String content = null;
|
String content = null;
|
||||||
BufferedImage image = null;
|
BufferedImage image;
|
||||||
try {
|
try (InputStream inputStream = Files.newInputStream(unecryptedFile)) {
|
||||||
image = ImageIO.read(inputStream);
|
image = ImageIO.read(inputStream);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error reading the image", e);
|
log.error("Error reading the image", e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upscale and grayscale the image
|
// Upscale, grayscale and deskew the image
|
||||||
BufferedImage resizedImage = Scalr.resize(image, Method.AUTOMATIC, Mode.AUTOMATIC, 3500, Scalr.OP_ANTIALIAS, Scalr.OP_GRAYSCALE);
|
BufferedImage resizedImage = Scalr.resize(image, Scalr.Method.AUTOMATIC, Scalr.Mode.AUTOMATIC, 3500, Scalr.OP_ANTIALIAS, Scalr.OP_GRAYSCALE);
|
||||||
image.flush();
|
image.flush();
|
||||||
image = resizedImage;
|
ImageDeskew imageDeskew = new ImageDeskew(resizedImage);
|
||||||
|
BufferedImage deskewedImage = Scalr.rotate(resizedImage, - imageDeskew.getSkewAngle(), Scalr.OP_ANTIALIAS, Scalr.OP_GRAYSCALE);
|
||||||
|
resizedImage.flush();
|
||||||
|
image = deskewedImage;
|
||||||
|
|
||||||
// OCR the file
|
// OCR the file
|
||||||
try {
|
try {
|
||||||
@@ -91,46 +102,45 @@ public class FileUtil {
|
|||||||
/**
|
/**
|
||||||
* Save a file on the storage filesystem.
|
* Save a file on the storage filesystem.
|
||||||
*
|
*
|
||||||
* @param inputStream Unencrypted input stream
|
* @param unencryptedFile Unencrypted file
|
||||||
* @param pdf
|
* @param unencryptedPdfFile Unencrypted PDF file
|
||||||
* @param file File to save
|
* @param file File to save
|
||||||
* @param privateKey Private key used for encryption
|
* @param privateKey Private key used for encryption
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public static void save(InputStream inputStream, InputStream pdfInputStream, File file, String privateKey) throws Exception {
|
public static void save(Path unencryptedFile, Path unencryptedPdfFile, File file, String privateKey) throws Exception {
|
||||||
Cipher cipher = EncryptionUtil.getEncryptionCipher(privateKey);
|
Cipher cipher = EncryptionUtil.getEncryptionCipher(privateKey);
|
||||||
Path path = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
Path path = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||||
|
try (InputStream inputStream = Files.newInputStream(unencryptedFile)) {
|
||||||
Files.copy(new CipherInputStream(inputStream, cipher), path);
|
Files.copy(new CipherInputStream(inputStream, cipher), path);
|
||||||
inputStream.reset();
|
}
|
||||||
|
|
||||||
// Generate file variations
|
// Generate file variations
|
||||||
saveVariations(file, inputStream, pdfInputStream, cipher);
|
saveVariations(file, unencryptedFile, unencryptedPdfFile, cipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate file variations.
|
* Generate file variations.
|
||||||
*
|
*
|
||||||
* @param file File from database
|
* @param file File from database
|
||||||
* @param inputStream Unencrypted input stream
|
* @param unencryptedFile Unencrypted file
|
||||||
* @param pdfInputStream Unencrypted PDF input stream
|
* @param unencryptedPdfFile Unencrypted PDF file
|
||||||
* @param cipher Cipher to use for encryption
|
* @param cipher Cipher to use for encryption
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public static void saveVariations(File file, InputStream inputStream, InputStream pdfInputStream, Cipher cipher) throws Exception {
|
private static void saveVariations(File file, Path unencryptedFile, Path unencryptedPdfFile, Cipher cipher) throws Exception {
|
||||||
BufferedImage image = null;
|
BufferedImage image = null;
|
||||||
if (ImageUtil.isImage(file.getMimeType())) {
|
if (ImageUtil.isImage(file.getMimeType())) {
|
||||||
|
try (InputStream inputStream = Files.newInputStream(unencryptedFile)) {
|
||||||
image = ImageIO.read(inputStream);
|
image = ImageIO.read(inputStream);
|
||||||
inputStream.reset();
|
}
|
||||||
} else if(pdfInputStream != null) {
|
} else if (unencryptedPdfFile != null) {
|
||||||
// Generate preview from the first page of the PDF
|
// Generate preview from the first page of the PDF
|
||||||
image = PdfUtil.renderFirstPage(pdfInputStream);
|
image = PdfUtil.renderFirstPage(unencryptedPdfFile);
|
||||||
pdfInputStream.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
// Generate thumbnails from image
|
// Generate thumbnails from image
|
||||||
BufferedImage web = Scalr.resize(image, Scalr.Method.AUTOMATIC, Scalr.Mode.AUTOMATIC, 1280, Scalr.OP_ANTIALIAS);
|
BufferedImage web = Scalr.resize(image, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.AUTOMATIC, 1280);
|
||||||
BufferedImage thumbnail = Scalr.resize(image, Scalr.Method.AUTOMATIC, Scalr.Mode.AUTOMATIC, 256, Scalr.OP_ANTIALIAS);
|
BufferedImage thumbnail = Scalr.resize(image, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.AUTOMATIC, 256);
|
||||||
image.flush();
|
image.flush();
|
||||||
|
|
||||||
// Write "web" encrypted image
|
// Write "web" encrypted image
|
||||||
@@ -151,7 +161,6 @@ public class FileUtil {
|
|||||||
* Remove a file from the storage filesystem.
|
* Remove a file from the storage filesystem.
|
||||||
*
|
*
|
||||||
* @param file File to delete
|
* @param file File to delete
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
public static void delete(File file) throws IOException {
|
public static void delete(File file) throws IOException {
|
||||||
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||||
@@ -168,4 +177,92 @@ public class FileUtil {
|
|||||||
Files.delete(thumbnailFile);
|
Files.delete(thumbnailFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new file.
|
||||||
|
*
|
||||||
|
* @param name File name, can be null
|
||||||
|
* @param unencryptedFile Path to the unencrypted file
|
||||||
|
* @param fileSize File size
|
||||||
|
* @param language File language, can be null if associated to no document
|
||||||
|
* @param userId User ID creating the file
|
||||||
|
* @param documentId Associated document ID or null if no document
|
||||||
|
* @return File ID
|
||||||
|
* @throws Exception e
|
||||||
|
*/
|
||||||
|
public static String createFile(String name, Path unencryptedFile, long fileSize, String language, String userId, String documentId) throws Exception {
|
||||||
|
// Validate mime type
|
||||||
|
String mimeType;
|
||||||
|
try {
|
||||||
|
mimeType = MimeTypeUtil.guessMimeType(unencryptedFile, name);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOException("ErrorGuessMime", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate user quota
|
||||||
|
UserDao userDao = new UserDao();
|
||||||
|
User user = userDao.getById(userId);
|
||||||
|
if (user.getStorageCurrent() + fileSize > user.getStorageQuota()) {
|
||||||
|
throw new IOException("QuotaReached");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate global quota
|
||||||
|
String globalStorageQuotaStr = System.getenv(Constants.GLOBAL_QUOTA_ENV);
|
||||||
|
if (!Strings.isNullOrEmpty(globalStorageQuotaStr)) {
|
||||||
|
long globalStorageQuota = Long.valueOf(globalStorageQuotaStr);
|
||||||
|
long globalStorageCurrent = userDao.getGlobalStorageCurrent();
|
||||||
|
if (globalStorageCurrent + fileSize > globalStorageQuota) {
|
||||||
|
throw new IOException("QuotaReached");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get files of this document
|
||||||
|
FileDao fileDao = new FileDao();
|
||||||
|
int order = 0;
|
||||||
|
if (documentId != null) {
|
||||||
|
for (File file : fileDao.getByDocumentId(userId, documentId)) {
|
||||||
|
file.setOrder(order++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the file
|
||||||
|
File file = new File();
|
||||||
|
file.setOrder(order);
|
||||||
|
file.setDocumentId(documentId);
|
||||||
|
file.setName(StringUtils.abbreviate(name, 200));
|
||||||
|
file.setMimeType(mimeType);
|
||||||
|
file.setUserId(userId);
|
||||||
|
String fileId = fileDao.create(file, userId);
|
||||||
|
|
||||||
|
// Guess the mime type a second time, for open document format (first detected as simple ZIP file)
|
||||||
|
file.setMimeType(MimeTypeUtil.guessOpenDocumentFormat(file, unencryptedFile));
|
||||||
|
|
||||||
|
// Convert to PDF if necessary (for thumbnail and text extraction)
|
||||||
|
java.nio.file.Path unencryptedPdfFile = PdfUtil.convertToPdf(file, unencryptedFile);
|
||||||
|
|
||||||
|
// Save the file
|
||||||
|
FileUtil.save(unencryptedFile, unencryptedPdfFile, file, user.getPrivateKey());
|
||||||
|
|
||||||
|
// Update the user quota
|
||||||
|
user.setStorageCurrent(user.getStorageCurrent() + fileSize);
|
||||||
|
userDao.updateQuota(user);
|
||||||
|
|
||||||
|
// Raise a new file created event and document updated event if we have a document
|
||||||
|
if (documentId != null) {
|
||||||
|
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
|
||||||
|
fileCreatedAsyncEvent.setUserId(userId);
|
||||||
|
fileCreatedAsyncEvent.setLanguage(language);
|
||||||
|
fileCreatedAsyncEvent.setFile(file);
|
||||||
|
fileCreatedAsyncEvent.setUnencryptedFile(unencryptedFile);
|
||||||
|
fileCreatedAsyncEvent.setUnencryptedPdfFile(unencryptedPdfFile);
|
||||||
|
ThreadLocalContext.get().addAsyncEvent(fileCreatedAsyncEvent);
|
||||||
|
|
||||||
|
DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent();
|
||||||
|
documentUpdatedAsyncEvent.setUserId(userId);
|
||||||
|
documentUpdatedAsyncEvent.setDocumentId(documentId);
|
||||||
|
ThreadLocalContext.get().addAsyncEvent(documentUpdatedAsyncEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package com.sismics.docs.core.util;
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import com.google.common.base.Charsets;
|
||||||
import java.io.ByteArrayInputStream;
|
import com.google.common.base.Strings;
|
||||||
import java.io.ByteArrayOutputStream;
|
import com.google.common.io.ByteStreams;
|
||||||
import java.io.IOException;
|
import com.google.common.io.Closer;
|
||||||
import java.io.InputStream;
|
import com.google.common.io.Resources;
|
||||||
import java.nio.file.Files;
|
import com.lowagie.text.*;
|
||||||
import java.nio.file.Path;
|
import com.lowagie.text.pdf.PdfWriter;
|
||||||
import java.text.SimpleDateFormat;
|
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
||||||
import java.util.Date;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
import java.util.List;
|
import com.sismics.docs.core.util.pdf.PdfPage;
|
||||||
|
import com.sismics.util.ImageUtil;
|
||||||
import javax.imageio.ImageIO;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
import com.sismics.util.mime.MimeType;
|
||||||
import org.apache.pdfbox.io.MemoryUsageSetting;
|
import org.apache.pdfbox.io.MemoryUsageSetting;
|
||||||
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
@@ -32,13 +32,17 @@ import org.odftoolkit.odfdom.doc.OdfTextDocument;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import javax.imageio.ImageIO;
|
||||||
import com.google.common.io.Closer;
|
import java.awt.image.BufferedImage;
|
||||||
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
import java.io.IOException;
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import java.io.InputStream;
|
||||||
import com.sismics.docs.core.util.pdf.PdfPage;
|
import java.io.OutputStream;
|
||||||
import com.sismics.util.ImageUtil;
|
import java.net.URL;
|
||||||
import com.sismics.util.mime.MimeType;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PDF utilities.
|
* PDF utilities.
|
||||||
@@ -54,17 +58,17 @@ public class PdfUtil {
|
|||||||
/**
|
/**
|
||||||
* Extract text from a PDF.
|
* Extract text from a PDF.
|
||||||
*
|
*
|
||||||
* @param inputStream Unencrypted input stream
|
* @param unencryptedPdfFile Unencrypted PDF file
|
||||||
* @return Content extracted
|
* @return Content extracted
|
||||||
*/
|
*/
|
||||||
public static String extractPdf(InputStream inputStream) {
|
public static String extractPdf(Path unencryptedPdfFile) {
|
||||||
String content = null;
|
String content = null;
|
||||||
PDDocument pdfDocument = null;
|
PDDocument pdfDocument = null;
|
||||||
try {
|
try (InputStream inputStream = Files.newInputStream(unencryptedPdfFile)) {
|
||||||
PDFTextStripper stripper = new PDFTextStripper();
|
PDFTextStripper stripper = new PDFTextStripper();
|
||||||
pdfDocument = PDDocument.load(inputStream);
|
pdfDocument = PDDocument.load(inputStream);
|
||||||
content = stripper.getText(pdfDocument);
|
content = stripper.getText(pdfDocument);
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
log.error("Error while extracting text from the PDF", e);
|
log.error("Error while extracting text from the PDF", e);
|
||||||
} finally {
|
} finally {
|
||||||
if (pdfDocument != null) {
|
if (pdfDocument != null) {
|
||||||
@@ -83,65 +87,86 @@ public class PdfUtil {
|
|||||||
* Convert a file to PDF if necessary.
|
* Convert a file to PDF if necessary.
|
||||||
*
|
*
|
||||||
* @param file File
|
* @param file File
|
||||||
* @param inputStream InputStream
|
* @param unencryptedFile Unencrypted file
|
||||||
* @param reset Reset the stream after usage
|
* @return PDF temporary file
|
||||||
* @return PDF input stream
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public static InputStream convertToPdf(File file, InputStream inputStream, boolean reset) throws Exception {
|
public static Path convertToPdf(File file, Path unencryptedFile) throws Exception {
|
||||||
if (file.getMimeType().equals(MimeType.APPLICATION_PDF)) {
|
if (file.getMimeType().equals(MimeType.APPLICATION_PDF)) {
|
||||||
// It's already PDF, just return the input
|
// It's already PDF, just return the file
|
||||||
return inputStream;
|
return unencryptedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.getMimeType().equals(MimeType.OFFICE_DOCUMENT)) {
|
if (file.getMimeType().equals(MimeType.OFFICE_DOCUMENT)) {
|
||||||
return convertOfficeDocument(inputStream, reset);
|
return convertOfficeDocument(unencryptedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.getMimeType().equals(MimeType.OPEN_DOCUMENT_TEXT)) {
|
if (file.getMimeType().equals(MimeType.OPEN_DOCUMENT_TEXT)) {
|
||||||
return convertOpenDocumentText(inputStream, reset);
|
return convertOpenDocumentText(unencryptedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.getMimeType().equals(MimeType.TEXT_PLAIN) || file.getMimeType().equals(MimeType.TEXT_CSV)) {
|
||||||
|
return convertTextPlain(unencryptedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// PDF conversion not necessary/possible
|
// PDF conversion not necessary/possible
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a text plain document to PDF.
|
||||||
|
*
|
||||||
|
* @param unencryptedFile Unencrypted file
|
||||||
|
* @return PDF file
|
||||||
|
*/
|
||||||
|
private static Path convertTextPlain(Path unencryptedFile) throws Exception {
|
||||||
|
Document output = new Document(PageSize.A4, 40, 40, 40, 40);
|
||||||
|
Path tempFile = ThreadLocalContext.get().createTemporaryFile();
|
||||||
|
OutputStream pdfOutputStream = Files.newOutputStream(tempFile);
|
||||||
|
PdfWriter.getInstance(output, pdfOutputStream);
|
||||||
|
|
||||||
|
output.open();
|
||||||
|
String content = new String(Files.readAllBytes(unencryptedFile), Charsets.UTF_8);
|
||||||
|
Font font = FontFactory.getFont("LiberationMono-Regular");
|
||||||
|
Paragraph paragraph = new Paragraph(content, font);
|
||||||
|
paragraph.setAlignment(Element.ALIGN_LEFT);
|
||||||
|
output.add(paragraph);
|
||||||
|
output.close();
|
||||||
|
|
||||||
|
return tempFile;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert an open document text file to PDF.
|
* Convert an open document text file to PDF.
|
||||||
*
|
*
|
||||||
* @param inputStream Unencrypted input stream
|
* @param unencryptedFile Unencrypted file
|
||||||
* @param reset Reset the stream after usage
|
* @return PDF file
|
||||||
* @return PDF input stream
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
private static InputStream convertOpenDocumentText(InputStream inputStream, boolean reset) throws Exception {
|
private static Path convertOpenDocumentText(Path unencryptedFile) throws Exception {
|
||||||
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
|
Path tempFile = ThreadLocalContext.get().createTemporaryFile();
|
||||||
|
try (InputStream inputStream = Files.newInputStream(unencryptedFile);
|
||||||
|
OutputStream outputStream = Files.newOutputStream(tempFile)) {
|
||||||
OdfTextDocument document = OdfTextDocument.loadDocument(inputStream);
|
OdfTextDocument document = OdfTextDocument.loadDocument(inputStream);
|
||||||
PdfOptions options = PdfOptions.create();
|
PdfOptions options = PdfOptions.create();
|
||||||
PdfConverter.getInstance().convert(document, pdfOutputStream, options);
|
PdfConverter.getInstance().convert(document, outputStream, options);
|
||||||
if (reset) {
|
|
||||||
inputStream.reset();
|
|
||||||
}
|
}
|
||||||
return new ByteArrayInputStream(pdfOutputStream.toByteArray());
|
return tempFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert an Office document to PDF.
|
* Convert an Office document to PDF.
|
||||||
*
|
*
|
||||||
* @param inputStream Unencrypted input stream
|
* @param unencryptedFile Unencrypted file
|
||||||
* @param reset Reset the stream after usage
|
* @return PDF file
|
||||||
* @return PDF input stream
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
private static InputStream convertOfficeDocument(InputStream inputStream, boolean reset) throws Exception {
|
private static Path convertOfficeDocument(Path unencryptedFile) throws Exception {
|
||||||
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
|
Path tempFile = ThreadLocalContext.get().createTemporaryFile();
|
||||||
|
try (InputStream inputStream = Files.newInputStream(unencryptedFile);
|
||||||
|
OutputStream outputStream = Files.newOutputStream(tempFile)) {
|
||||||
XWPFDocument document = new XWPFDocument(inputStream);
|
XWPFDocument document = new XWPFDocument(inputStream);
|
||||||
org.apache.poi.xwpf.converter.pdf.PdfOptions options = org.apache.poi.xwpf.converter.pdf.PdfOptions.create();
|
org.apache.poi.xwpf.converter.pdf.PdfOptions options = org.apache.poi.xwpf.converter.pdf.PdfOptions.create();
|
||||||
org.apache.poi.xwpf.converter.pdf.PdfConverter.getInstance().convert(document, pdfOutputStream, options);
|
org.apache.poi.xwpf.converter.pdf.PdfConverter.getInstance().convert(document, outputStream, options);
|
||||||
if (reset) {
|
|
||||||
inputStream.reset();
|
|
||||||
}
|
}
|
||||||
return new ByteArrayInputStream(pdfOutputStream.toByteArray());
|
return tempFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,11 +177,10 @@ public class PdfUtil {
|
|||||||
* @param fitImageToPage Fit images to the page
|
* @param fitImageToPage Fit images to the page
|
||||||
* @param metadata Add a page with metadata
|
* @param metadata Add a page with metadata
|
||||||
* @param margin Margins in millimeters
|
* @param margin Margins in millimeters
|
||||||
* @return PDF input stream
|
* @param outputStream Output stream to write to, will be closed
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
public static InputStream convertToPdf(DocumentDto documentDto, List<File> fileList,
|
public static void convertToPdf(DocumentDto documentDto, List<File> fileList,
|
||||||
boolean fitImageToPage, boolean metadata, int margin) throws Exception {
|
boolean fitImageToPage, boolean metadata, int margin, OutputStream outputStream) throws Exception {
|
||||||
// Setup PDFBox
|
// Setup PDFBox
|
||||||
Closer closer = Closer.create();
|
Closer closer = Closer.create();
|
||||||
MemoryUsageSetting memUsageSettings = MemoryUsageSetting.setupMixed(1000000); // 1MB max memory usage
|
MemoryUsageSetting memUsageSettings = MemoryUsageSetting.setupMixed(1000000); // 1MB max memory usage
|
||||||
@@ -211,11 +235,14 @@ public class PdfUtil {
|
|||||||
// Add files
|
// Add files
|
||||||
for (File file : fileList) {
|
for (File file : fileList) {
|
||||||
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||||
try (InputStream storedFileInputStream = file.getPrivateKey() == null ? // Try to decrypt the file if we have a private key available
|
|
||||||
Files.newInputStream(storedFile) : EncryptionUtil.decryptInputStream(Files.newInputStream(storedFile), file.getPrivateKey())) {
|
// Decrypt the file to a temporary file
|
||||||
|
Path unencryptedFile = EncryptionUtil.decryptFile(storedFile, file.getPrivateKey());
|
||||||
|
|
||||||
if (ImageUtil.isImage(file.getMimeType())) {
|
if (ImageUtil.isImage(file.getMimeType())) {
|
||||||
PDPage page = new PDPage(PDRectangle.A4); // Images into A4 pages
|
PDPage page = new PDPage(PDRectangle.A4); // Images into A4 pages
|
||||||
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
|
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page);
|
||||||
|
InputStream storedFileInputStream = Files.newInputStream(unencryptedFile)) {
|
||||||
// Read the image using the correct handler. PDFBox can't do it because it relies wrongly on file extension
|
// Read the image using the correct handler. PDFBox can't do it because it relies wrongly on file extension
|
||||||
PDImageXObject pdImage = null;
|
PDImageXObject pdImage = null;
|
||||||
if (file.getMimeType().equals(MimeType.IMAGE_JPEG)) {
|
if (file.getMimeType().equals(MimeType.IMAGE_JPEG)) {
|
||||||
@@ -250,44 +277,52 @@ public class PdfUtil {
|
|||||||
doc.addPage(page);
|
doc.addPage(page);
|
||||||
} else {
|
} else {
|
||||||
// Try to convert the file to PDF
|
// Try to convert the file to PDF
|
||||||
InputStream pdfInputStream = convertToPdf(file, storedFileInputStream, false);
|
Path unencryptedPdfFile = convertToPdf(file, unencryptedFile);
|
||||||
if (pdfInputStream != null) {
|
if (unencryptedPdfFile != null) {
|
||||||
// This file is convertible to PDF, just add it to the end
|
// This file is convertible to PDF, just add it to the end
|
||||||
try {
|
PDDocument mergeDoc = PDDocument.load(unencryptedPdfFile.toFile(), memUsageSettings);
|
||||||
PDDocument mergeDoc = PDDocument.load(pdfInputStream, memUsageSettings);
|
|
||||||
closer.register(mergeDoc);
|
closer.register(mergeDoc);
|
||||||
PDFMergerUtility pdfMergerUtility = new PDFMergerUtility();
|
PDFMergerUtility pdfMergerUtility = new PDFMergerUtility();
|
||||||
pdfMergerUtility.appendDocument(doc, mergeDoc);
|
pdfMergerUtility.appendDocument(doc, mergeDoc);
|
||||||
} finally {
|
|
||||||
pdfInputStream.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other non-PDF-convertible files are ignored
|
// All other non-PDF-convertible files are ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Save to a temporary file
|
doc.save(outputStream); // Write to the output stream
|
||||||
try (TemporaryFileStream temporaryFileStream = new TemporaryFileStream()) {
|
|
||||||
doc.save(temporaryFileStream.openWriteStream());
|
|
||||||
closer.close(); // Close all remaining opened PDF
|
closer.close(); // Close all remaining opened PDF
|
||||||
return temporaryFileStream.openReadStream();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the first page of a PDF.
|
* Render the first page of a PDF.
|
||||||
*
|
*
|
||||||
* @param inputStream PDF document
|
* @param unencryptedFile PDF document
|
||||||
* @return Render of the first page
|
* @return Render of the first page
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
public static BufferedImage renderFirstPage(InputStream inputStream) throws IOException {
|
public static BufferedImage renderFirstPage(Path unencryptedFile) throws IOException {
|
||||||
try (PDDocument pdfDocument = PDDocument.load(inputStream)) {
|
try (InputStream inputStream = Files.newInputStream(unencryptedFile);
|
||||||
|
PDDocument pdfDocument = PDDocument.load(inputStream)) {
|
||||||
PDFRenderer renderer = new PDFRenderer(pdfDocument);
|
PDFRenderer renderer = new PDFRenderer(pdfDocument);
|
||||||
return renderer.renderImage(0);
|
return renderer.renderImage(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register fonts.
|
||||||
|
*/
|
||||||
|
public static void registerFonts() {
|
||||||
|
URL url = Resources.getResource("fonts/LiberationMono-Regular.ttf");
|
||||||
|
try (InputStream is = url.openStream()) {
|
||||||
|
Path file = Files.createTempFile("sismics_docs_font_mono", ".ttf");
|
||||||
|
try (OutputStream os = Files.newOutputStream(file)) {
|
||||||
|
ByteStreams.copy(is, os);
|
||||||
|
}
|
||||||
|
FontFactory.register(file.toAbsolutePath().toString(), "LiberationMono-Regular");
|
||||||
|
FontFactory.registerDirectories();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error loading font", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.sismics.docs.core.constant.AclTargetType;
|
||||||
|
import com.sismics.docs.core.constant.AclType;
|
||||||
|
import com.sismics.docs.core.constant.PermType;
|
||||||
|
import com.sismics.docs.core.dao.jpa.AclDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.RouteStepDto;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
|
import com.sismics.docs.core.event.RouteStepValidateEvent;
|
||||||
|
import com.sismics.docs.core.model.context.AppContext;
|
||||||
|
import com.sismics.docs.core.model.jpa.Acl;
|
||||||
|
import com.sismics.docs.core.model.jpa.Document;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Routing utilities.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class RoutingUtil {
|
||||||
|
/**
|
||||||
|
* Update routing ACLs according to the current route step.
|
||||||
|
*
|
||||||
|
* @param sourceId Source ID
|
||||||
|
* @param currentStep Current route step
|
||||||
|
* @param previousStep Previous route step
|
||||||
|
* @param userId User ID
|
||||||
|
*/
|
||||||
|
public static void updateAcl(String sourceId, RouteStepDto currentStep, RouteStepDto previousStep, String userId) {
|
||||||
|
AclDao aclDao = new AclDao();
|
||||||
|
|
||||||
|
if (previousStep != null) {
|
||||||
|
// Remove the previous ACL
|
||||||
|
aclDao.delete(sourceId, PermType.READ, previousStep.getTargetId(), userId, AclType.ROUTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentStep != null) {
|
||||||
|
// Create a temporary READ ACL
|
||||||
|
Acl acl = new Acl();
|
||||||
|
acl.setPerm(PermType.READ);
|
||||||
|
acl.setType(AclType.ROUTING);
|
||||||
|
acl.setSourceId(sourceId);
|
||||||
|
acl.setTargetId(currentStep.getTargetId());
|
||||||
|
aclDao.create(acl, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendRouteStepEmail(String documentId, RouteStepDto routeStepDto) {
|
||||||
|
DocumentDao documentDao = new DocumentDao();
|
||||||
|
Document document = documentDao.getById(documentId);
|
||||||
|
|
||||||
|
List<UserDto> userDtoList = Lists.newArrayList();
|
||||||
|
UserDao userDao = new UserDao();
|
||||||
|
switch (AclTargetType.valueOf(routeStepDto.getTargetType())) {
|
||||||
|
case USER:
|
||||||
|
userDtoList.addAll(userDao.findByCriteria(new UserCriteria().setUserId(routeStepDto.getTargetId()), null));
|
||||||
|
break;
|
||||||
|
case GROUP:
|
||||||
|
userDtoList.addAll(userDao.findByCriteria(new UserCriteria().setGroupId(routeStepDto.getTargetId()), null));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire route step validate events
|
||||||
|
for (UserDto userDto : userDtoList) {
|
||||||
|
RouteStepValidateEvent routeStepValidateEvent = new RouteStepValidateEvent();
|
||||||
|
routeStepValidateEvent.setUser(userDto);
|
||||||
|
routeStepValidateEvent.setDocument(document);
|
||||||
|
AppContext.getInstance().getMailEventBus().post(routeStepValidateEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
|
import com.sismics.docs.core.constant.AclTargetType;
|
||||||
|
import com.sismics.docs.core.dao.jpa.GroupDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||||
|
import com.sismics.docs.core.model.jpa.Group;
|
||||||
|
import com.sismics.docs.core.model.jpa.User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security utilities.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class SecurityUtil {
|
||||||
|
/**
|
||||||
|
* Get an ACL target ID from an object name and type.
|
||||||
|
*
|
||||||
|
* @param name Object name
|
||||||
|
* @param type Object type
|
||||||
|
* @return Target ID
|
||||||
|
*/
|
||||||
|
public static String getTargetIdFromName(String name, AclTargetType type) {
|
||||||
|
switch (type) {
|
||||||
|
case USER:
|
||||||
|
UserDao userDao = new UserDao();
|
||||||
|
User user = userDao.getActiveByUsername(name);
|
||||||
|
if (user != null) {
|
||||||
|
return user.getId();
|
||||||
|
}
|
||||||
|
case GROUP:
|
||||||
|
GroupDao groupDao = new GroupDao();
|
||||||
|
Group group = groupDao.getActiveByName(name);
|
||||||
|
if (group != null) {
|
||||||
|
return group.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package com.sismics.docs.core.util;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.PushbackInputStream;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stream utilities.
|
|
||||||
*
|
|
||||||
* @author bgamard
|
|
||||||
*/
|
|
||||||
public class StreamUtil {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects if the stream is gzipped, and returns a uncompressed stream according to this.
|
|
||||||
*
|
|
||||||
* @param is InputStream
|
|
||||||
* @return InputStream
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public static InputStream detectGzip(InputStream is) throws IOException {
|
|
||||||
PushbackInputStream pb = new PushbackInputStream(is, 2);
|
|
||||||
byte [] signature = new byte[2];
|
|
||||||
pb.read(signature);
|
|
||||||
pb.unread(signature);
|
|
||||||
if(signature[0] == (byte) GZIPInputStream.GZIP_MAGIC && signature[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)) {
|
|
||||||
return new GZIPInputStream(pb);
|
|
||||||
} else {
|
|
||||||
return pb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package com.sismics.docs.core.util;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utilities for writing and reading to a temporary file.
|
|
||||||
*
|
|
||||||
* @author bgamard
|
|
||||||
*/
|
|
||||||
public class TemporaryFileStream implements Closeable {
|
|
||||||
/**
|
|
||||||
* Temporary file.
|
|
||||||
*/
|
|
||||||
private Path tempFile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a temporary file.
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public TemporaryFileStream() throws IOException {
|
|
||||||
tempFile = Files.createTempFile(UUID.randomUUID().toString(), ".tmp");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a stream for writing.
|
|
||||||
*
|
|
||||||
* @return OutputStream
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public OutputStream openWriteStream() throws IOException {
|
|
||||||
return Files.newOutputStream(tempFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a stream for reading.
|
|
||||||
*
|
|
||||||
* @return InputStream
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public InputStream openReadStream() throws IOException {
|
|
||||||
return Files.newInputStream(tempFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
Files.delete(tempFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,12 +22,12 @@ public class TransactionUtil {
|
|||||||
/**
|
/**
|
||||||
* Encapsulate a process into a transactionnal context.
|
* Encapsulate a process into a transactionnal context.
|
||||||
*
|
*
|
||||||
* @param runnable
|
* @param runnable Runnable
|
||||||
*/
|
*/
|
||||||
public static void handle(Runnable runnable) {
|
public static void handle(Runnable runnable) {
|
||||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
|
|
||||||
if (em != null) {
|
if (em != null && em.isOpen()) {
|
||||||
// We are already in a transactional context, nothing to do
|
// We are already in a transactional context, nothing to do
|
||||||
runnable.run();
|
runnable.run();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
package com.sismics.docs.core.util;
|
|
||||||
|
|
||||||
import com.sismics.docs.core.model.jpa.User;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utilitaires sur les utilisateurs.
|
|
||||||
*
|
|
||||||
* @author jtremeaux
|
|
||||||
*/
|
|
||||||
public class UserUtil {
|
|
||||||
/**
|
|
||||||
* Retourne the user's username.
|
|
||||||
*
|
|
||||||
* @param user User
|
|
||||||
* @return User name
|
|
||||||
*/
|
|
||||||
public static String getUserName(User user) {
|
|
||||||
return user.getUsername();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
package com.sismics.util;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Date utilities.
|
|
||||||
*
|
|
||||||
* @author jtremeaux
|
|
||||||
*/
|
|
||||||
public class DateUtil {
|
|
||||||
|
|
||||||
private final static ImmutableMap<String, String> TIMEZONE_CODE_MAP = new ImmutableMap.Builder<String, String>()
|
|
||||||
.put(" ACDT", " +10:30")
|
|
||||||
.put(" ACST", " +09:30")
|
|
||||||
.put(" ACT", " +08")
|
|
||||||
.put(" ADT", " −03")
|
|
||||||
.put(" AEDT", " +11")
|
|
||||||
.put(" AEST", " +10")
|
|
||||||
.put(" AFT", " +04:30")
|
|
||||||
.put(" AKDT", " −08")
|
|
||||||
.put(" AKST", " −09")
|
|
||||||
.put(" AMST", " +05")
|
|
||||||
.put(" AMT", " +04")
|
|
||||||
.put(" ART", " −03")
|
|
||||||
// .put(" AST", " +03")
|
|
||||||
.put(" AST", " −04")
|
|
||||||
.put(" AWDT", " +09")
|
|
||||||
.put(" AWST", " +08")
|
|
||||||
.put(" AZOST", " −01")
|
|
||||||
.put(" AZT", " +04")
|
|
||||||
.put(" BDT", " +08")
|
|
||||||
.put(" BIOT", " +06")
|
|
||||||
.put(" BIT", " −12")
|
|
||||||
.put(" BOT", " −04")
|
|
||||||
.put(" BRT", " −03")
|
|
||||||
// .put(" BST", " +06")
|
|
||||||
.put(" BST", " +01")
|
|
||||||
.put(" BTT", " +06")
|
|
||||||
.put(" CAT", " +02")
|
|
||||||
.put(" CCT", " +06:30")
|
|
||||||
.put(" CDT", " −05")
|
|
||||||
// .put(" CDT", " −04")
|
|
||||||
.put(" CEDT", " +02")
|
|
||||||
.put(" CEST", " +02")
|
|
||||||
.put(" CET", " +01")
|
|
||||||
.put(" CHADT", " +13:45")
|
|
||||||
.put(" CHAST", " +12:45")
|
|
||||||
.put(" CHOT", " −08")
|
|
||||||
.put(" ChST", " +10")
|
|
||||||
.put(" CHUT", " +10")
|
|
||||||
.put(" CIST", " −08")
|
|
||||||
.put(" CIT", " +08")
|
|
||||||
.put(" CKT", " −10")
|
|
||||||
.put(" CLST", " −03")
|
|
||||||
.put(" CLT", " −04")
|
|
||||||
.put(" COST", " −04")
|
|
||||||
.put(" COT", " −05")
|
|
||||||
.put(" CST", " −06")
|
|
||||||
// .put(" CST", " +08")
|
|
||||||
// .put(" CST", " +09:30")
|
|
||||||
// .put(" CST", " +10:30")
|
|
||||||
// .put(" CST", " −05")
|
|
||||||
.put(" CT", " +08")
|
|
||||||
.put(" CVT", " −01")
|
|
||||||
.put(" CWST", " +08:45")
|
|
||||||
.put(" CXT", " +07")
|
|
||||||
.put(" DAVT", " +07")
|
|
||||||
.put(" DDUT", " +10")
|
|
||||||
.put(" DFT", " +01")
|
|
||||||
.put(" EASST", " −05")
|
|
||||||
.put(" EAST", " −06")
|
|
||||||
.put(" EAT", " +03")
|
|
||||||
// .put(" ECT", " −04")
|
|
||||||
.put(" ECT", " −05")
|
|
||||||
.put(" EDT", " −04")
|
|
||||||
.put(" EEDT", " +03")
|
|
||||||
.put(" EEST", " +03")
|
|
||||||
.put(" EET", " +02")
|
|
||||||
.put(" EGST", " +00")
|
|
||||||
.put(" EGT", " −01")
|
|
||||||
.put(" EIT", " +09")
|
|
||||||
.put(" EST", " −05")
|
|
||||||
// .put(" EST", " +10")
|
|
||||||
.put(" FET", " +03")
|
|
||||||
.put(" FJT", " +12")
|
|
||||||
.put(" FKST", " −03")
|
|
||||||
.put(" FKT", " −04")
|
|
||||||
.put(" FNT", " −02")
|
|
||||||
.put(" GALT", " −06")
|
|
||||||
.put(" GAMT", " −09")
|
|
||||||
.put(" GET", " +04")
|
|
||||||
.put(" GFT", " −03")
|
|
||||||
.put(" GILT", " +12")
|
|
||||||
.put(" GIT", " −09")
|
|
||||||
.put(" GMT", " ")
|
|
||||||
// .put(" GST", " −02")
|
|
||||||
.put(" GST", " +04")
|
|
||||||
.put(" GYT", " −04")
|
|
||||||
.put(" HADT", " −09")
|
|
||||||
.put(" HAEC", " +02")
|
|
||||||
.put(" HAST", " −10")
|
|
||||||
.put(" HKT", " +08")
|
|
||||||
.put(" HMT", " +05")
|
|
||||||
.put(" HOVT", " +07")
|
|
||||||
.put(" HST", " −10")
|
|
||||||
.put(" ICT", " +07")
|
|
||||||
.put(" IDT", " +03")
|
|
||||||
.put(" IOT", " +03")
|
|
||||||
.put(" IRDT", " +08")
|
|
||||||
.put(" IRKT", " +09")
|
|
||||||
.put(" IRST", " +03:30")
|
|
||||||
.put(" IST", " +05:30")
|
|
||||||
// .put(" IST", " +01")
|
|
||||||
// .put(" IST", " +02")
|
|
||||||
// .put(" JST", " +09")
|
|
||||||
.put(" KGT", " +06")
|
|
||||||
.put(" KOST", " +11")
|
|
||||||
.put(" KRAT", " +07")
|
|
||||||
.put(" KST", " +09")
|
|
||||||
.put(" LHST", " +10:30")
|
|
||||||
// .put(" LHST", " +11")
|
|
||||||
.put(" LINT", " +14")
|
|
||||||
.put(" MAGT", " +12")
|
|
||||||
.put(" MART", " −09:30")
|
|
||||||
.put(" MAWT", " +05")
|
|
||||||
.put(" MDT", " −06")
|
|
||||||
.put(" MET", " +01")
|
|
||||||
.put(" MEST", " +02")
|
|
||||||
.put(" MHT", " +12")
|
|
||||||
.put(" MIST", " +11")
|
|
||||||
.put(" MIT", " −09:30")
|
|
||||||
.put(" MMT", " +06:30")
|
|
||||||
.put(" MSK", " +04")
|
|
||||||
// .put(" MST", " +08")
|
|
||||||
.put(" MST", " −07")
|
|
||||||
// .put(" MST", " +06:30")
|
|
||||||
.put(" MUT", " +04")
|
|
||||||
.put(" MVT", " +05")
|
|
||||||
.put(" MYT", " +08")
|
|
||||||
.put(" NCT", " +11")
|
|
||||||
.put(" NDT", " −02:30")
|
|
||||||
.put(" NFT", " +11:30")
|
|
||||||
.put(" NPT", " +05:45")
|
|
||||||
.put(" NST", " −03:30")
|
|
||||||
.put(" NT", " −03:30")
|
|
||||||
.put(" NUT", " −11:30")
|
|
||||||
.put(" NZDT", " +13")
|
|
||||||
.put(" NZST", " +12")
|
|
||||||
.put(" OMST", " +06")
|
|
||||||
.put(" ORAT", " +05")
|
|
||||||
.put(" PDT", " −07")
|
|
||||||
.put(" PET", " −05")
|
|
||||||
.put(" PETT", " +12")
|
|
||||||
.put(" PGT", " +10")
|
|
||||||
.put(" PHOT", " +13")
|
|
||||||
.put(" PHT", " +08")
|
|
||||||
.put(" PKT", " +05")
|
|
||||||
.put(" PMDT", " −02")
|
|
||||||
.put(" PMST", " −03")
|
|
||||||
.put(" PONT", " +11")
|
|
||||||
.put(" PST", " −08")
|
|
||||||
// .put(" PST", " +08")
|
|
||||||
.put(" RET", " +04")
|
|
||||||
.put(" ROTT", " −03")
|
|
||||||
.put(" SAKT", " +11")
|
|
||||||
.put(" SAMT", " +04")
|
|
||||||
.put(" SAST", " +02")
|
|
||||||
.put(" SBT", " +11")
|
|
||||||
.put(" SCT", " +04")
|
|
||||||
.put(" SGT", " +08")
|
|
||||||
.put(" SLT", " +05:30")
|
|
||||||
.put(" SRT", " −03")
|
|
||||||
.put(" SST", " −11")
|
|
||||||
// .put(" SST", " +08")
|
|
||||||
.put(" SYOT", " +03")
|
|
||||||
.put(" TAHT", " −10")
|
|
||||||
.put(" THA", " +07")
|
|
||||||
.put(" TFT", " +05")
|
|
||||||
.put(" TJT", " +05")
|
|
||||||
.put(" TKT", " +14")
|
|
||||||
.put(" TLT", " +09")
|
|
||||||
.put(" TMT", " +05")
|
|
||||||
.put(" TOT", " +13")
|
|
||||||
.put(" TVT", " +12")
|
|
||||||
.put(" UCT", " ")
|
|
||||||
.put(" ULAT", " +08")
|
|
||||||
.put(" UTC", " ")
|
|
||||||
.put(" UYST", " −02")
|
|
||||||
.put(" UYT", " −03")
|
|
||||||
.put(" UZT", " +05")
|
|
||||||
.put(" VET", " −04:30")
|
|
||||||
.put(" VLAT", " +10")
|
|
||||||
.put(" VOLT", " +04")
|
|
||||||
.put(" VOST", " +06")
|
|
||||||
.put(" VUT", " +11")
|
|
||||||
.put(" WAKT", " +12")
|
|
||||||
.put(" WAST", " +02")
|
|
||||||
.put(" WAT", " +01")
|
|
||||||
.put(" WEDT", " +01")
|
|
||||||
.put(" WEST", " +01")
|
|
||||||
.put(" WET", " ")
|
|
||||||
.put(" WST", " +08")
|
|
||||||
.put(" YAKT", " +09")
|
|
||||||
.put(" YEKT", " +05")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to guess the timezone code, and replace it with a timezone offset.
|
|
||||||
* Note: JodaTime can guess a few codes already, they didn't include all codes since they are not standardized.
|
|
||||||
* This method should only be used in last resort.
|
|
||||||
*
|
|
||||||
* @param date Formated date, supposedly ending with a timezone code
|
|
||||||
* @return Date with the code replaced by its offset if there is a match
|
|
||||||
*/
|
|
||||||
public static String guessTimezoneOffset(String date) {
|
|
||||||
for (Entry<String, String> entry : TIMEZONE_CODE_MAP.entrySet()) {
|
|
||||||
String code = entry.getKey();
|
|
||||||
String offset = entry.getValue();
|
|
||||||
if (date.endsWith(code)) {
|
|
||||||
return date.substring(0, date.length() - code.length()) + offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
286
docs-core/src/main/java/com/sismics/util/EmailUtil.java
Normal file
286
docs-core/src/main/java/com/sismics/util/EmailUtil.java
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
package com.sismics.util;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.sismics.docs.core.constant.ConfigType;
|
||||||
|
import com.sismics.docs.core.constant.Constants;
|
||||||
|
import com.sismics.docs.core.dao.jpa.ConfigDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
|
import com.sismics.docs.core.model.jpa.Config;
|
||||||
|
import com.sismics.docs.core.util.ConfigUtil;
|
||||||
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
import freemarker.template.Configuration;
|
||||||
|
import freemarker.template.DefaultObjectWrapperBuilder;
|
||||||
|
import freemarker.template.Template;
|
||||||
|
import org.apache.commons.mail.HtmlEmail;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.json.Json;
|
||||||
|
import javax.json.JsonObject;
|
||||||
|
import javax.json.JsonReader;
|
||||||
|
import javax.mail.Message;
|
||||||
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.Multipart;
|
||||||
|
import javax.mail.Part;
|
||||||
|
import javax.mail.internet.MimeBodyPart;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emails utilities.
|
||||||
|
*
|
||||||
|
* @author jtremeaux
|
||||||
|
*/
|
||||||
|
public class EmailUtil {
|
||||||
|
/**
|
||||||
|
* Logger.
|
||||||
|
*/
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(EmailUtil.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an email content as string.
|
||||||
|
* The content is formatted from the given Freemarker template and parameters.
|
||||||
|
*
|
||||||
|
* @param templateName Template name
|
||||||
|
* @param paramRootMap Map of Freemarker parameters
|
||||||
|
* @param locale Locale
|
||||||
|
* @return Template as string
|
||||||
|
* @throws Exception e
|
||||||
|
*/
|
||||||
|
private static String getFormattedHtml(String templateName, Map<String, Object> paramRootMap, Locale locale) throws Exception {
|
||||||
|
Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
|
||||||
|
cfg.setClassForTemplateLoading(EmailUtil.class, "/email_template");
|
||||||
|
cfg.setObjectWrapper(new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_23).build());
|
||||||
|
Template template = cfg.getTemplate(templateName + "/template.ftl");
|
||||||
|
paramRootMap.put("messages", new ResourceBundleModel(MessageUtil.getMessage(locale),
|
||||||
|
new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_23).build()));
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
template.process(paramRootMap, sw);
|
||||||
|
|
||||||
|
return sw.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sending an email to a user.
|
||||||
|
*
|
||||||
|
* @param templateName Template name
|
||||||
|
* @param recipientUser Recipient user
|
||||||
|
* @param subject Email subject
|
||||||
|
* @param paramMap Email parameters
|
||||||
|
*/
|
||||||
|
private static void sendEmail(String templateName, UserDto recipientUser, String subject, Map<String, Object> paramMap) {
|
||||||
|
if (log.isInfoEnabled()) {
|
||||||
|
log.info("Sending email from template=" + templateName + " to user " + recipientUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Build email headers
|
||||||
|
HtmlEmail email = new HtmlEmail();
|
||||||
|
email.setCharset("UTF-8");
|
||||||
|
ConfigDao configDao = new ConfigDao();
|
||||||
|
|
||||||
|
// Hostname
|
||||||
|
String envHostname = System.getenv(Constants.SMTP_HOSTNAME_ENV);
|
||||||
|
if (envHostname == null) {
|
||||||
|
email.setHostName(ConfigUtil.getConfigStringValue(ConfigType.SMTP_HOSTNAME));
|
||||||
|
} else {
|
||||||
|
email.setHostName(envHostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Port
|
||||||
|
String envPort = System.getenv(Constants.SMTP_PORT_ENV);
|
||||||
|
if (envPort == null) {
|
||||||
|
email.setSmtpPort(ConfigUtil.getConfigIntegerValue(ConfigType.SMTP_PORT));
|
||||||
|
} else {
|
||||||
|
email.setSmtpPort(Integer.valueOf(envPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Username and password
|
||||||
|
String envUsername = System.getenv(Constants.SMTP_USERNAME_ENV);
|
||||||
|
String envPassword = System.getenv(Constants.SMTP_PASSWORD_ENV);
|
||||||
|
if (envUsername == null || envPassword == null) {
|
||||||
|
Config usernameConfig = configDao.getById(ConfigType.SMTP_USERNAME);
|
||||||
|
Config passwordConfig = configDao.getById(ConfigType.SMTP_PASSWORD);
|
||||||
|
if (usernameConfig != null && passwordConfig != null) {
|
||||||
|
email.setAuthentication(usernameConfig.getValue(), passwordConfig.getValue());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
email.setAuthentication(envUsername, envPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recipient
|
||||||
|
email.addTo(recipientUser.getEmail(), recipientUser.getUsername());
|
||||||
|
|
||||||
|
// Application name
|
||||||
|
Config themeConfig = configDao.getById(ConfigType.THEME);
|
||||||
|
String appName = "Sismics Docs";
|
||||||
|
if (themeConfig != null) {
|
||||||
|
try (JsonReader reader = Json.createReader(new StringReader(themeConfig.getValue()))) {
|
||||||
|
JsonObject themeJson = reader.readObject();
|
||||||
|
appName = themeJson.getString("name", "Sismics Docs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// From email address (defined only by configuration value in the database)
|
||||||
|
email.setFrom(ConfigUtil.getConfigStringValue(ConfigType.SMTP_FROM), appName);
|
||||||
|
|
||||||
|
// Locale (defined only by environment variable)
|
||||||
|
java.util.Locale userLocale = LocaleUtil.getLocale(System.getenv(Constants.DEFAULT_LANGUAGE_ENV));
|
||||||
|
|
||||||
|
// Subject and content
|
||||||
|
email.setSubject(appName + " - " + subject);
|
||||||
|
email.setTextMsg(MessageUtil.getMessage(userLocale, "email.no_html.error"));
|
||||||
|
|
||||||
|
// Add automatic parameters
|
||||||
|
String baseUrl = System.getenv(Constants.BASE_URL_ENV);
|
||||||
|
if (Strings.isNullOrEmpty(baseUrl)) {
|
||||||
|
log.error("DOCS_BASE_URL environnement variable needs to be set for proper email links");
|
||||||
|
baseUrl = ""; // At least the mail will be sent...
|
||||||
|
}
|
||||||
|
paramMap.put("base_url", baseUrl);
|
||||||
|
paramMap.put("app_name", appName);
|
||||||
|
|
||||||
|
// Build HTML content from Freemarker template
|
||||||
|
String htmlEmailTemplate = getFormattedHtml(templateName, paramMap, userLocale);
|
||||||
|
email.setHtmlMsg(htmlEmailTemplate);
|
||||||
|
|
||||||
|
// Send the email
|
||||||
|
email.send();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error sending email with template=" + templateName + " to user " + recipientUser, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sending an email to a user.
|
||||||
|
*
|
||||||
|
* @param templateName Template name
|
||||||
|
* @param recipientUser Recipient user
|
||||||
|
* @param paramMap Email parameters
|
||||||
|
*/
|
||||||
|
public static void sendEmail(String templateName, UserDto recipientUser, Map<String, Object> paramMap) {
|
||||||
|
java.util.Locale userLocale = LocaleUtil.getLocale(System.getenv(Constants.DEFAULT_LANGUAGE_ENV));
|
||||||
|
String subject = MessageUtil.getMessage(userLocale, "email.template." + templateName + ".subject");
|
||||||
|
sendEmail(templateName, recipientUser, subject, paramMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an email content to be imported.
|
||||||
|
*
|
||||||
|
* @param part Email part
|
||||||
|
* @param mailContent Mail content modified by side-effect
|
||||||
|
*
|
||||||
|
* @throws MessagingException e
|
||||||
|
* @throws IOException e
|
||||||
|
*/
|
||||||
|
public static void parseMailContent(Part part, MailContent mailContent) throws MessagingException, IOException {
|
||||||
|
Object content = part.getContent();
|
||||||
|
if (content instanceof Multipart) {
|
||||||
|
Multipart multiPart = (Multipart) content;
|
||||||
|
int partCount = multiPart.getCount();
|
||||||
|
|
||||||
|
for (int partIndex = 0; partIndex < partCount; partIndex++) {
|
||||||
|
MimeBodyPart subPart = (MimeBodyPart) multiPart.getBodyPart(partIndex);
|
||||||
|
String disposition = subPart.getDisposition();
|
||||||
|
if (Part.ATTACHMENT.equalsIgnoreCase(disposition)) {
|
||||||
|
FileContent fileContent = new FileContent();
|
||||||
|
fileContent.name = subPart.getFileName();
|
||||||
|
fileContent.file = ThreadLocalContext.get().createTemporaryFile();
|
||||||
|
Files.copy(subPart.getInputStream(), fileContent.file, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
fileContent.size = Files.size(fileContent.file);
|
||||||
|
mailContent.fileContentList.add(fileContent);
|
||||||
|
} else {
|
||||||
|
parseMailContent(subPart, mailContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (content instanceof Message) {
|
||||||
|
// An email attached to an email, traverse its content
|
||||||
|
parseMailContent((Message) content, mailContent);
|
||||||
|
} else if (content instanceof String) {
|
||||||
|
if (mailContent.message == null) {
|
||||||
|
// Do not overwrite the content
|
||||||
|
if (part.isMimeType("text/plain")) {
|
||||||
|
mailContent.message = (String) content;
|
||||||
|
} else if (part.isMimeType("text/html")) {
|
||||||
|
// Convert HTML to plain text
|
||||||
|
mailContent.message = new HtmlToPlainText().getPlainText(Jsoup.parse((String) content));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (content instanceof InputStream) {
|
||||||
|
FileContent fileContent = new FileContent();
|
||||||
|
fileContent.file = ThreadLocalContext.get().createTemporaryFile();
|
||||||
|
Files.copy((InputStream) content, fileContent.file, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
fileContent.size = Files.size(fileContent.file);
|
||||||
|
mailContent.fileContentList.add(fileContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure defining a parsed email to be imported.
|
||||||
|
*/
|
||||||
|
public static class MailContent {
|
||||||
|
private String subject;
|
||||||
|
private String message;
|
||||||
|
private Date date;
|
||||||
|
|
||||||
|
List<FileContent> fileContentList = Lists.newArrayList();
|
||||||
|
|
||||||
|
public String getSubject() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FileContent> getFileContentList() {
|
||||||
|
return fileContentList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MailContent setSubject(String subject) {
|
||||||
|
this.subject = subject;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MailContent setDate(Date date) {
|
||||||
|
this.date = date;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure defining a file from an email to be imported.
|
||||||
|
*/
|
||||||
|
public static class FileContent {
|
||||||
|
private String name;
|
||||||
|
private Path file;
|
||||||
|
private long size;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getFile() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ public class EnvironmentUtil {
|
|||||||
* @return Running under Microsoft Windows
|
* @return Running under Microsoft Windows
|
||||||
*/
|
*/
|
||||||
public static boolean isWindows() {
|
public static boolean isWindows() {
|
||||||
return OS.indexOf("win") >= 0;
|
return OS.contains("win");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,7 +37,7 @@ public class EnvironmentUtil {
|
|||||||
* @return Running under Mac OS
|
* @return Running under Mac OS
|
||||||
*/
|
*/
|
||||||
public static boolean isMacOs() {
|
public static boolean isMacOs() {
|
||||||
return OS.indexOf("mac") >= 0;
|
return OS.contains("mac");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,7 +46,7 @@ public class EnvironmentUtil {
|
|||||||
* @return Running under UNIX
|
* @return Running under UNIX
|
||||||
*/
|
*/
|
||||||
public static boolean isUnix() {
|
public static boolean isUnix() {
|
||||||
return OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS.indexOf("aix") > 0;
|
return OS.contains("nix") || OS.contains("nux") || OS.contains("aix");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
130
docs-core/src/main/java/com/sismics/util/HtmlToPlainText.java
Normal file
130
docs-core/src/main/java/com/sismics/util/HtmlToPlainText.java
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package com.sismics.util;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.helper.StringUtil;
|
||||||
|
import org.jsoup.helper.Validate;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.nodes.Node;
|
||||||
|
import org.jsoup.nodes.TextNode;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import org.jsoup.select.NodeTraversor;
|
||||||
|
import org.jsoup.select.NodeVisitor;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTML to plain-text. This example program demonstrates the use of jsoup to convert HTML input to lightly-formatted
|
||||||
|
* plain-text. That is divergent from the general goal of jsoup's .text() methods, which is to get clean data from a
|
||||||
|
* scrape.
|
||||||
|
* <p>
|
||||||
|
* Note that this is a fairly simplistic formatter -- for real world use you'll want to embrace and extend.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* To invoke from the command line, assuming you've downloaded the jsoup jar to your current directory:</p>
|
||||||
|
* <p><code>java -cp jsoup.jar org.jsoup.examples.HtmlToPlainText url [selector]</code></p>
|
||||||
|
* where <i>url</i> is the URL to fetch, and <i>selector</i> is an optional CSS selector.
|
||||||
|
*
|
||||||
|
* @author Jonathan Hedley, jonathan@hedley.net
|
||||||
|
*/
|
||||||
|
public class HtmlToPlainText {
|
||||||
|
private static final String userAgent = "Mozilla/5.0 (jsoup)";
|
||||||
|
private static final int timeout = 5 * 1000;
|
||||||
|
|
||||||
|
public static void main(String... args) throws IOException {
|
||||||
|
Validate.isTrue(args.length == 1 || args.length == 2, "usage: java -cp jsoup.jar org.jsoup.examples.HtmlToPlainText url [selector]");
|
||||||
|
final String url = args[0];
|
||||||
|
final String selector = args.length == 2 ? args[1] : null;
|
||||||
|
|
||||||
|
// fetch the specified URL and parse to a HTML DOM
|
||||||
|
Document doc = Jsoup.connect(url).userAgent(userAgent).timeout(timeout).get();
|
||||||
|
|
||||||
|
HtmlToPlainText formatter = new HtmlToPlainText();
|
||||||
|
|
||||||
|
if (selector != null) {
|
||||||
|
Elements elements = doc.select(selector); // get each element that matches the CSS selector
|
||||||
|
for (Element element : elements) {
|
||||||
|
String plainText = formatter.getPlainText(element); // format that element to plain text
|
||||||
|
System.out.println(plainText);
|
||||||
|
}
|
||||||
|
} else { // format the whole doc
|
||||||
|
String plainText = formatter.getPlainText(doc);
|
||||||
|
System.out.println(plainText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format an Element to plain-text
|
||||||
|
* @param element the root element to format
|
||||||
|
* @return formatted text
|
||||||
|
*/
|
||||||
|
public String getPlainText(Element element) {
|
||||||
|
FormattingVisitor formatter = new FormattingVisitor();
|
||||||
|
NodeTraversor.traverse(formatter, element); // walk the DOM, and call .head() and .tail() for each node
|
||||||
|
|
||||||
|
return formatter.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// the formatting rules, implemented in a breadth-first DOM traverse
|
||||||
|
private class FormattingVisitor implements NodeVisitor {
|
||||||
|
private static final int maxWidth = 80;
|
||||||
|
private int width = 0;
|
||||||
|
private StringBuilder accum = new StringBuilder(); // holds the accumulated text
|
||||||
|
|
||||||
|
// hit when the node is first seen
|
||||||
|
public void head(Node node, int depth) {
|
||||||
|
String name = node.nodeName();
|
||||||
|
if (node instanceof TextNode)
|
||||||
|
append(((TextNode) node).text()); // TextNodes carry all user-readable text in the DOM.
|
||||||
|
else if (name.equals("li"))
|
||||||
|
append("\n * ");
|
||||||
|
else if (name.equals("dt"))
|
||||||
|
append(" ");
|
||||||
|
else if (StringUtil.in(name, "p", "h1", "h2", "h3", "h4", "h5", "tr"))
|
||||||
|
append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// hit when all of the node's children (if any) have been visited
|
||||||
|
public void tail(Node node, int depth) {
|
||||||
|
String name = node.nodeName();
|
||||||
|
if (StringUtil.in(name, "br", "dd", "dt", "p", "h1", "h2", "h3", "h4", "h5"))
|
||||||
|
append("\n");
|
||||||
|
else if (name.equals("a"))
|
||||||
|
append(String.format(" <%s>", node.absUrl("href")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// appends text to the string builder with a simple word wrap method
|
||||||
|
private void append(String text) {
|
||||||
|
if (text.startsWith("\n"))
|
||||||
|
width = 0; // reset counter if starts with a newline. only from formats above, not in natural text
|
||||||
|
if (text.equals(" ") &&
|
||||||
|
(accum.length() == 0 || StringUtil.in(accum.substring(accum.length() - 1), " ", "\n")))
|
||||||
|
return; // don't accumulate long runs of empty spaces
|
||||||
|
|
||||||
|
if (text.length() + width > maxWidth) { // won't fit, needs to wrap
|
||||||
|
String words[] = text.split("\\s+");
|
||||||
|
for (int i = 0; i < words.length; i++) {
|
||||||
|
String word = words[i];
|
||||||
|
boolean last = i == words.length - 1;
|
||||||
|
if (!last) // insert a space if not the last word
|
||||||
|
word = word + " ";
|
||||||
|
if (word.length() + width > maxWidth) { // wrap and reset counter
|
||||||
|
accum.append("\n").append(word);
|
||||||
|
width = word.length();
|
||||||
|
} else {
|
||||||
|
accum.append(word);
|
||||||
|
width += word.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // fits as is, without need to wrap text
|
||||||
|
accum.append(text);
|
||||||
|
width += text.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return accum.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,8 @@
|
|||||||
package com.sismics.util;
|
package com.sismics.util;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import java.text.SimpleDateFormat;
|
||||||
import org.slf4j.LoggerFactory;
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP request utilities.
|
* HTTP request utilities.
|
||||||
@@ -17,77 +11,17 @@ import java.net.URLConnection;
|
|||||||
*/
|
*/
|
||||||
public class HttpUtil {
|
public class HttpUtil {
|
||||||
/**
|
/**
|
||||||
* Logger.
|
* Format of the expires header.
|
||||||
*/
|
*/
|
||||||
private static final Logger log = LoggerFactory.getLogger(HttpUtil.class);
|
private static final SimpleDateFormat EXPIRES_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the content of an URL into a string.
|
* Build an Expires HTTP header.
|
||||||
*
|
*
|
||||||
* @param url URL to load
|
* @param futureTime Expire interval
|
||||||
* @return Contents of the resource
|
* @return Formatted header value
|
||||||
*/
|
*/
|
||||||
public static String readUrlIntoString(URL url) {
|
public static String buildExpiresHeader(long futureTime) {
|
||||||
URLConnection connection;
|
return EXPIRES_FORMAT.format(new Date().getTime() + futureTime);
|
||||||
BufferedReader in = null;
|
|
||||||
try {
|
|
||||||
connection = url.openConnection();
|
|
||||||
in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
String inputLine;
|
|
||||||
while ((inputLine = in.readLine()) != null) {
|
|
||||||
sb.append(inputLine);
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (log.isErrorEnabled()) {
|
|
||||||
log.error("Error reading URL", e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
if (in != null) {
|
|
||||||
try {
|
|
||||||
in.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
// NOP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String postUrl(URL url, String data) throws IOException {
|
|
||||||
OutputStreamWriter wr = null;
|
|
||||||
BufferedReader rd = null;
|
|
||||||
try {
|
|
||||||
URLConnection conn = url.openConnection();
|
|
||||||
conn.setDoOutput(true);
|
|
||||||
wr = new OutputStreamWriter(conn.getOutputStream());
|
|
||||||
wr.write(data);
|
|
||||||
wr.flush();
|
|
||||||
|
|
||||||
// Get the response
|
|
||||||
rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
String line = null;
|
|
||||||
while ((line = rd.readLine()) != null) {
|
|
||||||
sb.append(line).append("\n");
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
} finally {
|
|
||||||
if (wr != null) {
|
|
||||||
try {
|
|
||||||
wr.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// NOP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rd != null) {
|
|
||||||
try {
|
|
||||||
rd.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// NOP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
167
docs-core/src/main/java/com/sismics/util/ImageDeskew.java
Normal file
167
docs-core/src/main/java/com/sismics/util/ImageDeskew.java
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
package com.sismics.util;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <a url=http://www.jdeskew.com/>JDeskew</a>
|
||||||
|
*/
|
||||||
|
public class ImageDeskew {
|
||||||
|
/**
|
||||||
|
* Representation of a line in the image.
|
||||||
|
*/
|
||||||
|
public class HoughLine {
|
||||||
|
|
||||||
|
// count of points in the line
|
||||||
|
public int count = 0;
|
||||||
|
// index in matrix.
|
||||||
|
public int index = 0;
|
||||||
|
// the line is represented as all x, y that solve y * cos(alpha) - x *
|
||||||
|
// sin(alpha) = d
|
||||||
|
public double alpha;
|
||||||
|
public double d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the source image
|
||||||
|
private BufferedImage cImage;
|
||||||
|
// the range of angles to search for lines
|
||||||
|
private double cAlphaStart = -20;
|
||||||
|
private double cAlphaStep = 0.2;
|
||||||
|
private int cSteps = 40 * 5;
|
||||||
|
// pre-calculation of sin and cos
|
||||||
|
private double[] cSinA;
|
||||||
|
private double[] cCosA;
|
||||||
|
// range of d
|
||||||
|
private double cDMin;
|
||||||
|
private double cDStep = 1.0;
|
||||||
|
private int cDCount;
|
||||||
|
// count of points that fit in a line
|
||||||
|
private int[] cHMatrix;
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
public ImageDeskew(BufferedImage image) {
|
||||||
|
this.cImage = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the skew angle of the image cImage
|
||||||
|
public double getSkewAngle() {
|
||||||
|
ImageDeskew.HoughLine[] hl;
|
||||||
|
double sum = 0.0;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
// perform Hough Transformation
|
||||||
|
calc();
|
||||||
|
// top 20 of the detected lines in the image
|
||||||
|
hl = getTop(20);
|
||||||
|
|
||||||
|
if (hl.length >= 20) {
|
||||||
|
// average angle of the lines
|
||||||
|
for (int i = 0; i < 19; i++) {
|
||||||
|
sum += hl[i].alpha;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return (sum / count);
|
||||||
|
} else {
|
||||||
|
return 0.0d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the count lines in the image with most points
|
||||||
|
private ImageDeskew.HoughLine[] getTop(int count) {
|
||||||
|
|
||||||
|
ImageDeskew.HoughLine[] hl = new ImageDeskew.HoughLine[count];
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
hl[i] = new ImageDeskew.HoughLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageDeskew.HoughLine tmp;
|
||||||
|
|
||||||
|
for (int i = 0; i < (this.cHMatrix.length - 1); i++) {
|
||||||
|
if (this.cHMatrix[i] > hl[count - 1].count) {
|
||||||
|
hl[count - 1].count = this.cHMatrix[i];
|
||||||
|
hl[count - 1].index = i;
|
||||||
|
int j = count - 1;
|
||||||
|
while ((j > 0) && (hl[j].count > hl[j - 1].count)) {
|
||||||
|
tmp = hl[j];
|
||||||
|
hl[j] = hl[j - 1];
|
||||||
|
hl[j - 1] = tmp;
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int alphaIndex;
|
||||||
|
int dIndex;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
dIndex = hl[i].index / cSteps; // integer division, no
|
||||||
|
// remainder
|
||||||
|
alphaIndex = hl[i].index - dIndex * cSteps;
|
||||||
|
hl[i].alpha = getAlpha(alphaIndex);
|
||||||
|
hl[i].d = dIndex + cDMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hough Transformation
|
||||||
|
private void calc() {
|
||||||
|
int hMin = (int) ((this.cImage.getHeight()) / 4.0);
|
||||||
|
int hMax = (int) ((this.cImage.getHeight()) * 3.0 / 4.0);
|
||||||
|
init();
|
||||||
|
|
||||||
|
for (int y = hMin; y < hMax; y++) {
|
||||||
|
for (int x = 1; x < (this.cImage.getWidth() - 2); x++) {
|
||||||
|
// only lower edges are considered
|
||||||
|
if (ImageUtil.isBlack(this.cImage, x, y)) {
|
||||||
|
if (!ImageUtil.isBlack(this.cImage, x, y + 1)) {
|
||||||
|
calc(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate all lines through the point (x,y)
|
||||||
|
private void calc(int x, int y) {
|
||||||
|
double d;
|
||||||
|
int dIndex;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
for (int alpha = 0; alpha < (this.cSteps - 1); alpha++) {
|
||||||
|
d = y * this.cCosA[alpha] - x * this.cSinA[alpha];
|
||||||
|
dIndex = (int) (d - this.cDMin);
|
||||||
|
index = dIndex * this.cSteps + alpha;
|
||||||
|
try {
|
||||||
|
this.cHMatrix[index] += 1;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.out.println(ex.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
|
||||||
|
double angle;
|
||||||
|
|
||||||
|
// pre-calculation of sin and cos
|
||||||
|
this.cSinA = new double[this.cSteps - 1];
|
||||||
|
this.cCosA = new double[this.cSteps - 1];
|
||||||
|
|
||||||
|
for (int i = 0; i < (this.cSteps - 1); i++) {
|
||||||
|
angle = getAlpha(i) * Math.PI / 180.0;
|
||||||
|
this.cSinA[i] = Math.sin(angle);
|
||||||
|
this.cCosA[i] = Math.cos(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// range of d
|
||||||
|
this.cDMin = -this.cImage.getWidth();
|
||||||
|
this.cDCount = (int) (2.0 * ((this.cImage.getWidth() + this.cImage.getHeight())) / this.cDStep);
|
||||||
|
this.cHMatrix = new int[this.cDCount * this.cSteps];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getAlpha(int index) {
|
||||||
|
return this.cAlphaStart + (index * this.cAlphaStep);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,20 @@
|
|||||||
package com.sismics.util;
|
package com.sismics.util;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import com.google.common.base.Charsets;
|
||||||
import java.io.IOException;
|
import com.google.common.hash.Hashing;
|
||||||
import java.io.OutputStream;
|
import com.sismics.util.mime.MimeType;
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import javax.imageio.IIOImage;
|
import javax.imageio.IIOImage;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.ImageWriteParam;
|
import javax.imageio.ImageWriteParam;
|
||||||
import javax.imageio.ImageWriter;
|
import javax.imageio.ImageWriter;
|
||||||
import javax.imageio.stream.ImageOutputStream;
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
|
import java.awt.*;
|
||||||
import com.google.common.base.Charsets;
|
import java.awt.image.BufferedImage;
|
||||||
import com.google.common.hash.Hashing;
|
import java.awt.image.WritableRaster;
|
||||||
import com.sismics.util.mime.MimeType;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image processing utilities.
|
* Image processing utilities.
|
||||||
@@ -34,12 +35,23 @@ public class ImageUtil {
|
|||||||
ImageWriter writer = null;
|
ImageWriter writer = null;
|
||||||
ImageOutputStream imageOutputStream = null;
|
ImageOutputStream imageOutputStream = null;
|
||||||
try {
|
try {
|
||||||
writer = (ImageWriter) iter.next();
|
writer = iter.next();
|
||||||
ImageWriteParam iwp = writer.getDefaultWriteParam();
|
ImageWriteParam iwp = writer.getDefaultWriteParam();
|
||||||
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||||
iwp.setCompressionQuality(1.f);
|
iwp.setCompressionQuality(1.f);
|
||||||
imageOutputStream = ImageIO.createImageOutputStream(outputStream);
|
imageOutputStream = ImageIO.createImageOutputStream(outputStream);
|
||||||
writer.setOutput(imageOutputStream);
|
writer.setOutput(imageOutputStream);
|
||||||
|
|
||||||
|
if (image.getColorModel().hasAlpha()) {
|
||||||
|
// Strip alpha channel
|
||||||
|
BufferedImage noAlphaImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||||
|
Graphics graphics = noAlphaImage.getGraphics();
|
||||||
|
graphics.setColor(Color.WHITE);
|
||||||
|
graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
|
||||||
|
graphics.drawImage(image, 0, 0, null);
|
||||||
|
image = noAlphaImage;
|
||||||
|
}
|
||||||
|
|
||||||
IIOImage iioImage = new IIOImage(image, null, null);
|
IIOImage iioImage = new IIOImage(image, null, null);
|
||||||
writer.write(null, iioImage, iwp);
|
writer.write(null, iioImage, iwp);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -69,7 +81,7 @@ public class ImageUtil {
|
|||||||
* Compute Gravatar hash.
|
* Compute Gravatar hash.
|
||||||
* See https://en.gravatar.com/site/implement/hash/.
|
* See https://en.gravatar.com/site/implement/hash/.
|
||||||
*
|
*
|
||||||
* @param email
|
* @param email Email
|
||||||
* @return Gravatar hash
|
* @return Gravatar hash
|
||||||
*/
|
*/
|
||||||
public static String computeGravatar(String email) {
|
public static String computeGravatar(String email) {
|
||||||
@@ -81,4 +93,40 @@ public class ImageUtil {
|
|||||||
email.trim().toLowerCase(), Charsets.UTF_8)
|
email.trim().toLowerCase(), Charsets.UTF_8)
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isBlack(BufferedImage image, int x, int y) {
|
||||||
|
if (image.getType() == BufferedImage.TYPE_BYTE_BINARY) {
|
||||||
|
WritableRaster raster = image.getRaster();
|
||||||
|
int pixelRGBValue = raster.getSample(x, y, 0);
|
||||||
|
return pixelRGBValue == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int luminanceValue = 140;
|
||||||
|
return isBlack(image, x, y, luminanceValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isBlack(BufferedImage image, int x, int y, int luminanceCutOff) {
|
||||||
|
int pixelRGBValue;
|
||||||
|
int r;
|
||||||
|
int g;
|
||||||
|
int b;
|
||||||
|
double luminance = 0.0;
|
||||||
|
|
||||||
|
// return white on areas outside of image boundaries
|
||||||
|
if (x < 0 || y < 0 || x > image.getWidth() || y > image.getHeight()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
pixelRGBValue = image.getRGB(x, y);
|
||||||
|
r = (pixelRGBValue >> 16) & 0xff;
|
||||||
|
g = (pixelRGBValue >> 8) & 0xff;
|
||||||
|
b = (pixelRGBValue) & 0xff;
|
||||||
|
luminance = (r * 0.299) + (g * 0.587) + (b * 0.114);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore.
|
||||||
|
}
|
||||||
|
|
||||||
|
return luminance < luminanceCutOff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.sismics.rest.util;
|
package com.sismics.util;
|
||||||
|
|
||||||
import javax.json.Json;
|
import javax.json.Json;
|
||||||
import javax.json.JsonValue;
|
import javax.json.JsonValue;
|
||||||
@@ -34,4 +34,17 @@ public class JsonUtil {
|
|||||||
}
|
}
|
||||||
return Json.createObjectBuilder().add("_", value).build().get("_");
|
return Json.createObjectBuilder().add("_", value).build().get("_");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a JsonValue from an Long.
|
||||||
|
*
|
||||||
|
* @param value Value
|
||||||
|
* @return JsonValue
|
||||||
|
*/
|
||||||
|
public static JsonValue nullable(Long value) {
|
||||||
|
if (value == null) {
|
||||||
|
return JsonValue.NULL;
|
||||||
|
}
|
||||||
|
return Json.createObjectBuilder().add("_", value).build().get("_");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
36
docs-core/src/main/java/com/sismics/util/LocaleUtil.java
Normal file
36
docs-core/src/main/java/com/sismics/util/LocaleUtil.java
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package com.sismics.util;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locale utilities.
|
||||||
|
*
|
||||||
|
* @author jtremeaux
|
||||||
|
*/
|
||||||
|
public class LocaleUtil {
|
||||||
|
/**
|
||||||
|
* Returns a locale from the language / country / variation code (ex: fr_FR).
|
||||||
|
*
|
||||||
|
* @param localeCode Locale code
|
||||||
|
* @return Locale instance
|
||||||
|
*/
|
||||||
|
public static Locale getLocale(String localeCode) {
|
||||||
|
if (Strings.isNullOrEmpty(localeCode)) {
|
||||||
|
return Locale.ENGLISH;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] localeCodeArray = localeCode.split("_");
|
||||||
|
String language = localeCodeArray[0];
|
||||||
|
String country = "";
|
||||||
|
String variant = "";
|
||||||
|
if (localeCodeArray.length >= 2) {
|
||||||
|
country = localeCodeArray[1];
|
||||||
|
}
|
||||||
|
if (localeCodeArray.length >= 3) {
|
||||||
|
variant = localeCodeArray[2];
|
||||||
|
}
|
||||||
|
return new Locale(language, country, variant);
|
||||||
|
}
|
||||||
|
}
|
||||||
43
docs-core/src/main/java/com/sismics/util/MessageUtil.java
Normal file
43
docs-core/src/main/java/com/sismics/util/MessageUtil.java
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package com.sismics.util;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.MissingResourceException;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Messages utilities.
|
||||||
|
*
|
||||||
|
* @author jtremeaux
|
||||||
|
*/
|
||||||
|
public class MessageUtil {
|
||||||
|
/**
|
||||||
|
* Returns a localized message in the specified language.
|
||||||
|
* Returns **key** if no message exists for this key.
|
||||||
|
*
|
||||||
|
* @param locale Locale
|
||||||
|
* @param key Message key
|
||||||
|
* @param args Arguments to format
|
||||||
|
* @return Formatted message
|
||||||
|
*/
|
||||||
|
public static String getMessage(Locale locale, String key, Object... args) {
|
||||||
|
ResourceBundle resources = ResourceBundle.getBundle("messages", locale);
|
||||||
|
String message;
|
||||||
|
try {
|
||||||
|
message = resources.getString(key);
|
||||||
|
} catch (MissingResourceException e) {
|
||||||
|
message = "**" + key + "**";
|
||||||
|
}
|
||||||
|
return MessageFormat.format(message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the resource bundle corresponding to the specified language.
|
||||||
|
*
|
||||||
|
* @param locale Locale
|
||||||
|
* @return Resource bundle
|
||||||
|
*/
|
||||||
|
public static ResourceBundle getMessage(Locale locale) {
|
||||||
|
return ResourceBundle.getBundle("messages", locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package com.sismics.util;
|
||||||
|
|
||||||
|
import freemarker.ext.beans.BeansWrapper;
|
||||||
|
import freemarker.ext.beans.StringModel;
|
||||||
|
import freemarker.template.TemplateModel;
|
||||||
|
import freemarker.template.TemplateModelException;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.MissingResourceException;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override of {@link freemarker.ext.beans.ResourceBundleModel}
|
||||||
|
* to threat single quotes uniformely.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class ResourceBundleModel extends freemarker.ext.beans.ResourceBundleModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor.
|
||||||
|
*
|
||||||
|
* @param bundle Resource bundle
|
||||||
|
* @param wrapper Beans wrapper
|
||||||
|
*/
|
||||||
|
public ResourceBundleModel(ResourceBundle bundle, BeansWrapper wrapper) {
|
||||||
|
super(bundle, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@Override
|
||||||
|
public Object exec(List arguments) throws TemplateModelException {
|
||||||
|
// Must have at least one argument - the key
|
||||||
|
if (arguments.size() < 1)
|
||||||
|
throw new TemplateModelException("No message key was specified");
|
||||||
|
// Read it
|
||||||
|
Iterator it = arguments.iterator();
|
||||||
|
String key = unwrap((TemplateModel) it.next()).toString();
|
||||||
|
try {
|
||||||
|
// Copy remaining arguments into an Object[]
|
||||||
|
int args = arguments.size() - 1;
|
||||||
|
Object[] params = new Object[args];
|
||||||
|
for (int i = 0; i < args; ++i)
|
||||||
|
params[i] = unwrap((TemplateModel) it.next());
|
||||||
|
|
||||||
|
// Invoke format
|
||||||
|
return new StringModel(format(key, params), wrapper);
|
||||||
|
} catch (MissingResourceException e) {
|
||||||
|
throw new TemplateModelException("No such key: " + key);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new TemplateModelException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user