Compare commits
201 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c695572b28 | ||
|
|
79141edf70 | ||
|
|
b1e58396d1 | ||
|
|
b9cd113dc0 | ||
|
|
4a512af178 | ||
|
|
9506e9b8b4 | ||
|
|
3ff41d2002 | ||
|
|
f41dafe76d | ||
|
|
6f89a50fe5 | ||
|
|
e234440ce6 | ||
|
|
26685334a1 | ||
|
|
4d79dd7076 | ||
|
|
f5394534f7 | ||
|
|
faa66e01b6 | ||
|
|
bf4cb02de5 | ||
|
|
642b9a63d3 | ||
|
|
1ed7422171 | ||
|
|
3dd8a52f7d | ||
|
|
a55c55bbdb | ||
|
|
b851fd0ecc | ||
|
|
c8f7fe15ef | ||
|
|
73133f5ba5 | ||
|
|
eaf2e816b4 | ||
|
|
62020864ef | ||
|
|
f12e3ec663 | ||
|
|
5226df53a2 | ||
|
|
a59c67d774 | ||
|
|
1b1d5e9b4c | ||
|
|
37fc2d09bb | ||
|
|
bc94466cf7 | ||
|
|
6af7b6fce9 | ||
|
|
f2ae899938 | ||
|
|
c398a3c4f5 | ||
|
|
27027ec412 | ||
|
|
ddf9e83a9b | ||
|
|
0f661e5a34 | ||
|
|
09a53d5c4e | ||
|
|
542ab737a2 | ||
|
|
6e1276293f | ||
|
|
4e768e9103 | ||
|
|
55cdca0c7d | ||
|
|
9b52395786 | ||
|
|
50b02c800c | ||
|
|
2bdae5ea5c | ||
|
|
c49827ce25 | ||
|
|
64db701498 | ||
|
|
e16ce4b4f1 | ||
|
|
77d1e87fdb | ||
|
|
7d7adeeca0 | ||
|
|
8ad9c529b6 | ||
|
|
274512a58e | ||
|
|
ef16561272 | ||
|
|
98350860eb | ||
|
|
1343948d33 | ||
|
|
e616add75a | ||
|
|
b33b7115ef | ||
|
|
fb0bb62eaf | ||
|
|
5f84da61c8 | ||
|
|
6e6babd2e3 | ||
|
|
b28e08e2c7 | ||
|
|
718728a672 | ||
|
|
5de77e35dc | ||
|
|
5a41e9555e | ||
|
|
6598b585a2 | ||
|
|
a81474b40a | ||
|
|
ee159f5b36 | ||
|
|
ced64a5d1f | ||
|
|
689a4e6aae | ||
|
|
21b3ba2bf6 | ||
|
|
7be2e1b9e5 | ||
|
|
c1c2228937 | ||
|
|
3b9a66d1d8 | ||
|
|
a5ce5bf9ec | ||
|
|
43a1575187 | ||
|
|
eb5f207cc1 | ||
|
|
de3f055323 | ||
|
|
6012cdd9a5 | ||
|
|
0fab8ff935 | ||
|
|
00ee2d3bf6 | ||
|
|
c2a2e9f585 | ||
|
|
31fff7e021 | ||
|
|
0dda01269f | ||
|
|
d58b0e8f74 | ||
|
|
1bbb21c7c6 | ||
|
|
24713f54e2 | ||
|
|
5e2bd76e10 | ||
|
|
78d4b5797b | ||
|
|
ff91521a67 | ||
|
|
0525754337 | ||
|
|
ca8c525de0 | ||
|
|
1e7d2fcfd9 | ||
|
|
7e983bebb9 | ||
|
|
f927193ae9 | ||
|
|
a102bf04f4 | ||
|
|
919948489d | ||
|
|
12efd5c11f | ||
|
|
25a2144b31 | ||
|
|
59682b5ba6 | ||
|
|
7deaeca7b5 | ||
|
|
a7a6adfa34 | ||
|
|
7f19f8c112 | ||
|
|
943465a390 | ||
|
|
2824878065 | ||
|
|
508a1230e9 | ||
|
|
0ad7ef43d5 | ||
|
|
67171e05b9 | ||
|
|
adebb7ff6d | ||
|
|
6fbcd46a76 | ||
|
|
ef3a592807 | ||
|
|
d8d01b077d | ||
|
|
831e2e60ed | ||
|
|
2d858e6e11 | ||
|
|
f9c3715d8d | ||
|
|
359f5b5f49 | ||
|
|
ed51b77b0e | ||
|
|
47082ceee9 | ||
|
|
98497f2a37 | ||
|
|
d3a74ed361 | ||
|
|
7f2f480b25 | ||
|
|
34d1422868 | ||
|
|
509ab82745 | ||
|
|
7f325e3eb5 | ||
|
|
e23ca4b8c1 | ||
|
|
a0f309c957 | ||
|
|
0db4f1643d | ||
|
|
cfa5888be9 | ||
|
|
3172a5f216 | ||
|
|
456fc5b991 | ||
|
|
d9509474b0 | ||
|
|
e7a289ffb5 | ||
|
|
b9a4f0f1e0 | ||
|
|
0f4e5a8f6d | ||
|
|
83e1191a8a | ||
|
|
2c791f5123 | ||
|
|
25a17ae2da | ||
|
|
0591f8a39f | ||
|
|
eb61b06784 | ||
|
|
0d1a4ec7ea | ||
|
|
5f82752416 | ||
|
|
332de409b8 | ||
|
|
24d8784e1b | ||
|
|
7708f61343 | ||
|
|
1a37d97a61 | ||
|
|
046984a447 | ||
|
|
5f516047bd | ||
|
|
e930ce4d47 | ||
|
|
3dbdf88124 | ||
|
|
d428ce162b | ||
|
|
bc323e9945 | ||
|
|
ef69feeae7 | ||
|
|
e36143b61c | ||
|
|
aa97253ec7 | ||
|
|
0fab0e4fc0 | ||
|
|
90a4949d76 | ||
|
|
1466fb4d6c | ||
|
|
d41172abb6 | ||
|
|
24ca81e91c | ||
|
|
1cae964c09 | ||
|
|
dd671795e6 | ||
|
|
2948c0c860 | ||
|
|
978fbf2cf9 | ||
|
|
60ee000b6c | ||
|
|
634ab7ec38 | ||
|
|
7e5aa9aecf | ||
|
|
1c7381376c | ||
|
|
c7ce42fb3f | ||
|
|
fc3a8bb4ae | ||
|
|
82b39586f0 | ||
|
|
c365c6f6e0 | ||
|
|
9afd52108b | ||
|
|
97252bb5da | ||
|
|
7eeaeb01a0 | ||
|
|
b3e44b84d2 | ||
|
|
af23cd4948 | ||
|
|
dc05ca0484 | ||
|
|
f94e069792 | ||
|
|
cd32f452e9 | ||
|
|
66cb7333c1 | ||
|
|
08633a993d | ||
|
|
2c782a23d8 | ||
|
|
c7b7527183 | ||
|
|
80bd11b44e | ||
|
|
99a596b2e1 | ||
|
|
cfde218d32 | ||
|
|
d8cefddebd | ||
|
|
50c7066f88 | ||
|
|
a95dcf488d | ||
|
|
0fe51d355c | ||
|
|
97694d5d59 | ||
|
|
e72fe3683c | ||
|
|
44c10b60cd | ||
|
|
467d14bacb | ||
|
|
6d73554967 | ||
|
|
36b5bf3bb2 | ||
|
|
d14db1d3fb | ||
|
|
9c97ab14f8 | ||
|
|
6558ff7e05 | ||
|
|
86473d5639 | ||
|
|
374310d13c | ||
|
|
4396ef83a3 | ||
|
|
6b9ef4ab31 |
4
.gitignore
vendored
@@ -5,7 +5,7 @@
|
||||
/*/gen
|
||||
/*/target
|
||||
/*/build
|
||||
/*/*.iml
|
||||
/out
|
||||
/.idea
|
||||
/.project
|
||||
/.project
|
||||
*.iml
|
||||
|
||||
11
.travis.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: java
|
||||
before_install:
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-jpn
|
||||
- sudo apt-get -y -q install haveged && sudo service haveged start
|
||||
env:
|
||||
global:
|
||||
- TESSDATA_PREFIX=/usr/share/tesseract-ocr
|
||||
- LC_NUMERIC=C
|
||||
28
README.md
@@ -1,4 +1,4 @@
|
||||
Sismics Docs
|
||||
Sismics Docs [](http://travis-ci.org/sismics/docs)
|
||||
============
|
||||
|
||||
_Web interface_
|
||||
@@ -7,7 +7,7 @@ _Web interface_
|
||||
|
||||
_Android application_
|
||||
|
||||
  
|
||||
   
|
||||
|
||||
What is Docs?
|
||||
---------------
|
||||
@@ -21,15 +21,21 @@ Features
|
||||
|
||||
- Responsive user interface
|
||||
- Optical character recognition
|
||||
- Support image and PDF files
|
||||
- Support image, PDF, ODT and DOCX files
|
||||
- Flexible search engine
|
||||
- Full text search in image and PDF
|
||||
- SHA-256 encryption
|
||||
- Tag system
|
||||
- Multi-users ACL system
|
||||
- Full text search in all supported files
|
||||
- All [Dublin Core](http://dublincore.org/) metadata
|
||||
- 256-bit AES encryption of stored files
|
||||
- Tag system with nesting
|
||||
- User/group permission system
|
||||
- Hierarchical groups
|
||||
- Audit log
|
||||
- Comments
|
||||
- Storage quota per user
|
||||
- Document sharing by URL
|
||||
- RESTful Web API
|
||||
- Modern Android client
|
||||
- Fully featured Android client
|
||||
- Tested to 100k documents
|
||||
|
||||
Download
|
||||
--------
|
||||
@@ -44,7 +50,6 @@ Prerequisites: JDK 7 with JCE, Maven 3, Tesseract 3.02
|
||||
|
||||
Docs is organized in several Maven modules:
|
||||
|
||||
- docs-parent
|
||||
- docs-core
|
||||
- docs-web
|
||||
- docs-web-common
|
||||
@@ -54,9 +59,8 @@ or download the sources from GitHub.
|
||||
|
||||
#### Launch the build
|
||||
|
||||
From the `docs-parent` directory:
|
||||
From the root directory:
|
||||
|
||||
mvn -Pinit validate -N
|
||||
mvn clean -DskipTests install
|
||||
|
||||
#### Run a stand-alone version
|
||||
@@ -71,7 +75,7 @@ From the `docs-web` directory:
|
||||
|
||||
mvn -Pprod -DskipTests clean install
|
||||
|
||||
You will get your deployable WAR in the `target` directory.
|
||||
You will get your deployable WAR in the `docs-web/target` directory.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="docs-android" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
<option name="GRADLE_PROJECT_PATH" value=":app" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="debug" />
|
||||
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
|
||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
|
||||
<option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
|
||||
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
|
||||
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
|
||||
<option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugAndroidTestSources" />
|
||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
||||
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
|
||||
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
|
||||
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/apk" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/dependency-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/dex" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.1.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/22.0.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.shamanland/fab/0.0.6/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/it.sephiroth.android.library.easing/android-easing/1.0.3/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/it.sephiroth.android.library.imagezoom/imagezoom/1.0.5/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/libs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/ndk" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/pre-dexed" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/rs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/source" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-22.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="fab-0.0.6" level="project" />
|
||||
<orderEntry type="library" exported="" name="android-easing-1.0.3" level="project" />
|
||||
<orderEntry type="library" exported="" name="imagezoom-1.0.5" level="project" />
|
||||
<orderEntry type="library" exported="" name="eventbus-2.4.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="android-query.0.26.8" level="project" />
|
||||
<orderEntry type="library" exported="" name="tokenautocomplete-1.2.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-22.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-annotations-22.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="recyclerview-v7-22.0.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="android-async-http-1.4.6" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,24 +1,24 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.2.3'
|
||||
classpath 'com.android.tools.build:gradle:2.1.0-beta1'
|
||||
}
|
||||
}
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion "22.0.1"
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion '23.0.3'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 22
|
||||
targetSdkVersion 23
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
@@ -50,10 +50,13 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: '*.jar')
|
||||
compile 'com.android.support:appcompat-v7:22.1.1'
|
||||
compile 'com.android.support:recyclerview-v7:22.0.0'
|
||||
compile 'com.loopj.android:android-async-http:1.4.6'
|
||||
compile 'com.android.support:appcompat-v7:23.3.0'
|
||||
compile 'com.android.support:recyclerview-v7:23.3.0'
|
||||
compile 'com.android.support:design:23.3.0'
|
||||
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
|
||||
compile 'de.greenrobot:eventbus:2.4.0'
|
||||
compile 'com.shamanland:fab:0.0.6'
|
||||
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'
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.sismics.docs" >
|
||||
package="com.sismics.docs"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
@@ -47,6 +48,16 @@
|
||||
android:name=".activity.DocumentEditActivity"
|
||||
android:label="@string/new_document">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.AuditLogActivity"
|
||||
android:label="@string/latest_activity">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.UserProfileActivity">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.GroupProfileActivity">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.SettingsActivity"
|
||||
android:label="@string/settings">
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.sismics.docs;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.androidquery.callback.BitmapAjaxCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
|
||||
@@ -20,14 +19,8 @@ public class MainApplication extends Application {
|
||||
JSONObject json = PreferenceUtil.getCachedJson(getApplicationContext(), PreferenceUtil.PREF_CACHED_USER_INFO_JSON);
|
||||
ApplicationContext.getInstance().setUserInfo(getApplicationContext(), json);
|
||||
|
||||
// TODO google docs app: right drawer with all actions, with acls, with deep metadatas
|
||||
// TODO Provide documents to intent action get content
|
||||
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
BitmapAjaxCallback.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.adapter.AuditLogListAdapter;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.AuditLogResource;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Audit log activity.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class AuditLogActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Check if logged in
|
||||
if (!ApplicationContext.getInstance().isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle activity context
|
||||
if (getIntent() == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Input document ID (optional)
|
||||
final String documentId = getIntent().getStringExtra("documentId");
|
||||
|
||||
// Setup the activity
|
||||
setContentView(R.layout.auditlog_activity);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
}
|
||||
|
||||
// Configure the swipe refresh layout
|
||||
SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
|
||||
swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
|
||||
android.R.color.holo_green_light,
|
||||
android.R.color.holo_orange_light,
|
||||
android.R.color.holo_red_light);
|
||||
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
refreshView(documentId);
|
||||
}
|
||||
});
|
||||
|
||||
// Navigate to user profile on click
|
||||
final ListView auditLogListView = (ListView) findViewById(R.id.auditLogListView);
|
||||
auditLogListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (auditLogListView.getAdapter() == null) {
|
||||
return;
|
||||
}
|
||||
AuditLogListAdapter adapter = (AuditLogListAdapter) auditLogListView.getAdapter();
|
||||
String username = adapter.getItem(position).optString("username");
|
||||
Intent intent = new Intent(AuditLogActivity.this, UserProfileActivity.class);
|
||||
intent.putExtra("username", username);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
// Get audit log list
|
||||
refreshView(documentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the view.
|
||||
*/
|
||||
private void refreshView(String documentId) {
|
||||
final SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
|
||||
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
final ListView auditLogListView = (ListView) findViewById(R.id.auditLogListView);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
auditLogListView.setVisibility(View.GONE);
|
||||
AuditLogResource.list(this, documentId, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject response) {
|
||||
auditLogListView.setAdapter(new AuditLogListAdapter(response.optJSONArray("logs")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
auditLogListView.setVisibility(View.VISIBLE);
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
@@ -17,7 +16,7 @@ import com.sismics.docs.adapter.LanguageAdapter;
|
||||
import com.sismics.docs.adapter.TagAutoCompleteAdapter;
|
||||
import com.sismics.docs.event.DocumentAddEvent;
|
||||
import com.sismics.docs.event.DocumentEditEvent;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.resource.DocumentResource;
|
||||
import com.sismics.docs.ui.form.Validator;
|
||||
import com.sismics.docs.ui.form.validator.Required;
|
||||
@@ -25,7 +24,7 @@ import com.sismics.docs.ui.view.DatePickerView;
|
||||
import com.sismics.docs.ui.view.TagsCompleteTextView;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -36,8 +35,6 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Document edition activity.
|
||||
*
|
||||
@@ -124,7 +121,7 @@ public class DocumentEditActivity extends AppCompatActivity {
|
||||
} else {
|
||||
setTitle(R.string.edit_document);
|
||||
titleEditText.setText(document.optString("title"));
|
||||
descriptionEditText.setText(document.optString("description"));
|
||||
descriptionEditText.setText(document.isNull("description") ? "" : document.optString("description"));
|
||||
datePickerView.setDate(new Date(document.optLong("create_date")));
|
||||
languageSpinner.setSelection(languageAdapter.getItemPosition(document.optString("language")));
|
||||
JSONArray documentTags = document.optJSONArray("tags");
|
||||
@@ -165,18 +162,12 @@ public class DocumentEditActivity extends AppCompatActivity {
|
||||
// Cancellable progress dialog
|
||||
final ProgressDialog progressDialog = ProgressDialog.show(this,
|
||||
getString(R.string.please_wait),
|
||||
getString(R.string.document_editing_message), true, true,
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
DocumentResource.cancel(DocumentEditActivity.this);
|
||||
}
|
||||
});
|
||||
getString(R.string.document_editing_message), true, true);
|
||||
|
||||
// Server callback
|
||||
JsonHttpResponseHandler callback = new JsonHttpResponseHandler() {
|
||||
HttpCallback callback = new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
// Build a fake document JSON to update the UI
|
||||
final JSONObject outputDoc = new JSONObject();
|
||||
try {
|
||||
@@ -211,7 +202,7 @@ public class DocumentEditActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
Toast.makeText(DocumentEditActivity.this, R.string.error_editing_document, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.DownloadManager;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.view.ViewPager;
|
||||
@@ -19,11 +16,15 @@ import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
@@ -31,22 +32,30 @@ import android.widget.Toast;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.adapter.AclListAdapter;
|
||||
import com.sismics.docs.adapter.CommentListAdapter;
|
||||
import com.sismics.docs.adapter.FilePagerAdapter;
|
||||
import com.sismics.docs.event.CommentAddEvent;
|
||||
import com.sismics.docs.event.CommentDeleteEvent;
|
||||
import com.sismics.docs.event.DocumentDeleteEvent;
|
||||
import com.sismics.docs.event.DocumentEditEvent;
|
||||
import com.sismics.docs.event.DocumentFullscreenEvent;
|
||||
import com.sismics.docs.event.FileAddEvent;
|
||||
import com.sismics.docs.event.FileDeleteEvent;
|
||||
import com.sismics.docs.fragment.DocExportPdfFragment;
|
||||
import com.sismics.docs.fragment.DocShareFragment;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.CommentResource;
|
||||
import com.sismics.docs.resource.DocumentResource;
|
||||
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 org.apache.http.Header;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -55,8 +64,6 @@ import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Document activity.
|
||||
*
|
||||
@@ -83,6 +90,11 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*/
|
||||
private FilePagerAdapter filePagerAdapter;
|
||||
|
||||
/**
|
||||
* Comment list adapter.
|
||||
*/
|
||||
private CommentListAdapter commentListAdapter;
|
||||
|
||||
/**
|
||||
* Document displayed.
|
||||
*/
|
||||
@@ -142,7 +154,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param document Document in JSON format
|
||||
*/
|
||||
private void refreshDocument(JSONObject document) {
|
||||
private void refreshDocument(final JSONObject document) {
|
||||
this.document = document;
|
||||
|
||||
String title = document.optString("title");
|
||||
@@ -168,7 +180,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
createdDateTextView.setText(date);
|
||||
|
||||
TextView descriptionTextView = (TextView) findViewById(R.id.descriptionTextView);
|
||||
if (description == null || description.isEmpty()) {
|
||||
if (description.isEmpty() || document.isNull("description")) {
|
||||
descriptionTextView.setVisibility(View.GONE);
|
||||
} else {
|
||||
descriptionTextView.setVisibility(View.VISIBLE);
|
||||
@@ -231,20 +243,75 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
}
|
||||
});
|
||||
|
||||
// Action export PDF
|
||||
button = (Button) findViewById(R.id.actionExportPdf);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
DialogFragment dialog = DocExportPdfFragment.newInstance(
|
||||
document.optString("id"), document.optString("title"));
|
||||
dialog.show(getSupportFragmentManager(), "DocExportPdfFragment");
|
||||
}
|
||||
});
|
||||
|
||||
// Action share
|
||||
button = (Button) findViewById(R.id.actionSharing);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
DialogFragment dialog = DocShareFragment.newInstance(DocumentViewActivity.this.document.optString("id"));
|
||||
DialogFragment dialog = DocShareFragment.newInstance(document.optString("id"));
|
||||
dialog.show(getSupportFragmentManager(), "DocShareFragment");
|
||||
}
|
||||
});
|
||||
|
||||
// Action audit log
|
||||
button = (Button) findViewById(R.id.actionAuditLog);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, AuditLogActivity.class);
|
||||
intent.putExtra("documentId", document.optString("id"));
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
// Button add a comment
|
||||
ImageButton imageButton = (ImageButton) findViewById(R.id.addCommentBtn);
|
||||
imageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
final EditText commentEditText = (EditText) findViewById(R.id.commentEditText);
|
||||
if (commentEditText.getText().length() == 0) {
|
||||
// No content for the new comment
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.makeText(DocumentViewActivity.this, R.string.adding_comment, Toast.LENGTH_LONG).show();
|
||||
|
||||
CommentResource.add(DocumentViewActivity.this,
|
||||
DocumentViewActivity.this.document.optString("id"),
|
||||
commentEditText.getText().toString(),
|
||||
new HttpCallback() {
|
||||
public void onSuccess(JSONObject response) {
|
||||
EventBus.getDefault().post(new CommentAddEvent(response));
|
||||
commentEditText.setText("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
Toast.makeText(DocumentViewActivity.this, R.string.comment_add_failure, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Grab the comments
|
||||
updateComments();
|
||||
|
||||
// Grab the attached files
|
||||
updateFiles();
|
||||
|
||||
// Grab the full document (used for ACLs and writable status)
|
||||
// Grab the full document (used for ACLs, remaining metadata and writable status)
|
||||
updateDocument();
|
||||
}
|
||||
|
||||
@@ -268,6 +335,15 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.comments:
|
||||
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||
if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
|
||||
drawerLayout.closeDrawer(GravityCompat.START);
|
||||
} else {
|
||||
drawerLayout.openDrawer(GravityCompat.START);
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.download_file:
|
||||
downloadCurrentFile();
|
||||
return true;
|
||||
@@ -299,11 +375,11 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
int position = fileViewPager.getCurrentItem();
|
||||
if (mimeType == null || !mimeType.contains("/")) return;
|
||||
String ext = mimeType.split("/")[1];
|
||||
String fileName = getTitle() + "-" + position + "." + ext;
|
||||
String fileName = document.optString("title") + "-" + position + "." + ext;
|
||||
|
||||
// Download the file
|
||||
String fileUrl = PreferenceUtil.getServerUrl(this) + "/api/file/" + file.optString("id") + "/data";
|
||||
downloadFile(fileUrl, fileName, getTitle().toString(), getString(R.string.downloading_file, position + 1));
|
||||
NetworkUtil.downloadFile(this, fileUrl, fileName, document.optString("title"), getString(R.string.download_file_title));
|
||||
}
|
||||
|
||||
private void deleteCurrentFile() {
|
||||
@@ -326,24 +402,18 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
// Show a progress dialog while deleting
|
||||
final ProgressDialog progressDialog = ProgressDialog.show(DocumentViewActivity.this,
|
||||
getString(R.string.please_wait),
|
||||
getString(R.string.file_deleting_message), true, true,
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
FileResource.cancel(DocumentViewActivity.this);
|
||||
}
|
||||
});
|
||||
getString(R.string.file_deleting_message), true, true);
|
||||
|
||||
// Actual delete server call
|
||||
final String fileId = file.optString("id");
|
||||
FileResource.delete(DocumentViewActivity.this, fileId, new JsonHttpResponseHandler() {
|
||||
FileResource.delete(DocumentViewActivity.this, fileId, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
EventBus.getDefault().post(new FileDeleteEvent(fileId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
Toast.makeText(DocumentViewActivity.this, R.string.file_delete_failure, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@@ -368,28 +438,8 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
private void downloadZip() {
|
||||
if (document == null) return;
|
||||
String url = PreferenceUtil.getServerUrl(this) + "/api/file/zip?id=" + document.optString("id");
|
||||
String fileName = getTitle() + ".zip";
|
||||
downloadFile(url, fileName, getTitle().toString(), getString(R.string.downloading_document));
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a file using Android download manager.
|
||||
*
|
||||
* @param url URL to download
|
||||
* @param fileName Destination file name
|
||||
* @param title Notification title
|
||||
* @param description Notification description
|
||||
*/
|
||||
private void downloadFile(String url, String fileName, String title, String description) {
|
||||
String authToken = PreferenceUtil.getAuthToken(this);
|
||||
DownloadManager downloadManager = (DownloadManager) 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);
|
||||
request.addRequestHeader("Cookie", "auth_token=" + authToken);
|
||||
request.setTitle(title);
|
||||
request.setDescription(description);
|
||||
downloadManager.enqueue(request);
|
||||
String fileName = document.optString("title") + ".zip";
|
||||
NetworkUtil.downloadFile(this, url, fileName, document.optString("title"), getString(R.string.download_document_title));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -411,24 +461,18 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
// Show a progress dialog while deleting
|
||||
final ProgressDialog progressDialog = ProgressDialog.show(DocumentViewActivity.this,
|
||||
getString(R.string.please_wait),
|
||||
getString(R.string.document_deleting_message), true, true,
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
DocumentResource.cancel(DocumentViewActivity.this);
|
||||
}
|
||||
});
|
||||
getString(R.string.document_deleting_message), true, true);
|
||||
|
||||
// Actual delete server call
|
||||
final String documentId = document.optString("id");
|
||||
DocumentResource.delete(DocumentViewActivity.this, documentId, new JsonHttpResponseHandler() {
|
||||
DocumentResource.delete(DocumentViewActivity.this, documentId, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
EventBus.getDefault().post(new DocumentDeleteEvent(documentId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
Toast.makeText(DocumentViewActivity.this, R.string.document_delete_failure, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@@ -453,6 +497,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param event Document fullscreen event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DocumentFullscreenEvent event) {
|
||||
findViewById(R.id.detailLayout).setVisibility(event.isFullscreen() ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
@@ -462,6 +507,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param event Document edit event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DocumentEditEvent event) {
|
||||
if (document == null) return;
|
||||
if (event.getDocument().optString("id").equals(document.optString("id"))) {
|
||||
@@ -475,6 +521,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param event Document delete event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DocumentDeleteEvent event) {
|
||||
if (document == null) return;
|
||||
if (event.getDocumentId().equals(document.optString("id"))) {
|
||||
@@ -488,6 +535,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param event File delete event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(FileDeleteEvent event) {
|
||||
if (filePagerAdapter == null) return;
|
||||
filePagerAdapter.remove(event.getFileId());
|
||||
@@ -500,6 +548,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param event File add event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(FileAddEvent event) {
|
||||
if (document == null) return;
|
||||
if (document.optString("id").equals(event.getDocumentId())) {
|
||||
@@ -507,6 +556,38 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A comment add event has been fired.
|
||||
*
|
||||
* @param event Comment add event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(CommentAddEvent event) {
|
||||
if (commentListAdapter == null) return;
|
||||
TextView emptyView = (TextView) findViewById(R.id.commentEmptyView);
|
||||
ListView listView = (ListView) findViewById(R.id.commentListView);
|
||||
emptyView.setVisibility(View.GONE);
|
||||
listView.setVisibility(View.VISIBLE);
|
||||
commentListAdapter.add(event.getComment());
|
||||
}
|
||||
|
||||
/**
|
||||
* A comment delete event has been fired.
|
||||
*
|
||||
* @param event Comment add event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(CommentDeleteEvent event) {
|
||||
if (commentListAdapter == null) return;
|
||||
TextView emptyView = (TextView) findViewById(R.id.commentEmptyView);
|
||||
ListView listView = (ListView) findViewById(R.id.commentListView);
|
||||
commentListAdapter.remove(event.getCommentId());
|
||||
if (commentListAdapter.getCount() == 0) {
|
||||
emptyView.setVisibility(View.VISIBLE);
|
||||
listView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (document == null) return;
|
||||
@@ -550,9 +631,9 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
// Silently get the document to know if it is writable by the current user
|
||||
// If this call fails or is slow and the document is read-only,
|
||||
// write actions will be allowed and will fail
|
||||
DocumentResource.get(this, document.optString("id"), new JsonHttpResponseHandler() {
|
||||
DocumentResource.get(this, document.optString("id"), new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
document = response;
|
||||
boolean writable = document.optBoolean("writable");
|
||||
|
||||
@@ -560,6 +641,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
menu.findItem(R.id.delete_file).setVisible(writable);
|
||||
}
|
||||
|
||||
// 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);
|
||||
@@ -567,7 +649,119 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
|
||||
// ACLs
|
||||
ListView aclListView = (ListView) findViewById(R.id.aclListView);
|
||||
aclListView.setAdapter(new AclListAdapter(document.optJSONArray("acls")));
|
||||
final AclListAdapter aclListAdapter = new AclListAdapter(document.optJSONArray("acls"));
|
||||
aclListView.setAdapter(aclListAdapter);
|
||||
aclListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
AclListAdapter.AclItem acl = aclListAdapter.getItem(position);
|
||||
if (acl.getType().equals("USER")) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, UserProfileActivity.class);
|
||||
intent.putExtra("username", acl.getName());
|
||||
startActivity(intent);
|
||||
} else if (acl.getType().equals("GROUP")) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, GroupProfileActivity.class);
|
||||
intent.putExtra("name", acl.getName());
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Remaining metadata
|
||||
TextView creatorTextView = (TextView) findViewById(R.id.creatorTextView);
|
||||
final String creator = document.optString("creator");
|
||||
creatorTextView.setText(creator);
|
||||
creatorTextView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, UserProfileActivity.class);
|
||||
intent.putExtra("username", creator);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
|
||||
switch (view.getId()) {
|
||||
case R.id.commentListView:
|
||||
if (commentListAdapter == null || document == null) return;
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
|
||||
JSONObject comment = commentListAdapter.getItem(info.position);
|
||||
boolean writable = document.optBoolean("writable");
|
||||
String creator = comment.optString("creator");
|
||||
String username = ApplicationContext.getInstance().getUserInfo().optString("username");
|
||||
if (writable || creator.equals(username)) {
|
||||
menu.add(Menu.NONE, 0, 0, getString(R.string.comment_delete));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
// Use real ids if more than one item someday
|
||||
if (item.getItemId() == 0) {
|
||||
// Delete a comment
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
|
||||
if (commentListAdapter == null) return false;
|
||||
JSONObject comment = commentListAdapter.getItem(info.position);
|
||||
final String commentId = comment.optString("id");
|
||||
Toast.makeText(DocumentViewActivity.this, R.string.deleting_comment, Toast.LENGTH_LONG).show();
|
||||
|
||||
CommentResource.remove(DocumentViewActivity.this, commentId, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject response) {
|
||||
EventBus.getDefault().post(new CommentDeleteEvent(commentId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
Toast.makeText(DocumentViewActivity.this, R.string.error_deleting_comment, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh comments list.
|
||||
*/
|
||||
private void updateComments() {
|
||||
if (document == null) return;
|
||||
|
||||
final View progressBar = findViewById(R.id.commentProgressView);
|
||||
final TextView emptyView = (TextView) findViewById(R.id.commentEmptyView);
|
||||
final ListView listView = (ListView) findViewById(R.id.commentListView);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
emptyView.setVisibility(View.GONE);
|
||||
listView.setVisibility(View.GONE);
|
||||
registerForContextMenu(listView);
|
||||
|
||||
CommentResource.list(this, document.optString("id"), new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject response) {
|
||||
JSONArray comments = response.optJSONArray("comments");
|
||||
commentListAdapter = new CommentListAdapter(DocumentViewActivity.this, comments);
|
||||
listView.setAdapter(commentListAdapter);
|
||||
listView.setVisibility(View.VISIBLE);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
if (comments.length() == 0) {
|
||||
listView.setVisibility(View.GONE);
|
||||
emptyView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
emptyView.setText(R.string.error_loading_comments);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
listView.setVisibility(View.GONE);
|
||||
emptyView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -586,9 +780,9 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
filesEmptyView.setVisibility(View.GONE);
|
||||
|
||||
FileResource.list(this, document.optString("id"), new JsonHttpResponseHandler() {
|
||||
FileResource.list(this, document.optString("id"), new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
JSONArray files = response.optJSONArray("files");
|
||||
filePagerAdapter = new FilePagerAdapter(DocumentViewActivity.this, files);
|
||||
fileViewPager.setAdapter(filePagerAdapter);
|
||||
@@ -598,7 +792,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
filesEmptyView.setText(R.string.error_loading_files);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
filesEmptyView.setVisibility(View.VISIBLE);
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.UserResource;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Group profile activity.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class GroupProfileActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Check if logged in
|
||||
if (!ApplicationContext.getInstance().isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle activity context
|
||||
if (getIntent() == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Input name
|
||||
final String name = getIntent().getStringExtra("name");
|
||||
if (name == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup the activity
|
||||
setTitle(name);
|
||||
setContentView(R.layout.groupprofile_activity);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
}
|
||||
|
||||
// Get the group and populate the view
|
||||
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
final View layoutView = findViewById(R.id.layout);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
layoutView.setVisibility(View.GONE);
|
||||
UserResource.get(this, name, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject json) {
|
||||
TextView membersTextView = (TextView) findViewById(R.id.membersTextView);
|
||||
JSONArray members = json.optJSONArray("members");
|
||||
String output = "";
|
||||
for (int i = 0; i < members.length(); i++) {
|
||||
output += members.optString(i) + "; ";
|
||||
}
|
||||
membersTextView.setText(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
layoutView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,11 @@ import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.androidquery.AQuery;
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.listener.CallbackListener;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.UserResource;
|
||||
import com.sismics.docs.ui.form.Validator;
|
||||
@@ -22,7 +22,6 @@ import com.sismics.docs.ui.form.validator.Required;
|
||||
import com.sismics.docs.util.DialogUtil;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
@@ -31,7 +30,6 @@ import org.json.JSONObject;
|
||||
* @author bgamard
|
||||
*/
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
|
||||
/**
|
||||
* User interface.
|
||||
*/
|
||||
@@ -42,19 +40,18 @@ public class LoginActivity extends AppCompatActivity {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.login_activity);
|
||||
|
||||
AQuery aq = new AQuery(this);
|
||||
aq.id(R.id.loginExplain)
|
||||
.text(Html.fromHtml(getString(R.string.login_explain)))
|
||||
.getTextView()
|
||||
.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
final EditText txtServer = aq.id(R.id.txtServer).getEditText();
|
||||
final EditText txtUsername = aq.id(R.id.txtUsername).getEditText();
|
||||
final EditText txtPassword = aq.id(R.id.txtPassword).getEditText();
|
||||
final Button btnConnect = aq.id(R.id.btnConnect).getButton();
|
||||
loginForm = aq.id(R.id.loginForm).getView();
|
||||
progressBar = aq.id(R.id.progressBar).getView();
|
||||
|
||||
TextView loginExplainTextView = (TextView) findViewById(R.id.loginExplain);
|
||||
loginExplainTextView.setText(Html.fromHtml(getString(R.string.login_explain)));
|
||||
loginExplainTextView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
final EditText txtServer = (EditText) findViewById(R.id.txtServer);
|
||||
final EditText txtUsername = (EditText) findViewById(R.id.txtUsername);
|
||||
final EditText txtPassword = (EditText) findViewById(R.id.txtPassword);
|
||||
final EditText txtValidationCode = (EditText) findViewById(R.id.txtValidationCode);
|
||||
final Button btnConnect = (Button) findViewById(R.id.btnConnect);
|
||||
loginForm = findViewById(R.id.loginForm);
|
||||
progressBar = findViewById(R.id.progressBar);
|
||||
|
||||
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
|
||||
|
||||
@@ -91,9 +88,11 @@ public class LoginActivity extends AppCompatActivity {
|
||||
PreferenceUtil.setServerUrl(LoginActivity.this, txtServer.getText().toString());
|
||||
|
||||
try {
|
||||
UserResource.login(getApplicationContext(), txtUsername.getText().toString(), txtPassword.getText().toString(), new JsonHttpResponseHandler() {
|
||||
UserResource.login(getApplicationContext(), txtUsername.getText().toString(),
|
||||
txtPassword.getText().toString(), txtValidationCode.getText().toString(),
|
||||
new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject json) {
|
||||
public void onSuccess(JSONObject json) {
|
||||
// Empty previous user caches
|
||||
PreferenceUtil.resetUserCache(getApplicationContext());
|
||||
|
||||
@@ -109,12 +108,16 @@ public class LoginActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
loginForm.setVisibility(View.VISIBLE);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
|
||||
if (responseBytes != null && new String(responseBytes).contains("\"ForbiddenError\"")) {
|
||||
if (json != null && json.optString("type").equals("ForbiddenError")) {
|
||||
DialogUtil.showOkDialog(LoginActivity.this, R.string.login_fail_title, R.string.login_fail);
|
||||
} else if (json != null && json.optString("type").equals("ValidationCodeRequired")) {
|
||||
txtValidationCode.setVisibility(View.VISIBLE);
|
||||
validator.addValidable(txtValidationCode, new Required());
|
||||
validator.validate();
|
||||
} else {
|
||||
DialogUtil.showOkDialog(LoginActivity.this, R.string.network_error_title, R.string.network_error);
|
||||
}
|
||||
@@ -151,9 +154,9 @@ public class LoginActivity extends AppCompatActivity {
|
||||
finish();
|
||||
} else {
|
||||
// Trying to get user data
|
||||
UserResource.info(getApplicationContext(), new JsonHttpResponseHandler() {
|
||||
UserResource.info(getApplicationContext(), new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, final JSONObject json) {
|
||||
public void onSuccess(final JSONObject json) {
|
||||
if (json.optBoolean("anonymous", true)) {
|
||||
loginForm.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
@@ -169,7 +172,7 @@ public class LoginActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
DialogUtil.showOkDialog(LoginActivity.this, R.string.network_error_title, R.string.network_error);
|
||||
loginForm.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@@ -18,24 +18,23 @@ import android.widget.ListView;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.androidquery.util.AQUtility;
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.adapter.TagListAdapter;
|
||||
import com.sismics.docs.event.AdvancedSearchEvent;
|
||||
import com.sismics.docs.event.SearchEvent;
|
||||
import com.sismics.docs.fragment.SearchFragment;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.provider.RecentSuggestionsProvider;
|
||||
import com.sismics.docs.resource.TagResource;
|
||||
import com.sismics.docs.resource.UserResource;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Main activity.
|
||||
*
|
||||
@@ -43,7 +42,6 @@ import de.greenrobot.event.EventBus;
|
||||
*/
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private ActionBarDrawerToggle drawerToggle;
|
||||
private MenuItem searchItem;
|
||||
private DrawerLayout drawerLayout;
|
||||
@@ -73,7 +71,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
// between the sliding drawer and the action bar app icon
|
||||
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
|
||||
R.string.drawer_open, R.string.drawer_close);
|
||||
drawerLayout.setDrawerListener(drawerToggle);
|
||||
drawerLayout.addDrawerListener(drawerToggle);
|
||||
|
||||
// Fill the drawer user info
|
||||
JSONObject userInfo = ApplicationContext.getInstance().getUserInfo();
|
||||
@@ -91,9 +89,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
if (cacheTags != null) {
|
||||
tagListView.setAdapter(new TagListAdapter(cacheTags.optJSONArray("stats")));
|
||||
}
|
||||
TagResource.stats(this, new JsonHttpResponseHandler() {
|
||||
TagResource.stats(this, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
PreferenceUtil.setCachedJson(MainActivity.this, PreferenceUtil.PREF_CACHED_TAGS_JSON, response);
|
||||
tagListView.setAdapter(new TagListAdapter(response.optJSONArray("stats")));
|
||||
tagProgressView.setVisibility(View.GONE);
|
||||
@@ -101,7 +99,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
tagEmptyView.setText(R.string.error_loading_tags);
|
||||
tagProgressView.setVisibility(View.GONE);
|
||||
tagListView.setEmptyView(tagEmptyView);
|
||||
@@ -114,9 +112,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
TagListAdapter adapter = (TagListAdapter) tagListView.getAdapter();
|
||||
if (adapter == null) return;
|
||||
JSONObject tag = adapter.getItem(position);
|
||||
if (tag == null) return;
|
||||
searchQuery("tag:" + tag.optString("name"));
|
||||
TagListAdapter.TagItem tagItem = adapter.getItem(position);
|
||||
if (tagItem == null) return;
|
||||
searchQuery("tag:" + tagItem.getName());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -138,6 +136,15 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
});
|
||||
|
||||
// Click on Latest activity
|
||||
View auditLogLayout = findViewById(R.id.auditLogLayout);
|
||||
auditLogLayout.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
startActivity(new Intent(MainActivity.this, AuditLogActivity.class));
|
||||
}
|
||||
});
|
||||
|
||||
handleIntent(getIntent());
|
||||
|
||||
EventBus.getDefault().register(this);
|
||||
@@ -147,7 +154,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.logout:
|
||||
UserResource.logout(getApplicationContext(), new JsonHttpResponseHandler() {
|
||||
UserResource.logout(getApplicationContext(), new HttpCallback() {
|
||||
@Override
|
||||
public void onFinish() {
|
||||
// Force logout in all cases, so the user is not stuck in case of network error
|
||||
@@ -267,6 +274,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param event Advanced search event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(AdvancedSearchEvent event) {
|
||||
searchQuery(event.getQuery());
|
||||
}
|
||||
@@ -274,10 +282,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
EventBus.getDefault().unregister(this);
|
||||
if(isTaskRoot()) {
|
||||
int cacheSizeMb = PreferenceUtil.getIntegerPreference(this, PreferenceUtil.PREF_CACHE_SIZE, 10);
|
||||
AQUtility.cleanCacheAsync(this, cacheSizeMb * 1000000, cacheSizeMb * 1000000);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import com.sismics.docs.fragment.SettingsFragment;
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.UserResource;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* User profile activity.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class UserProfileActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Check if logged in
|
||||
if (!ApplicationContext.getInstance().isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle activity context
|
||||
if (getIntent() == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Input username
|
||||
final String username = getIntent().getStringExtra("username");
|
||||
if (username == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup the activity
|
||||
setTitle(username);
|
||||
setContentView(R.layout.userprofile_activity);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
}
|
||||
|
||||
// Get the user and populate the view
|
||||
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
final View layoutView = findViewById(R.id.layout);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
layoutView.setVisibility(View.GONE);
|
||||
UserResource.get(this, username, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject json) {
|
||||
TextView emailTextView = (TextView) findViewById(R.id.emailTextView);
|
||||
emailTextView.setText(json.optString("email"));
|
||||
|
||||
TextView quotaTextView = (TextView) findViewById(R.id.quotaTextView);
|
||||
quotaTextView.setText(getString(R.string.storage_display,
|
||||
Math.round(json.optLong("storage_current") / 1000000),
|
||||
Math.round(json.optLong("storage_quota") / 1000000)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
layoutView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.sismics.docs.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -24,7 +25,7 @@ public class AclListAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Shares.
|
||||
*/
|
||||
private List<JSONObject> acls;
|
||||
private List<AclItem> aclItemList;
|
||||
|
||||
/**
|
||||
* ACL list adapter.
|
||||
@@ -32,28 +33,46 @@ public class AclListAdapter extends BaseAdapter {
|
||||
* @param acls ACLs
|
||||
*/
|
||||
public AclListAdapter(JSONArray acls) {
|
||||
this.acls = new ArrayList<>();
|
||||
this.aclItemList = new ArrayList<>();
|
||||
|
||||
// Extract only share ACLs
|
||||
// Group ACLs
|
||||
for (int i = 0; i < acls.length(); i++) {
|
||||
JSONObject acl = acls.optJSONObject(i);
|
||||
this.acls.add(acl);
|
||||
String type = acl.optString("type");
|
||||
String name = acl.optString("name");
|
||||
String perm = acl.optString("perm");
|
||||
|
||||
boolean found = false;
|
||||
for (AclItem aclItem : aclItemList) {
|
||||
if (aclItem.type.equals(type) && aclItem.name.equals(name)) {
|
||||
aclItem.permList.add(perm);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
AclItem aclItem = new AclItem();
|
||||
aclItem.type = type;
|
||||
aclItem.name = name;
|
||||
aclItem.permList.add(perm);
|
||||
this.aclItemList.add(aclItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return acls.size();
|
||||
return aclItemList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getItem(int position) {
|
||||
return acls.get(position);
|
||||
public AclItem getItem(int position) {
|
||||
return aclItemList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getItem(position).optString("id").hashCode();
|
||||
return getItem(position).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,14 +83,37 @@ public class AclListAdapter extends BaseAdapter {
|
||||
}
|
||||
|
||||
// Fill the view
|
||||
final JSONObject acl = getItem(position);
|
||||
final AclItem aclItem = getItem(position);
|
||||
TextView typeTextView = (TextView) view.findViewById(R.id.typeTextView);
|
||||
typeTextView.setText(acl.optString("type"));
|
||||
typeTextView.setText(aclItem.type);
|
||||
TextView nameTextView = (TextView) view.findViewById(R.id.nameTextView);
|
||||
nameTextView.setText(acl.optString("name"));
|
||||
nameTextView.setText(aclItem.name);
|
||||
TextView permTextView = (TextView) view.findViewById(R.id.permTextView);
|
||||
permTextView.setText(acl.optString("perm"));
|
||||
permTextView.setText(TextUtils.join(" + ", aclItem.permList));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* An ACL item in the list.
|
||||
* Permissions are grouped together.
|
||||
*/
|
||||
public static class AclItem {
|
||||
private String type;
|
||||
private String name;
|
||||
private List<String> permList = new ArrayList<>();
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (type + name).hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.sismics.docs.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Audit log list adapter.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class AuditLogListAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Shares.
|
||||
*/
|
||||
private List<JSONObject> logList;
|
||||
|
||||
/**
|
||||
* Audit log list adapter.
|
||||
*
|
||||
* @param logs Logs
|
||||
*/
|
||||
public AuditLogListAdapter(JSONArray logs) {
|
||||
this.logList = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < logs.length(); i++) {
|
||||
logList.add(logs.optJSONObject(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return logList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getItem(int position) {
|
||||
return logList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getItem(position).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, final ViewGroup parent) {
|
||||
if (view == null) {
|
||||
LayoutInflater vi = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
view = vi.inflate(R.layout.auditlog_list_item, parent, false);
|
||||
}
|
||||
|
||||
// Build message
|
||||
final JSONObject log = getItem(position);
|
||||
StringBuilder message = new StringBuilder(log.optString("class"));
|
||||
switch (log.optString("type")) {
|
||||
case "CREATE": message.append(" created"); break;
|
||||
case "UPDATE": message.append(" updated"); break;
|
||||
case "DELETE": message.append(" deleted"); break;
|
||||
}
|
||||
switch (log.optString("class")) {
|
||||
case "Document":
|
||||
case "Acl":
|
||||
case "Tag":
|
||||
case "User":
|
||||
case "Group":
|
||||
message.append(" : ");
|
||||
message.append(log.optString("message"));
|
||||
break;
|
||||
}
|
||||
|
||||
// Fill the view
|
||||
TextView usernameTextView = (TextView) view.findViewById(R.id.usernameTextView);
|
||||
TextView messageTextView = (TextView) view.findViewById(R.id.messageTextView);
|
||||
TextView dateTextView = (TextView) view.findViewById(R.id.dateTextView);
|
||||
usernameTextView.setText(log.optString("username"));
|
||||
messageTextView.setText(message);
|
||||
String date = DateFormat.getDateFormat(parent.getContext()).format(new Date(log.optLong("create_date")));
|
||||
dateTextView.setText(date);
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.sismics.docs.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Comment list adapter.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class CommentListAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Tags.
|
||||
*/
|
||||
private List<JSONObject> commentList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Context.
|
||||
*/
|
||||
private Context context;
|
||||
|
||||
/**
|
||||
* Comment list adapter.
|
||||
*
|
||||
* @param commentsArray Comments
|
||||
*/
|
||||
public CommentListAdapter(Context context, JSONArray commentsArray) {
|
||||
this.context = context;
|
||||
for (int i = 0; i < commentsArray.length(); i++) {
|
||||
commentList.add(commentsArray.optJSONObject(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return commentList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getItem(int position) {
|
||||
return commentList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getItem(position).optString("id").hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup parent) {
|
||||
if (view == null) {
|
||||
LayoutInflater vi = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
view = vi.inflate(R.layout.comment_list_item, parent, false);
|
||||
}
|
||||
|
||||
// Fill the view
|
||||
JSONObject comment = getItem(position);
|
||||
TextView creatorTextView = (TextView) view.findViewById(R.id.creatorTextView);
|
||||
TextView dateTextView = (TextView) view.findViewById(R.id.dateTextView);
|
||||
TextView contentTextView = (TextView) view.findViewById(R.id.contentTextView);
|
||||
ImageView gravatarImageView = (ImageView) view.findViewById(R.id.gravatarImageView);
|
||||
creatorTextView.setText(comment.optString("creator"));
|
||||
dateTextView.setText(DateFormat.getDateFormat(dateTextView.getContext()).format(new Date(comment.optLong("create_date"))));
|
||||
contentTextView.setText(comment.optString("content"));
|
||||
|
||||
// Gravatar image
|
||||
String gravatarUrl = "http://www.gravatar.com/avatar/" + comment.optString("creator_gravatar") + "?s=128d=identicon";
|
||||
OkHttpUtil.picasso(context)
|
||||
.load(gravatarUrl)
|
||||
.into(gravatarImageView);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new comment.
|
||||
*
|
||||
* @param comment Comment
|
||||
*/
|
||||
public void add(JSONObject comment) {
|
||||
commentList.add(comment);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a comment.
|
||||
*
|
||||
* @param commentId Comment ID
|
||||
*/
|
||||
public void remove(String commentId) {
|
||||
for (JSONObject comment : commentList) {
|
||||
if (comment.optString("id").equals(commentId)) {
|
||||
commentList.remove(comment);
|
||||
notifyDataSetChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,11 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.androidquery.AQuery;
|
||||
import com.androidquery.callback.BitmapAjaxCallback;
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
import com.squareup.picasso.Callback;
|
||||
import com.squareup.picasso.MemoryPolicy;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
@@ -30,21 +31,11 @@ public class FilePagerAdapter extends PagerAdapter {
|
||||
*/
|
||||
private List<JSONObject> files;
|
||||
|
||||
/**
|
||||
* AQuery.
|
||||
*/
|
||||
private AQuery aq;
|
||||
|
||||
/**
|
||||
* Context.
|
||||
*/
|
||||
private Context context;
|
||||
|
||||
/**
|
||||
* Auth token used to download files.
|
||||
*/
|
||||
private String authToken;
|
||||
|
||||
/**
|
||||
* File pager adapter.
|
||||
*
|
||||
@@ -57,8 +48,6 @@ public class FilePagerAdapter extends PagerAdapter {
|
||||
files.add(filesArray.optJSONObject(i));
|
||||
}
|
||||
this.context = context;
|
||||
this.authToken = PreferenceUtil.getAuthToken(context);
|
||||
aq = new AQuery(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,15 +55,20 @@ public class FilePagerAdapter extends PagerAdapter {
|
||||
View view = LayoutInflater.from(container.getContext()).inflate(R.layout.file_viewpager_item, container, false);
|
||||
|
||||
ImageViewTouch fileImageView = (ImageViewTouch) view.findViewById(R.id.fileImageView);
|
||||
ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.fileProgressBar);
|
||||
final ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.fileProgressBar);
|
||||
JSONObject file = files.get(position);
|
||||
String fileUrl = PreferenceUtil.getServerUrl(context) + "/api/file/" + file.optString("id") + "/data?size=web";
|
||||
aq.id(fileImageView)
|
||||
.image(new BitmapAjaxCallback()
|
||||
.url(fileUrl)
|
||||
.progress(progressBar)
|
||||
.animation(AQuery.FADE_IN_NETWORK)
|
||||
.cookie("auth_token", authToken));
|
||||
|
||||
// Load image
|
||||
OkHttpUtil.picasso(context)
|
||||
.load(fileUrl)
|
||||
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE) // Don't memory cache the images
|
||||
.into(fileImageView, new Callback.EmptyCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
|
||||
fileImageView.setDisplayType(ImageViewTouchBase.DisplayType.FIT_TO_SCREEN);
|
||||
|
||||
@@ -109,7 +103,7 @@ public class FilePagerAdapter extends PagerAdapter {
|
||||
* @return Object
|
||||
*/
|
||||
public JSONObject getObjectAt(int position) {
|
||||
if (files == null) {
|
||||
if (files == null || position < 0 || position >= files.size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,14 +12,13 @@ import com.sismics.docs.R;
|
||||
import com.sismics.docs.event.ShareDeleteEvent;
|
||||
import com.sismics.docs.event.ShareSendEvent;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Share list adapter.
|
||||
*
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.sismics.docs.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -17,9 +19,8 @@ import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Tag list adapter.
|
||||
@@ -30,7 +31,7 @@ public class TagListAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Tags.
|
||||
*/
|
||||
private List<JSONObject> tags;
|
||||
private List<TagItem> tagItemList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Tag list adapter.
|
||||
@@ -38,33 +39,53 @@ public class TagListAdapter extends BaseAdapter {
|
||||
* @param tagsArray Tags
|
||||
*/
|
||||
public TagListAdapter(JSONArray tagsArray) {
|
||||
this.tags = new ArrayList<>();
|
||||
List<JSONObject> tags = new ArrayList<>();
|
||||
for (int i = 0; i < tagsArray.length(); i++) {
|
||||
tags.add(tagsArray.optJSONObject(i));
|
||||
}
|
||||
|
||||
// Sort tags by count desc
|
||||
Collections.sort(tags, new Comparator<JSONObject>() {
|
||||
@Override
|
||||
public int compare(JSONObject lhs, JSONObject rhs) {
|
||||
return lhs.optInt("count") < rhs.optInt("count") ? 1 : -1;
|
||||
// Reorder tags by parent/child relation and compute depth
|
||||
int depth = 0;
|
||||
initTags(tags, JSONObject.NULL.toString(), depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init tags model recursively.
|
||||
*
|
||||
* @param tags All tags from server
|
||||
* @param parentId Parent ID
|
||||
* @param depth Depth
|
||||
*/
|
||||
private void initTags(List<JSONObject> tags, String parentId, int depth) {
|
||||
// Get all tags with this parent
|
||||
for (JSONObject tag : tags) {
|
||||
String tagParentId = tag.optString("parent");
|
||||
if (tagParentId.equals(parentId)) {
|
||||
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);
|
||||
initTags(tags, tagItem.id, depth + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return tags.size();
|
||||
return tagItemList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getItem(int position) {
|
||||
return tags.get(position);
|
||||
public TagItem getItem(int position) {
|
||||
return tagItemList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getItem(position).optString("id").hashCode();
|
||||
return getItem(position).id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,19 +96,41 @@ public class TagListAdapter extends BaseAdapter {
|
||||
}
|
||||
|
||||
// Fill the view
|
||||
JSONObject tag = getItem(position);
|
||||
TagItem tagItem = getItem(position);
|
||||
TextView tagTextView = (TextView) view.findViewById(R.id.tagTextView);
|
||||
tagTextView.setText(tag.optString("name"));
|
||||
tagTextView.setText(tagItem.name);
|
||||
TextView tagCountTextView = (TextView) view.findViewById(R.id.tagCountTextView);
|
||||
tagCountTextView.setText(tag.optString("count"));
|
||||
tagCountTextView.setText(String.format(Locale.ENGLISH, "%d", tagItem.count));
|
||||
|
||||
// Label color filtering
|
||||
ImageView labelImageView = (ImageView) view.findViewById(R.id.labelImageView);
|
||||
Drawable labelDrawable = labelImageView.getDrawable().mutate();
|
||||
labelDrawable.setColorFilter(Color.parseColor(tag.optString("color")), PorterDuff.Mode.MULTIPLY);
|
||||
labelDrawable.setColorFilter(Color.parseColor(tagItem.color), PorterDuff.Mode.MULTIPLY);
|
||||
labelImageView.setImageDrawable(labelDrawable);
|
||||
labelImageView.invalidate();
|
||||
|
||||
// Offset according to depth
|
||||
Resources resources = parent.getContext().getResources();
|
||||
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) labelImageView.getLayoutParams();
|
||||
layoutParams.leftMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tagItem.depth * 12, resources.getDisplayMetrics());
|
||||
labelImageView.setLayoutParams(layoutParams);
|
||||
labelImageView.requestLayout();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tag item in the tags list.
|
||||
*/
|
||||
public static class TagItem {
|
||||
private String id;
|
||||
private String name;
|
||||
private int count;
|
||||
private String color;
|
||||
private int depth;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.sismics.docs.event;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Comment add event.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class CommentAddEvent {
|
||||
/**
|
||||
* Comment.
|
||||
*/
|
||||
private JSONObject comment;
|
||||
|
||||
/**
|
||||
* Create a comment add event.
|
||||
*
|
||||
* @param comment Comment
|
||||
*/
|
||||
public CommentAddEvent(JSONObject comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of comment.
|
||||
*
|
||||
* @return comment
|
||||
*/
|
||||
public JSONObject getComment() {
|
||||
return comment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.sismics.docs.event;
|
||||
|
||||
/**
|
||||
* Comment delete event.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class CommentDeleteEvent {
|
||||
/**
|
||||
* Comment ID.
|
||||
*/
|
||||
private String commentId;
|
||||
|
||||
/**
|
||||
* Create a comment add event.
|
||||
*
|
||||
* @param commentId Comment ID
|
||||
*/
|
||||
public CommentDeleteEvent(String commentId) {
|
||||
this.commentId = commentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of commentId.
|
||||
*
|
||||
* @return commentId
|
||||
*/
|
||||
public String getCommentId() {
|
||||
return commentId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.sismics.docs.fragment;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.util.NetworkUtil;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Export PDF dialog fragment.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class DocExportPdfFragment extends DialogFragment {
|
||||
/**
|
||||
* Export PDF dialog fragment.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @param title Document title
|
||||
*/
|
||||
public static DocExportPdfFragment newInstance(String id, String title) {
|
||||
DocExportPdfFragment fragment = new DocExportPdfFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString("id", id);
|
||||
args.putString("title", title);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
// Setup the view
|
||||
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||
View view = inflater.inflate(R.layout.document_export_pdf_dialog, null);
|
||||
final SeekBar marginSeekBar = (SeekBar) view.findViewById(R.id.marginSeekBar);
|
||||
final CheckBox exportMetadataCheckbox = (CheckBox) view.findViewById(R.id.exportMetadataCheckbox);
|
||||
final CheckBox exportCommentsCheckbox = (CheckBox) view.findViewById(R.id.exportCommentsCheckbox);
|
||||
final CheckBox fitToPageCheckbox = (CheckBox) view.findViewById(R.id.fitToPageCheckbox);
|
||||
final TextView marginValueText = (TextView) view.findViewById(R.id.marginValueText);
|
||||
|
||||
// Margin label follow seekbar value
|
||||
marginSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
marginValueText.setText(String.format(Locale.ENGLISH, "%d", progress));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Build the dialog
|
||||
builder.setView(view)
|
||||
.setPositiveButton(R.string.download, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
// Download the PDF
|
||||
String pdfUrl = PreferenceUtil.getServerUrl(getActivity()) + "/api/document/" + getArguments().getString("id") + "/pdf?" +
|
||||
"metadata=" + exportMetadataCheckbox.isChecked() + "&comments=" + exportCommentsCheckbox.isChecked() + "&fitimagetopage=" + fitToPageCheckbox.isChecked() +
|
||||
"&margin=" + marginSeekBar.getProgress();
|
||||
String title = getArguments().getString("title");
|
||||
NetworkUtil.downloadFile(getActivity(), pdfUrl, title + ".pdf", title, getString(R.string.download_pdf_title));
|
||||
|
||||
getDialog().cancel();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
getDialog().cancel();
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
}
|
||||
}
|
||||
@@ -21,17 +21,17 @@ import com.sismics.docs.event.DocumentAddEvent;
|
||||
import com.sismics.docs.event.DocumentDeleteEvent;
|
||||
import com.sismics.docs.event.DocumentEditEvent;
|
||||
import com.sismics.docs.event.SearchEvent;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.listener.RecyclerItemClickListener;
|
||||
import com.sismics.docs.resource.DocumentResource;
|
||||
import com.sismics.docs.ui.view.DividerItemDecoration;
|
||||
import com.sismics.docs.ui.view.EmptyRecyclerView;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* @author bgamard.
|
||||
*/
|
||||
@@ -100,7 +100,7 @@ public class DocListFragment extends Fragment {
|
||||
}));
|
||||
|
||||
// Infinite scrolling
|
||||
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
@@ -150,6 +150,7 @@ public class DocListFragment extends Fragment {
|
||||
*
|
||||
* @param event Search event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(SearchEvent event) {
|
||||
query = event.getQuery();
|
||||
loadDocuments(getView(), true);
|
||||
@@ -160,6 +161,7 @@ public class DocListFragment extends Fragment {
|
||||
*
|
||||
* @param event Document edit event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DocumentEditEvent event) {
|
||||
adapter.updateDocument(event.getDocument());
|
||||
}
|
||||
@@ -169,6 +171,7 @@ public class DocListFragment extends Fragment {
|
||||
*
|
||||
* @param event Document delete event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DocumentDeleteEvent event) {
|
||||
adapter.deleteDocument(event.getDocumentId());
|
||||
}
|
||||
@@ -178,6 +181,7 @@ public class DocListFragment extends Fragment {
|
||||
*
|
||||
* @param event Document add event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DocumentAddEvent event) {
|
||||
// Refresh the list, maybe the new document fit in it
|
||||
loadDocuments(getView(), true);
|
||||
@@ -218,16 +222,16 @@ public class DocListFragment extends Fragment {
|
||||
|
||||
recyclerView.setEmptyView(progressBar);
|
||||
|
||||
DocumentResource.list(getActivity(), reset ? 0 : adapter.getItemCount(), query, new JsonHttpResponseHandler() {
|
||||
DocumentResource.list(getActivity(), reset ? 0 : adapter.getItemCount(), query, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
adapter.addDocuments(response.optJSONArray("documents"));
|
||||
documentsEmptyView.setText(R.string.no_documents);
|
||||
recyclerView.setEmptyView(documentsEmptyView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject response, Exception e) {
|
||||
documentsEmptyView.setText(R.string.error_loading_documents);
|
||||
recyclerView.setEmptyView(documentsEmptyView);
|
||||
|
||||
|
||||
@@ -21,17 +21,17 @@ import com.sismics.docs.R;
|
||||
import com.sismics.docs.adapter.ShareListAdapter;
|
||||
import com.sismics.docs.event.ShareDeleteEvent;
|
||||
import com.sismics.docs.event.ShareSendEvent;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.resource.DocumentResource;
|
||||
import com.sismics.docs.resource.ShareResource;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Document sharing dialog fragment.
|
||||
*
|
||||
@@ -44,7 +44,8 @@ public class DocShareFragment extends DialogFragment {
|
||||
private JSONObject document;
|
||||
|
||||
/**
|
||||
* Document sharing dialog fragment
|
||||
* Document sharing dialog fragment.
|
||||
*
|
||||
* @param id Document ID
|
||||
*/
|
||||
public static DocShareFragment newInstance(String id) {
|
||||
@@ -74,15 +75,15 @@ public class DocShareFragment extends DialogFragment {
|
||||
shareAddButton.setEnabled(false);
|
||||
|
||||
ShareResource.add(getActivity(), getArguments().getString("id"), shareNameEditText.getText().toString(),
|
||||
new JsonHttpResponseHandler() {
|
||||
new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
shareNameEditText.setText("");
|
||||
loadShares(getDialog().getWindow().getDecorView());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
Toast.makeText(getActivity(), R.string.error_adding_share, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@@ -121,9 +122,9 @@ public class DocShareFragment extends DialogFragment {
|
||||
final ProgressBar shareProgressBar = (ProgressBar) view.findViewById(R.id.shareProgressBar);
|
||||
|
||||
shareListView.setEmptyView(shareProgressBar);
|
||||
DocumentResource.get(getActivity(), getArguments().getString("id"), new JsonHttpResponseHandler() {
|
||||
DocumentResource.get(getActivity(), getArguments().getString("id"), new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
document = response;
|
||||
JSONArray acls = response.optJSONArray("acls");
|
||||
shareProgressBar.setVisibility(View.GONE);
|
||||
@@ -132,27 +133,39 @@ public class DocShareFragment extends DialogFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
getDialog().cancel();
|
||||
Toast.makeText(getActivity(), R.string.error_loading_shares, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A share delete event has been fired.
|
||||
*
|
||||
* @param event Share delete event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(ShareDeleteEvent event) {
|
||||
ShareResource.delete(getActivity(), event.getId(), new JsonHttpResponseHandler() {
|
||||
ShareResource.delete(getActivity(), event.getId(), new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
loadShares(getDialog().getWindow().getDecorView());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
Toast.makeText(getActivity(), R.string.error_deleting_share, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A share send event has been fired.
|
||||
*
|
||||
* @param event Share send event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(ShareSendEvent event) {
|
||||
if (document == null) return;
|
||||
|
||||
|
||||
@@ -22,14 +22,13 @@ import com.sismics.docs.ui.view.TagsCompleteTextView;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
import com.sismics.docs.util.SearchQueryBuilder;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Advanced search fragment.
|
||||
*
|
||||
@@ -56,6 +55,7 @@ public class SearchFragment extends DialogFragment {
|
||||
View view = inflater.inflate(R.layout.search_dialog, null);
|
||||
final EditText searchEditText = (EditText) view.findViewById(R.id.searchEditText);
|
||||
final EditText fulltextEditText = (EditText) view.findViewById(R.id.fulltextEditText);
|
||||
final EditText creatorEditText = (EditText) view.findViewById(R.id.creatorEditText);
|
||||
final CheckBox sharedCheckbox = (CheckBox) view.findViewById(R.id.sharedCheckbox);
|
||||
final Spinner languageSpinner = (Spinner) view.findViewById(R.id.languageSpinner);
|
||||
final DatePickerView beforeDatePicker = (DatePickerView) view.findViewById(R.id.beforeDatePicker);
|
||||
@@ -90,6 +90,7 @@ public class SearchFragment extends DialogFragment {
|
||||
// Build the simple criterias
|
||||
SearchQueryBuilder queryBuilder = new SearchQueryBuilder()
|
||||
.simpleSearch(searchEditText.getText().toString())
|
||||
.creator(creatorEditText.getText().toString())
|
||||
.shared(sharedCheckbox.isChecked())
|
||||
.language(((LanguageAdapter.Language) languageSpinner.getSelectedItem()).getId())
|
||||
.before(beforeDatePicker.getDate())
|
||||
|
||||
@@ -9,10 +9,10 @@ import android.preference.PreferenceManager;
|
||||
import android.provider.SearchRecentSuggestions;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.androidquery.util.AQUtility;
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.provider.RecentSuggestionsProvider;
|
||||
import com.sismics.docs.util.ApplicationUtil;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
|
||||
/**
|
||||
@@ -52,7 +52,7 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer
|
||||
clearCachePref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
AQUtility.cleanCacheAsync(getActivity());
|
||||
OkHttpUtil.clearCache(getActivity());
|
||||
Toast.makeText(getActivity(), R.string.pref_clear_cache_success, Toast.LENGTH_LONG).show();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.sismics.docs.listener;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.Response;
|
||||
|
||||
/**
|
||||
* An HTTP callback.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class HttpCallback {
|
||||
public void onSuccess(JSONObject json) {
|
||||
// Implement me
|
||||
}
|
||||
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
// Implement me
|
||||
}
|
||||
|
||||
public void onFinish() {
|
||||
// Implement me
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an OkHttp Callback from a HttpCallback.
|
||||
*
|
||||
* @param httpCallback HttpCallback
|
||||
* @return OkHttp Callback
|
||||
*/
|
||||
public static Callback buildOkHttpCallback(final HttpCallback httpCallback) {
|
||||
return new Callback() {
|
||||
@Override
|
||||
public void onResponse(final Call call, final Response response) throws IOException {
|
||||
final String body = response.body().string();
|
||||
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (response.isSuccessful()) {
|
||||
try {
|
||||
httpCallback.onSuccess(new JSONObject(body));
|
||||
} catch (Exception e) {
|
||||
httpCallback.onFailure(null, e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
httpCallback.onFailure(new JSONObject(body), null);
|
||||
} catch (Exception e) {
|
||||
httpCallback.onFailure(null, e);
|
||||
}
|
||||
}
|
||||
|
||||
httpCallback.onFinish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Call call, final IOException e) {
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
httpCallback.onFailure(null, e);
|
||||
httpCallback.onFinish();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
Android Asynchronous Http Client
|
||||
Copyright (c) 2011 James Smith <james@loopj.com>
|
||||
http://loopj.com
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package com.sismics.docs.listener;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.loopj.android.http.TextHttpResponseHandler;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
|
||||
/**
|
||||
* Used to intercept and handle the responses from requests made using {@link com.loopj.android.http.AsyncHttpClient}, with
|
||||
* automatic parsing into a {@link JSONObject} or {@link JSONArray}. <p> </p> This class is
|
||||
* designed to be passed to get, post, put and delete requests with the {@link #onSuccess(int,
|
||||
* org.apache.http.Header[], org.json.JSONArray)} or {@link #onSuccess(int,
|
||||
* org.apache.http.Header[], org.json.JSONObject)} methods anonymously overridden. <p> </p>
|
||||
* Additionally, you can override the other event methods from the parent class.
|
||||
*/
|
||||
public class JsonHttpResponseHandler extends TextHttpResponseHandler {
|
||||
|
||||
private static final String LOG_TAG = "JsonHttpResponseHandler";
|
||||
|
||||
/**
|
||||
* Creates new JsonHttpResponseHandler, with JSON String encoding UTF-8
|
||||
*/
|
||||
public JsonHttpResponseHandler() {
|
||||
super(DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new JsonHttpRespnseHandler with given JSON String encoding
|
||||
*
|
||||
* @param encoding String encoding to be used when parsing JSON
|
||||
*/
|
||||
public JsonHttpResponseHandler(String encoding) {
|
||||
super(encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns when request succeeds
|
||||
*
|
||||
* @param statusCode http response status line
|
||||
* @param headers response headers if any
|
||||
* @param response parsed response if any
|
||||
*/
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
Log.w(LOG_TAG, "onSuccess(int, Header[], JSONObject) was not overriden, but callback was received");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns when request succeeds
|
||||
*
|
||||
* @param statusCode http response status line
|
||||
* @param headers response headers if any
|
||||
* @param response parsed response if any
|
||||
*/
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
|
||||
Log.w(LOG_TAG, "onSuccess(int, Header[], JSONArray) was not overriden, but callback was received");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns when request failed
|
||||
*
|
||||
* @param statusCode http response status line
|
||||
* @param headers response headers if any
|
||||
* @param throwable throwable describing the way request failed
|
||||
* @param errorResponse parsed response if any
|
||||
*/
|
||||
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
|
||||
Log.w(LOG_TAG, "onFailure(int, Header[], Throwable, JSONObject) was not overriden, but callback was received", throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns when request failed
|
||||
*
|
||||
* @param statusCode http response status line
|
||||
* @param headers response headers if any
|
||||
* @param throwable throwable describing the way request failed
|
||||
* @param errorResponse parsed response if any
|
||||
*/
|
||||
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
|
||||
Log.w(LOG_TAG, "onFailure(int, Header[], Throwable, JSONArray) was not overriden, but callback was received", throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
|
||||
Log.w(LOG_TAG, "onFailure(int, Header[], String, Throwable) was not overriden, but callback was received", throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, String responseString) {
|
||||
Log.w(LOG_TAG, "onSuccess(int, Header[], String) was not overriden, but callback was received");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onSuccess(final int statusCode, final Header[] headers, final byte[] responseBytes) {
|
||||
if (statusCode != HttpStatus.SC_NO_CONTENT) {
|
||||
Runnable parser = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final Object jsonResponse = parseResponse(responseBytes);
|
||||
postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (jsonResponse instanceof JSONObject) {
|
||||
onSuccess(statusCode, headers, (JSONObject) jsonResponse);
|
||||
} else if (jsonResponse instanceof JSONArray) {
|
||||
onSuccess(statusCode, headers, (JSONArray) jsonResponse);
|
||||
} else if (jsonResponse instanceof String) {
|
||||
onFailure(statusCode, headers, (String) jsonResponse, new JSONException("Response cannot be parsed as JSON data"));
|
||||
} else {
|
||||
onFailure(statusCode, headers, new JSONException("Unexpected response type " + jsonResponse.getClass().getName()), (JSONObject) null);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
} catch (final JSONException ex) {
|
||||
postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onFailure(statusCode, headers, ex, (JSONObject) null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!getUseSynchronousMode()) {
|
||||
new Thread(parser).start();
|
||||
} else {
|
||||
// In synchronous mode everything should be run on one thread
|
||||
parser.run();
|
||||
}
|
||||
} else {
|
||||
onSuccess(statusCode, headers, new JSONObject());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onFailure(final int statusCode, final Header[] headers, final byte[] responseBytes, final Throwable throwable) {
|
||||
if (responseBytes != null) {
|
||||
Runnable parser = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final Object jsonResponse = parseResponse(responseBytes);
|
||||
postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (jsonResponse instanceof JSONObject) {
|
||||
onFailure(statusCode, headers, throwable, (JSONObject) jsonResponse);
|
||||
} else if (jsonResponse instanceof JSONArray) {
|
||||
onFailure(statusCode, headers, throwable, (JSONArray) jsonResponse);
|
||||
} else if (jsonResponse instanceof String) {
|
||||
onFailure(statusCode, headers, (String) jsonResponse, throwable);
|
||||
} else {
|
||||
onFailure(statusCode, headers, new JSONException("Unexpected response type " + jsonResponse.getClass().getName()), (JSONObject) null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (final JSONException ex) {
|
||||
postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onFailure(statusCode, headers, ex, (JSONObject) null);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!getUseSynchronousMode()) {
|
||||
new Thread(parser).start();
|
||||
} else {
|
||||
// In synchronous mode everything should be run on one thread
|
||||
parser.run();
|
||||
}
|
||||
} else {
|
||||
Log.v(LOG_TAG, "response body is null, calling onFailure(Throwable, JSONObject)");
|
||||
onFailure(statusCode, headers, throwable, (JSONObject) null);
|
||||
}
|
||||
|
||||
// In all cases, call the default failure listener
|
||||
onAllFailure(statusCode, headers, responseBytes, throwable);
|
||||
}
|
||||
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
// All failures go there
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Object of type {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, Long,
|
||||
* Double or {@link JSONObject#NULL}, see {@link org.json.JSONTokener#nextValue()}
|
||||
*
|
||||
* @param responseBody response bytes to be assembled in String and parsed as JSON
|
||||
* @return Object parsedResponse
|
||||
* @throws org.json.JSONException exception if thrown while parsing JSON
|
||||
*/
|
||||
protected Object parseResponse(byte[] responseBody) throws JSONException {
|
||||
if (null == responseBody)
|
||||
return null;
|
||||
Object result = null;
|
||||
//trim the string to prevent start with blank, and test if the string is valid JSON, because the parser don't do this :(. If JSON is not valid this will return null
|
||||
String jsonString = getResponseString(responseBody, getCharset());
|
||||
if (jsonString != null) {
|
||||
jsonString = jsonString.trim();
|
||||
if (jsonString.startsWith(UTF8_BOM)) {
|
||||
jsonString = jsonString.substring(1);
|
||||
}
|
||||
if (jsonString.startsWith("{") || jsonString.startsWith("[")) {
|
||||
result = new JSONTokener(jsonString).nextValue();
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = jsonString;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListen
|
||||
private OnItemClickListener mListener;
|
||||
|
||||
public interface OnItemClickListener {
|
||||
public void onItemClick(View view, int position);
|
||||
void onItemClick(View view, int position);
|
||||
}
|
||||
|
||||
GestureDetector mGestureDetector;
|
||||
@@ -25,13 +25,18 @@ public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListen
|
||||
});
|
||||
}
|
||||
|
||||
@Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
|
||||
View childView = view.findChildViewUnder(e.getX(), e.getY());
|
||||
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
|
||||
mListener.onItemClick(childView, view.getChildPosition(childView));
|
||||
mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
|
||||
@Override
|
||||
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
|
||||
|
||||
@Override
|
||||
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }
|
||||
}
|
||||
@@ -4,11 +4,10 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import com.sismics.docs.listener.CallbackListener;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.resource.UserResource;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
@@ -80,9 +79,9 @@ public class ApplicationContext {
|
||||
* @param callbackListener CallbackListener
|
||||
*/
|
||||
public void fetchUserInfo(final Activity activity, final CallbackListener callbackListener) {
|
||||
UserResource.info(activity.getApplicationContext(), new JsonHttpResponseHandler() {
|
||||
UserResource.info(activity.getApplicationContext(), new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, final JSONObject json) {
|
||||
public void onSuccess(JSONObject json) {
|
||||
// Save data in application context
|
||||
if (!json.optBoolean("anonymous", true)) {
|
||||
setUserInfo(activity.getApplicationContext(), json);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
|
||||
/**
|
||||
* Access to /auditlog API.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class AuditLogResource extends BaseResource {
|
||||
/**
|
||||
* GET /auditlog.
|
||||
*
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void list(Context context, String documentId, HttpCallback callback) {
|
||||
HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(getApiUrl(context) + "/auditlog")
|
||||
.newBuilder();
|
||||
if (documentId != null) {
|
||||
httpUrlBuilder.addQueryParameter("document", documentId);
|
||||
}
|
||||
Request request = new Request.Builder()
|
||||
.url(httpUrlBuilder.build())
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
}
|
||||
@@ -1,125 +1,15 @@
|
||||
package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import com.androidquery.callback.AbstractAjaxCallback;
|
||||
import com.loopj.android.http.AsyncHttpClient;
|
||||
import com.loopj.android.http.PersistentCookieStore;
|
||||
import com.sismics.docs.util.ApplicationUtil;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
|
||||
import org.apache.http.conn.ssl.SSLSocketFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
/**
|
||||
* Base class for API access.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class BaseResource {
|
||||
|
||||
/**
|
||||
* User-Agent to use.
|
||||
*/
|
||||
protected static String USER_AGENT = null;
|
||||
|
||||
/**
|
||||
* Accept-Language header.
|
||||
*/
|
||||
protected static String ACCEPT_LANGUAGE = null;
|
||||
|
||||
/**
|
||||
* HTTP client.
|
||||
*/
|
||||
protected static AsyncHttpClient client = new AsyncHttpClient();
|
||||
|
||||
static {
|
||||
// 20sec default timeout
|
||||
client.setTimeout(60000);
|
||||
try {
|
||||
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
trustStore.load(null, null);
|
||||
MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
|
||||
sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
|
||||
client.setSSLSocketFactory(sf);
|
||||
AbstractAjaxCallback.setSSF(sf);
|
||||
} catch (Exception e) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resource initialization.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
protected static void init(Context context) {
|
||||
client.setCookieStore(new PersistentCookieStore(context));
|
||||
|
||||
if (USER_AGENT == null) {
|
||||
USER_AGENT = "Sismics Docs Android " + ApplicationUtil.getVersionName(context) + "/Android " + Build.VERSION.RELEASE + "/" + Build.MODEL;
|
||||
client.setUserAgent(USER_AGENT);
|
||||
}
|
||||
|
||||
if (ACCEPT_LANGUAGE == null) {
|
||||
Locale locale = Locale.getDefault();
|
||||
ACCEPT_LANGUAGE = locale.getLanguage() + "_" + locale.getCountry();
|
||||
client.addHeader("Accept-Language", ACCEPT_LANGUAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket factory to allow self-signed certificates.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public static class MySSLSocketFactory extends SSLSocketFactory {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
|
||||
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||
super(truststore);
|
||||
|
||||
TrustManager tm = new X509TrustManager() {
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
sslContext.init(null, new TrustManager[] { tm }, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
|
||||
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket() throws IOException {
|
||||
return sslContext.getSocketFactory().createSocket();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cleaned API URL.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
|
||||
|
||||
/**
|
||||
* Access to /comment API.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class CommentResource extends BaseResource {
|
||||
/**
|
||||
* GET /comment/id.
|
||||
*
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void list(Context context, String documentId, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/comment/" + documentId))
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /comment.
|
||||
*
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param content Comment content
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void add(Context context, String documentId, String content, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/comment"))
|
||||
.put(new FormBody.Builder()
|
||||
.add("id", documentId)
|
||||
.add("content", content)
|
||||
.build())
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /comment/id.
|
||||
*
|
||||
* @param context Context
|
||||
* @param commentId Comment ID
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void remove(Context context, String commentId, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/comment/" + commentId))
|
||||
.delete()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,15 @@ package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.loopj.android.http.RequestParams;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
|
||||
/**
|
||||
* Access to /document API.
|
||||
*
|
||||
@@ -19,18 +23,23 @@ public class DocumentResource extends BaseResource {
|
||||
* @param context Context
|
||||
* @param offset Offset
|
||||
* @param query Search query
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void list(Context context, int offset, String query, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
params.put("limit", 20);
|
||||
params.put("offset", offset);
|
||||
params.put("sort_column", 3);
|
||||
params.put("asc", false);
|
||||
params.put("search", query);
|
||||
client.get(getApiUrl(context) + "/document/list", params, responseHandler);
|
||||
public static void list(Context context, int offset, String query, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/document/list")
|
||||
.newBuilder()
|
||||
.addQueryParameter("limit", "20")
|
||||
.addQueryParameter("offset", Integer.toString(offset))
|
||||
.addQueryParameter("sort_column", "3")
|
||||
.addQueryParameter("asc", "false")
|
||||
.addQueryParameter("search", query)
|
||||
.build())
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,12 +47,16 @@ public class DocumentResource extends BaseResource {
|
||||
*
|
||||
* @param context Context
|
||||
* @param id ID
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void get(Context context, String id, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
client.get(getApiUrl(context) + "/document/" + id, responseHandler);
|
||||
public static void get(Context context, String id, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/document/" + id))
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,12 +64,16 @@ public class DocumentResource extends BaseResource {
|
||||
*
|
||||
* @param context Context
|
||||
* @param id ID
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void delete(Context context, String id, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
client.delete(getApiUrl(context) + "/document/" + id, responseHandler);
|
||||
public static void delete(Context context, String id, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/document/" + id))
|
||||
.delete()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,19 +85,26 @@ public class DocumentResource extends BaseResource {
|
||||
* @param tagIdList Tags ID list
|
||||
* @param language Language
|
||||
* @param createDate Create date
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void add(Context context, String title, String description,
|
||||
Set<String> tagIdList, String language, long createDate, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
Set<String> tagIdList, String language, long createDate, HttpCallback callback) {
|
||||
FormBody.Builder formBuilder = new FormBody.Builder()
|
||||
.add("title", title)
|
||||
.add("description", description)
|
||||
.add("language", language)
|
||||
.add("create_date", Long.toString(createDate));
|
||||
for( String tagId : tagIdList) {
|
||||
formBuilder.add("tags", tagId);
|
||||
}
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
params.put("title", title);
|
||||
params.put("description", description);
|
||||
params.put("tags", tagIdList);
|
||||
params.put("language", language);
|
||||
params.put("create_date", createDate);
|
||||
client.put(getApiUrl(context) + "/document", params, responseHandler);
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/document"))
|
||||
.put(formBuilder.build())
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,27 +117,25 @@ public class DocumentResource extends BaseResource {
|
||||
* @param tagIdList Tags ID list
|
||||
* @param language Language
|
||||
* @param createDate Create date
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void edit(Context context, String id, String title, String description,
|
||||
Set<String> tagIdList, String language, long createDate, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
Set<String> tagIdList, String language, long createDate, HttpCallback callback) {
|
||||
FormBody.Builder formBuilder = new FormBody.Builder()
|
||||
.add("title", title)
|
||||
.add("description", description)
|
||||
.add("language", language)
|
||||
.add("create_date", Long.toString(createDate));
|
||||
for( String tagId : tagIdList) {
|
||||
formBuilder.add("tags", tagId);
|
||||
}
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
params.put("title", title);
|
||||
params.put("description", description);
|
||||
params.put("tags", tagIdList);
|
||||
params.put("language", language);
|
||||
params.put("create_date", createDate);
|
||||
client.post(getApiUrl(context) + "/document/" + id, params, responseHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel pending requests.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
public static void cancel(Context context) {
|
||||
client.cancelRequests(context, true);
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/document/" + id))
|
||||
.post(formBuilder.build())
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,24 @@ package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.loopj.android.http.PersistentCookieStore;
|
||||
import com.loopj.android.http.RequestParams;
|
||||
import com.loopj.android.http.SyncHttpClient;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.internal.Util;
|
||||
import okio.BufferedSink;
|
||||
import okio.Okio;
|
||||
import okio.Source;
|
||||
|
||||
|
||||
/**
|
||||
@@ -22,12 +33,19 @@ public class FileResource extends BaseResource {
|
||||
*
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void list(Context context, String documentId, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
client.get(getApiUrl(context) + "/file/list?id=" + documentId, responseHandler);
|
||||
public static void list(Context context, String documentId, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/file/list")
|
||||
.newBuilder()
|
||||
.addQueryParameter("id", documentId)
|
||||
.build())
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,12 +53,16 @@ public class FileResource extends BaseResource {
|
||||
*
|
||||
* @param context Context
|
||||
* @param id ID
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void delete(Context context, String id, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
client.delete(getApiUrl(context) + "/file/" + id, responseHandler);
|
||||
public static void delete(Context context, String id, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/file/" + id))
|
||||
.delete()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,34 +71,53 @@ public class FileResource extends BaseResource {
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param is Input stream
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void addSync(Context context, String documentId, InputStream is, JsonHttpResponseHandler responseHandler) throws Exception {
|
||||
init(context);
|
||||
public static void addSync(Context context, String documentId, final InputStream is, HttpCallback callback) throws Exception {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/file"))
|
||||
.put(new MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addFormDataPart("id", documentId)
|
||||
.addFormDataPart("file", "file", new RequestBody() {
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return MediaType.parse("application/octet-stream");
|
||||
}
|
||||
|
||||
SyncHttpClient client = new SyncHttpClient();
|
||||
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
trustStore.load(null, null);
|
||||
MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
|
||||
sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
|
||||
client.setSSLSocketFactory(sf);
|
||||
client.setCookieStore(new PersistentCookieStore(context));
|
||||
client.setUserAgent(USER_AGENT);
|
||||
client.addHeader("Accept-Language", ACCEPT_LANGUAGE);
|
||||
@Override
|
||||
public void writeTo(BufferedSink sink) throws IOException {
|
||||
Source source = Okio.source(is);
|
||||
try {
|
||||
sink.writeAll(source);
|
||||
} finally {
|
||||
Util.closeQuietly(source);
|
||||
}
|
||||
}
|
||||
})
|
||||
.build())
|
||||
.build();
|
||||
Response response = OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.execute();
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
params.put("id", documentId);
|
||||
params.put("file", is, "file", "application/octet-stream", true);
|
||||
client.put(getApiUrl(context) + "/file", params, responseHandler);
|
||||
}
|
||||
// Call the right callback
|
||||
final String body = response.body().string();
|
||||
if (response.isSuccessful()) {
|
||||
try {
|
||||
callback.onSuccess(new JSONObject(body));
|
||||
} catch (Exception e) {
|
||||
callback.onFailure(null, e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
callback.onFailure(new JSONObject(body), null);
|
||||
} catch (Exception e) {
|
||||
callback.onFailure(null, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel pending requests.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
public static void cancel(Context context) {
|
||||
client.cancelRequests(context, true);
|
||||
callback.onFinish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,12 @@ package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.loopj.android.http.RequestParams;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
|
||||
|
||||
/**
|
||||
@@ -18,15 +22,19 @@ public class ShareResource extends BaseResource {
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param name Name
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void add(Context context, String documentId, String name, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
params.put("id", documentId);
|
||||
params.put("name", name);
|
||||
client.put(getApiUrl(context) + "/share", params, responseHandler);
|
||||
public static void add(Context context, String documentId, String name, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/share"))
|
||||
.put(new FormBody.Builder()
|
||||
.add("id", documentId)
|
||||
.add("name", name)
|
||||
.build())
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,11 +42,15 @@ public class ShareResource extends BaseResource {
|
||||
*
|
||||
* @param context Context
|
||||
* @param id ID
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void delete(Context context, String id, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
client.delete(getApiUrl(context) + "/share/" + id, responseHandler);
|
||||
public static void delete(Context context, String id, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/share/" + id))
|
||||
.delete()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,11 @@ package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
|
||||
|
||||
/**
|
||||
@@ -15,11 +19,15 @@ public class TagResource extends BaseResource {
|
||||
* GET /tag/stats.
|
||||
*
|
||||
* @param context Context
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void stats(Context context, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
client.get(getApiUrl(context) + "/tag/stats", responseHandler);
|
||||
public static void stats(Context context, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/tag/stats"))
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,12 @@ package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.loopj.android.http.RequestParams;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
|
||||
/**
|
||||
* Access to /user API.
|
||||
@@ -18,41 +22,69 @@ public class UserResource extends BaseResource {
|
||||
* @param context Context
|
||||
* @param username Username
|
||||
* @param password Password
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void login(Context context, String username, String password, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
params.put("username", username);
|
||||
params.put("password", password);
|
||||
params.put("remember", "true");
|
||||
client.post(getApiUrl(context) + "/user/login", params, responseHandler);
|
||||
public static void login(Context context, String username, String password, String code, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/user/login"))
|
||||
.post(new FormBody.Builder()
|
||||
.add("username", username)
|
||||
.add("password", password)
|
||||
.add("code", code)
|
||||
.add("remember", "true")
|
||||
.build())
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /user.
|
||||
*
|
||||
* @param context Context
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void info(Context context, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
client.get(getApiUrl(context) + "/user", params, responseHandler);
|
||||
public static void info(Context context, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/user"))
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /user/username.
|
||||
*
|
||||
* @param context Context
|
||||
* param username Username
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void get(Context context, String username, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/user/" + username))
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /user/logout.
|
||||
*
|
||||
* @param context Context
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void logout(Context context, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
client.post(getApiUrl(context) + "/user/logout", params, responseHandler);
|
||||
public static void logout(Context context, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/user/logout"))
|
||||
.post(new FormBody.Builder().build())
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
package com.sismics.docs.resource.cookie;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.net.CookieStore;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* A persistent cookie store which implements the Apache HttpClient CookieStore interface.
|
||||
* Cookies are stored and will persist on the user's device between application sessions since they
|
||||
* are serialized and stored in SharedPreferences.
|
||||
*/
|
||||
public class PersistentCookieStore implements CookieStore {
|
||||
|
||||
private static final String LOG_TAG = "PersistentCookieStore";
|
||||
private static final String COOKIE_PREFS = "CookiePrefsFileOkHttp";
|
||||
private static final String COOKIE_NAME_PREFIX = "cookie_okhttp_";
|
||||
|
||||
private final HashMap<String, ConcurrentHashMap<String, HttpCookie>> cookies;
|
||||
private final SharedPreferences cookiePrefs;
|
||||
|
||||
/**
|
||||
* Construct a persistent cookie store.
|
||||
*
|
||||
* @param context Context to attach cookie store to
|
||||
*/
|
||||
public PersistentCookieStore(Context context) {
|
||||
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
|
||||
cookies = new HashMap<>();
|
||||
|
||||
// Load any previously stored cookies into the store
|
||||
Map<String, ?> prefsMap = cookiePrefs.getAll();
|
||||
for (Map.Entry<String, ?> entry : prefsMap.entrySet()) {
|
||||
if (entry.getValue() != null && !((String) entry.getValue()).startsWith(COOKIE_NAME_PREFIX)) {
|
||||
String[] cookieNames = TextUtils.split((String) entry.getValue(), ",");
|
||||
for (String name : cookieNames) {
|
||||
String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + name, null);
|
||||
if (encodedCookie != null) {
|
||||
HttpCookie decodedCookie = decodeCookie(encodedCookie);
|
||||
if (decodedCookie != null) {
|
||||
if (!cookies.containsKey(entry.getKey()))
|
||||
cookies.put(entry.getKey(), new ConcurrentHashMap<String, HttpCookie>());
|
||||
cookies.get(entry.getKey()).put(name, decodedCookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(URI uri, HttpCookie cookie) {
|
||||
String name = getCookieToken(uri, cookie);
|
||||
|
||||
// Save cookie into local store, or remove if expired
|
||||
if (!cookie.hasExpired()) {
|
||||
if (!cookies.containsKey(uri.getHost()))
|
||||
cookies.put(uri.getHost(), new ConcurrentHashMap<String, HttpCookie>());
|
||||
cookies.get(uri.getHost()).put(name, cookie);
|
||||
} else {
|
||||
if (cookies.containsKey(uri.toString()))
|
||||
cookies.get(uri.getHost()).remove(name);
|
||||
}
|
||||
|
||||
// Save cookie into persistent store
|
||||
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
|
||||
prefsWriter.putString(uri.getHost(), TextUtils.join(",", cookies.get(uri.getHost()).keySet()));
|
||||
prefsWriter.putString(COOKIE_NAME_PREFIX + name, encodeCookie(new SerializableHttpCookie(cookie)));
|
||||
prefsWriter.apply();
|
||||
}
|
||||
|
||||
protected String getCookieToken(URI uri, HttpCookie cookie) {
|
||||
return cookie.getName() + cookie.getDomain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HttpCookie> get(URI uri) {
|
||||
ArrayList<HttpCookie> ret = new ArrayList<>();
|
||||
if (cookies.containsKey(uri.getHost()))
|
||||
ret.addAll(cookies.get(uri.getHost()).values());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll() {
|
||||
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
|
||||
prefsWriter.clear();
|
||||
prefsWriter.apply();
|
||||
cookies.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean remove(URI uri, HttpCookie cookie) {
|
||||
String name = getCookieToken(uri, cookie);
|
||||
|
||||
if (cookies.containsKey(uri.getHost()) && cookies.get(uri.getHost()).containsKey(name)) {
|
||||
cookies.get(uri.getHost()).remove(name);
|
||||
|
||||
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
|
||||
if (cookiePrefs.contains(COOKIE_NAME_PREFIX + name)) {
|
||||
prefsWriter.remove(COOKIE_NAME_PREFIX + name);
|
||||
}
|
||||
prefsWriter.putString(uri.getHost(), TextUtils.join(",", cookies.get(uri.getHost()).keySet()));
|
||||
prefsWriter.apply();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HttpCookie> getCookies() {
|
||||
ArrayList<HttpCookie> ret = new ArrayList<>();
|
||||
for (String key : cookies.keySet())
|
||||
ret.addAll(cookies.get(key).values());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<URI> getURIs() {
|
||||
ArrayList<URI> ret = new ArrayList<>();
|
||||
for (String key : cookies.keySet())
|
||||
try {
|
||||
ret.add(new URI(key));
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes Cookie object into String
|
||||
*
|
||||
* @param cookie cookie to be encoded, can be null
|
||||
* @return cookie encoded as String
|
||||
*/
|
||||
protected String encodeCookie(SerializableHttpCookie cookie) {
|
||||
if (cookie == null)
|
||||
return null;
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
try {
|
||||
ObjectOutputStream outputStream = new ObjectOutputStream(os);
|
||||
outputStream.writeObject(cookie);
|
||||
} catch (IOException e) {
|
||||
Log.d(LOG_TAG, "IOException in encodeCookie", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
return byteArrayToHexString(os.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cookie decoded from cookie string
|
||||
*
|
||||
* @param cookieString string of cookie as returned from http request
|
||||
* @return decoded cookie or null if exception occured
|
||||
*/
|
||||
protected HttpCookie decodeCookie(String cookieString) {
|
||||
byte[] bytes = hexStringToByteArray(cookieString);
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
|
||||
HttpCookie cookie = null;
|
||||
try {
|
||||
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
|
||||
cookie = ((SerializableHttpCookie) objectInputStream.readObject()).getCookie();
|
||||
} catch (IOException e) {
|
||||
Log.d(LOG_TAG, "IOException in decodeCookie", e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e);
|
||||
}
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Using some super basic byte array <-> hex conversions so we don't have to rely on any
|
||||
* large Base64 libraries. Can be overridden if you like!
|
||||
*
|
||||
* @param bytes byte array to be converted
|
||||
* @return string containing hex values
|
||||
*/
|
||||
protected String byteArrayToHexString(byte[] bytes) {
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 2);
|
||||
for (byte element : bytes) {
|
||||
int v = element & 0xff;
|
||||
if (v < 16) {
|
||||
sb.append('0');
|
||||
}
|
||||
sb.append(Integer.toHexString(v));
|
||||
}
|
||||
return sb.toString().toUpperCase(Locale.US);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts hex values from strings to byte arra
|
||||
*
|
||||
* @param hexString string of hex-encoded values
|
||||
* @return decoded byte array
|
||||
*/
|
||||
protected byte[] hexStringToByteArray(String hexString) {
|
||||
int len = hexString.length();
|
||||
byte[] data = new byte[len / 2];
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.sismics.docs.resource.cookie;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.HttpCookie;
|
||||
|
||||
public class SerializableHttpCookie implements Serializable {
|
||||
private static final long serialVersionUID = 6374381323722046732L;
|
||||
|
||||
private transient final HttpCookie cookie;
|
||||
private transient HttpCookie clientCookie;
|
||||
|
||||
public SerializableHttpCookie(HttpCookie cookie) {
|
||||
this.cookie = cookie;
|
||||
}
|
||||
|
||||
public HttpCookie getCookie() {
|
||||
HttpCookie bestCookie = cookie;
|
||||
if (clientCookie != null) {
|
||||
bestCookie = clientCookie;
|
||||
}
|
||||
return bestCookie;
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
out.writeObject(cookie.getName());
|
||||
out.writeObject(cookie.getValue());
|
||||
out.writeObject(cookie.getComment());
|
||||
out.writeObject(cookie.getCommentURL());
|
||||
out.writeObject(cookie.getDomain());
|
||||
out.writeLong(cookie.getMaxAge());
|
||||
out.writeObject(cookie.getPath());
|
||||
out.writeObject(cookie.getPortlist());
|
||||
out.writeInt(cookie.getVersion());
|
||||
out.writeBoolean(cookie.getSecure());
|
||||
out.writeBoolean(cookie.getDiscard());
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
String name = (String) in.readObject();
|
||||
String value = (String) in.readObject();
|
||||
clientCookie = new HttpCookie(name, value);
|
||||
clientCookie.setComment((String) in.readObject());
|
||||
clientCookie.setCommentURL((String) in.readObject());
|
||||
clientCookie.setDomain((String) in.readObject());
|
||||
clientCookie.setMaxAge(in.readLong());
|
||||
clientCookie.setPath((String) in.readObject());
|
||||
clientCookie.setPortlist((String) in.readObject());
|
||||
clientCookie.setVersion(in.readInt());
|
||||
clientCookie.setSecure(in.readBoolean());
|
||||
clientCookie.setDiscard(in.readBoolean());
|
||||
}
|
||||
}
|
||||
@@ -12,16 +12,16 @@ import android.util.Log;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.event.FileAddEvent;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.resource.FileResource;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
import okhttp3.internal.Util;
|
||||
|
||||
/**
|
||||
* Service to upload a file to a document in the background.
|
||||
@@ -81,17 +81,22 @@ public class FileUploadService extends IntentService {
|
||||
*/
|
||||
private void handleFileUpload(final String documentId, final Uri uri) throws Exception {
|
||||
final InputStream is = getContentResolver().openInputStream(uri);
|
||||
FileResource.addSync(this, documentId, is, new JsonHttpResponseHandler() {
|
||||
FileResource.addSync(this, documentId, is, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
EventBus.getDefault().post(new FileAddEvent(documentId, response.optString("id")));
|
||||
FileUploadService.this.onComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
FileUploadService.this.onError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
Util.closeQuietly(is);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
||||
* @author bgamard
|
||||
*/
|
||||
public class ApplicationUtil {
|
||||
|
||||
/**
|
||||
* Returns version name.
|
||||
*
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.sismics.docs.R;
|
||||
* @author bgamard
|
||||
*/
|
||||
public class DialogUtil {
|
||||
|
||||
/**
|
||||
* Create a dialog with an OK button.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.sismics.docs.util;
|
||||
|
||||
import android.app.DownloadManager;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
|
||||
/**
|
||||
* Utility class for network actions.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class NetworkUtil {
|
||||
/**
|
||||
* Download a file using Android download manager.
|
||||
*
|
||||
* @param url URL to download
|
||||
* @param fileName Destination file name
|
||||
* @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);
|
||||
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
|
||||
request.addRequestHeader("Cookie", "auth_token=" + authToken);
|
||||
request.setTitle(title);
|
||||
request.setDescription(description);
|
||||
downloadManager.enqueue(request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package com.sismics.docs.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.jakewharton.picasso.OkHttp3Downloader;
|
||||
import com.sismics.docs.resource.cookie.PersistentCookieStore;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.CookieManager;
|
||||
import java.net.CookiePolicy;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.JavaNetCookieJar;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
/**
|
||||
* Utilities for OkHttp.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class OkHttpUtil {
|
||||
/**
|
||||
* OkHttp singleton client.
|
||||
*/
|
||||
private static OkHttpClient okHttpClient = new OkHttpClient();
|
||||
|
||||
/**
|
||||
* Singleton cache.
|
||||
*/
|
||||
private static Cache cache = null;
|
||||
|
||||
/**
|
||||
* User-Agent to use.
|
||||
*/
|
||||
protected static String userAgent = null;
|
||||
|
||||
/**
|
||||
* Accept-Language header.
|
||||
*/
|
||||
protected static String acceptLanguage = null;
|
||||
|
||||
static {
|
||||
// OkHttp configuration
|
||||
try {
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
final TrustManager[] trustAllCerts = new TrustManager[] {
|
||||
new X509TrustManager() {
|
||||
@Override
|
||||
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Install the all-trusting trust manager
|
||||
final SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
|
||||
final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||
|
||||
// Configure OkHttpClient
|
||||
okHttpClient = okHttpClient.newBuilder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(30, TimeUnit.SECONDS)
|
||||
.sslSocketFactory(sslSocketFactory)
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Picasso object with base config.
|
||||
*
|
||||
* @param context Context
|
||||
* @return Picasso object
|
||||
*/
|
||||
public static Picasso picasso(Context context) {
|
||||
OkHttpClient okHttpClient = buildClient(context)
|
||||
.newBuilder()
|
||||
.addInterceptor(new Interceptor() {
|
||||
@Override
|
||||
public Response intercept(Interceptor.Chain chain) throws IOException { // Override cache configuration
|
||||
final Request original = chain.request();
|
||||
return chain.proceed(original.newBuilder()
|
||||
.header("Cache-Control", "max-age=" + (3600 * 24 * 365))
|
||||
.method(original.method(), original.body())
|
||||
.build());
|
||||
}
|
||||
})
|
||||
.cache(getCache(context))
|
||||
.build();
|
||||
|
||||
Picasso picasso = new Picasso.Builder(context)
|
||||
.downloader(new OkHttp3Downloader(okHttpClient))
|
||||
.build();
|
||||
picasso.setIndicatorsEnabled(false); // Debug stuff
|
||||
return picasso;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and eventually build the singleton cache.
|
||||
*
|
||||
* @param context Context
|
||||
* @return Cache
|
||||
*/
|
||||
private static Cache getCache(Context context) {
|
||||
if (cache == null) {
|
||||
cache = new Cache(context.getCacheDir(),
|
||||
PreferenceUtil.getIntegerPreference(context, PreferenceUtil.PREF_CACHE_SIZE, 0) * 1000000);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the HTTP cache.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
public static void clearCache(Context context) {
|
||||
Cache cache = getCache(context);
|
||||
try {
|
||||
cache.evictAll();
|
||||
} catch (IOException e) {
|
||||
Log.e("OKHttpUtil", "Error clearing cache", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an OkHttpClient.
|
||||
*
|
||||
* @param context Context
|
||||
* @return OkHttpClient
|
||||
*/
|
||||
public static OkHttpClient buildClient(final Context context) {
|
||||
// One-time header computation
|
||||
if (userAgent == null) {
|
||||
userAgent = "Sismics Docs Android " + ApplicationUtil.getVersionName(context) + "/Android " + Build.VERSION.RELEASE + "/" + Build.MODEL;
|
||||
}
|
||||
|
||||
if (acceptLanguage == null) {
|
||||
Locale locale = Locale.getDefault();
|
||||
acceptLanguage = locale.getLanguage() + "_" + locale.getCountry();
|
||||
}
|
||||
|
||||
// Cookie handling
|
||||
PersistentCookieStore cookieStore = new PersistentCookieStore(context);
|
||||
CookieManager cookieManager = new CookieManager(cookieStore, CookiePolicy.ACCEPT_ALL);
|
||||
|
||||
// Runtime configuration
|
||||
return okHttpClient.newBuilder()
|
||||
.cookieJar(new JavaNetCookieJar(cookieManager))
|
||||
.addNetworkInterceptor(new Interceptor() {
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request original = chain.request();
|
||||
return chain.proceed(original.newBuilder()
|
||||
.header("User-Agent", userAgent)
|
||||
.header("Accept-Language", acceptLanguage)
|
||||
.method(original.method(), original.body())
|
||||
.build());
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.loopj.android.http.PersistentCookieStore;
|
||||
import com.sismics.docs.resource.cookie.PersistentCookieStore;
|
||||
|
||||
import org.apache.http.cookie.Cookie;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -26,6 +26,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Returns a preference of boolean type.
|
||||
*
|
||||
* @param context Context
|
||||
* @param key Shared preference key
|
||||
* @return Shared preference value
|
||||
@@ -37,6 +38,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Returns a preference of string type.
|
||||
*
|
||||
* @param context Context
|
||||
* @param key Shared preference key
|
||||
* @return Shared preference value
|
||||
@@ -48,6 +50,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Returns a preference of integer type.
|
||||
*
|
||||
* @param context Context
|
||||
* @param key Shared preference key
|
||||
* @return Shared preference value
|
||||
@@ -69,6 +72,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Update JSON cache.
|
||||
*
|
||||
* @param context Context
|
||||
* @param key Shared preference key
|
||||
* @param json JSON data
|
||||
@@ -80,6 +84,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Returns a JSON cache.
|
||||
*
|
||||
* @param context Context
|
||||
* @param key Shared preference key
|
||||
* @return JSON data
|
||||
@@ -96,6 +101,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Update server URL.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
public static void setServerUrl(Context context, String serverUrl) {
|
||||
@@ -105,6 +111,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Empty user caches.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
public static void resetUserCache(Context context) {
|
||||
@@ -118,12 +125,13 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Returns auth token cookie from shared preferences.
|
||||
*
|
||||
* @return Auth token
|
||||
*/
|
||||
public static String getAuthToken(Context context) {
|
||||
PersistentCookieStore cookieStore = new PersistentCookieStore(context);
|
||||
List<Cookie> cookieList = cookieStore.getCookies();
|
||||
for (Cookie cookie : cookieList) {
|
||||
List<HttpCookie> cookieList = cookieStore.getCookies();
|
||||
for (HttpCookie cookie : cookieList) {
|
||||
if (cookie.getName().equals("auth_token")) {
|
||||
return cookie.getValue();
|
||||
}
|
||||
@@ -134,6 +142,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Returns cleaned server URL.
|
||||
*
|
||||
* @param context Context
|
||||
* @return Server URL
|
||||
*/
|
||||
|
||||
@@ -59,6 +59,21 @@ public class SearchQueryBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a creator criteria.
|
||||
*
|
||||
* @param creator Creator criteria
|
||||
* @return The builder
|
||||
*/
|
||||
public SearchQueryBuilder creator(String creator) {
|
||||
if (isValid(creator)) {
|
||||
query.append(SEARCH_SEPARATOR)
|
||||
.append("by:")
|
||||
.append(creator);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a language criteria.
|
||||
*
|
||||
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 353 B |
|
After Width: | Height: | Size: 448 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 461 B |
|
After Width: | Height: | Size: 565 B |
30
docs-android/app/src/main/res/layout/auditlog_activity.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v4.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/auditLogListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="singleChoice"
|
||||
android:dividerHeight="0dp"
|
||||
android:visibility="gone">
|
||||
</ListView>
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
60
docs-android/app/src/main/res/layout/auditlog_list_item.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/assignImageView"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_assignment_grey600_48dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/usernameTextView"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@+id/assignImageView"
|
||||
android:layout_toEndOf="@+id/assignImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:textColor="#212121"
|
||||
android:text="admin"
|
||||
android:textSize="16sp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/messageTextView"
|
||||
android:layout_below="@+id/usernameTextView"
|
||||
android:layout_toRightOf="@+id/assignImageView"
|
||||
android:layout_toEndOf="@+id/assignImageView"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:textColor="#777777"
|
||||
android:text="Document created : test doc 1"
|
||||
android:textSize="16sp"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="2014-12-02"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:textColor="#777777"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</RelativeLayout>
|
||||
57
docs-android/app/src/main/res/layout/comment_list_item.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/gravatarImageView"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="12dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/creatorTextView"
|
||||
android:layout_toRightOf="@id/gravatarImageView"
|
||||
android:layout_toEndOf="@id/gravatarImageView"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#212121"
|
||||
android:text="Creator"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contentTextView"
|
||||
android:layout_toRightOf="@id/gravatarImageView"
|
||||
android:layout_toEndOf="@id/gravatarImageView"
|
||||
android:layout_below="@id/creatorTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textColor="#212121"
|
||||
android:text="Comment content"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateTextView"
|
||||
android:layout_toRightOf="@id/gravatarImageView"
|
||||
android:layout_toEndOf="@id/gravatarImageView"
|
||||
android:layout_below="@id/contentTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textColor="#888"
|
||||
android:text="2015-11-10"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -37,7 +37,7 @@
|
||||
android:textSize="16sp"
|
||||
android:layout_centerInParent="true"/>
|
||||
|
||||
<com.shamanland.fab.FloatingActionButton
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/addDocumentButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -48,6 +48,6 @@
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:src="@drawable/ic_add_white_24dp"
|
||||
app:floatingActionButtonColor="#263238"/>
|
||||
app:fabSize="normal"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -13,6 +13,7 @@
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:id="@+id/folderImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -22,7 +23,9 @@
|
||||
android:id="@+id/titleTextView"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@+id/folderImageView"
|
||||
android:layout_toEndOf="@+id/folderImageView"
|
||||
android:layout_toLeftOf="@+id/dateTextView"
|
||||
android:layout_toStartOf="@+id/dateTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="12dp">
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="6dp"
|
||||
android:text="@string/export_metadata"
|
||||
android:id="@+id/exportMetadataCheckbox" />
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="6dp"
|
||||
android:text="@string/export_comments"
|
||||
android:id="@+id/exportCommentsCheckbox" />
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="6dp"
|
||||
android:checked="true"
|
||||
android:text="@string/fit_image_to_page"
|
||||
android:id="@+id/fitToPageCheckbox" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/margin"
|
||||
android:layout_weight="0"/>
|
||||
|
||||
<SeekBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:id="@+id/marginSeekBar"
|
||||
android:progress="10"
|
||||
android:max="50"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/marginValueText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:text="10"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="0"
|
||||
android:text="@string/mm"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -37,6 +37,109 @@
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Left drawer -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/left_drawer"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:orientation="vertical"
|
||||
android:clickable="true"
|
||||
android:background="#fff"
|
||||
android:elevation="5dp">
|
||||
|
||||
<!-- Comments -->
|
||||
|
||||
<TextView
|
||||
android:drawableStart="@drawable/ic_comment_grey600_24dp"
|
||||
android:drawableLeft="@drawable/ic_comment_grey600_24dp"
|
||||
android:drawablePadding="6dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center"
|
||||
android:textColor="#de000000"
|
||||
android:text="@string/comments"
|
||||
android:layout_margin="12dp"/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#eee"/>
|
||||
|
||||
<ListView
|
||||
android:layout_weight="1"
|
||||
android:id="@+id/commentListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:choiceMode="singleChoice"
|
||||
android:divider="@android:color/transparent"
|
||||
android:transcriptMode="normal"
|
||||
android:dividerHeight="0dp"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_weight="1"
|
||||
android:id="@+id/commentProgressView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<ProgressBar
|
||||
style="?android:progressBarStyle"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/commentEmptyView"
|
||||
android:visibility="gone"
|
||||
android:padding="12dp"
|
||||
android:gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/no_comments"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#eee"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="6dp"
|
||||
android:gravity="center">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/commentEditText"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:lines="1"
|
||||
android:inputType="text"
|
||||
android:hint="@string/add_comment"
|
||||
android:maxLength="4000"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/addCommentBtn"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@drawable/ic_send_grey600_24dp"
|
||||
android:contentDescription="@string/send"
|
||||
android:background="?android:selectableItemBackground"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Right drawer -->
|
||||
|
||||
<LinearLayout
|
||||
@@ -70,7 +173,7 @@
|
||||
android:drawableTop="@drawable/ic_create_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/edit_document"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
@@ -81,7 +184,7 @@
|
||||
android:drawableTop="@drawable/ic_file_upload_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/upload_file"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
@@ -92,7 +195,7 @@
|
||||
android:drawableTop="@drawable/ic_file_download_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/download_document"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
@@ -104,6 +207,17 @@
|
||||
android:orientation="horizontal"
|
||||
style="?android:buttonBarStyle">
|
||||
|
||||
<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"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionSharing"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -111,9 +225,20 @@
|
||||
android:drawableTop="@drawable/ic_share_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/share"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionAuditLog"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_assignment_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/activity"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionDelete"
|
||||
@@ -122,9 +247,9 @@
|
||||
android:drawableTop="@drawable/ic_delete_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/delete_document"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -166,12 +291,33 @@
|
||||
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/createdDateLabel"
|
||||
android:layout_below="@id/creatorLabel"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:maxLines="1"
|
||||
@@ -219,7 +365,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/primary_text_default_material_light"
|
||||
android:textColor="#de000000"
|
||||
android:text="@string/who_can_access"
|
||||
android:layout_margin="12dp"/>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="15dip"
|
||||
android:id="@+id/fileProgressBar"
|
||||
android:indeterminate="false"
|
||||
android:indeterminate="true"
|
||||
android:layout_centerInParent="true"/>
|
||||
|
||||
<it.sephiroth.android.library.imagezoom.ImageViewTouch
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/membersTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -61,6 +61,17 @@
|
||||
android:inputType="textPassword">
|
||||
</EditText>
|
||||
|
||||
<EditText
|
||||
android:visibility="gone"
|
||||
android:id="@+id/txtValidationCode"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
android:ems="10"
|
||||
android:hint="@string/validation_code"
|
||||
android:inputType="number">
|
||||
</EditText>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnConnect"
|
||||
android:layout_width="fill_parent"
|
||||
|
||||
@@ -117,6 +117,40 @@
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Audit log -->
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/auditLogLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:clickable="true"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/auditLogImageView"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_assignment_grey600_24dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toRightOf="@+id/auditLogImageView"
|
||||
android:layout_toEndOf="@+id/auditLogImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textColor="#212121"
|
||||
android:text="@string/latest_activity"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Separator -->
|
||||
|
||||
<View
|
||||
|
||||
@@ -27,6 +27,15 @@
|
||||
android:textSize="18sp"
|
||||
android:hint="@string/fulltext_search"/>
|
||||
|
||||
<!-- Creator -->
|
||||
<EditText
|
||||
android:id="@+id/creatorEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:textSize="18sp"
|
||||
android:hint="@string/creator"/>
|
||||
|
||||
<!-- Language -->
|
||||
<Spinner
|
||||
android:id="@+id/languageSpinner"
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:gravity="end"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/email"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/emailTextView"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="16sp"
|
||||
android:text="user1@sismicsdocs.com"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:gravity="end"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/storage_quota"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/quotaTextView"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="16sp"
|
||||
android:text="35/500 MB"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -9,6 +9,12 @@
|
||||
android:title="@string/toggle_informations">
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/comments"
|
||||
app:showAsAction="collapseActionView"
|
||||
android:title="@string/comments">
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/download_file"
|
||||
app:showAsAction="collapseActionView"
|
||||
|
||||
@@ -28,9 +28,7 @@
|
||||
<string name="crash_toast_text">A crash occurred, a report has been sent to resolve this problem</string>
|
||||
<string name="created_date">Created date</string>
|
||||
<string name="download_file">Download current file</string>
|
||||
<string name="downloading_file">Downloading file number %1s</string>
|
||||
<string name="download_document">Download</string>
|
||||
<string name="downloading_document">Downloading document</string>
|
||||
<string name="action_search">Search documents</string>
|
||||
<string name="all_documents">All documents</string>
|
||||
<string name="shared_documents">Shared documents</string>
|
||||
@@ -101,12 +99,38 @@
|
||||
<string name="title">Title</string>
|
||||
<string name="simple_search">Simple search</string>
|
||||
<string name="fulltext_search">Fulltext search</string>
|
||||
<string name="creator">Creator</string>
|
||||
<string name="after_date">After date</string>
|
||||
<string name="before_date">Before date</string>
|
||||
<string name="search_tags">Search tags</string>
|
||||
<string name="all_languages">All languages</string>
|
||||
<string name="toggle_informations">Toggle informations</string>
|
||||
<string name="who_can_access">Who can access</string>
|
||||
|
||||
<string name="comments">Comments</string>
|
||||
<string name="no_comments">No comments</string>
|
||||
<string name="error_loading_comments">Error loading comments</string>
|
||||
<string name="send">Send</string>
|
||||
<string name="add_comment">Add a comment</string>
|
||||
<string name="comment_add_failure">Error adding a comment</string>
|
||||
<string name="adding_comment">Adding a comment</string>
|
||||
<string name="comment_delete">Delete comment</string>
|
||||
<string name="deleting_comment">Deleting comment</string>
|
||||
<string name="error_deleting_comment">Error deleting comment</string>
|
||||
<string name="export_pdf">PDF</string>
|
||||
<string name="download">Download</string>
|
||||
<string name="margin">Margin</string>
|
||||
<string name="fit_image_to_page">Fit image to page</string>
|
||||
<string name="export_comments">Export comments</string>
|
||||
<string name="export_metadata">Export metadata</string>
|
||||
<string name="mm">mm</string>
|
||||
<string name="download_file_title">Sismics Docs file export</string>
|
||||
<string name="download_document_title">Sismics Docs document export</string>
|
||||
<string name="download_pdf_title">Sismics Docs PDF export</string>
|
||||
<string name="latest_activity">Latest activity</string>
|
||||
<string name="activity">Activity</string>
|
||||
<string name="email">E-mail</string>
|
||||
<string name="storage_quota">Storage quota</string>
|
||||
<string name="storage_display">%1$d/%2$d MB</string>
|
||||
<string name="validation_code">Validation code</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#Wed Nov 26 21:58:48 CET 2014
|
||||
#Sat Jan 16 19:15:13 CET 2016
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<parent>
|
||||
<groupId>com.sismics.docs</groupId>
|
||||
<artifactId>docs-parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../docs-parent</relativePath>
|
||||
<version>1.4</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -26,6 +26,11 @@
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-c3p0</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Other external dependencies -->
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
@@ -72,26 +77,6 @@
|
||||
<artifactId>jbcrypt</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-core-asl</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-mapper-asl</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.ccil.cowan.tagsoup</groupId>
|
||||
<artifactId>tagsoup</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
|
||||
<artifactId>owasp-java-html-sanitizer</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-core</artifactId>
|
||||
@@ -107,9 +92,10 @@
|
||||
<artifactId>lucene-queryparser</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Only there to read old index and rebuild them -->
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-highlighter</artifactId>
|
||||
<artifactId>lucene-backward-codecs</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -127,15 +113,35 @@
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- OCR dependencies -->
|
||||
<dependency>
|
||||
<groupId>fr.opensagres.xdocreport</groupId>
|
||||
<artifactId>org.odftoolkit.odfdom.converter.pdf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>fr.opensagres.xdocreport</groupId>
|
||||
<artifactId>org.apache.poi.xwpf.converter.pdf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jna</groupId>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- ImageIO plugins -->
|
||||
<dependency>
|
||||
<groupId>com.levigo.jbig2</groupId>
|
||||
<artifactId>levigo-jbig2-imageio</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jai</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<groupId>com.github.jai-imageio</groupId>
|
||||
<artifactId>jai-imageio-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
@@ -146,8 +152,8 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
update T_CONFIG set CFG_VALUE_C = 'RAM' where CFG_ID_C = 'LUCENE_DIRECTORY_STORAGE';
|
||||
@@ -10,4 +10,8 @@ public enum ConfigType {
|
||||
* Lucene directory storage type.
|
||||
*/
|
||||
LUCENE_DIRECTORY_STORAGE,
|
||||
/**
|
||||
* Theme configuration.
|
||||
*/
|
||||
THEME
|
||||
}
|
||||
|
||||
@@ -10,21 +10,11 @@ import com.google.common.collect.Lists;
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public class Constants {
|
||||
/**
|
||||
* Default locale.
|
||||
*/
|
||||
public static final String DEFAULT_LOCALE_ID = "en";
|
||||
|
||||
/**
|
||||
* Default timezone ID.
|
||||
*/
|
||||
public static final String DEFAULT_TIMEZONE_ID = "Europe/London";
|
||||
|
||||
/**
|
||||
* Default theme ID.
|
||||
*/
|
||||
public static final String DEFAULT_THEME_ID = "default.less";
|
||||
|
||||
/**
|
||||
* Administrator's default password ("admin").
|
||||
*/
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.sismics.docs.core.dao.file.theme;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sismics.docs.core.util.DirectoryUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Theme DAO.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public class ThemeDao {
|
||||
private final static FilenameFilter CSS_FILTER = new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.endsWith(".css") || name.endsWith(".less");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the list of all themes.
|
||||
*
|
||||
* @return List of themes
|
||||
*/
|
||||
public List<String> findAll() {
|
||||
final File themeDirectory = DirectoryUtil.getThemeDirectory();
|
||||
if (themeDirectory != null) {
|
||||
return Lists.newArrayList(themeDirectory.list(CSS_FILTER));
|
||||
} else {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,10 +26,10 @@ public class AclDao {
|
||||
* Creates a new ACL.
|
||||
*
|
||||
* @param acl ACL
|
||||
* @param userId User ID
|
||||
* @return New ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public String create(Acl acl) {
|
||||
public String create(Acl acl, String userId) {
|
||||
// Create the UUID
|
||||
acl.setId(UUID.randomUUID().toString());
|
||||
|
||||
@@ -38,7 +38,7 @@ public class AclDao {
|
||||
em.persist(acl);
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(acl, AuditLogType.CREATE);
|
||||
AuditLogUtil.create(acl, AuditLogType.CREATE, userId);
|
||||
|
||||
return acl.getId();
|
||||
}
|
||||
@@ -67,10 +67,12 @@ public class AclDao {
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<AclDto> getBySourceId(String sourceId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select a.ACL_ID_C, a.ACL_PERM_C, a.ACL_TARGETID_C, u.USE_USERNAME_C, s.SHA_NAME_C");
|
||||
StringBuilder sb = new StringBuilder("select a.ACL_ID_C, a.ACL_PERM_C, a.ACL_TARGETID_C, ");
|
||||
sb.append(" u.USE_USERNAME_C, s.SHA_ID_C, s.SHA_NAME_C, g.GRP_NAME_C ");
|
||||
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 ");
|
||||
|
||||
// Perform the query
|
||||
@@ -79,7 +81,7 @@ public class AclDao {
|
||||
List<Object[]> l = q.getResultList();
|
||||
|
||||
// Assemble results
|
||||
List<AclDto> aclDtoList = new ArrayList<AclDto>();
|
||||
List<AclDto> aclDtoList = new ArrayList<>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
AclDto aclDto = new AclDto();
|
||||
@@ -87,10 +89,21 @@ public class AclDao {
|
||||
aclDto.setPerm(PermType.valueOf((String) o[i++]));
|
||||
aclDto.setTargetId((String) o[i++]);
|
||||
String userName = (String) o[i++];
|
||||
String shareId = (String) o[i++];
|
||||
String shareName = (String) o[i++];
|
||||
aclDto.setTargetName(userName == null ? shareName : userName);
|
||||
aclDto.setTargetType(userName == null ?
|
||||
AclTargetType.SHARE.name() : AclTargetType.USER.name());
|
||||
String groupName = (String) o[i];
|
||||
if (userName != null) {
|
||||
aclDto.setTargetName(userName);
|
||||
aclDto.setTargetType(AclTargetType.USER.name());
|
||||
}
|
||||
if (shareId != null) { // Use ID because share name is nullable
|
||||
aclDto.setTargetName(shareName);
|
||||
aclDto.setTargetType(AclTargetType.SHARE.name());
|
||||
}
|
||||
if (groupName != null) {
|
||||
aclDto.setTargetName(groupName);
|
||||
aclDto.setTargetType(AclTargetType.GROUP.name());
|
||||
}
|
||||
aclDtoList.add(aclDto);
|
||||
}
|
||||
return aclDtoList;
|
||||
@@ -100,23 +113,25 @@ public class AclDao {
|
||||
* Check if a source is accessible to a target.
|
||||
*
|
||||
* @param sourceId ACL source entity ID
|
||||
* @parm perm Necessary permission
|
||||
* @param targetId ACL target entity ID
|
||||
* @param perm Necessary permission
|
||||
* @param targetIdList List of targets
|
||||
* @return True if the document is accessible
|
||||
*/
|
||||
public boolean checkPermission(String sourceId, PermType perm, String targetId) {
|
||||
public boolean checkPermission(String sourceId, PermType perm, List<String> targetIdList) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select a from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.deleteDate is null");
|
||||
StringBuilder sb = new StringBuilder("select a.ACL_ID_C from T_ACL a ");
|
||||
sb.append(" where a.ACL_TARGETID_C in (:targetIdList) and a.ACL_SOURCEID_C = :sourceId and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null ");
|
||||
sb.append(" union all ");
|
||||
sb.append(" select a.ACL_ID_C from T_ACL a, T_DOCUMENT_TAG dt ");
|
||||
sb.append(" where a.ACL_SOURCEID_C = dt.DOT_IDTAG_C and dt.DOT_IDDOCUMENT_C = :sourceId and dt.DOT_DELETEDATE_D is null ");
|
||||
sb.append(" and a.ACL_TARGETID_C in (:targetIdList) and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null ");
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("sourceId", sourceId);
|
||||
q.setParameter("perm", perm);
|
||||
q.setParameter("targetId", targetId);
|
||||
|
||||
q.setParameter("perm", perm.name());
|
||||
q.setParameter("targetIdList", targetIdList);
|
||||
|
||||
// We have a matching permission
|
||||
if (q.getResultList().size() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return q.getResultList().size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,9 +140,10 @@ public class AclDao {
|
||||
* @param sourceId Source ID
|
||||
* @param perm Permission
|
||||
* @param targetId Target ID
|
||||
* @param userId User ID
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void delete(String sourceId, PermType perm, String targetId) {
|
||||
public void delete(String sourceId, PermType perm, String targetId, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Create audit log
|
||||
@@ -137,7 +153,7 @@ public class AclDao {
|
||||
q.setParameter("targetId", targetId);
|
||||
List<Acl> aclList = q.getResultList();
|
||||
for (Acl acl : aclList) {
|
||||
AuditLogUtil.create(acl, AuditLogType.DELETE);
|
||||
AuditLogUtil.create(acl, AuditLogType.DELETE, userId);
|
||||
}
|
||||
|
||||
// Soft delete the ACLs
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.UUID;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sismics.docs.core.constant.AuditLogType;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.AuditLogCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.dto.AuditLogDto;
|
||||
@@ -53,41 +54,33 @@ public class AuditLogDao {
|
||||
* @param criteria Search criteria
|
||||
* @param sortCriteria Sort criteria
|
||||
* @return List of audit logs
|
||||
* @throws Exception
|
||||
*/
|
||||
public void findByCriteria(PaginatedList<AuditLogDto> paginatedList, AuditLogCriteria criteria, SortCriteria sortCriteria) throws Exception {
|
||||
public void findByCriteria(PaginatedList<AuditLogDto> paginatedList, AuditLogCriteria criteria, SortCriteria sortCriteria) {
|
||||
Map<String, Object> parameterMap = new HashMap<String, Object>();
|
||||
List<String> criteriaList = new ArrayList<String>();
|
||||
|
||||
StringBuilder sb = new StringBuilder("select l.LOG_ID_C c0, l.LOG_CREATEDATE_D c1, l.LOG_IDENTITY_C c2, l.LOG_CLASSENTITY_C c3, l.LOG_TYPE_C c4, l.LOG_MESSAGE_C c5 ");
|
||||
sb.append(" from T_AUDIT_LOG l ");
|
||||
StringBuilder baseQuery = new StringBuilder("select l.LOG_ID_C c0, l.LOG_CREATEDATE_D c1, u.USE_USERNAME_C c2, l.LOG_IDENTITY_C c3, l.LOG_CLASSENTITY_C c4, l.LOG_TYPE_C c5, l.LOG_MESSAGE_C c6 from T_AUDIT_LOG l ");
|
||||
baseQuery.append(" join T_USER u on l.LOG_IDUSER_C = u.USE_ID_C ");
|
||||
List<String> queries = Lists.newArrayList();
|
||||
|
||||
// Adds search criteria
|
||||
if (criteria.getDocumentId() != null) {
|
||||
// ACL on document is not checked here, it's assumed
|
||||
StringBuilder sb0 = new StringBuilder(" (l.LOG_IDENTITY_C = :documentId and l.LOG_CLASSENTITY_C = 'Document' ");
|
||||
sb0.append(" or l.LOG_IDENTITY_C in (select f.FIL_ID_C from T_FILE f where f.FIL_IDDOC_C = :documentId) and l.LOG_CLASSENTITY_C = 'File' ");
|
||||
sb0.append(" or l.LOG_IDENTITY_C in (select a.ACL_ID_C from T_ACL a where a.ACL_SOURCEID_C = :documentId) and l.LOG_CLASSENTITY_C = 'Acl') ");
|
||||
criteriaList.add(sb0.toString());
|
||||
// ACL on document is not checked here, rights have been checked before
|
||||
queries.add(baseQuery + " where l.LOG_IDENTITY_C = :documentId ");
|
||||
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select f.FIL_ID_C from T_FILE f where f.FIL_IDDOC_C = :documentId) ");
|
||||
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select c.COM_ID_C from T_COMMENT c where c.COM_IDDOC_C = :documentId) ");
|
||||
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select a.ACL_ID_C from T_ACL a where a.ACL_SOURCEID_C = :documentId) ");
|
||||
parameterMap.put("documentId", criteria.getDocumentId());
|
||||
}
|
||||
|
||||
if (criteria.getUserId() != null) {
|
||||
StringBuilder sb0 = new StringBuilder(" (l.LOG_IDENTITY_C = :userId and l.LOG_CLASSENTITY_C = 'User' ");
|
||||
sb0.append(" or l.LOG_IDENTITY_C in (select t.TAG_ID_C from T_TAG t where t.TAG_IDUSER_C = :userId) and l.LOG_CLASSENTITY_C = 'Tag' ");
|
||||
// Show only logs from owned documents, ACL are lost on delete
|
||||
sb0.append(" or l.LOG_IDENTITY_C in (select d.DOC_ID_C from T_DOCUMENT d where d.DOC_IDUSER_C = :userId) and l.LOG_CLASSENTITY_C = 'Document') ");
|
||||
criteriaList.add(sb0.toString());
|
||||
// Get all logs originating from the user, not necessarly on owned items
|
||||
// Filter out ACL logs
|
||||
queries.add(baseQuery + " where l.LOG_IDUSER_C = :userId and l.LOG_CLASSENTITY_C != 'Acl' ");
|
||||
parameterMap.put("userId", criteria.getUserId());
|
||||
}
|
||||
|
||||
if (!criteriaList.isEmpty()) {
|
||||
sb.append(" where ");
|
||||
sb.append(Joiner.on(" and ").join(criteriaList));
|
||||
}
|
||||
|
||||
// Perform the search
|
||||
QueryParam queryParam = new QueryParam(sb.toString(), parameterMap);
|
||||
QueryParam queryParam = new QueryParam(Joiner.on(" union ").join(queries), parameterMap);
|
||||
List<Object[]> l = PaginatedLists.executePaginatedQuery(paginatedList, queryParam, sortCriteria);
|
||||
|
||||
// Assemble results
|
||||
@@ -97,6 +90,7 @@ public class AuditLogDao {
|
||||
AuditLogDto auditLogDto = new AuditLogDto();
|
||||
auditLogDto.setId((String) o[i++]);
|
||||
auditLogDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
||||
auditLogDto.setUsername((String) o[i++]);
|
||||
auditLogDto.setEntityId((String) o[i++]);
|
||||
auditLogDto.setEntityClass((String) o[i++]);
|
||||
auditLogDto.setType(AuditLogType.valueOf((String) o[i++]));
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.sismics.docs.core.dao.jpa;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import com.sismics.docs.core.constant.AuditLogType;
|
||||
import com.sismics.docs.core.dao.jpa.dto.CommentDto;
|
||||
import com.sismics.docs.core.model.jpa.Comment;
|
||||
import com.sismics.docs.core.util.AuditLogUtil;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
/**
|
||||
* Comment DAO.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class CommentDao {
|
||||
/**
|
||||
* Creates a new comment.
|
||||
*
|
||||
* @param comment Comment
|
||||
* @param userId User ID
|
||||
* @return New ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public String create(Comment comment, String userId) {
|
||||
// Create the UUID
|
||||
comment.setId(UUID.randomUUID().toString());
|
||||
|
||||
// Create the comment
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
comment.setCreateDate(new Date());
|
||||
em.persist(comment);
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(comment, AuditLogType.CREATE, userId);
|
||||
|
||||
return comment.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a comment.
|
||||
*
|
||||
* @param id Comment ID
|
||||
* @param userId User ID
|
||||
*/
|
||||
public void delete(String id, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the comment
|
||||
Query q = em.createQuery("select c from Comment c where c.id = :id and c.deleteDate is null");
|
||||
q.setParameter("id", id);
|
||||
Comment commentDb = (Comment) q.getSingleResult();
|
||||
|
||||
// Delete the comment
|
||||
Date dateNow = new Date();
|
||||
commentDb.setDeleteDate(dateNow);
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(commentDb, AuditLogType.DELETE, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an active comment by its ID.
|
||||
*
|
||||
* @param id Comment ID
|
||||
* @return Comment
|
||||
*/
|
||||
public Comment getActiveById(String id) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
try {
|
||||
Query q = em.createQuery("select c from Comment c where c.id = :id and c.deleteDate is null");
|
||||
q.setParameter("id", id);
|
||||
return (Comment) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all comments on a document.
|
||||
*
|
||||
* @param documentId Document ID
|
||||
* @return List of comments
|
||||
*/
|
||||
public List<CommentDto> getByDocumentId(String documentId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select c.COM_ID_C, c.COM_CONTENT_C, c.COM_CREATEDATE_D, u.USE_USERNAME_C, u.USE_EMAIL_C from T_COMMENT c, T_USER u");
|
||||
sb.append(" where c.COM_IDDOC_C = :documentId and c.COM_IDUSER_C = u.USE_ID_C and c.COM_DELETEDATE_D is null ");
|
||||
sb.append(" order by c.COM_CREATEDATE_D asc ");
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("documentId", documentId);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object[]> l = q.getResultList();
|
||||
|
||||
List<CommentDto> commentDtoList = new ArrayList<CommentDto>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
CommentDto commentDto = new CommentDto();
|
||||
commentDto.setId((String) o[i++]);
|
||||
commentDto.setContent((String) o[i++]);
|
||||
commentDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
||||
commentDto.setCreatorName((String) o[i++]);
|
||||
commentDto.setCreatorEmail((String) o[i++]);
|
||||
commentDtoList.add(commentDto);
|
||||
}
|
||||
return commentDtoList;
|
||||
}
|
||||
}
|
||||
@@ -33,4 +33,23 @@ public class ConfigDao {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a configuration parameter.
|
||||
*
|
||||
* @param id Configuration parameter ID
|
||||
* @param value Configuration parameter value
|
||||
*/
|
||||
public void update(ConfigType id, String value) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Config config = getById(id);
|
||||
if (config == null) {
|
||||
config = new Config();
|
||||
config.setId(id);
|
||||
config.setValue(value);
|
||||
em.persist(config);
|
||||
} else {
|
||||
config.setValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.sismics.docs.core.dao.jpa;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import com.sismics.docs.core.dao.jpa.dto.ContributorDto;
|
||||
import com.sismics.docs.core.model.jpa.Contributor;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
/**
|
||||
* Contributor DAO.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class ContributorDao {
|
||||
/**
|
||||
* Creates a new contributor.
|
||||
*
|
||||
* @param contributor Contributor
|
||||
* @return New ID
|
||||
*/
|
||||
public String create(Contributor contributor) {
|
||||
// Create the UUID
|
||||
contributor.setId(UUID.randomUUID().toString());
|
||||
|
||||
// Create the contributor
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
em.persist(contributor);
|
||||
|
||||
return contributor.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all contributors by document.
|
||||
*
|
||||
* @param documentId Document ID
|
||||
* @return List of contributors
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Contributor> findByDocumentId(String documentId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select c from Contributor c where c.documentId = :documentId");
|
||||
q.setParameter("documentId", documentId);
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all contributors by document.
|
||||
*
|
||||
* @param documentId Document ID
|
||||
* @return List of contributors
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<ContributorDto> getByDocumentId(String documentId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select u.USE_USERNAME_C, u.USE_EMAIL_C from T_CONTRIBUTOR c ");
|
||||
sb.append(" join T_USER u on u.USE_ID_C = c.CTR_IDUSER_C ");
|
||||
sb.append(" where c.CTR_IDDOC_C = :documentId ");
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("documentId", documentId);
|
||||
List<Object[]> l = q.getResultList();
|
||||
|
||||
// Assemble results
|
||||
List<ContributorDto> contributorDtoList = new ArrayList<>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
ContributorDto contributorDto = new ContributorDto();
|
||||
contributorDto.setUsername((String) o[i++]);
|
||||
contributorDto.setEmail((String) o[i]);
|
||||
contributorDtoList.add(contributorDto);
|
||||
}
|
||||
return contributorDtoList;
|
||||
}
|
||||
}
|
||||
@@ -38,10 +38,10 @@ public class DocumentDao {
|
||||
* Creates a new document.
|
||||
*
|
||||
* @param document Document
|
||||
* @param userId User ID
|
||||
* @return New ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public String create(Document document) {
|
||||
public String create(Document document, String userId) {
|
||||
// Create the UUID
|
||||
document.setId(UUID.randomUUID().toString());
|
||||
|
||||
@@ -50,13 +50,13 @@ public class DocumentDao {
|
||||
em.persist(document);
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(document, AuditLogType.CREATE);
|
||||
AuditLogUtil.create(document, AuditLogType.CREATE, userId);
|
||||
|
||||
return document.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all documents.
|
||||
* Returns the list of all active documents.
|
||||
*
|
||||
* @return List of documents
|
||||
*/
|
||||
@@ -68,61 +68,80 @@ public class DocumentDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an active document.
|
||||
* Returns the list of all active documents from a user.
|
||||
*
|
||||
* @param userId User ID
|
||||
* @return List of documents
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Document> findByUserId(String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select d from Document d where d.userId = :userId and d.deleteDate is null");
|
||||
q.setParameter("userId", userId);
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an active document with permission checking.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @param perm Permission needed
|
||||
* @param targetIdList List of targets
|
||||
* @return Document
|
||||
*/
|
||||
public DocumentDto getDocument(String id) {
|
||||
public DocumentDto getDocument(String id, PermType perm, List<String> targetIdList) {
|
||||
AclDao aclDao = new AclDao();
|
||||
if (!aclDao.checkPermission(id, perm, targetIdList)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select d.DOC_ID_C, d.DOC_TITLE_C, d.DOC_DESCRIPTION_C, d.DOC_CREATEDATE_D, d.DOC_LANGUAGE_C, ");
|
||||
StringBuilder sb = new StringBuilder("select distinct d.DOC_ID_C, d.DOC_TITLE_C, d.DOC_DESCRIPTION_C, d.DOC_SUBJECT_C, d.DOC_IDENTIFIER_C, d.DOC_PUBLISHER_C, d.DOC_FORMAT_C, d.DOC_SOURCE_C, d.DOC_TYPE_C, d.DOC_COVERAGE_C, d.DOC_RIGHTS_C, d.DOC_CREATEDATE_D, d.DOC_LANGUAGE_C, ");
|
||||
sb.append(" (select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null), ");
|
||||
sb.append(" (select count(f.FIL_ID_C) from T_FILE f where f.FIL_DELETEDATE_D is null and f.FIL_IDDOC_C = d.DOC_ID_C), ");
|
||||
sb.append(" u.USE_USERNAME_C ");
|
||||
sb.append(" from T_DOCUMENT d, T_USER u ");
|
||||
sb.append(" where d.DOC_IDUSER_C = u.USE_ID_C and d.DOC_ID_C = :id and d.DOC_DELETEDATE_D is null ");
|
||||
sb.append(" from T_DOCUMENT d ");
|
||||
sb.append(" join T_USER u on d.DOC_IDUSER_C = u.USE_ID_C ");
|
||||
sb.append(" where d.DOC_ID_C = :id and d.DOC_DELETEDATE_D is null ");
|
||||
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("id", id);
|
||||
Object[] o = (Object[]) q.getSingleResult();
|
||||
|
||||
Object[] o;
|
||||
try {
|
||||
o = (Object[]) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DocumentDto documentDto = new DocumentDto();
|
||||
int i = 0;
|
||||
documentDto.setId((String) o[i++]);
|
||||
documentDto.setTitle((String) o[i++]);
|
||||
documentDto.setDescription((String) o[i++]);
|
||||
documentDto.setSubject((String) o[i++]);
|
||||
documentDto.setIdentifier((String) o[i++]);
|
||||
documentDto.setPublisher((String) o[i++]);
|
||||
documentDto.setFormat((String) o[i++]);
|
||||
documentDto.setSource((String) o[i++]);
|
||||
documentDto.setType((String) o[i++]);
|
||||
documentDto.setCoverage((String) o[i++]);
|
||||
documentDto.setRights((String) o[i++]);
|
||||
documentDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
||||
documentDto.setLanguage((String) o[i++]);
|
||||
documentDto.setShared(((Number) o[i++]).intValue() > 0);
|
||||
documentDto.setFileCount(((Number) o[i++]).intValue());
|
||||
documentDto.setCreator((String) o[i++]);
|
||||
documentDto.setCreator((String) o[i]);
|
||||
return documentDto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an active document.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @param perm Permission needed
|
||||
* @param userId User ID
|
||||
* @return Document
|
||||
*/
|
||||
public Document getDocument(String id, PermType perm, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createNativeQuery("select d.* from T_DOCUMENT d "
|
||||
+ " join T_ACL a on a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_TARGETID_C = :userId and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null "
|
||||
+ " where d.DOC_ID_C = :id and d.DOC_DELETEDATE_D is null", Document.class);
|
||||
q.setParameter("id", id);
|
||||
q.setParameter("perm", perm.name());
|
||||
q.setParameter("userId", userId);
|
||||
return (Document) q.getSingleResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a document.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @param userId User ID
|
||||
*/
|
||||
public void delete(String id) {
|
||||
public void delete(String id, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the document
|
||||
@@ -140,31 +159,37 @@ public class DocumentDao {
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
// TODO Delete share from deleted ACLs
|
||||
// q = em.createQuery("update Share s set s.deleteDate = :dateNow where s.documentId = :documentId and s.deleteDate is null");
|
||||
// q.setParameter("documentId", id);
|
||||
// q.setParameter("dateNow", dateNow);
|
||||
// q.executeUpdate();
|
||||
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :documentId and a.deleteDate is null");
|
||||
q.setParameter("documentId", id);
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :documentId");
|
||||
q = em.createQuery("update DocumentTag dt set dt.deleteDate = :dateNow where dt.documentId = :documentId and dt.deleteDate is not null");
|
||||
q.setParameter("documentId", id);
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
q = em.createQuery("update Relation r set r.deleteDate = :dateNow where (r.fromDocumentId = :documentId or r.toDocumentId = :documentId) and r.deleteDate is not null");
|
||||
q.setParameter("documentId", id);
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(documentDb, AuditLogType.DELETE);
|
||||
AuditLogUtil.create(documentDb, AuditLogType.DELETE, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a document by its ID.
|
||||
* Gets an active document by its ID.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @return Document
|
||||
*/
|
||||
public Document getById(String id) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select d from Document d where d.id = :id and d.deleteDate is null");
|
||||
q.setParameter("id", id);
|
||||
try {
|
||||
return em.find(Document.class, id);
|
||||
return (Document) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
@@ -176,23 +201,25 @@ public class DocumentDao {
|
||||
* @param paginatedList List of documents (updated by side effects)
|
||||
* @param criteria Search criteria
|
||||
* @param sortCriteria Sort criteria
|
||||
* @return List of documents
|
||||
* @throws Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
public void findByCriteria(PaginatedList<DocumentDto> paginatedList, DocumentCriteria criteria, SortCriteria sortCriteria) throws Exception {
|
||||
Map<String, Object> parameterMap = new HashMap<String, Object>();
|
||||
List<String> criteriaList = new ArrayList<String>();
|
||||
Map<String, Object> parameterMap = new HashMap<>();
|
||||
List<String> criteriaList = new ArrayList<>();
|
||||
|
||||
StringBuilder sb = new StringBuilder("select distinct d.DOC_ID_C c0, d.DOC_TITLE_C c1, d.DOC_DESCRIPTION_C c2, d.DOC_CREATEDATE_D c3, d.DOC_LANGUAGE_C c4, ");
|
||||
sb.append(" (select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null) c5, ");
|
||||
sb.append(" (select count(f.FIL_ID_C) from T_FILE f where f.FIL_DELETEDATE_D is null and f.FIL_IDDOC_C = d.DOC_ID_C) c6 ");
|
||||
sb.append(" from T_DOCUMENT d ");
|
||||
|
||||
// Adds search criteria
|
||||
if (criteria.getUserId() != null) {
|
||||
// Add search criterias
|
||||
if (criteria.getTargetIdList() != null) {
|
||||
// Read permission is enough for searching
|
||||
sb.append(" join T_ACL a on a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_TARGETID_C = :userId and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null ");
|
||||
parameterMap.put("userId", criteria.getUserId());
|
||||
sb.append(" left join T_ACL a on a.ACL_TARGETID_C in (:targetIdList) and a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null ");
|
||||
sb.append(" left join T_DOCUMENT_TAG dta on dta.DOT_IDDOCUMENT_C = d.DOC_ID_C and dta.DOT_DELETEDATE_D is null ");
|
||||
sb.append(" left join T_ACL a2 on a2.ACL_TARGETID_C in (:targetIdList) and a2.ACL_SOURCEID_C = dta.DOT_IDTAG_C and a2.ACL_PERM_C = 'READ' and a2.ACL_DELETEDATE_D is null ");
|
||||
criteriaList.add("(a.ACL_ID_C is not null or a2.ACL_ID_C is not null)");
|
||||
parameterMap.put("targetIdList", criteria.getTargetIdList());
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(criteria.getSearch()) || !Strings.isNullOrEmpty(criteria.getFullSearch())) {
|
||||
LuceneDao luceneDao = new LuceneDao();
|
||||
@@ -215,8 +242,7 @@ public class DocumentDao {
|
||||
if (criteria.getTagIdList() != null && !criteria.getTagIdList().isEmpty()) {
|
||||
int index = 0;
|
||||
for (String tagId : criteria.getTagIdList()) {
|
||||
sb.append(" left join T_DOCUMENT_TAG dt" + index + " on dt" + index + ".DOT_IDDOCUMENT_C = d.DOC_ID_C and dt" + index + ".DOT_IDTAG_C = :tagId" + index + " ");
|
||||
criteriaList.add("dt" + index + ".DOT_ID_C is not null");
|
||||
sb.append(String.format(" join T_DOCUMENT_TAG dt%d on dt%d.DOT_IDDOCUMENT_C = d.DOC_ID_C and dt%d.DOT_IDTAG_C = :tagId%d and dt%d.DOT_DELETEDATE_D is null ", index, index, index, index, index));
|
||||
parameterMap.put("tagId" + index, tagId);
|
||||
index++;
|
||||
}
|
||||
@@ -228,6 +254,10 @@ public class DocumentDao {
|
||||
criteriaList.add("d.DOC_LANGUAGE_C = :language");
|
||||
parameterMap.put("language", criteria.getLanguage());
|
||||
}
|
||||
if (criteria.getCreatorId() != null) {
|
||||
criteriaList.add("d.DOC_IDUSER_C = :creatorId");
|
||||
parameterMap.put("creatorId", criteria.getCreatorId());
|
||||
}
|
||||
|
||||
criteriaList.add("d.DOC_DELETEDATE_D is null");
|
||||
|
||||
@@ -241,7 +271,7 @@ public class DocumentDao {
|
||||
List<Object[]> l = PaginatedLists.executePaginatedQuery(paginatedList, queryParam, sortCriteria);
|
||||
|
||||
// Assemble results
|
||||
List<DocumentDto> documentDtoList = new ArrayList<DocumentDto>();
|
||||
List<DocumentDto> documentDtoList = new ArrayList<>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
DocumentDto documentDto = new DocumentDto();
|
||||
@@ -251,7 +281,7 @@ public class DocumentDao {
|
||||
documentDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
||||
documentDto.setLanguage((String) o[i++]);
|
||||
documentDto.setShared(((Number) o[i++]).intValue() > 0);
|
||||
documentDto.setFileCount(((Number) o[i++]).intValue());
|
||||
documentDto.setFileCount(((Number) o[i]).intValue());
|
||||
documentDtoList.add(documentDto);
|
||||
}
|
||||
|
||||
@@ -262,9 +292,10 @@ public class DocumentDao {
|
||||
* Update a document.
|
||||
*
|
||||
* @param document Document to update
|
||||
* @param userId User ID
|
||||
* @return Updated document
|
||||
*/
|
||||
public Document update(Document document) {
|
||||
public Document update(Document document, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the document
|
||||
@@ -275,11 +306,19 @@ public class DocumentDao {
|
||||
// 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());
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(documentFromDb, AuditLogType.UPDATE);
|
||||
AuditLogUtil.create(documentFromDb, AuditLogType.UPDATE, userId);
|
||||
|
||||
return documentFromDb;
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@ public class FileDao {
|
||||
* Creates a new file.
|
||||
*
|
||||
* @param file File
|
||||
* @param userId User ID
|
||||
* @return New ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public String create(File file) {
|
||||
public String create(File file, String userId) {
|
||||
// Create the UUID
|
||||
file.setId(UUID.randomUUID().toString());
|
||||
|
||||
@@ -36,7 +36,7 @@ public class FileDao {
|
||||
em.persist(file);
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(file, AuditLogType.CREATE);
|
||||
AuditLogUtil.create(file, AuditLogType.CREATE, userId);
|
||||
|
||||
return file.getId();
|
||||
}
|
||||
@@ -53,6 +53,20 @@ public class FileDao {
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all files from a user.
|
||||
*
|
||||
* @param userId User ID
|
||||
* @return List of files
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<File> findByUserId(String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select f from File f where f.userId = :userId and f.deleteDate is null");
|
||||
q.setParameter("userId", userId);
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an active file.
|
||||
*
|
||||
@@ -63,7 +77,11 @@ public class FileDao {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select f from File f where f.id = :id and f.deleteDate is null");
|
||||
q.setParameter("id", id);
|
||||
return (File) q.getSingleResult();
|
||||
try {
|
||||
return (File) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,15 +96,20 @@ public class FileDao {
|
||||
Query q = em.createQuery("select f from File f where f.id = :id and f.userId = :userId and f.deleteDate is null");
|
||||
q.setParameter("id", id);
|
||||
q.setParameter("userId", userId);
|
||||
return (File) q.getSingleResult();
|
||||
try {
|
||||
return (File) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file.
|
||||
*
|
||||
* @param id File ID
|
||||
* @param userId User ID
|
||||
*/
|
||||
public void delete(String id) {
|
||||
public void delete(String id, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the file
|
||||
@@ -99,7 +122,7 @@ public class FileDao {
|
||||
fileDb.setDeleteDate(dateNow);
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(fileDb, AuditLogType.DELETE);
|
||||
AuditLogUtil.create(fileDb, AuditLogType.DELETE, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,9 +143,7 @@ public class FileDao {
|
||||
fileFromDb.setDocumentId(file.getDocumentId());
|
||||
fileFromDb.setContent(file.getContent());
|
||||
fileFromDb.setOrder(file.getOrder());
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(fileFromDb, AuditLogType.UPDATE);
|
||||
fileFromDb.setMimeType(file.getMimeType());
|
||||
|
||||
return file;
|
||||
}
|
||||
@@ -133,10 +154,12 @@ public class FileDao {
|
||||
* @param id File ID
|
||||
* @return File
|
||||
*/
|
||||
public File getById(String id) {
|
||||
public File getActiveById(String id) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select f from File f where f.id = :id and f.deleteDate is null");
|
||||
q.setParameter("id", id);
|
||||
try {
|
||||
return em.find(File.class, id);
|
||||
return (File) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
@@ -145,7 +168,7 @@ public class FileDao {
|
||||
/**
|
||||
* Get files by document ID or all orphan files of an user.
|
||||
*
|
||||
* @parma userId User ID
|
||||
* @param userId User ID
|
||||
* @param documentId Document ID
|
||||
* @return List of files
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
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;
|
||||
import com.sismics.docs.core.dao.jpa.dto.GroupDto;
|
||||
import com.sismics.docs.core.model.jpa.Group;
|
||||
import com.sismics.docs.core.model.jpa.UserGroup;
|
||||
import com.sismics.docs.core.util.AuditLogUtil;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Group DAO.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class GroupDao {
|
||||
/**
|
||||
* Returns a group by name.
|
||||
*
|
||||
* @param name Name
|
||||
* @return Group
|
||||
*/
|
||||
public Group getActiveByName(String name) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select g from Group g where g.name = :name and g.deleteDate is null");
|
||||
q.setParameter("name", name);
|
||||
try {
|
||||
return (Group) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group by ID.
|
||||
*
|
||||
* @param id Group ID
|
||||
* @return Group
|
||||
*/
|
||||
public Group getActiveById(String id) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select g from Group g where g.id = :id and g.deleteDate is null");
|
||||
q.setParameter("id", id);
|
||||
try {
|
||||
return (Group) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new group.
|
||||
*
|
||||
* @param group Group
|
||||
* @param userId User ID
|
||||
* @return New ID
|
||||
*/
|
||||
public String create(Group group, String userId) {
|
||||
// Create the UUID
|
||||
group.setId(UUID.randomUUID().toString());
|
||||
|
||||
// Create the group
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
em.persist(group);
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(group, AuditLogType.CREATE, userId);
|
||||
|
||||
return group.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a group.
|
||||
*
|
||||
* @param groupId Group ID
|
||||
* @param userId User ID
|
||||
*/
|
||||
public void delete(String groupId, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the group
|
||||
Query q = em.createQuery("select g from Group g where g.id = :id and g.deleteDate is null");
|
||||
q.setParameter("id", groupId);
|
||||
Group groupDb = (Group) q.getSingleResult();
|
||||
|
||||
// Delete the group
|
||||
Date dateNow = new Date();
|
||||
groupDb.setDeleteDate(dateNow);
|
||||
|
||||
// Delete linked data
|
||||
q = em.createQuery("update UserGroup ug set ug.deleteDate = :dateNow where ug.groupId = :groupId and ug.deleteDate is not null");
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.setParameter("groupId", groupId);
|
||||
q.executeUpdate();
|
||||
|
||||
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.targetId = :groupId and a.deleteDate is null");
|
||||
q.setParameter("groupId", groupDb.getId());
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
q = em.createQuery("update Group g set g.parentId = null where g.parentId = :groupId and g.deleteDate is null");
|
||||
q.setParameter("groupId", groupDb.getId());
|
||||
q.executeUpdate();
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(groupDb, AuditLogType.DELETE, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an user to a group.
|
||||
*
|
||||
* @param userGroup User group
|
||||
* @return New ID
|
||||
*/
|
||||
public String addMember(UserGroup userGroup) {
|
||||
// Create the UUID
|
||||
userGroup.setId(UUID.randomUUID().toString());
|
||||
|
||||
// Create the user group
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
em.persist(userGroup);
|
||||
|
||||
return userGroup.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an user from a group.
|
||||
*
|
||||
* @param groupId Group ID
|
||||
* @param userId User ID
|
||||
*/
|
||||
public void removeMember(String groupId, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the user group
|
||||
Query q = em.createQuery("select ug from UserGroup ug where ug.groupId = :groupId and ug.userId = :userId and ug.deleteDate is null");
|
||||
q.setParameter("groupId", groupId);
|
||||
q.setParameter("userId", userId);
|
||||
UserGroup userGroupDb = (UserGroup) q.getSingleResult();
|
||||
|
||||
// Delete the user group
|
||||
Date dateNow = new Date();
|
||||
userGroupDb.setDeleteDate(dateNow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all groups.
|
||||
*
|
||||
* @param criteria Search criteria
|
||||
* @param sortCriteria Sort criteria
|
||||
* @return List of groups
|
||||
*/
|
||||
public List<GroupDto> findByCriteria(GroupCriteria criteria, SortCriteria sortCriteria) {
|
||||
Map<String, Object> parameterMap = new HashMap<>();
|
||||
List<String> criteriaList = new ArrayList<>();
|
||||
|
||||
StringBuilder sb = new StringBuilder("select g.GRP_ID_C as c0, g.GRP_NAME_C as c1, g.GRP_IDPARENT_C as c2, gp.GRP_NAME_C as c3, g.GRP_IDROLE_C ");
|
||||
if (criteria.getUserId() != null) {
|
||||
sb.append(" , ug.UGP_ID_C ");
|
||||
}
|
||||
sb.append(" from T_GROUP g ");
|
||||
sb.append(" left join T_GROUP gp on g.GRP_IDPARENT_C = gp.GRP_ID_C ");
|
||||
|
||||
// Add search criterias
|
||||
if (criteria.getSearch() != null) {
|
||||
criteriaList.add("lower(g.GRP_NAME_C) like lower(:search)");
|
||||
parameterMap.put("search", "%" + criteria.getSearch() + "%");
|
||||
}
|
||||
if (criteria.getUserId() != null) {
|
||||
// Left join and post-filtering for recursive groups
|
||||
sb.append(criteria.isRecursive() ? " left " : "");
|
||||
sb.append(" join T_USER_GROUP ug on ug.UGP_IDGROUP_C = g.GRP_ID_C and ug.UGP_IDUSER_C = :userId and ug.UGP_DELETEDATE_D is null ");
|
||||
parameterMap.put("userId", criteria.getUserId());
|
||||
}
|
||||
|
||||
criteriaList.add("g.GRP_DELETEDATE_D is null");
|
||||
|
||||
if (!criteriaList.isEmpty()) {
|
||||
sb.append(" where ");
|
||||
sb.append(Joiner.on(" and ").join(criteriaList));
|
||||
}
|
||||
|
||||
// Perform the search
|
||||
QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object[]> l = QueryUtil.getNativeQuery(queryParam).getResultList();
|
||||
|
||||
// Assemble results
|
||||
List<GroupDto> groupDtoList = new ArrayList<>();
|
||||
List<GroupDto> userGroupDtoList = new ArrayList<>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
GroupDto groupDto = new GroupDto()
|
||||
.setId((String) o[i++])
|
||||
.setName((String) o[i++])
|
||||
.setParentId((String) o[i++])
|
||||
.setParentName((String) o[i++])
|
||||
.setRoleId((String) o[i++]);
|
||||
groupDtoList.add(groupDto);
|
||||
if (criteria.getUserId() != null && o[i] != null) {
|
||||
userGroupDtoList.add(groupDto);
|
||||
}
|
||||
}
|
||||
|
||||
// Post-query filtering for recursive groups
|
||||
if (criteria.getUserId() != null && criteria.isRecursive()) {
|
||||
Set<GroupDto> filteredGroupDtoSet = new HashSet<>();
|
||||
for (GroupDto userGroupDto : userGroupDtoList) {
|
||||
filteredGroupDtoSet.add(userGroupDto); // Direct group
|
||||
findGroupParentHierarchy(filteredGroupDtoSet, groupDtoList, userGroupDto, 0); // Indirect groups
|
||||
}
|
||||
groupDtoList = new ArrayList<>(filteredGroupDtoSet);
|
||||
}
|
||||
|
||||
return groupDtoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively search group's parents.
|
||||
*
|
||||
* @param parentGroupDtoSet Resulting parents
|
||||
* @param groupDtoList All groups
|
||||
* @param userGroupDto Reference group to search from
|
||||
* @param depth Depth
|
||||
*/
|
||||
private void findGroupParentHierarchy(Set<GroupDto> parentGroupDtoSet, List<GroupDto> groupDtoList, GroupDto userGroupDto, int depth) {
|
||||
if (userGroupDto.getParentId() == null || depth == 10) { // Max depth 10 to avoid infinite loop
|
||||
return;
|
||||
}
|
||||
|
||||
for (GroupDto groupDto : groupDtoList) {
|
||||
if (groupDto.getId().equals(userGroupDto.getParentId())) {
|
||||
parentGroupDtoSet.add(groupDto); // Add parent
|
||||
findGroupParentHierarchy(parentGroupDtoSet, groupDtoList, groupDto, depth + 1); // Find parent's parents
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a group.
|
||||
*
|
||||
* @param group Group to update
|
||||
* @param userId User ID
|
||||
* @return Updated group
|
||||
*/
|
||||
public Group update(Group group, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// 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();
|
||||
|
||||
// Update the group
|
||||
groupFromDb.setName(group.getName());
|
||||
groupFromDb.setParentId(group.getParentId());
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(groupFromDb, AuditLogType.UPDATE, userId);
|
||||
|
||||
return groupFromDb;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.sismics.docs.core.dao.jpa;
|
||||
|
||||
import com.sismics.docs.core.model.jpa.Locale;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Locale DAO.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public class LocaleDao {
|
||||
/**
|
||||
* Gets a locale by its ID.
|
||||
*
|
||||
* @param id Locale ID
|
||||
* @return Locale
|
||||
*/
|
||||
public Locale getById(String id) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
try {
|
||||
return em.find(Locale.class, id);
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all locales.
|
||||
*
|
||||
* @return List of locales
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Locale> findAll() {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select l from Locale l order by l.id");
|
||||
return q.getResultList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.sismics.docs.core.dao.jpa;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import com.sismics.docs.core.dao.jpa.dto.RelationDto;
|
||||
import com.sismics.docs.core.model.jpa.Relation;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
/**
|
||||
* Relation DAO.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class RelationDao {
|
||||
/**
|
||||
* Get all relations from/to a document.
|
||||
*
|
||||
* @param documentId Document ID
|
||||
* @return List of relations
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<RelationDto> getByDocumentId(String documentId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select d.DOC_ID_C, d.DOC_TITLE_C, r.REL_IDDOCFROM_C ");
|
||||
sb.append(" from T_RELATION r ");
|
||||
sb.append(" join T_DOCUMENT d on d.DOC_ID_C = r.REL_IDDOCFROM_C and r.REL_IDDOCFROM_C != :documentId or d.DOC_ID_C = r.REL_IDDOCTO_C and r.REL_IDDOCTO_C != :documentId ");
|
||||
sb.append(" where (r.REL_IDDOCFROM_C = :documentId or r.REL_IDDOCTO_C = :documentId) ");
|
||||
sb.append(" and r.REL_DELETEDATE_D is null ");
|
||||
sb.append(" order by d.DOC_TITLE_C ");
|
||||
|
||||
// Perform the query
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("documentId", documentId);
|
||||
List<Object[]> l = q.getResultList();
|
||||
|
||||
// Assemble results
|
||||
List<RelationDto> relationDtoList = new ArrayList<RelationDto>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
RelationDto relationDto = new RelationDto();
|
||||
relationDto.setId((String) o[i++]);
|
||||
relationDto.setTitle((String) o[i++]);
|
||||
String fromDocId = (String) o[i++];
|
||||
relationDto.setSource(documentId.equals(fromDocId));
|
||||
relationDtoList.add(relationDto);
|
||||
}
|
||||
return relationDtoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update relations on a document.
|
||||
*
|
||||
* @param documentId Document ID
|
||||
* @param documentIdSet Set of document ID
|
||||
*/
|
||||
public void updateRelationList(String documentId, Set<String> documentIdSet) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get current relations from this document
|
||||
Query q = em.createQuery("select r from Relation r where r.fromDocumentId = :documentId and r.deleteDate is null");
|
||||
q.setParameter("documentId", documentId);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Relation> relationList = q.getResultList();
|
||||
|
||||
// Deleting relations no longer there
|
||||
for (Relation relation : relationList) {
|
||||
if (!documentIdSet.contains(relation.getToDocumentId())) {
|
||||
relation.setDeleteDate(new Date());
|
||||
}
|
||||
}
|
||||
|
||||
// Adding new relations
|
||||
for (String targetDocId : documentIdSet) {
|
||||
boolean found = false;
|
||||
for (Relation relation : relationList) {
|
||||
if (relation.getToDocumentId().equals(targetDocId)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Relation relation = new Relation();
|
||||
relation.setId(UUID.randomUUID().toString());
|
||||
relation.setFromDocumentId(documentId);
|
||||
relation.setToDocumentId(targetDocId);
|
||||
em.persist(relation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,17 @@ public class RoleBaseFunctionDao {
|
||||
/**
|
||||
* Find the set of base functions of a role.
|
||||
*
|
||||
* @param roleId Role ID
|
||||
* @param roleIdSet Set of role ID
|
||||
* @return Set of base functions
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set<String> findByRoleId(String roleId) {
|
||||
public Set<String> findByRoleId(Set<String> roleIdSet) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select rbf.RBF_IDBASEFUNCTION_C from T_ROLE_BASE_FUNCTION rbf, T_ROLE r");
|
||||
sb.append(" where rbf.RBF_IDROLE_C = :roleId and rbf.RBF_DELETEDATE_D is null");
|
||||
sb.append(" where rbf.RBF_IDROLE_C in (:roleIdSet) and rbf.RBF_DELETEDATE_D is null");
|
||||
sb.append(" and r.ROL_ID_C = rbf.RBF_IDROLE_C and r.ROL_DELETEDATE_D is null");
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("roleId", roleId);
|
||||
q.setParameter("roleIdSet", roleIdSet);
|
||||
return Sets.newHashSet(q.getResultList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ 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;
|
||||
|
||||
@@ -10,12 +12,16 @@ 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;
|
||||
import com.sismics.docs.core.dao.jpa.dto.TagDto;
|
||||
import com.sismics.docs.core.dao.jpa.dto.TagStatDto;
|
||||
import com.sismics.docs.core.model.jpa.DocumentTag;
|
||||
import com.sismics.docs.core.model.jpa.Tag;
|
||||
import com.sismics.docs.core.util.AuditLogUtil;
|
||||
import com.sismics.docs.core.util.jpa.QueryParam;
|
||||
import com.sismics.docs.core.util.jpa.QueryUtil;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
/**
|
||||
@@ -39,19 +45,6 @@ public class TagDao {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all tags.
|
||||
*
|
||||
* @return List of tags
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Tag> getByUserId(String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select t from Tag t where t.userId = :userId and t.deleteDate is null order by t.name");
|
||||
q.setParameter("userId", userId);
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update tags on a document.
|
||||
*
|
||||
@@ -93,85 +86,15 @@ public class TagDao {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns tag list on a document.
|
||||
*
|
||||
* @param documentId Document ID
|
||||
* @return List of tags
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<TagDto> getByDocumentId(String documentId, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_C, t.TAG_COLOR_C from T_DOCUMENT_TAG dt ");
|
||||
sb.append(" join T_TAG t on t.TAG_ID_C = dt.DOT_IDTAG_C ");
|
||||
sb.append(" where dt.DOT_IDDOCUMENT_C = :documentId and t.TAG_DELETEDATE_D is null ");
|
||||
sb.append(" and t.TAG_IDUSER_C = :userId and dt.DOT_DELETEDATE_D is null ");
|
||||
sb.append(" order by t.TAG_NAME_C ");
|
||||
|
||||
// Perform the query
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("documentId", documentId);
|
||||
q.setParameter("userId", userId);
|
||||
List<Object[]> l = q.getResultList();
|
||||
|
||||
// Assemble results
|
||||
List<TagDto> tagDtoList = new ArrayList<TagDto>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
TagDto tagDto = new TagDto();
|
||||
tagDto.setId((String) o[i++]);
|
||||
tagDto.setName((String) o[i++]);
|
||||
tagDto.setColor((String) o[i++]);
|
||||
tagDtoList.add(tagDto);
|
||||
}
|
||||
return tagDtoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns stats on tags.
|
||||
*
|
||||
* @param documentId Document ID
|
||||
* @return Stats by tag
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<TagStatDto> getStats(String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_C, t.TAG_COLOR_C, count(d.DOC_ID_C) ");
|
||||
sb.append(" from T_TAG t ");
|
||||
sb.append(" left join T_DOCUMENT_TAG dt on t.TAG_ID_C = dt.DOT_IDTAG_C and dt.DOT_DELETEDATE_D is null ");
|
||||
sb.append(" left join T_DOCUMENT d on d.DOC_ID_C = dt.DOT_IDDOCUMENT_C and d.DOC_DELETEDATE_D is null and d.DOC_IDUSER_C = :userId ");
|
||||
sb.append(" where t.TAG_IDUSER_C = :userId and t.TAG_DELETEDATE_D is null ");
|
||||
sb.append(" group by t.TAG_ID_C ");
|
||||
sb.append(" order by t.TAG_NAME_C ");
|
||||
|
||||
// Perform the query
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("userId", userId);
|
||||
List<Object[]> l = q.getResultList();
|
||||
|
||||
// Assemble results
|
||||
List<TagStatDto> tagStatDtoList = new ArrayList<TagStatDto>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
TagStatDto tagDto = new TagStatDto();
|
||||
tagDto.setId((String) o[i++]);
|
||||
tagDto.setName((String) o[i++]);
|
||||
tagDto.setColor((String) o[i++]);
|
||||
tagDto.setCount(((Number) o[i++]).intValue());
|
||||
tagStatDtoList.add(tagDto);
|
||||
}
|
||||
return tagStatDtoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tag.
|
||||
*
|
||||
* @param tag Tag
|
||||
* @param userId User ID
|
||||
* @return New ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public String create(Tag tag) {
|
||||
public String create(Tag tag, String userId) {
|
||||
// Create the UUID
|
||||
tag.setId(UUID.randomUUID().toString());
|
||||
|
||||
@@ -181,55 +104,18 @@ public class TagDao {
|
||||
em.persist(tag);
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(tag, AuditLogType.CREATE);
|
||||
AuditLogUtil.create(tag, AuditLogType.CREATE, userId);
|
||||
|
||||
return tag.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a tag by name.
|
||||
*
|
||||
* @param userId User ID
|
||||
* @param name Name
|
||||
* @return Tag
|
||||
*/
|
||||
public Tag getByName(String userId, String name) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select t from Tag t where t.name = :name and t.userId = :userId and t.deleteDate is null");
|
||||
q.setParameter("userId", userId);
|
||||
q.setParameter("name", name);
|
||||
try {
|
||||
return (Tag) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a tag by ID.
|
||||
*
|
||||
* @param userId User ID
|
||||
* @param tagId Tag ID
|
||||
* @return Tag
|
||||
*/
|
||||
public Tag getByTagId(String userId, String tagId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select t from Tag t where t.id = :tagId and t.userId = :userId and t.deleteDate is null");
|
||||
q.setParameter("userId", userId);
|
||||
q.setParameter("tagId", tagId);
|
||||
try {
|
||||
return (Tag) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a tag.
|
||||
*
|
||||
* @param tagId Tag ID
|
||||
* @param userId User ID
|
||||
*/
|
||||
public void delete(String tagId) {
|
||||
public void delete(String tagId, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the tag
|
||||
@@ -238,39 +124,32 @@ public class TagDao {
|
||||
Tag tagDb = (Tag) q.getSingleResult();
|
||||
|
||||
// Delete the tag
|
||||
tagDb.setDeleteDate(new Date());
|
||||
Date dateNow = new Date();
|
||||
tagDb.setDeleteDate(dateNow);
|
||||
|
||||
// Delete linked data
|
||||
q = em.createQuery("delete DocumentTag dt where dt.tagId = :tagId");
|
||||
q = em.createQuery("update DocumentTag dt set dt.deleteDate = :dateNow where dt.tagId = :tagId and dt.deleteDate is not null");
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.setParameter("tagId", tagId);
|
||||
q.executeUpdate();
|
||||
|
||||
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :tagId and a.deleteDate is null");
|
||||
q.setParameter("tagId", tagId);
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(tagDb, AuditLogType.DELETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search tags by name.
|
||||
*
|
||||
* @param name Tag name
|
||||
* @return List of found tags
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Tag> findByName(String userId, String name) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select t from Tag t where t.name like :name and t.userId = :userId and t.deleteDate is null");
|
||||
q.setParameter("userId", userId);
|
||||
q.setParameter("name", "%" + name + "%");
|
||||
return q.getResultList();
|
||||
AuditLogUtil.create(tagDb, AuditLogType.DELETE, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a tag.
|
||||
*
|
||||
* @param tag Tag to update
|
||||
* @param userId User ID
|
||||
* @return Updated tag
|
||||
*/
|
||||
public Tag update(Tag tag) {
|
||||
public Tag update(Tag tag, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the tag
|
||||
@@ -281,10 +160,79 @@ public class TagDao {
|
||||
// Update the tag
|
||||
tagFromDb.setName(tag.getName());
|
||||
tagFromDb.setColor(tag.getColor());
|
||||
tagFromDb.setParentId(tag.getParentId());
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(tagFromDb, AuditLogType.UPDATE);
|
||||
AuditLogUtil.create(tagFromDb, AuditLogType.UPDATE, userId);
|
||||
|
||||
return tagFromDb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all tags.
|
||||
*
|
||||
* @param criteria Search criteria
|
||||
* @param sortCriteria Sort criteria
|
||||
* @return List of groups
|
||||
*/
|
||||
public List<TagDto> findByCriteria(TagCriteria criteria, SortCriteria sortCriteria) {
|
||||
Map<String, Object> parameterMap = new HashMap<>();
|
||||
List<String> criteriaList = new ArrayList<>();
|
||||
|
||||
StringBuilder sb = new StringBuilder("select distinct t.TAG_ID_C as c0, t.TAG_NAME_C as c1, t.TAG_COLOR_C as c2, t.TAG_IDPARENT_C as c3, u.USE_USERNAME_C as c4 ");
|
||||
sb.append(" from T_TAG t ");
|
||||
sb.append(" join T_USER u on t.TAG_IDUSER_C = u.USE_ID_C ");
|
||||
|
||||
// Add search criterias
|
||||
if (criteria.getId() != null) {
|
||||
criteriaList.add("t.TAG_ID_C = :id");
|
||||
parameterMap.put("id", criteria.getId());
|
||||
}
|
||||
if (criteria.getTargetIdList() != null) {
|
||||
sb.append(" left join T_ACL a on a.ACL_TARGETID_C in (:targetIdList) and a.ACL_SOURCEID_C = t.TAG_ID_C and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null ");
|
||||
criteriaList.add("a.ACL_ID_C is not null");
|
||||
parameterMap.put("targetIdList", criteria.getTargetIdList());
|
||||
}
|
||||
if (criteria.getDocumentId() != null) {
|
||||
sb.append(" join T_DOCUMENT_TAG dt on dt.DOT_IDTAG_C = t.TAG_ID_C and dt.DOT_DELETEDATE_D is null ");
|
||||
criteriaList.add("dt.DOT_IDDOCUMENT_C = :documentId");
|
||||
parameterMap.put("documentId", criteria.getDocumentId());
|
||||
}
|
||||
if (criteria.getName() != null) {
|
||||
criteriaList.add("t.TAG_NAME_C = :name");
|
||||
parameterMap.put("name", criteria.getName());
|
||||
}
|
||||
if (criteria.getNameLike() != null) {
|
||||
criteriaList.add("t.TAG_NAME_C like :nameLike");
|
||||
parameterMap.put("nameLike", "%" + criteria.getNameLike() + "%");
|
||||
}
|
||||
|
||||
criteriaList.add("t.TAG_DELETEDATE_D is null");
|
||||
|
||||
if (!criteriaList.isEmpty()) {
|
||||
sb.append(" where ");
|
||||
sb.append(Joiner.on(" and ").join(criteriaList));
|
||||
}
|
||||
|
||||
// Perform the search
|
||||
QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object[]> l = QueryUtil.getNativeQuery(queryParam).getResultList();
|
||||
|
||||
// Assemble results
|
||||
List<TagDto> tagDtoList = new ArrayList<>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
TagDto tagDto = new TagDto()
|
||||
.setId((String) o[i++])
|
||||
.setName((String) o[i++])
|
||||
.setColor((String) o[i++])
|
||||
.setParentId((String) o[i++])
|
||||
.setCreator((String) o[i]);
|
||||
tagDtoList.add(tagDto);
|
||||
}
|
||||
|
||||
return tagDtoList;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,12 @@ import org.mindrot.jbcrypt.BCrypt;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.sismics.docs.core.constant.AuditLogType;
|
||||
import com.sismics.docs.core.constant.Constants;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
import com.sismics.docs.core.util.AuditLogUtil;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||
import com.sismics.docs.core.util.jpa.QueryParam;
|
||||
import com.sismics.docs.core.util.jpa.QueryUtil;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
@@ -38,9 +36,9 @@ public class UserDao {
|
||||
*
|
||||
* @param username User login
|
||||
* @param password User password
|
||||
* @return ID of the authenticated user or null
|
||||
* @return The authenticated user or null
|
||||
*/
|
||||
public String authenticate(String username, String password) {
|
||||
public User authenticate(String username, String password) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select u from User u where u.username = :username and u.deleteDate is null");
|
||||
q.setParameter("username", username);
|
||||
@@ -49,7 +47,7 @@ public class UserDao {
|
||||
if (!BCrypt.checkpw(password, user.getPassword())) {
|
||||
return null;
|
||||
}
|
||||
return user.getId();
|
||||
return user;
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
@@ -59,10 +57,11 @@ public class UserDao {
|
||||
* Creates a new user.
|
||||
*
|
||||
* @param user User to create
|
||||
* @param userId User ID
|
||||
* @return User ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public String create(User user) throws Exception {
|
||||
public String create(User user, String userId) throws Exception {
|
||||
// Create the user UUID
|
||||
user.setId(UUID.randomUUID().toString());
|
||||
|
||||
@@ -78,11 +77,10 @@ public class UserDao {
|
||||
// Create the user
|
||||
user.setCreateDate(new Date());
|
||||
user.setPassword(hashPassword(user.getPassword()));
|
||||
user.setTheme(Constants.DEFAULT_THEME_ID);
|
||||
em.persist(user);
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(user, AuditLogType.CREATE);
|
||||
AuditLogUtil.create(user, AuditLogType.CREATE, userId);
|
||||
|
||||
return user.getId();
|
||||
}
|
||||
@@ -91,9 +89,36 @@ public class UserDao {
|
||||
* Updates a user.
|
||||
*
|
||||
* @param user User to update
|
||||
* @param userId User ID
|
||||
* @return Updated user
|
||||
*/
|
||||
public User update(User user) {
|
||||
public User update(User user, String userId) {
|
||||
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();
|
||||
|
||||
// Update the user (except password)
|
||||
userFromDb.setEmail(user.getEmail());
|
||||
userFromDb.setStorageQuota(user.getStorageQuota());
|
||||
userFromDb.setStorageCurrent(user.getStorageCurrent());
|
||||
userFromDb.setTotpKey(user.getTotpKey());
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE, userId);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a user's quota.
|
||||
*
|
||||
* @param user User to update
|
||||
* @return Updated user
|
||||
*/
|
||||
public User updateQuota(User user) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the user
|
||||
@@ -102,13 +127,7 @@ public class UserDao {
|
||||
User userFromDb = (User) q.getSingleResult();
|
||||
|
||||
// Update the user
|
||||
userFromDb.setLocaleId(user.getLocaleId());
|
||||
userFromDb.setEmail(user.getEmail());
|
||||
userFromDb.setTheme(user.getTheme());
|
||||
userFromDb.setFirstConnection(user.isFirstConnection());
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE);
|
||||
userFromDb.setStorageQuota(user.getStorageQuota());
|
||||
|
||||
return user;
|
||||
}
|
||||
@@ -117,9 +136,10 @@ public class UserDao {
|
||||
* Update the user password.
|
||||
*
|
||||
* @param user User to update
|
||||
* @param userId User ID
|
||||
* @return Updated user
|
||||
*/
|
||||
public User updatePassword(User user) {
|
||||
public User updatePassword(User user, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the user
|
||||
@@ -131,7 +151,7 @@ public class UserDao {
|
||||
userFromDb.setPassword(hashPassword(user.getPassword()));
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE);
|
||||
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE, userId);
|
||||
|
||||
return user;
|
||||
}
|
||||
@@ -168,29 +188,13 @@ public class UserDao {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an active user by its password recovery token.
|
||||
*
|
||||
* @param passwordResetKey Password recovery token
|
||||
* @return User
|
||||
*/
|
||||
public User getActiveByPasswordResetKey(String passwordResetKey) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
try {
|
||||
Query q = em.createQuery("select u from User u where u.passwordResetKey = :passwordResetKey and u.deleteDate is null");
|
||||
q.setParameter("passwordResetKey", passwordResetKey);
|
||||
return (User) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a user.
|
||||
*
|
||||
* @param username User's username
|
||||
* @param userId User ID
|
||||
*/
|
||||
public void delete(String username) {
|
||||
public void delete(String username, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the user
|
||||
@@ -207,8 +211,28 @@ public class UserDao {
|
||||
q.setParameter("userId", userFromDb.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("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("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("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("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(userFromDb, AuditLogType.DELETE);
|
||||
AuditLogUtil.create(userFromDb, AuditLogType.DELETE, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,21 +241,22 @@ public class UserDao {
|
||||
* @param password Clear password
|
||||
* @return Hashed password
|
||||
*/
|
||||
protected String hashPassword(String password) {
|
||||
private String hashPassword(String password) {
|
||||
return BCrypt.hashpw(password, BCrypt.gensalt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all users.
|
||||
*
|
||||
* @param paginatedList List of users (updated by side effects)
|
||||
* @param criteria Search criteria
|
||||
* @param sortCriteria Sort criteria
|
||||
* @return List of users
|
||||
*/
|
||||
public void findByCriteria(PaginatedList<UserDto> paginatedList, UserCriteria criteria, SortCriteria sortCriteria) {
|
||||
Map<String, Object> parameterMap = new HashMap<String, Object>();
|
||||
List<String> criteriaList = new ArrayList<String>();
|
||||
public List<UserDto> findByCriteria(UserCriteria criteria, SortCriteria sortCriteria) {
|
||||
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_IDLOCALE_C as c4");
|
||||
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");
|
||||
sb.append(" from T_USER u ");
|
||||
|
||||
// Add search criterias
|
||||
@@ -240,6 +265,11 @@ public class UserDao {
|
||||
parameterMap.put("search", "%" + criteria.getSearch() + "%");
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
criteriaList.add("u.USE_DELETEDATE_D is null");
|
||||
|
||||
if (!criteriaList.isEmpty()) {
|
||||
@@ -248,11 +278,12 @@ public class UserDao {
|
||||
}
|
||||
|
||||
// Perform the search
|
||||
QueryParam queryParam = new QueryParam(sb.toString(), parameterMap);
|
||||
List<Object[]> l = PaginatedLists.executePaginatedQuery(paginatedList, queryParam, sortCriteria);
|
||||
QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object[]> l = QueryUtil.getNativeQuery(queryParam).getResultList();
|
||||
|
||||
// Assemble results
|
||||
List<UserDto> userDtoList = new ArrayList<UserDto>();
|
||||
List<UserDto> userDtoList = new ArrayList<>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
UserDto userDto = new UserDto();
|
||||
@@ -260,9 +291,10 @@ public class UserDao {
|
||||
userDto.setUsername((String) o[i++]);
|
||||
userDto.setEmail((String) o[i++]);
|
||||
userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
||||
userDto.setLocaleId((String) o[i++]);
|
||||
userDto.setStorageCurrent(((Number) o[i++]).longValue());
|
||||
userDto.setStorageQuota(((Number) o[i]).longValue());
|
||||
userDtoList.add(userDto);
|
||||
}
|
||||
paginatedList.setResultList(userDtoList);
|
||||
return userDtoList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.sismics.docs.core.dao.jpa;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import com.sismics.docs.core.model.jpa.Vocabulary;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
/**
|
||||
* Vocabulary DAO.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class VocabularyDao {
|
||||
/**
|
||||
* Creates a new vocabulary entry.
|
||||
*
|
||||
* @param vocabulary Vocabulary
|
||||
* @return New ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public String create(Vocabulary vocabulary) {
|
||||
// Create the UUID
|
||||
vocabulary.setId(UUID.randomUUID().toString());
|
||||
|
||||
// Create the comment
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
em.persist(vocabulary);
|
||||
|
||||
return vocabulary.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all vocabulary entries sharing a single name.
|
||||
*
|
||||
* @param name Name
|
||||
* @return Vocabulary entries
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Vocabulary> getByName(String name) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the entries
|
||||
Query q = em.createQuery("select v from Vocabulary v where v.name = :name order by v.order");
|
||||
q.setParameter("name", name);
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a vocabulary entry by ID.
|
||||
*
|
||||
* @param id ID
|
||||
* @return Vocabulary
|
||||
*/
|
||||
public Vocabulary getById(String id) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
try {
|
||||
return em.find(Vocabulary.class, id);
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a vocabulary entry.
|
||||
*
|
||||
* @param vocabulary Vocabulary to update
|
||||
*/
|
||||
public Vocabulary update(Vocabulary vocabulary) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// 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();
|
||||
|
||||
// Update the vocabulary entry
|
||||
vocabularyFromDb.setName(vocabulary.getName());
|
||||
vocabularyFromDb.setValue(vocabulary.getValue());
|
||||
vocabularyFromDb.setOrder(vocabulary.getOrder());
|
||||
|
||||
return vocabularyFromDb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a vocabulary entry.
|
||||
*
|
||||
* @param id Vocabulary ID
|
||||
*/
|
||||
public void delete(String id) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the vocabulary
|
||||
Query q = em.createQuery("select v from Vocabulary v where v.id = :id");
|
||||
q.setParameter("id", id);
|
||||
Vocabulary vocabularyDb = (Vocabulary) q.getSingleResult();
|
||||
|
||||
em.remove(vocabularyDb);
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,9 @@ import java.util.List;
|
||||
*/
|
||||
public class DocumentCriteria {
|
||||
/**
|
||||
* User ID.
|
||||
* ACL target ID list.
|
||||
*/
|
||||
private String userId;
|
||||
private List<String> targetIdList;
|
||||
|
||||
/**
|
||||
* Search query.
|
||||
@@ -51,146 +51,79 @@ public class DocumentCriteria {
|
||||
private String language;
|
||||
|
||||
/**
|
||||
* Getter of userId.
|
||||
*
|
||||
* @return userId
|
||||
* Creator ID.
|
||||
*/
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
private String creatorId;
|
||||
|
||||
public List<String> getTargetIdList() {
|
||||
return targetIdList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of userId.
|
||||
*
|
||||
* @param userId userId
|
||||
*/
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
public void setTargetIdList(List<String> targetIdList) {
|
||||
this.targetIdList = targetIdList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of search.
|
||||
*
|
||||
* @return the search
|
||||
*/
|
||||
public String getSearch() {
|
||||
return search;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of search.
|
||||
*
|
||||
* @param search search
|
||||
*/
|
||||
public void setSearch(String search) {
|
||||
this.search = search;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of fullSearch.
|
||||
*
|
||||
* @return the fullSearch
|
||||
*/
|
||||
public String getFullSearch() {
|
||||
return fullSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of fullSearch.
|
||||
*
|
||||
* @param fullSearch fullSearch
|
||||
*/
|
||||
public void setFullSearch(String fullSearch) {
|
||||
this.fullSearch = fullSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of createDateMin.
|
||||
*
|
||||
* @return the createDateMin
|
||||
*/
|
||||
public Date getCreateDateMin() {
|
||||
return createDateMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of createDateMin.
|
||||
*
|
||||
* @param createDateMin createDateMin
|
||||
*/
|
||||
public void setCreateDateMin(Date createDateMin) {
|
||||
this.createDateMin = createDateMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of createDateMax.
|
||||
*
|
||||
* @return the createDateMax
|
||||
*/
|
||||
public Date getCreateDateMax() {
|
||||
return createDateMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of createDateMax.
|
||||
*
|
||||
* @param createDateMax createDateMax
|
||||
*/
|
||||
public void setCreateDateMax(Date createDateMax) {
|
||||
this.createDateMax = createDateMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of tagIdList.
|
||||
*
|
||||
* @return the tagIdList
|
||||
*/
|
||||
public List<String> getTagIdList() {
|
||||
return tagIdList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of tagIdList.
|
||||
*
|
||||
* @param tagIdList tagIdList
|
||||
*/
|
||||
public void setTagIdList(List<String> tagIdList) {
|
||||
this.tagIdList = tagIdList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of shared.
|
||||
*
|
||||
* @return the shared
|
||||
*/
|
||||
public Boolean getShared() {
|
||||
return shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of shared.
|
||||
*
|
||||
* @param shared shared
|
||||
*/
|
||||
public void setShared(Boolean shared) {
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of language.
|
||||
*
|
||||
* @return the language
|
||||
*/
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of language.
|
||||
*
|
||||
* @param language language
|
||||
*/
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public String getCreatorId() {
|
||||
return creatorId;
|
||||
}
|
||||
|
||||
public void setCreatorId(String creatorId) {
|
||||
this.creatorId = creatorId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.sismics.docs.core.dao.jpa.criteria;
|
||||
|
||||
/**
|
||||
* Group criteria.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class GroupCriteria {
|
||||
/**
|
||||
* Search query.
|
||||
*/
|
||||
private String search;
|
||||
|
||||
/**
|
||||
* User ID.
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* Retrieve user groups recursively.
|
||||
*/
|
||||
private boolean recursive = false;
|
||||
|
||||
public String getSearch() {
|
||||
return search;
|
||||
}
|
||||
|
||||
public GroupCriteria setSearch(String search) {
|
||||
this.search = search;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public GroupCriteria setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isRecursive() {
|
||||
return recursive;
|
||||
}
|
||||
|
||||
public GroupCriteria setRecursive(boolean recursive) {
|
||||
this.recursive = recursive;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.sismics.docs.core.dao.jpa.criteria;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tag criteria.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class TagCriteria {
|
||||
/**
|
||||
* Tag ID.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* ACL target ID list.
|
||||
*/
|
||||
private List<String> targetIdList;
|
||||
|
||||
/**
|
||||
* Document ID.
|
||||
*/
|
||||
private String documentId;
|
||||
|
||||
/**
|
||||
* Tag name.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Approximate tag name.
|
||||
*/
|
||||
private String nameLike;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public TagCriteria setId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> getTargetIdList() {
|
||||
return targetIdList;
|
||||
}
|
||||
|
||||
public TagCriteria setTargetIdList(List<String> targetIdList) {
|
||||
this.targetIdList = targetIdList;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDocumentId() {
|
||||
return documentId;
|
||||
}
|
||||
|
||||
public TagCriteria setDocumentId(String documentId) {
|
||||
this.documentId = documentId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public TagCriteria setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getNameLike() {
|
||||
return nameLike;
|
||||
}
|
||||
|
||||
public TagCriteria setNameLike(String nameLike) {
|
||||
this.nameLike = nameLike;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.sismics.docs.core.dao.jpa.criteria;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* User criteria.
|
||||
*
|
||||
@@ -12,23 +10,27 @@ public class UserCriteria {
|
||||
* Search query.
|
||||
*/
|
||||
private String search;
|
||||
|
||||
|
||||
/**
|
||||
* Getter of search.
|
||||
*
|
||||
* @return the search
|
||||
* Group ID.
|
||||
*/
|
||||
private String groupId;
|
||||
|
||||
public String getSearch() {
|
||||
return search;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of search.
|
||||
*
|
||||
* @param search search
|
||||
*/
|
||||
public UserCriteria setSearch(String search) {
|
||||
this.search = search;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public UserCriteria setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.sismics.docs.core.dao.jpa.dto;
|
||||
|
||||
import javax.persistence.Id;
|
||||
|
||||
import com.sismics.docs.core.constant.PermType;
|
||||
|
||||
/**
|
||||
@@ -13,7 +11,6 @@ public class AclDto {
|
||||
/**
|
||||
* Acl ID.
|
||||
*/
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
/**
|
||||
|
||||