mirror of
https://github.com/sismics/docs.git
synced 2025-12-14 10:16:21 +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
|
||||
/.project
|
||||
*.iml
|
||||
node_modules
|
||||
import_test
|
||||
|
||||
19
.travis.yml
19
.travis.yml
@@ -3,9 +3,22 @@ dist: trusty
|
||||
language: java
|
||||
before_install:
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-jpn
|
||||
- sudo apt-get -y -q install 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
|
||||
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:
|
||||
global:
|
||||
- TESSDATA_PREFIX=/usr/share/tesseract-ocr
|
||||
- LC_NUMERIC=C
|
||||
- TESSDATA_PREFIX=/usr/share/tesseract-ocr
|
||||
- LC_NUMERIC=C
|
||||
- secure: LRGpjWORb0qy6VuypZjTAfA8uRHlFUMTwb77cenS9PPRBxuSnctC531asS9Xg3DqC5nsRxBBprgfCKotn5S8nBSD1ceHh84NASyzLSBft3xSMbg7f/2i7MQ+pGVwLncusBU6E/drnMFwZBleo+9M8Tf96axY5zuUp90MUTpSgt0=
|
||||
- secure: bCDDR6+I7PmSkuTYZv1HF/z98ANX/SFEESUCqxVmV5Gs0zFC0vQXaPJQ2xaJNRop1HZBFMZLeMMPleb0iOs985smpvK2F6Rbop9Tu+Vyo0uKqv9tbZ7F8Nfgnv9suHKZlL84FNeUQZJX6vsFIYPEJ/r7K5P/M0PdUy++fEwxEhU=
|
||||
- secure: ewXnzbkgCIHpDWtaWGMa1OYZJ/ki99zcIl4jcDPIC0eB3njX/WgfcC6i0Ke9mLqDqwXarWJ6helm22sNh+xtQiz6isfBtBX+novfRt9AANrBe3koCMUemMDy7oh5VflBaFNP0DVb8LSCnwf6dx6ZB5E9EB8knvk40quc/cXpGjY=
|
||||
- COMMIT=${TRAVIS_COMMIT::8}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
FROM sismics/debian-java7-jetty9
|
||||
MAINTAINER benjamin.gam@gmail.com
|
||||
FROM sismics/jetty:9.2.20-jdk7
|
||||
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 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-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.
|
||||
|
||||
Docs is written in Java, and may be run on any operating system with Java support.
|
||||
A demo is available at [demo.sismicsdocs.com](https://demo.sismicsdocs.com)
|
||||
- Guest login is enabled with read access on all documents
|
||||
- "admin" login with "admin" password
|
||||
- "demo" login with "password" password
|
||||
|
||||
Features
|
||||
--------
|
||||
@@ -25,9 +33,13 @@ Features
|
||||
- Flexible search engine
|
||||
- Full text search in all supported files
|
||||
- All [Dublin Core](http://dublincore.org/) metadata
|
||||
- Workflow system 
|
||||
- 256-bit AES encryption of stored files
|
||||
- Tag system with nesting
|
||||
- Import document from email (EML format) 
|
||||
- Automatic inbox scanning and importing 
|
||||
- User/group permission system
|
||||
- 2-factor authentication
|
||||
- Hierarchical groups
|
||||
- Audit log
|
||||
- Comments
|
||||
@@ -35,13 +47,23 @@ Features
|
||||
- Document sharing by URL
|
||||
- RESTful Web API
|
||||
- Fully featured Android client
|
||||
- [Mass files importer](https://github.com/sismics/docs/tree/master/docs-importer) (single or scan mode) 
|
||||
- Tested to 100k documents
|
||||
|
||||
Download
|
||||
--------
|
||||
|
||||
The latest release is downloadable here: <https://github.com/sismics/docs/releases> in WAR format.
|
||||
You will need a Java webapp server to run it, like [Jetty](http://eclipse.org/jetty/) or [Tomcat](http://tomcat.apache.org/)
|
||||
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
|
||||
----------------------------------
|
||||
|
||||
@@ -1,46 +1,27 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.1.0-beta1'
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
}
|
||||
}
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion '23.0.3'
|
||||
compileSdkVersion 26
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 23
|
||||
targetSdkVersion 26
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile file(System.getenv("TRACKINO_STORE_PATH"))
|
||||
storePassword System.getenv("TRACKINO_STORE_PASS")
|
||||
keyAlias System.getenv("TRACKINO_STORE_ALIAS")
|
||||
keyPassword System.getenv("TRACKINO_STORE_KEYPASS")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
versionName '1.0'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
@@ -50,13 +31,13 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: '*.jar')
|
||||
compile 'com.android.support:appcompat-v7:23.3.0'
|
||||
compile 'com.android.support:recyclerview-v7:23.3.0'
|
||||
compile 'com.android.support:design:23.3.0'
|
||||
compile 'com.android.support:appcompat-v7:26.1.0'
|
||||
compile 'com.android.support:recyclerview-v7:26.1.0'
|
||||
compile 'com.android.support:design:26.1.0'
|
||||
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
|
||||
compile 'org.greenrobot:eventbus:3.0.0'
|
||||
compile 'com.squareup.picasso:picasso:2.5.2'
|
||||
compile 'com.squareup.okhttp3:okhttp:3.1.1'
|
||||
compile "com.squareup.okhttp3:okhttp-urlconnection:3.1.1"
|
||||
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.0.2'
|
||||
compile 'com.squareup.okhttp3:okhttp:3.7.0'
|
||||
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.4.0'
|
||||
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ public class DocumentEditActivity extends AppCompatActivity {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
JSONArray tagArray = tags.optJSONArray("stats");
|
||||
JSONArray tagArray = tags.optJSONArray("tags");
|
||||
|
||||
List<JSONObject> tagList = new ArrayList<>();
|
||||
for (int i = 0; i < tagArray.length(); i++) {
|
||||
|
||||
@@ -16,6 +16,7 @@ import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@@ -51,7 +52,7 @@ import com.sismics.docs.resource.FileResource;
|
||||
import com.sismics.docs.service.FileUploadService;
|
||||
import com.sismics.docs.util.NetworkUtil;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
import com.sismics.docs.util.TagUtil;
|
||||
import com.sismics.docs.util.SpannableUtil;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
@@ -75,11 +76,6 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*/
|
||||
public static final int REQUEST_CODE_ADD_FILE = 1;
|
||||
|
||||
/**
|
||||
* Request code of editing document.
|
||||
*/
|
||||
public static final int REQUEST_CODE_EDIT_DOCUMENT = 2;
|
||||
|
||||
/**
|
||||
* File view pager.
|
||||
*/
|
||||
@@ -176,9 +172,11 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
// Fill the layout
|
||||
// Create date
|
||||
TextView createdDateTextView = (TextView) findViewById(R.id.createdDateTextView);
|
||||
createdDateTextView.setText(date);
|
||||
|
||||
// Description
|
||||
TextView descriptionTextView = (TextView) findViewById(R.id.descriptionTextView);
|
||||
if (description.isEmpty() || document.isNull("description")) {
|
||||
descriptionTextView.setVisibility(View.GONE);
|
||||
@@ -187,17 +185,20 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
descriptionTextView.setText(description);
|
||||
}
|
||||
|
||||
// Tags
|
||||
TextView tagTextView = (TextView) findViewById(R.id.tagTextView);
|
||||
if (tags.length() == 0) {
|
||||
tagTextView.setVisibility(View.GONE);
|
||||
} else {
|
||||
tagTextView.setVisibility(View.VISIBLE);
|
||||
tagTextView.setText(TagUtil.buildSpannable(tags));
|
||||
tagTextView.setText(SpannableUtil.buildSpannableTags(tags));
|
||||
}
|
||||
|
||||
// Language
|
||||
ImageView languageImageView = (ImageView) findViewById(R.id.languageImageView);
|
||||
languageImageView.setImageResource(getResources().getIdentifier(language, "drawable", getPackageName()));
|
||||
|
||||
// Shared status
|
||||
ImageView sharedImageView = (ImageView) findViewById(R.id.sharedImageView);
|
||||
sharedImageView.setVisibility(shared ? View.VISIBLE : View.GONE);
|
||||
|
||||
@@ -208,7 +209,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, DocumentEditActivity.class);
|
||||
intent.putExtra("document", DocumentViewActivity.this.document.toString());
|
||||
startActivityForResult(intent, REQUEST_CODE_EDIT_DOCUMENT);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -642,10 +643,10 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
// Action only available if the document is writable
|
||||
findViewById(R.id.actionEditDocument).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
||||
findViewById(R.id.actionUploadFile).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
||||
findViewById(R.id.actionSharing).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
||||
findViewById(R.id.actionDelete).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
||||
findViewById(R.id.actionEditDocument).setVisibility(writable ? View.VISIBLE : View.GONE);
|
||||
findViewById(R.id.actionUploadFile).setVisibility(writable ? View.VISIBLE : View.GONE);
|
||||
findViewById(R.id.actionSharing).setVisibility(writable ? View.VISIBLE : View.GONE);
|
||||
findViewById(R.id.actionDelete).setVisibility(writable ? View.VISIBLE : View.GONE);
|
||||
|
||||
// ACLs
|
||||
ListView aclListView = (ListView) findViewById(R.id.aclListView);
|
||||
@@ -679,10 +680,54 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
// Contributors
|
||||
TextView contributorsTextView = (TextView) findViewById(R.id.contributorsTextView);
|
||||
contributorsTextView.setText(SpannableUtil.buildSpannableContributors(document.optJSONArray("contributors")));
|
||||
|
||||
// Relations
|
||||
JSONArray relations = document.optJSONArray("relations");
|
||||
if (relations.length() > 0) {
|
||||
TextView relationsTextView = (TextView) findViewById(R.id.relationsTextView);
|
||||
relationsTextView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
relationsTextView.setText(SpannableUtil.buildSpannableRelations(relations));
|
||||
} else {
|
||||
findViewById(R.id.relationsLayout).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Additional dublincore metadata
|
||||
displayDublincoreMetadata(R.id.subjectTextView, R.id.subjectLayout, "subject");
|
||||
displayDublincoreMetadata(R.id.identifierTextView, R.id.identifierLayout, "identifier");
|
||||
displayDublincoreMetadata(R.id.publisherTextView, R.id.publisherLayout, "publisher");
|
||||
displayDublincoreMetadata(R.id.formatTextView, R.id.formatLayout, "format");
|
||||
displayDublincoreMetadata(R.id.sourceTextView, R.id.sourceLayout, "source");
|
||||
displayDublincoreMetadata(R.id.typeTextView, R.id.typeLayout, "type");
|
||||
displayDublincoreMetadata(R.id.coverageTextView, R.id.coverageLayout, "coverage");
|
||||
displayDublincoreMetadata(R.id.rightsTextView, R.id.rightsLayout, "rights");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a dublincore metadata.
|
||||
*
|
||||
* @param textViewId TextView ID
|
||||
* @param blockViewId View ID
|
||||
* @param name Name
|
||||
*/
|
||||
private void displayDublincoreMetadata(int textViewId, int blockViewId, String name) {
|
||||
if (document == null) return;
|
||||
String value = document.optString(name);
|
||||
if (document.isNull(name) || value.isEmpty()) {
|
||||
findViewById(blockViewId).setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
findViewById(blockViewId).setVisibility(View.VISIBLE);
|
||||
TextView textView = (TextView) findViewById(textViewId);
|
||||
textView.setText(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
|
||||
switch (view.getId()) {
|
||||
|
||||
@@ -87,13 +87,13 @@ public class MainActivity extends AppCompatActivity {
|
||||
tagListView.setEmptyView(tagProgressView);
|
||||
JSONObject cacheTags = PreferenceUtil.getCachedJson(this, PreferenceUtil.PREF_CACHED_TAGS_JSON);
|
||||
if (cacheTags != null) {
|
||||
tagListView.setAdapter(new TagListAdapter(cacheTags.optJSONArray("stats")));
|
||||
tagListView.setAdapter(new TagListAdapter(cacheTags.optJSONArray("tags")));
|
||||
}
|
||||
TagResource.stats(this, new HttpCallback() {
|
||||
TagResource.list(this, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject response) {
|
||||
PreferenceUtil.setCachedJson(MainActivity.this, PreferenceUtil.PREF_CACHED_TAGS_JSON, response);
|
||||
tagListView.setAdapter(new TagListAdapter(response.optJSONArray("stats")));
|
||||
tagListView.setAdapter(new TagListAdapter(response.optJSONArray("tags")));
|
||||
tagProgressView.setVisibility(View.GONE);
|
||||
tagListView.setEmptyView(tagEmptyView);
|
||||
}
|
||||
@@ -158,6 +158,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public void onFinish() {
|
||||
// Force logout in all cases, so the user is not stuck in case of network error
|
||||
PreferenceUtil.clearAuthToken(MainActivity.this);
|
||||
ApplicationContext.getInstance().setUserInfo(getApplicationContext(), null);
|
||||
startActivity(new Intent(MainActivity.this, LoginActivity.class));
|
||||
finish();
|
||||
|
||||
@@ -9,7 +9,7 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.util.TagUtil;
|
||||
import com.sismics.docs.util.SpannableUtil;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
@@ -69,7 +69,7 @@ public class DocListAdapter extends RecyclerView.Adapter<DocListAdapter.ViewHold
|
||||
holder.titleTextView.setText(document.optString("title"));
|
||||
|
||||
JSONArray tags = document.optJSONArray("tags");
|
||||
holder.subtitleTextView.setText(TagUtil.buildSpannable(tags));
|
||||
holder.subtitleTextView.setText(SpannableUtil.buildSpannableTags(tags));
|
||||
|
||||
String date = DateFormat.getDateFormat(holder.dateTextView.getContext()).format(new Date(document.optLong("create_date")));
|
||||
holder.dateTextView.setText(date);
|
||||
|
||||
@@ -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("eng", R.string.language_english, R.drawable.eng));
|
||||
languageList.add(new Language("jpn", R.string.language_japanese, R.drawable.jpn));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -46,7 +46,7 @@ public class TagListAdapter extends BaseAdapter {
|
||||
|
||||
// Reorder tags by parent/child relation and compute depth
|
||||
int depth = 0;
|
||||
initTags(tags, JSONObject.NULL.toString(), depth);
|
||||
initTags(tags, "", depth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,11 +60,10 @@ public class TagListAdapter extends BaseAdapter {
|
||||
// Get all tags with this parent
|
||||
for (JSONObject tag : tags) {
|
||||
String tagParentId = tag.optString("parent");
|
||||
if (tagParentId.equals(parentId)) {
|
||||
if (parentId.equals(tagParentId)) {
|
||||
TagItem tagItem = new TagItem();
|
||||
tagItem.id = tag.optString("id");
|
||||
tagItem.name = tag.optString("name");
|
||||
tagItem.count = tag.optInt("count");
|
||||
tagItem.color = tag.optString("color");
|
||||
tagItem.depth = depth;
|
||||
tagItemList.add(tagItem);
|
||||
@@ -99,8 +98,6 @@ public class TagListAdapter extends BaseAdapter {
|
||||
TagItem tagItem = getItem(position);
|
||||
TextView tagTextView = (TextView) view.findViewById(R.id.tagTextView);
|
||||
tagTextView.setText(tagItem.name);
|
||||
TextView tagCountTextView = (TextView) view.findViewById(R.id.tagCountTextView);
|
||||
tagCountTextView.setText(String.format(Locale.ENGLISH, "%d", tagItem.count));
|
||||
|
||||
// Label color filtering
|
||||
ImageView labelImageView = (ImageView) view.findViewById(R.id.labelImageView);
|
||||
@@ -125,7 +122,6 @@ public class TagListAdapter extends BaseAdapter {
|
||||
public static class TagItem {
|
||||
private String id;
|
||||
private String name;
|
||||
private int count;
|
||||
private String color;
|
||||
private int depth;
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ public class SearchFragment extends DialogFragment {
|
||||
dialog.cancel();
|
||||
return dialog;
|
||||
}
|
||||
JSONArray tagArray = tags.optJSONArray("stats");
|
||||
JSONArray tagArray = tags.optJSONArray("tags");
|
||||
|
||||
List<JSONObject> tagList = new ArrayList<>();
|
||||
for (int i = 0; i < tagArray.length(); i++) {
|
||||
|
||||
@@ -16,14 +16,14 @@ import okhttp3.Request;
|
||||
*/
|
||||
public class TagResource extends BaseResource {
|
||||
/**
|
||||
* GET /tag/stats.
|
||||
* GET /tag/list.
|
||||
*
|
||||
* @param context Context
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void stats(Context context, HttpCallback callback) {
|
||||
public static void list(Context context, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/tag/stats"))
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/tag/list"))
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
|
||||
@@ -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;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.DownloadManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
/**
|
||||
* Utility class for network actions.
|
||||
@@ -19,9 +24,14 @@ public class NetworkUtil {
|
||||
* @param title Notification title
|
||||
* @param description Notification description
|
||||
*/
|
||||
public static void downloadFile(Context context, String url, String fileName, String title, String description) {
|
||||
String authToken = PreferenceUtil.getAuthToken(context);
|
||||
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
public static void downloadFile(Activity activity, String url, String fileName, String title, String description) {
|
||||
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(activity, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
String authToken = PreferenceUtil.getAuthToken(activity);
|
||||
DownloadManager downloadManager = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
|
||||
|
||||
@@ -126,6 +126,7 @@ public class PreferenceUtil {
|
||||
/**
|
||||
* Returns auth token cookie from shared preferences.
|
||||
*
|
||||
* @param context Context
|
||||
* @return Auth token
|
||||
*/
|
||||
public static String getAuthToken(Context context) {
|
||||
@@ -140,6 +141,16 @@ public class PreferenceUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all auth tokens.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
public static void clearAuthToken(Context context) {
|
||||
PersistentCookieStore cookieStore = new PersistentCookieStore(context);
|
||||
cookieStore.removeAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cleaned server URL.
|
||||
*
|
||||
|
||||
@@ -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,241 +142,523 @@
|
||||
|
||||
<!-- Right drawer -->
|
||||
|
||||
<LinearLayout
|
||||
<ScrollView
|
||||
android:id="@+id/right_drawer"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:orientation="vertical"
|
||||
android:clickable="true"
|
||||
android:background="#fff"
|
||||
android:elevation="5dp">
|
||||
|
||||
<!-- Actions -->
|
||||
android:elevation="5dp"
|
||||
android:layout_gravity="end">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Actions -->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
style="?android:buttonBarStyle">
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionEditDocument"
|
||||
<LinearLayout
|
||||
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"/>
|
||||
android:orientation="horizontal"
|
||||
style="?android:buttonBarStyle">
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionUploadFile"
|
||||
<Button
|
||||
android:id="@+id/actionDownload"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_file_download_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/download_document"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionExportPdf"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_description_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/export_pdf"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionAuditLog"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_assignment_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/activity"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
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"/>
|
||||
android:orientation="horizontal"
|
||||
style="?android:buttonBarStyle">
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionDownload"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_file_download_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/download_document"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
<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="0dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionUploadFile"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_file_upload_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/upload_file"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionSharing"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_share_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/share"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionDelete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_delete_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/delete_document"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:background="#eee"/>
|
||||
|
||||
<!-- Document metadata -->
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/detailLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
style="?android:buttonBarStyle">
|
||||
android:padding="12dp">
|
||||
|
||||
<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="0dp"/>
|
||||
<TextView
|
||||
android:id="@+id/createdDateLabel"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/created_date"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionSharing"
|
||||
<TextView
|
||||
android:id="@+id/createdDateTextView"
|
||||
android:layout_toRightOf="@id/createdDateLabel"
|
||||
android:layout_toEndOf="@id/createdDateLabel"
|
||||
android:layout_toLeftOf="@id/sharedImageView"
|
||||
android:layout_toStartOf="@id/sharedImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_share_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/share"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="0dp"/>
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_alignParentTop="true"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<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"/>
|
||||
<TextView
|
||||
android:id="@+id/creatorLabel"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_below="@+id/createdDateLabel"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/creator"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionDelete"
|
||||
<TextView
|
||||
android:id="@+id/creatorTextView"
|
||||
android:layout_toRightOf="@id/creatorLabel"
|
||||
android:layout_toEndOf="@id/creatorLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_below="@+id/createdDateTextView"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagTextView"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_delete_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/delete_document"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="0dp"/>
|
||||
android:layout_below="@id/creatorLabel"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:maxLines="1"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/descriptionTextView"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_below="@id/tagTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<ImageView
|
||||
android:contentDescription="@string/shared"
|
||||
android:id="@+id/sharedImageView"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_folder_shared_grey600_24dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_toLeftOf="@+id/languageImageView"
|
||||
android:layout_toStartOf="@+id/languageImageView"/>
|
||||
|
||||
<ImageView
|
||||
android:contentDescription="@string/language"
|
||||
android:id="@+id/languageImageView"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Additional dublincore metadata -->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<!-- Subject -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/subjectLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/subject"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subjectTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Identifier -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/identifierLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/identifier"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/identifierTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Publisher -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/publisherLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/publisher"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/publisherTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Format -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/formatLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/format"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/formatTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Source -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/sourceLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/source"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sourceTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Type -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/typeLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/type"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/typeTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Coverage -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/coverageLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/coverage"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/coverageTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Rights -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/rightsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/rights"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rightsTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Contributors -->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/contributors"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contributorsTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Relations -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/relationsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="2dp">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/relations"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/relationsTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:background="#eee"/>
|
||||
|
||||
<!-- ACLs -->
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="#de000000"
|
||||
android:text="@string/who_can_access"
|
||||
android:layout_margin="12dp"/>
|
||||
|
||||
<com.sismics.docs.ui.view.NonScrollListView
|
||||
android:id="@+id/aclListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:choiceMode="singleChoice"
|
||||
android:divider="@android:color/transparent"
|
||||
android:dividerHeight="0dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:background="#eee"/>
|
||||
|
||||
<!-- Document metadata -->
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/detailLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/createdDateLabel"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/created_date"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/createdDateTextView"
|
||||
android:layout_toRightOf="@id/createdDateLabel"
|
||||
android:layout_toEndOf="@id/createdDateLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_alignParentTop="true"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/creatorLabel"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_below="@+id/createdDateLabel"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/creator"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/creatorTextView"
|
||||
android:layout_toRightOf="@id/creatorLabel"
|
||||
android:layout_toEndOf="@id/creatorLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_below="@+id/createdDateTextView"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagTextView"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/creatorLabel"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:maxLines="1"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/descriptionTextView"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_below="@id/tagTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sharedImageView"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_folder_shared_grey600_24dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_toLeftOf="@+id/languageImageView"
|
||||
android:layout_toStartOf="@+id/languageImageView"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/languageImageView"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:background="#eee"/>
|
||||
|
||||
<!-- ACLs -->
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="#de000000"
|
||||
android:text="@string/who_can_access"
|
||||
android:layout_margin="12dp"/>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/aclListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="singleChoice"
|
||||
android:divider="@android:color/transparent"
|
||||
android:dividerHeight="0dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</android.support.v4.widget.DrawerLayout>
|
||||
@@ -29,16 +29,4 @@
|
||||
android:text="Appartement"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagCountTextView"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textColor="#888"
|
||||
android:text="5"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
</RelativeLayout>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 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>
|
||||
|
||||
<!-- 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_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="username">Username</string>
|
||||
<string name="password">Password</string>
|
||||
@@ -46,7 +46,7 @@
|
||||
<string name="add_share_hint">Name the share (optional)</string>
|
||||
<string name="document_not_shared">This document is not currently shared</string>
|
||||
<string name="delete_share">Delete this share</string>
|
||||
<string name="send_share">Send this share</string>
|
||||
<string name="send_share">Send this share link</string>
|
||||
<string name="error_loading_shares">Error loading shares</string>
|
||||
<string name="error_adding_share">Error adding share</string>
|
||||
<string name="share_default_name">Share link</string>
|
||||
@@ -69,9 +69,8 @@
|
||||
<string name="pref_clear_history_summary">Wipe the recent search suggestions</string>
|
||||
<string name="pref_clear_history_success">Search history cleared</string>
|
||||
<string name="pref_cache_size">Cache size</string>
|
||||
<string name="language_french">French</string>
|
||||
<string name="language_english">English</string>
|
||||
<string name="language_japanese">Japanese</string>
|
||||
<string name="language_french" translatable="false">Français</string>
|
||||
<string name="language_english" translatable="false">English</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="edit_document">Edit</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_display">%1$d/%2$d MB</string>
|
||||
<string name="validation_code">Validation code</string>
|
||||
<string name="shared">Shared</string>
|
||||
<string name="language">Language</string>
|
||||
<string name="coverage">Coverage</string>
|
||||
<string name="type">Type</string>
|
||||
<string name="source">Source</string>
|
||||
<string name="format">Format</string>
|
||||
<string name="publisher">Publisher</string>
|
||||
<string name="identifier">Identifier</string>
|
||||
<string name="subject">Subject</string>
|
||||
<string name="rights">Rights</string>
|
||||
<string name="contributors">Contributors</string>
|
||||
<string name="relations">Relations</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
org.gradle.jvmargs=-Xmx3072m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#Sat Jan 16 19:15:13 CET 2016
|
||||
#Tue Nov 14 23:55:56 CET 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.sismics.docs</groupId>
|
||||
<artifactId>docs-parent</artifactId>
|
||||
<version>1.4</version>
|
||||
<version>1.5</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -52,6 +52,26 @@
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</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>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
@@ -92,6 +112,11 @@
|
||||
<artifactId>lucene-queryparser</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>javax.mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Only there to read old index and rebuild them -->
|
||||
<dependency>
|
||||
<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
|
||||
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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Application constants.
|
||||
*
|
||||
@@ -30,6 +30,11 @@ public class Constants {
|
||||
*/
|
||||
public static final String LUCENE_DIRECTORY_STORAGE_FILE = "FILE";
|
||||
|
||||
/**
|
||||
* Guest user ID.
|
||||
*/
|
||||
public static final String GUEST_USER_ID = "guest";
|
||||
|
||||
/**
|
||||
* Default generic user role.
|
||||
*/
|
||||
@@ -38,5 +43,48 @@ public class Constants {
|
||||
/**
|
||||
* Supported document languages.
|
||||
*/
|
||||
public static final List<String> SUPPORTED_LANGUAGES = Lists.newArrayList("eng", "fra", "jpn");
|
||||
public static final List<String> SUPPORTED_LANGUAGES = Lists.newArrayList("eng", "fra", "ita", "deu", "spa", "por", "pol", "rus", "ukr", "ara", "hin", "chi_sim", "chi_tra", "jpn", "tha", "kor");
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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.AclType;
|
||||
import com.sismics.docs.core.constant.AuditLogType;
|
||||
import com.sismics.docs.core.constant.PermType;
|
||||
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.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.
|
||||
*
|
||||
@@ -65,19 +65,20 @@ public class AclDao {
|
||||
* @return ACL DTO list
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<AclDto> getBySourceId(String sourceId) {
|
||||
public List<AclDto> getBySourceId(String sourceId, AclType type) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select a.ACL_ID_C, a.ACL_PERM_C, a.ACL_TARGETID_C, ");
|
||||
sb.append(" u.USE_USERNAME_C, s.SHA_ID_C, s.SHA_NAME_C, g.GRP_NAME_C ");
|
||||
sb.append(" from T_ACL a ");
|
||||
sb.append(" left join T_USER u on u.USE_ID_C = a.ACL_TARGETID_C ");
|
||||
sb.append(" left join T_SHARE s on s.SHA_ID_C = a.ACL_TARGETID_C ");
|
||||
sb.append(" left join T_GROUP g on g.GRP_ID_C = a.ACL_TARGETID_C ");
|
||||
sb.append(" where a.ACL_DELETEDATE_D is null and a.ACL_SOURCEID_C = :sourceId ");
|
||||
StringBuilder sb = new StringBuilder("select a.ACL_ID_C, a.ACL_PERM_C, a.ACL_TARGETID_C, ")
|
||||
.append(" u.USE_USERNAME_C, s.SHA_ID_C, s.SHA_NAME_C, g.GRP_NAME_C ")
|
||||
.append(" from T_ACL a ")
|
||||
.append(" left join T_USER u on u.USE_ID_C = a.ACL_TARGETID_C ")
|
||||
.append(" left join T_SHARE s on s.SHA_ID_C = a.ACL_TARGETID_C ")
|
||||
.append(" left join T_GROUP g on g.GRP_ID_C = a.ACL_TARGETID_C ")
|
||||
.append(" where a.ACL_DELETEDATE_D is null and a.ACL_SOURCEID_C = :sourceId and a.ACL_TYPE_C = :type ");
|
||||
|
||||
// Perform the query
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("sourceId", sourceId);
|
||||
q.setParameter("type", type.name());
|
||||
List<Object[]> l = q.getResultList();
|
||||
|
||||
// Assemble results
|
||||
@@ -141,26 +142,29 @@ public class AclDao {
|
||||
* @param perm Permission
|
||||
* @param targetId Target ID
|
||||
* @param userId User ID
|
||||
* @param type Type
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void delete(String sourceId, PermType perm, String targetId, String userId) {
|
||||
public void delete(String sourceId, PermType perm, String targetId, String userId, AclType type) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Create audit log
|
||||
Query q = em.createQuery("from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId");
|
||||
Query q = em.createQuery("from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.type = :type and a.deleteDate is null");
|
||||
q.setParameter("sourceId", sourceId);
|
||||
q.setParameter("perm", perm);
|
||||
q.setParameter("targetId", targetId);
|
||||
q.setParameter("type", type);
|
||||
List<Acl> aclList = q.getResultList();
|
||||
for (Acl acl : aclList) {
|
||||
AuditLogUtil.create(acl, AuditLogType.DELETE, userId);
|
||||
}
|
||||
|
||||
// Soft delete the ACLs
|
||||
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId");
|
||||
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.type = :type and a.deleteDate is null");
|
||||
q.setParameter("sourceId", sourceId);
|
||||
q.setParameter("perm", perm);
|
||||
q.setParameter("targetId", targetId);
|
||||
q.setParameter("type", type);
|
||||
q.setParameter("dateNow", new Date());
|
||||
q.executeUpdate();
|
||||
}
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
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.Strings;
|
||||
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.util.context.ThreadLocalContext;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Document DAO.
|
||||
*
|
||||
@@ -224,7 +217,7 @@ public class DocumentDao {
|
||||
if (!Strings.isNullOrEmpty(criteria.getSearch()) || !Strings.isNullOrEmpty(criteria.getFullSearch())) {
|
||||
LuceneDao luceneDao = new LuceneDao();
|
||||
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
|
||||
documentIdList.add(UUID.randomUUID().toString());
|
||||
}
|
||||
@@ -301,25 +294,36 @@ public class DocumentDao {
|
||||
// Get the document
|
||||
Query q = em.createQuery("select d from Document d where d.id = :id and d.deleteDate is null");
|
||||
q.setParameter("id", document.getId());
|
||||
Document documentFromDb = (Document) q.getSingleResult();
|
||||
Document documentDb = (Document) q.getSingleResult();
|
||||
|
||||
// Update the document
|
||||
documentFromDb.setTitle(document.getTitle());
|
||||
documentFromDb.setDescription(document.getDescription());
|
||||
documentFromDb.setSubject(document.getSubject());
|
||||
documentFromDb.setIdentifier(document.getIdentifier());
|
||||
documentFromDb.setPublisher(document.getPublisher());
|
||||
documentFromDb.setFormat(document.getFormat());
|
||||
documentFromDb.setSource(document.getSource());
|
||||
documentFromDb.setType(document.getType());
|
||||
documentFromDb.setCoverage(document.getCoverage());
|
||||
documentFromDb.setRights(document.getRights());
|
||||
documentFromDb.setCreateDate(document.getCreateDate());
|
||||
documentFromDb.setLanguage(document.getLanguage());
|
||||
documentDb.setTitle(document.getTitle());
|
||||
documentDb.setDescription(document.getDescription());
|
||||
documentDb.setSubject(document.getSubject());
|
||||
documentDb.setIdentifier(document.getIdentifier());
|
||||
documentDb.setPublisher(document.getPublisher());
|
||||
documentDb.setFormat(document.getFormat());
|
||||
documentDb.setSource(document.getSource());
|
||||
documentDb.setType(document.getType());
|
||||
documentDb.setCoverage(document.getCoverage());
|
||||
documentDb.setRights(document.getRights());
|
||||
documentDb.setCreateDate(document.getCreateDate());
|
||||
documentDb.setLanguage(document.getLanguage());
|
||||
|
||||
// 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;
|
||||
|
||||
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.model.jpa.File;
|
||||
import com.sismics.docs.core.util.AuditLogUtil;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* File DAO.
|
||||
*
|
||||
@@ -137,13 +136,13 @@ public class FileDao {
|
||||
// Get the file
|
||||
Query q = em.createQuery("select f from File f where f.id = :id and f.deleteDate is null");
|
||||
q.setParameter("id", file.getId());
|
||||
File fileFromDb = (File) q.getSingleResult();
|
||||
File fileDb = (File) q.getSingleResult();
|
||||
|
||||
// Update the file
|
||||
fileFromDb.setDocumentId(file.getDocumentId());
|
||||
fileFromDb.setContent(file.getContent());
|
||||
fileFromDb.setOrder(file.getOrder());
|
||||
fileFromDb.setMimeType(file.getMimeType());
|
||||
fileDb.setDocumentId(file.getDocumentId());
|
||||
fileDb.setContent(file.getContent());
|
||||
fileDb.setOrder(file.getOrder());
|
||||
fileDb.setMimeType(file.getMimeType());
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
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.sismics.docs.core.constant.AuditLogType;
|
||||
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.util.context.ThreadLocalContext;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Group DAO.
|
||||
*
|
||||
@@ -266,16 +258,16 @@ public class GroupDao {
|
||||
// Get the group
|
||||
Query q = em.createQuery("select g from Group g where g.id = :id and g.deleteDate is null");
|
||||
q.setParameter("id", group.getId());
|
||||
Group groupFromDb = (Group) q.getSingleResult();
|
||||
Group groupDb = (Group) q.getSingleResult();
|
||||
|
||||
// Update the group
|
||||
groupFromDb.setName(group.getName());
|
||||
groupFromDb.setParentId(group.getParentId());
|
||||
groupDb.setName(group.getName());
|
||||
groupDb.setParentId(group.getParentId());
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(groupFromDb, AuditLogType.UPDATE, userId);
|
||||
AuditLogUtil.create(groupDb, AuditLogType.UPDATE, userId);
|
||||
|
||||
return groupFromDb;
|
||||
return groupDb;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
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.sismics.docs.core.constant.AuditLogType;
|
||||
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.util.context.ThreadLocalContext;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Tag DAO.
|
||||
*
|
||||
@@ -155,17 +148,17 @@ public class TagDao {
|
||||
// Get the tag
|
||||
Query q = em.createQuery("select t from Tag t where t.id = :id and t.deleteDate is null");
|
||||
q.setParameter("id", tag.getId());
|
||||
Tag tagFromDb = (Tag) q.getSingleResult();
|
||||
Tag tagDb = (Tag) q.getSingleResult();
|
||||
|
||||
// Update the tag
|
||||
tagFromDb.setName(tag.getName());
|
||||
tagFromDb.setColor(tag.getColor());
|
||||
tagFromDb.setParentId(tag.getParentId());
|
||||
tagDb.setName(tag.getName());
|
||||
tagDb.setColor(tag.getColor());
|
||||
tagDb.setParentId(tag.getParentId());
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(tagFromDb, AuditLogType.UPDATE, userId);
|
||||
AuditLogUtil.create(tagDb, AuditLogType.UPDATE, userId);
|
||||
|
||||
return tagFromDb;
|
||||
return tagDb;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,7 +197,7 @@ public class TagDao {
|
||||
}
|
||||
if (criteria.getNameLike() != null) {
|
||||
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");
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
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.sismics.docs.core.constant.AuditLogType;
|
||||
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.SortCriteria;
|
||||
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.
|
||||
@@ -44,7 +38,7 @@ public class UserDao {
|
||||
q.setParameter("username", username);
|
||||
try {
|
||||
User user = (User) q.getSingleResult();
|
||||
if (!BCrypt.checkpw(password, user.getPassword())) {
|
||||
if (!BCrypt.checkpw(password, user.getPassword()) || user.getDisableDate() != null) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
@@ -59,7 +53,7 @@ public class UserDao {
|
||||
* @param user User to create
|
||||
* @param userId User ID
|
||||
* @return User ID
|
||||
* @throws Exception
|
||||
* @throws Exception e
|
||||
*/
|
||||
public String create(User user, String userId) throws Exception {
|
||||
// Create the user UUID
|
||||
@@ -98,16 +92,17 @@ public class UserDao {
|
||||
// Get the user
|
||||
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
|
||||
q.setParameter("id", user.getId());
|
||||
User userFromDb = (User) q.getSingleResult();
|
||||
User userDb = (User) q.getSingleResult();
|
||||
|
||||
// Update the user (except password)
|
||||
userFromDb.setEmail(user.getEmail());
|
||||
userFromDb.setStorageQuota(user.getStorageQuota());
|
||||
userFromDb.setStorageCurrent(user.getStorageCurrent());
|
||||
userFromDb.setTotpKey(user.getTotpKey());
|
||||
userDb.setEmail(user.getEmail());
|
||||
userDb.setStorageQuota(user.getStorageQuota());
|
||||
userDb.setStorageCurrent(user.getStorageCurrent());
|
||||
userDb.setTotpKey(user.getTotpKey());
|
||||
userDb.setDisableDate(user.getDisableDate());
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE, userId);
|
||||
AuditLogUtil.create(userDb, AuditLogType.UPDATE, userId);
|
||||
|
||||
return user;
|
||||
}
|
||||
@@ -116,20 +111,17 @@ public class UserDao {
|
||||
* Updates a user's quota.
|
||||
*
|
||||
* @param user User to update
|
||||
* @return Updated user
|
||||
*/
|
||||
public User updateQuota(User user) {
|
||||
public void updateQuota(User user) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the user
|
||||
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
|
||||
q.setParameter("id", user.getId());
|
||||
User userFromDb = (User) q.getSingleResult();
|
||||
User userDb = (User) q.getSingleResult();
|
||||
|
||||
// Update the user
|
||||
userFromDb.setStorageQuota(user.getStorageQuota());
|
||||
|
||||
return user;
|
||||
userDb.setStorageCurrent(user.getStorageCurrent());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,13 +137,33 @@ public class UserDao {
|
||||
// Get the user
|
||||
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
|
||||
q.setParameter("id", user.getId());
|
||||
User userFromDb = (User) q.getSingleResult();
|
||||
User userDb = (User) q.getSingleResult();
|
||||
|
||||
// Update the user
|
||||
userFromDb.setPassword(hashPassword(user.getPassword()));
|
||||
userDb.setPassword(hashPassword(user.getPassword()));
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE, userId);
|
||||
AuditLogUtil.create(userDb, AuditLogType.UPDATE, userId);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the hashed password silently.
|
||||
*
|
||||
* @param user User to update
|
||||
* @return Updated user
|
||||
*/
|
||||
public User updateHashedPassword(User user) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the user
|
||||
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
|
||||
q.setParameter("id", user.getId());
|
||||
User userDb = (User) q.getSingleResult();
|
||||
|
||||
// Update the user
|
||||
userDb.setPassword(user.getPassword());
|
||||
|
||||
return user;
|
||||
}
|
||||
@@ -200,39 +212,39 @@ public class UserDao {
|
||||
// Get the user
|
||||
Query q = em.createQuery("select u from User u where u.username = :username and u.deleteDate is null");
|
||||
q.setParameter("username", username);
|
||||
User userFromDb = (User) q.getSingleResult();
|
||||
User userDb = (User) q.getSingleResult();
|
||||
|
||||
// Delete the user
|
||||
Date dateNow = new Date();
|
||||
userFromDb.setDeleteDate(dateNow);
|
||||
userDb.setDeleteDate(dateNow);
|
||||
|
||||
// Delete linked data
|
||||
q = em.createQuery("delete from AuthenticationToken at where at.userId = :userId");
|
||||
q.setParameter("userId", userFromDb.getId());
|
||||
q.setParameter("userId", userDb.getId());
|
||||
q.executeUpdate();
|
||||
|
||||
q = em.createQuery("update Document d set d.deleteDate = :dateNow where d.userId = :userId and d.deleteDate is null");
|
||||
q.setParameter("userId", userFromDb.getId());
|
||||
q.setParameter("userId", userDb.getId());
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
q = em.createQuery("update File f set f.deleteDate = :dateNow where f.userId = :userId and f.deleteDate is null");
|
||||
q.setParameter("userId", userFromDb.getId());
|
||||
q.setParameter("userId", userDb.getId());
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.targetId = :userId and a.deleteDate is null");
|
||||
q.setParameter("userId", userFromDb.getId());
|
||||
q.setParameter("userId", userDb.getId());
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
q = em.createQuery("update Comment c set c.deleteDate = :dateNow where c.userId = :userId and c.deleteDate is null");
|
||||
q.setParameter("userId", userFromDb.getId());
|
||||
q.setParameter("userId", userDb.getId());
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(userFromDb, AuditLogType.DELETE, userId);
|
||||
AuditLogUtil.create(userDb, AuditLogType.DELETE, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,7 +268,7 @@ public class UserDao {
|
||||
Map<String, Object> parameterMap = new HashMap<>();
|
||||
List<String> criteriaList = new ArrayList<>();
|
||||
|
||||
StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3, u.USE_STORAGECURRENT_N as c4, u.USE_STORAGEQUOTA_N as c5");
|
||||
StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3, u.USE_STORAGECURRENT_N as c4, u.USE_STORAGEQUOTA_N as c5, u.USE_TOTPKEY_C as c6, u.USE_DISABLEDATE_D as c7");
|
||||
sb.append(" from T_USER u ");
|
||||
|
||||
// Add search criterias
|
||||
@@ -264,7 +276,14 @@ public class UserDao {
|
||||
criteriaList.add("lower(u.USE_USERNAME_C) like lower(:search)");
|
||||
parameterMap.put("search", "%" + criteria.getSearch() + "%");
|
||||
}
|
||||
|
||||
if (criteria.getUserId() != null) {
|
||||
criteriaList.add("u.USE_ID_C = :userId");
|
||||
parameterMap.put("userId", criteria.getUserId());
|
||||
}
|
||||
if (criteria.getUserName() != null) {
|
||||
criteriaList.add("u.USE_USERNAME_C = :userName");
|
||||
parameterMap.put("userName", criteria.getUserName());
|
||||
}
|
||||
if (criteria.getGroupId() != null) {
|
||||
sb.append(" join T_USER_GROUP ug on ug.UGP_IDUSER_C = u.USE_ID_C and ug.UGP_IDGROUP_C = :groupId and ug.UGP_DELETEDATE_D is null ");
|
||||
parameterMap.put("groupId", criteria.getGroupId());
|
||||
@@ -292,9 +311,39 @@ public class UserDao {
|
||||
userDto.setEmail((String) o[i++]);
|
||||
userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
||||
userDto.setStorageCurrent(((Number) o[i++]).longValue());
|
||||
userDto.setStorageQuota(((Number) o[i]).longValue());
|
||||
userDto.setStorageQuota(((Number) o[i++]).longValue());
|
||||
userDto.setTotpKey((String) o[i++]);
|
||||
if (o[i] != null) {
|
||||
userDto.setDisableTimestamp(((Timestamp) o[i]).getTime());
|
||||
}
|
||||
userDtoList.add(userDto);
|
||||
}
|
||||
return userDtoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the global storage used by all users.
|
||||
*
|
||||
* @return Current global storage
|
||||
*/
|
||||
public long getGlobalStorageCurrent() {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query query = em.createNativeQuery("select sum(u.USE_STORAGECURRENT_N) from T_USER u where u.USE_DELETEDATE_D is null");
|
||||
return ((Number) query.getSingleResult()).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of active users.
|
||||
*
|
||||
* @return Number of active users
|
||||
*/
|
||||
public long getActiveUserCount() {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query query = em.createNativeQuery("select count(u.USE_ID_C) from T_USER u where u.USE_DELETEDATE_D is null and (u.USE_DISABLEDATE_D is null or u.USE_DISABLEDATE_D >= :fromDate and u.USE_DISABLEDATE_D < :toDate)");
|
||||
DateTime fromDate = DateTime.now().minusMonths(1).dayOfMonth().withMinimumValue().withTimeAtStartOfDay();
|
||||
DateTime toDate = fromDate.plusMonths(1);
|
||||
query.setParameter("fromDate", fromDate.toDate());
|
||||
query.setParameter("toDate", toDate.toDate());
|
||||
return ((Number) query.getSingleResult()).longValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package com.sismics.docs.core.dao.jpa;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import com.sismics.docs.core.model.jpa.Vocabulary;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import com.sismics.docs.core.model.jpa.Vocabulary;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Vocabulary DAO.
|
||||
@@ -76,14 +75,14 @@ public class VocabularyDao {
|
||||
// Get the vocabulary entry
|
||||
Query q = em.createQuery("select v from Vocabulary v where v.id = :id");
|
||||
q.setParameter("id", vocabulary.getId());
|
||||
Vocabulary vocabularyFromDb = (Vocabulary) q.getSingleResult();
|
||||
Vocabulary vocabularyDb = (Vocabulary) q.getSingleResult();
|
||||
|
||||
// Update the vocabulary entry
|
||||
vocabularyFromDb.setName(vocabulary.getName());
|
||||
vocabularyFromDb.setValue(vocabulary.getValue());
|
||||
vocabularyFromDb.setOrder(vocabulary.getOrder());
|
||||
vocabularyDb.setName(vocabulary.getName());
|
||||
vocabularyDb.setValue(vocabulary.getValue());
|
||||
vocabularyDb.setOrder(vocabulary.getOrder());
|
||||
|
||||
return vocabularyFromDb;
|
||||
return vocabularyDb;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.sismics.docs.core.dao.jpa.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;
|
||||
|
||||
/**
|
||||
* User ID.
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* Username.
|
||||
*/
|
||||
private String userName;
|
||||
|
||||
public String getSearch() {
|
||||
return search;
|
||||
}
|
||||
@@ -33,4 +43,22 @@ public class UserCriteria {
|
||||
this.groupId = groupId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public UserCriteria setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public UserCriteria setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
/**
|
||||
* User DTO.
|
||||
*
|
||||
@@ -26,6 +28,11 @@ public class UserDto {
|
||||
*/
|
||||
private Long createTimestamp;
|
||||
|
||||
/**
|
||||
* Disable date of this user.
|
||||
*/
|
||||
private Long disableTimestamp;
|
||||
|
||||
/**
|
||||
* Storage quota.
|
||||
*/
|
||||
@@ -36,6 +43,11 @@ public class UserDto {
|
||||
*/
|
||||
private Long storageCurrent;
|
||||
|
||||
/**
|
||||
* TOTP key.
|
||||
*/
|
||||
private String totpKey;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -68,6 +80,15 @@ public class UserDto {
|
||||
this.createTimestamp = createTimestamp;
|
||||
}
|
||||
|
||||
public Long getDisableTimestamp() {
|
||||
return disableTimestamp;
|
||||
}
|
||||
|
||||
public UserDto setDisableTimestamp(Long disableTimestamp) {
|
||||
this.disableTimestamp = disableTimestamp;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Long getStorageQuota() {
|
||||
return storageQuota;
|
||||
}
|
||||
@@ -83,4 +104,21 @@ public class UserDto {
|
||||
public void setStorageCurrent(Long 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.
|
||||
*
|
||||
* @param fileList
|
||||
* @param fileList List of files
|
||||
*/
|
||||
public void rebuildIndex(final List<Document> documentList, final List<File> fileList) {
|
||||
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.
|
||||
*
|
||||
@@ -166,7 +151,7 @@ public class LuceneDao {
|
||||
|
||||
// Search
|
||||
DirectoryReader directoryReader = AppContext.getInstance().getIndexingService().getDirectoryReader();
|
||||
Set<String> documentIdList = new HashSet<String>();
|
||||
Set<String> documentIdList = new HashSet<>();
|
||||
if (directoryReader == null) {
|
||||
// The directory reader is not yet initialized (probably because there is nothing indexed)
|
||||
return documentIdList;
|
||||
@@ -176,8 +161,8 @@ public class LuceneDao {
|
||||
ScoreDoc[] docs = topDocs.scoreDocs;
|
||||
|
||||
// Extract document IDs
|
||||
for (int i = 0; i < docs.length; i++) {
|
||||
org.apache.lucene.document.Document document = searcher.doc(docs[i].doc);
|
||||
for (ScoreDoc doc : docs) {
|
||||
org.apache.lucene.document.Document document = searcher.doc(doc.doc);
|
||||
String type = document.get("doctype");
|
||||
String documentId = null;
|
||||
if (type.equals("document")) {
|
||||
@@ -194,7 +179,7 @@ public class LuceneDao {
|
||||
/**
|
||||
* Build Lucene document from database document.
|
||||
*
|
||||
* @param documentDto Document
|
||||
* @param document Document
|
||||
* @return Document
|
||||
*/
|
||||
private org.apache.lucene.document.Document getDocumentFromDocument(Document document) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.sismics.docs.core.event;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
@@ -22,16 +23,16 @@ public class FileCreatedAsyncEvent extends UserEvent {
|
||||
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
|
||||
* of the file. May be null if the PDF conversion is not
|
||||
* Unencrypted file containing PDF representation
|
||||
* of the original file. May be null if the PDF conversion is not
|
||||
* necessary or not possible.
|
||||
*/
|
||||
private InputStream pdfInputStream;
|
||||
private Path unencryptedPdfFile;
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
@@ -49,20 +50,22 @@ public class FileCreatedAsyncEvent extends UserEvent {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return inputStream;
|
||||
public Path getUnencryptedFile() {
|
||||
return unencryptedFile;
|
||||
}
|
||||
|
||||
public void setInputStream(InputStream inputStream) {
|
||||
this.inputStream = inputStream;
|
||||
public FileCreatedAsyncEvent setUnencryptedFile(Path unencryptedFile) {
|
||||
this.unencryptedFile = unencryptedFile;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InputStream getPdfInputStream() {
|
||||
return pdfInputStream;
|
||||
public Path getUnencryptedPdfFile() {
|
||||
return unencryptedPdfFile;
|
||||
}
|
||||
|
||||
public void setPdfInputStream(InputStream pdfInputStream) {
|
||||
this.pdfInputStream = pdfInputStream;
|
||||
public FileCreatedAsyncEvent setUnencryptedPdfFile(Path unencryptedPdfFile) {
|
||||
this.unencryptedPdfFile = unencryptedPdfFile;
|
||||
return this;
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.sismics.docs.core.dao.jpa.ContributorDao;
|
||||
import com.sismics.docs.core.dao.lucene.LuceneDao;
|
||||
import com.sismics.docs.core.event.DocumentCreatedAsyncEvent;
|
||||
import com.sismics.docs.core.model.jpa.Contributor;
|
||||
import com.sismics.docs.core.util.TransactionUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Listener on document created.
|
||||
@@ -25,10 +24,9 @@ public class DocumentCreatedAsyncListener {
|
||||
* Document created.
|
||||
*
|
||||
* @param event Document created event
|
||||
* @throws Exception
|
||||
*/
|
||||
@Subscribe
|
||||
public void on(final DocumentCreatedAsyncEvent event) throws Exception {
|
||||
public void on(final DocumentCreatedAsyncEvent event) {
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("Document created event: " + event.toString());
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
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.sismics.docs.core.dao.jpa.ContributorDao;
|
||||
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.model.jpa.Contributor;
|
||||
import com.sismics.docs.core.util.TransactionUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Listener on document updated.
|
||||
@@ -31,7 +30,7 @@ public class DocumentUpdatedAsyncListener {
|
||||
* @throws Exception
|
||||
*/
|
||||
@Subscribe
|
||||
public void on(final DocumentUpdatedAsyncEvent event) throws Exception {
|
||||
public void on(final DocumentUpdatedAsyncEvent event) {
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("Document updated event: " + event.toString());
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
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.sismics.docs.core.dao.jpa.FileDao;
|
||||
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.util.FileUtil;
|
||||
import com.sismics.docs.core.util.TransactionUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
/**
|
||||
* Listener on file created.
|
||||
@@ -28,7 +27,7 @@ public class FileCreatedAsyncListener {
|
||||
* File created.
|
||||
*
|
||||
* @param fileCreatedAsyncEvent File created event
|
||||
* @throws Exception
|
||||
* @throws Exception e
|
||||
*/
|
||||
@Subscribe
|
||||
public void on(final FileCreatedAsyncEvent fileCreatedAsyncEvent) throws Exception {
|
||||
@@ -42,11 +41,7 @@ public class FileCreatedAsyncListener {
|
||||
// Extract text content from the file
|
||||
long startTime = System.currentTimeMillis();
|
||||
final String content = FileUtil.extractContent(fileCreatedAsyncEvent.getLanguage(), file,
|
||||
fileCreatedAsyncEvent.getInputStream(), fileCreatedAsyncEvent.getPdfInputStream());
|
||||
fileCreatedAsyncEvent.getInputStream().close();
|
||||
if (fileCreatedAsyncEvent.getPdfInputStream() != null) {
|
||||
fileCreatedAsyncEvent.getPdfInputStream().close();
|
||||
}
|
||||
fileCreatedAsyncEvent.getUnencryptedFile(), fileCreatedAsyncEvent.getUnencryptedPdfFile());
|
||||
log.info(MessageFormat.format("File content extracted in {0}ms", System.currentTimeMillis() - startTime));
|
||||
|
||||
// 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;
|
||||
|
||||
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.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -7,21 +22,6 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
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.
|
||||
*
|
||||
@@ -43,11 +43,21 @@ public class AppContext {
|
||||
*/
|
||||
private EventBus asyncEventBus;
|
||||
|
||||
/**
|
||||
* Asynchronous bus for email sending.
|
||||
*/
|
||||
private EventBus mailEventBus;
|
||||
|
||||
/**
|
||||
* Indexing service.
|
||||
*/
|
||||
private IndexingService indexingService;
|
||||
|
||||
/**
|
||||
* Inbox scanning service.
|
||||
*/
|
||||
private InboxService inboxService;
|
||||
|
||||
/**
|
||||
* Asynchronous executors.
|
||||
*/
|
||||
@@ -59,10 +69,29 @@ public class AppContext {
|
||||
private AppContext() {
|
||||
resetEventBus();
|
||||
|
||||
// Start indexing service
|
||||
ConfigDao configDao = new ConfigDao();
|
||||
Config luceneStorageConfig = configDao.getById(ConfigType.LUCENE_DIRECTORY_STORAGE);
|
||||
indexingService = new IndexingService(luceneStorageConfig != null ? luceneStorageConfig.getValue() : null);
|
||||
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 DocumentDeletedAsyncListener());
|
||||
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()) {
|
||||
return new EventBus();
|
||||
} else {
|
||||
// /!\ Don't add more threads because a cleanup event is fired at the end of each request
|
||||
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<Runnable>());
|
||||
@@ -135,30 +170,23 @@ public class AppContext {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of eventBus.
|
||||
*
|
||||
* @return eventBus
|
||||
*/
|
||||
public EventBus getEventBus() {
|
||||
return eventBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of asyncEventBus.
|
||||
*
|
||||
* @return asyncEventBus
|
||||
*/
|
||||
public EventBus getAsyncEventBus() {
|
||||
return asyncEventBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of indexingService.
|
||||
*
|
||||
* @return indexingService
|
||||
*/
|
||||
public EventBus getMailEventBus() {
|
||||
return mailEventBus;
|
||||
}
|
||||
|
||||
public IndexingService getIndexingService() {
|
||||
return indexingService;
|
||||
}
|
||||
|
||||
public InboxService getInboxService() {
|
||||
return inboxService;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
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.sismics.docs.core.constant.AclType;
|
||||
import com.sismics.docs.core.constant.PermType;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* ACL entity.
|
||||
*
|
||||
@@ -34,6 +29,13 @@ public class Acl implements Loggable {
|
||||
@Enumerated(EnumType.STRING)
|
||||
private PermType perm;
|
||||
|
||||
/**
|
||||
* ACL type.
|
||||
*/
|
||||
@Column(name = "ACL_TYPE_C", length = 30, nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private AclType type;
|
||||
|
||||
/**
|
||||
* ACL source ID.
|
||||
*/
|
||||
@@ -84,6 +86,15 @@ public class Acl implements Loggable {
|
||||
this.targetId = targetId;
|
||||
}
|
||||
|
||||
public AclType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Acl setType(AclType type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getDeleteDate() {
|
||||
return deleteDate;
|
||||
@@ -100,6 +111,7 @@ public class Acl implements Loggable {
|
||||
.add("perm", perm)
|
||||
.add("sourceId", sourceId)
|
||||
.add("targetId", targetId)
|
||||
.add("type", type)
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
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.Strings;
|
||||
import com.sismics.util.mime.MimeTypeUtil;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* File entity.
|
||||
@@ -38,6 +34,12 @@ public class File implements Loggable {
|
||||
@Column(name = "FIL_IDUSER_C", length = 36, nullable = false)
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* Name.
|
||||
*/
|
||||
@Column(name = "FIL_NAME_C", length = 200)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* MIME type.
|
||||
*/
|
||||
@@ -92,6 +94,15 @@ public class File implements Loggable {
|
||||
this.documentId = documentId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public File setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
@@ -153,11 +164,26 @@ public class File implements Loggable {
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("id", id)
|
||||
.add("name", name)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
public String toMessage();
|
||||
String toMessage();
|
||||
|
||||
/**
|
||||
* Loggable are soft deletable.
|
||||
*
|
||||
* @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;
|
||||
|
||||
import java.util.Date;
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* User entity.
|
||||
@@ -84,6 +83,12 @@ public class User implements Loggable {
|
||||
@Column(name = "USE_DELETEDATE_D")
|
||||
private Date deleteDate;
|
||||
|
||||
/**
|
||||
* Disable date.
|
||||
*/
|
||||
@Column(name = "USE_DISABLEDATE_D")
|
||||
private Date disableDate;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -148,6 +153,15 @@ public class User implements Loggable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getDisableDate() {
|
||||
return disableDate;
|
||||
}
|
||||
|
||||
public User setDisableDate(Date disableDate) {
|
||||
this.disableDate = disableDate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
@@ -189,6 +203,7 @@ public class User implements Loggable {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("id", id)
|
||||
.add("username", username)
|
||||
.add("email", email)
|
||||
.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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.common.util.concurrent.AbstractScheduledService;
|
||||
import com.sismics.docs.core.constant.Constants;
|
||||
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.Status;
|
||||
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.LoggerFactory;
|
||||
|
||||
import com.google.common.util.concurrent.AbstractScheduledService;
|
||||
import com.sismics.docs.core.constant.Constants;
|
||||
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 java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Indexing service.
|
||||
@@ -115,7 +114,7 @@ public class IndexingService extends AbstractScheduledService {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runOneIteration() throws Exception {
|
||||
protected void runOneIteration() {
|
||||
TransactionUtil.handle(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -129,16 +128,6 @@ public class IndexingService extends AbstractScheduledService {
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.sismics.docs.core.util;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import com.sismics.docs.core.constant.AuditLogType;
|
||||
import com.sismics.docs.core.dao.jpa.AuditLogDao;
|
||||
import com.sismics.docs.core.model.jpa.AuditLog;
|
||||
import com.sismics.docs.core.model.jpa.Loggable;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/**
|
||||
* Audit log utilities.
|
||||
*
|
||||
@@ -17,10 +17,15 @@ public class AuditLogUtil {
|
||||
/**
|
||||
* Create an audit log.
|
||||
*
|
||||
* @param entity Entity
|
||||
* @param loggable Loggable
|
||||
* @param type Audit log type
|
||||
* @param userId User ID
|
||||
*/
|
||||
public static void create(Loggable loggable, AuditLogType type, String userId) {
|
||||
if (userId == null) {
|
||||
userId = "admin";
|
||||
}
|
||||
|
||||
// Get the entity ID
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
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;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import com.google.common.base.Strings;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
|
||||
/**
|
||||
* Encryption utilities.
|
||||
@@ -22,7 +24,6 @@ import com.google.common.base.Strings;
|
||||
* @author bgamard
|
||||
*/
|
||||
public class EncryptionUtil {
|
||||
|
||||
/**
|
||||
* Salt.
|
||||
*/
|
||||
@@ -56,6 +57,27 @@ public class EncryptionUtil {
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
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.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -7,21 +29,6 @@ import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
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.
|
||||
*
|
||||
@@ -38,43 +45,47 @@ public class FileUtil {
|
||||
*
|
||||
* @param language Language to extract
|
||||
* @param file File to extract
|
||||
* @param inputStream Unencrypted input stream
|
||||
* @param pdfInputStream Unencrypted PDF input stream
|
||||
* @param unencryptedFile Unencrypted file
|
||||
* @param unencryptedPdfFile Unencrypted PDF file
|
||||
* @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;
|
||||
|
||||
if (ImageUtil.isImage(file.getMimeType())) {
|
||||
content = ocrFile(inputStream, language);
|
||||
} else if (pdfInputStream != null) {
|
||||
content = PdfUtil.extractPdf(pdfInputStream);
|
||||
content = ocrFile(unencryptedFile, language);
|
||||
} else if (unencryptedPdfFile != null) {
|
||||
content = PdfUtil.extractPdf(unencryptedPdfFile);
|
||||
}
|
||||
|
||||
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
|
||||
* @return Content extracted
|
||||
*/
|
||||
private static String ocrFile(InputStream inputStream, String language) {
|
||||
private static String ocrFile(Path unecryptedFile, String language) {
|
||||
Tesseract instance = Tesseract.getInstance();
|
||||
String content = null;
|
||||
BufferedImage image = null;
|
||||
try {
|
||||
BufferedImage image;
|
||||
try (InputStream inputStream = Files.newInputStream(unecryptedFile)) {
|
||||
image = ImageIO.read(inputStream);
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading the image", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Upscale and grayscale the image
|
||||
BufferedImage resizedImage = Scalr.resize(image, Method.AUTOMATIC, Mode.AUTOMATIC, 3500, Scalr.OP_ANTIALIAS, Scalr.OP_GRAYSCALE);
|
||||
// Upscale, grayscale and deskew the image
|
||||
BufferedImage resizedImage = Scalr.resize(image, Scalr.Method.AUTOMATIC, Scalr.Mode.AUTOMATIC, 3500, Scalr.OP_ANTIALIAS, Scalr.OP_GRAYSCALE);
|
||||
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
|
||||
try {
|
||||
@@ -91,46 +102,45 @@ public class FileUtil {
|
||||
/**
|
||||
* Save a file on the storage filesystem.
|
||||
*
|
||||
* @param inputStream Unencrypted input stream
|
||||
* @param pdf
|
||||
* @param unencryptedFile Unencrypted file
|
||||
* @param unencryptedPdfFile Unencrypted PDF file
|
||||
* @param file File to save
|
||||
* @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);
|
||||
Path path = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||
Files.copy(new CipherInputStream(inputStream, cipher), path);
|
||||
inputStream.reset();
|
||||
try (InputStream inputStream = Files.newInputStream(unencryptedFile)) {
|
||||
Files.copy(new CipherInputStream(inputStream, cipher), path);
|
||||
}
|
||||
|
||||
// Generate file variations
|
||||
saveVariations(file, inputStream, pdfInputStream, cipher);
|
||||
saveVariations(file, unencryptedFile, unencryptedPdfFile, cipher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate file variations.
|
||||
*
|
||||
* @param file File from database
|
||||
* @param inputStream Unencrypted input stream
|
||||
* @param pdfInputStream Unencrypted PDF input stream
|
||||
* @param unencryptedFile Unencrypted file
|
||||
* @param unencryptedPdfFile Unencrypted PDF file
|
||||
* @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;
|
||||
if (ImageUtil.isImage(file.getMimeType())) {
|
||||
image = ImageIO.read(inputStream);
|
||||
inputStream.reset();
|
||||
} else if(pdfInputStream != null) {
|
||||
try (InputStream inputStream = Files.newInputStream(unencryptedFile)) {
|
||||
image = ImageIO.read(inputStream);
|
||||
}
|
||||
} else if (unencryptedPdfFile != null) {
|
||||
// Generate preview from the first page of the PDF
|
||||
image = PdfUtil.renderFirstPage(pdfInputStream);
|
||||
pdfInputStream.reset();
|
||||
image = PdfUtil.renderFirstPage(unencryptedPdfFile);
|
||||
}
|
||||
|
||||
if (image != null) {
|
||||
// Generate thumbnails from image
|
||||
BufferedImage web = Scalr.resize(image, Scalr.Method.AUTOMATIC, Scalr.Mode.AUTOMATIC, 1280, Scalr.OP_ANTIALIAS);
|
||||
BufferedImage thumbnail = Scalr.resize(image, Scalr.Method.AUTOMATIC, Scalr.Mode.AUTOMATIC, 256, Scalr.OP_ANTIALIAS);
|
||||
BufferedImage web = Scalr.resize(image, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.AUTOMATIC, 1280);
|
||||
BufferedImage thumbnail = Scalr.resize(image, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.AUTOMATIC, 256);
|
||||
image.flush();
|
||||
|
||||
// Write "web" encrypted image
|
||||
@@ -151,7 +161,6 @@ public class FileUtil {
|
||||
* Remove a file from the storage filesystem.
|
||||
*
|
||||
* @param file File to delete
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void delete(File file) throws IOException {
|
||||
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||
@@ -168,4 +177,92 @@ public class FileUtil {
|
||||
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;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.Closer;
|
||||
import com.google.common.io.Resources;
|
||||
import com.lowagie.text.*;
|
||||
import com.lowagie.text.pdf.PdfWriter;
|
||||
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.docs.core.util.pdf.PdfPage;
|
||||
import com.sismics.util.ImageUtil;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
import com.sismics.util.mime.MimeType;
|
||||
import org.apache.pdfbox.io.MemoryUsageSetting;
|
||||
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
@@ -32,13 +32,17 @@ import org.odftoolkit.odfdom.doc.OdfTextDocument;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.io.Closer;
|
||||
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.docs.core.util.pdf.PdfPage;
|
||||
import com.sismics.util.ImageUtil;
|
||||
import com.sismics.util.mime.MimeType;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* PDF utilities.
|
||||
@@ -54,17 +58,17 @@ public class PdfUtil {
|
||||
/**
|
||||
* Extract text from a PDF.
|
||||
*
|
||||
* @param inputStream Unencrypted input stream
|
||||
* @param unencryptedPdfFile Unencrypted PDF file
|
||||
* @return Content extracted
|
||||
*/
|
||||
public static String extractPdf(InputStream inputStream) {
|
||||
public static String extractPdf(Path unencryptedPdfFile) {
|
||||
String content = null;
|
||||
PDDocument pdfDocument = null;
|
||||
try {
|
||||
try (InputStream inputStream = Files.newInputStream(unencryptedPdfFile)) {
|
||||
PDFTextStripper stripper = new PDFTextStripper();
|
||||
pdfDocument = PDDocument.load(inputStream);
|
||||
content = stripper.getText(pdfDocument);
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
log.error("Error while extracting text from the PDF", e);
|
||||
} finally {
|
||||
if (pdfDocument != null) {
|
||||
@@ -83,65 +87,86 @@ public class PdfUtil {
|
||||
* Convert a file to PDF if necessary.
|
||||
*
|
||||
* @param file File
|
||||
* @param inputStream InputStream
|
||||
* @param reset Reset the stream after usage
|
||||
* @return PDF input stream
|
||||
* @throws Exception
|
||||
* @param unencryptedFile Unencrypted file
|
||||
* @return PDF temporary file
|
||||
*/
|
||||
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)) {
|
||||
// It's already PDF, just return the input
|
||||
return inputStream;
|
||||
// It's already PDF, just return the file
|
||||
return unencryptedFile;
|
||||
}
|
||||
|
||||
if (file.getMimeType().equals(MimeType.OFFICE_DOCUMENT)) {
|
||||
return convertOfficeDocument(inputStream, reset);
|
||||
return convertOfficeDocument(unencryptedFile);
|
||||
}
|
||||
|
||||
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
|
||||
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.
|
||||
*
|
||||
* @param inputStream Unencrypted input stream
|
||||
* @param reset Reset the stream after usage
|
||||
* @return PDF input stream
|
||||
* @throws Exception
|
||||
* @param unencryptedFile Unencrypted file
|
||||
* @return PDF file
|
||||
*/
|
||||
private static InputStream convertOpenDocumentText(InputStream inputStream, boolean reset) throws Exception {
|
||||
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
|
||||
OdfTextDocument document = OdfTextDocument.loadDocument(inputStream);
|
||||
PdfOptions options = PdfOptions.create();
|
||||
PdfConverter.getInstance().convert(document, pdfOutputStream, options);
|
||||
if (reset) {
|
||||
inputStream.reset();
|
||||
private static Path convertOpenDocumentText(Path unencryptedFile) throws Exception {
|
||||
Path tempFile = ThreadLocalContext.get().createTemporaryFile();
|
||||
try (InputStream inputStream = Files.newInputStream(unencryptedFile);
|
||||
OutputStream outputStream = Files.newOutputStream(tempFile)) {
|
||||
OdfTextDocument document = OdfTextDocument.loadDocument(inputStream);
|
||||
PdfOptions options = PdfOptions.create();
|
||||
PdfConverter.getInstance().convert(document, outputStream, options);
|
||||
}
|
||||
return new ByteArrayInputStream(pdfOutputStream.toByteArray());
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an Office document to PDF.
|
||||
*
|
||||
* @param inputStream Unencrypted input stream
|
||||
* @param reset Reset the stream after usage
|
||||
* @return PDF input stream
|
||||
* @throws Exception
|
||||
* @param unencryptedFile Unencrypted file
|
||||
* @return PDF file
|
||||
*/
|
||||
private static InputStream convertOfficeDocument(InputStream inputStream, boolean reset) throws Exception {
|
||||
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
|
||||
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.PdfConverter.getInstance().convert(document, pdfOutputStream, options);
|
||||
if (reset) {
|
||||
inputStream.reset();
|
||||
private static Path convertOfficeDocument(Path unencryptedFile) throws Exception {
|
||||
Path tempFile = ThreadLocalContext.get().createTemporaryFile();
|
||||
try (InputStream inputStream = Files.newInputStream(unencryptedFile);
|
||||
OutputStream outputStream = Files.newOutputStream(tempFile)) {
|
||||
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.PdfConverter.getInstance().convert(document, outputStream, options);
|
||||
}
|
||||
return new ByteArrayInputStream(pdfOutputStream.toByteArray());
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,11 +177,10 @@ public class PdfUtil {
|
||||
* @param fitImageToPage Fit images to the page
|
||||
* @param metadata Add a page with metadata
|
||||
* @param margin Margins in millimeters
|
||||
* @return PDF input stream
|
||||
* @throws IOException
|
||||
* @param outputStream Output stream to write to, will be closed
|
||||
*/
|
||||
public static InputStream convertToPdf(DocumentDto documentDto, List<File> fileList,
|
||||
boolean fitImageToPage, boolean metadata, int margin) throws Exception {
|
||||
public static void convertToPdf(DocumentDto documentDto, List<File> fileList,
|
||||
boolean fitImageToPage, boolean metadata, int margin, OutputStream outputStream) throws Exception {
|
||||
// Setup PDFBox
|
||||
Closer closer = Closer.create();
|
||||
MemoryUsageSetting memUsageSettings = MemoryUsageSetting.setupMixed(1000000); // 1MB max memory usage
|
||||
@@ -211,83 +235,94 @@ public class PdfUtil {
|
||||
// Add files
|
||||
for (File file : fileList) {
|
||||
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())) {
|
||||
if (ImageUtil.isImage(file.getMimeType())) {
|
||||
PDPage page = new PDPage(PDRectangle.A4); // Images into A4 pages
|
||||
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
|
||||
// Read the image using the correct handler. PDFBox can't do it because it relies wrongly on file extension
|
||||
PDImageXObject pdImage = null;
|
||||
if (file.getMimeType().equals(MimeType.IMAGE_JPEG)) {
|
||||
pdImage = JPEGFactory.createFromStream(doc, storedFileInputStream);
|
||||
} else if (file.getMimeType().equals(MimeType.IMAGE_GIF) || file.getMimeType().equals(MimeType.IMAGE_PNG)) {
|
||||
BufferedImage bim = ImageIO.read(storedFileInputStream);
|
||||
pdImage = LosslessFactory.createFromImage(doc, bim);
|
||||
}
|
||||
|
||||
// Do we want to fill the page with the image?
|
||||
if (fitImageToPage) {
|
||||
// Fill the page with the image
|
||||
float widthAvailable = page.getMediaBox().getWidth() - 2 * margin * mmPerInch;
|
||||
float heightAvailable = page.getMediaBox().getHeight() - 2 * margin * mmPerInch;
|
||||
// Decrypt the file to a temporary file
|
||||
Path unencryptedFile = EncryptionUtil.decryptFile(storedFile, file.getPrivateKey());
|
||||
|
||||
// Compare page format and image format
|
||||
if (widthAvailable / heightAvailable < (float) pdImage.getWidth() / (float) pdImage.getHeight()) {
|
||||
float imageHeight = widthAvailable / pdImage.getWidth() * pdImage.getHeight();
|
||||
contentStream.drawImage(pdImage, margin * mmPerInch, heightAvailable + margin * mmPerInch - imageHeight,
|
||||
widthAvailable, imageHeight);
|
||||
} else {
|
||||
float imageWidth = heightAvailable / pdImage.getHeight() * pdImage.getWidth();
|
||||
contentStream.drawImage(pdImage, margin * mmPerInch, margin * mmPerInch,
|
||||
imageWidth, heightAvailable);
|
||||
}
|
||||
if (ImageUtil.isImage(file.getMimeType())) {
|
||||
PDPage page = new PDPage(PDRectangle.A4); // Images into A4 pages
|
||||
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
|
||||
PDImageXObject pdImage = null;
|
||||
if (file.getMimeType().equals(MimeType.IMAGE_JPEG)) {
|
||||
pdImage = JPEGFactory.createFromStream(doc, storedFileInputStream);
|
||||
} else if (file.getMimeType().equals(MimeType.IMAGE_GIF) || file.getMimeType().equals(MimeType.IMAGE_PNG)) {
|
||||
BufferedImage bim = ImageIO.read(storedFileInputStream);
|
||||
pdImage = LosslessFactory.createFromImage(doc, bim);
|
||||
}
|
||||
|
||||
// Do we want to fill the page with the image?
|
||||
if (fitImageToPage) {
|
||||
// Fill the page with the image
|
||||
float widthAvailable = page.getMediaBox().getWidth() - 2 * margin * mmPerInch;
|
||||
float heightAvailable = page.getMediaBox().getHeight() - 2 * margin * mmPerInch;
|
||||
|
||||
// Compare page format and image format
|
||||
if (widthAvailable / heightAvailable < (float) pdImage.getWidth() / (float) pdImage.getHeight()) {
|
||||
float imageHeight = widthAvailable / pdImage.getWidth() * pdImage.getHeight();
|
||||
contentStream.drawImage(pdImage, margin * mmPerInch, heightAvailable + margin * mmPerInch - imageHeight,
|
||||
widthAvailable, imageHeight);
|
||||
} else {
|
||||
// Draw the image as is
|
||||
contentStream.drawImage(pdImage, margin * mmPerInch,
|
||||
page.getMediaBox().getHeight() - pdImage.getHeight() - margin * mmPerInch);
|
||||
float imageWidth = heightAvailable / pdImage.getHeight() * pdImage.getWidth();
|
||||
contentStream.drawImage(pdImage, margin * mmPerInch, margin * mmPerInch,
|
||||
imageWidth, heightAvailable);
|
||||
}
|
||||
} else {
|
||||
// Draw the image as is
|
||||
contentStream.drawImage(pdImage, margin * mmPerInch,
|
||||
page.getMediaBox().getHeight() - pdImage.getHeight() - margin * mmPerInch);
|
||||
}
|
||||
doc.addPage(page);
|
||||
} else {
|
||||
// Try to convert the file to PDF
|
||||
InputStream pdfInputStream = convertToPdf(file, storedFileInputStream, false);
|
||||
if (pdfInputStream != null) {
|
||||
// This file is convertible to PDF, just add it to the end
|
||||
try {
|
||||
PDDocument mergeDoc = PDDocument.load(pdfInputStream, memUsageSettings);
|
||||
closer.register(mergeDoc);
|
||||
PDFMergerUtility pdfMergerUtility = new PDFMergerUtility();
|
||||
pdfMergerUtility.appendDocument(doc, mergeDoc);
|
||||
} finally {
|
||||
pdfInputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
// All other non-PDF-convertible files are ignored
|
||||
}
|
||||
doc.addPage(page);
|
||||
} else {
|
||||
// Try to convert the file to PDF
|
||||
Path unencryptedPdfFile = convertToPdf(file, unencryptedFile);
|
||||
if (unencryptedPdfFile != null) {
|
||||
// This file is convertible to PDF, just add it to the end
|
||||
PDDocument mergeDoc = PDDocument.load(unencryptedPdfFile.toFile(), memUsageSettings);
|
||||
closer.register(mergeDoc);
|
||||
PDFMergerUtility pdfMergerUtility = new PDFMergerUtility();
|
||||
pdfMergerUtility.appendDocument(doc, mergeDoc);
|
||||
}
|
||||
|
||||
// All other non-PDF-convertible files are ignored
|
||||
}
|
||||
}
|
||||
|
||||
// Save to a temporary file
|
||||
try (TemporaryFileStream temporaryFileStream = new TemporaryFileStream()) {
|
||||
doc.save(temporaryFileStream.openWriteStream());
|
||||
closer.close(); // Close all remaining opened PDF
|
||||
return temporaryFileStream.openReadStream();
|
||||
}
|
||||
doc.save(outputStream); // Write to the output stream
|
||||
closer.close(); // Close all remaining opened PDF
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the first page of a PDF.
|
||||
*
|
||||
* @param inputStream PDF document
|
||||
* @param unencryptedFile PDF document
|
||||
* @return Render of the first page
|
||||
* @throws IOException
|
||||
*/
|
||||
public static BufferedImage renderFirstPage(InputStream inputStream) throws IOException {
|
||||
try (PDDocument pdfDocument = PDDocument.load(inputStream)) {
|
||||
public static BufferedImage renderFirstPage(Path unencryptedFile) throws IOException {
|
||||
try (InputStream inputStream = Files.newInputStream(unencryptedFile);
|
||||
PDDocument pdfDocument = PDDocument.load(inputStream)) {
|
||||
PDFRenderer renderer = new PDFRenderer(pdfDocument);
|
||||
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.
|
||||
*
|
||||
* @param runnable
|
||||
* @param runnable Runnable
|
||||
*/
|
||||
public static void handle(Runnable runnable) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
if (em != null) {
|
||||
if (em != null && em.isOpen()) {
|
||||
// We are already in a transactional context, nothing to do
|
||||
runnable.run();
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public static boolean isMacOs() {
|
||||
return OS.indexOf("mac") >= 0;
|
||||
return OS.contains("mac");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,7 @@ public class EnvironmentUtil {
|
||||
* @return Running under UNIX
|
||||
*/
|
||||
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;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* HTTP request utilities.
|
||||
@@ -17,77 +11,17 @@ import java.net.URLConnection;
|
||||
*/
|
||||
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
|
||||
* @return Contents of the resource
|
||||
* @param futureTime Expire interval
|
||||
* @return Formatted header value
|
||||
*/
|
||||
public static String readUrlIntoString(URL url) {
|
||||
URLConnection connection;
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
public static String buildExpiresHeader(long futureTime) {
|
||||
return EXPIRES_FORMAT.format(new Date().getTime() + futureTime);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Iterator;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.sismics.util.mime.MimeType;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.sismics.util.mime.MimeType;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Image processing utilities.
|
||||
@@ -34,12 +35,23 @@ public class ImageUtil {
|
||||
ImageWriter writer = null;
|
||||
ImageOutputStream imageOutputStream = null;
|
||||
try {
|
||||
writer = (ImageWriter) iter.next();
|
||||
writer = iter.next();
|
||||
ImageWriteParam iwp = writer.getDefaultWriteParam();
|
||||
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
iwp.setCompressionQuality(1.f);
|
||||
imageOutputStream = ImageIO.createImageOutputStream(outputStream);
|
||||
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);
|
||||
writer.write(null, iioImage, iwp);
|
||||
} finally {
|
||||
@@ -69,7 +81,7 @@ public class ImageUtil {
|
||||
* Compute Gravatar hash.
|
||||
* See https://en.gravatar.com/site/implement/hash/.
|
||||
*
|
||||
* @param email
|
||||
* @param email Email
|
||||
* @return Gravatar hash
|
||||
*/
|
||||
public static String computeGravatar(String email) {
|
||||
@@ -81,4 +93,40 @@ public class ImageUtil {
|
||||
email.trim().toLowerCase(), Charsets.UTF_8)
|
||||
.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.JsonValue;
|
||||
@@ -34,4 +34,17 @@ public class JsonUtil {
|
||||
}
|
||||
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