mirror of
https://github.com/sismics/docs.git
synced 2024-11-22 22:07:56 +01:00
commit
8477920475
@ -28,6 +28,7 @@ Features
|
|||||||
- Tag system with relations
|
- Tag system with relations
|
||||||
- Multi-users ACL system
|
- Multi-users ACL system
|
||||||
- Audit log
|
- Audit log
|
||||||
|
- Comments
|
||||||
- Document sharing by URL
|
- Document sharing by URL
|
||||||
- RESTful Web API
|
- RESTful Web API
|
||||||
- Fully featured Android client
|
- Fully featured Android client
|
||||||
|
@ -71,12 +71,11 @@
|
|||||||
<excludeFolder url="file://$MODULE_DIR$/build/dex" />
|
<excludeFolder url="file://$MODULE_DIR$/build/dex" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/incremental" />
|
<excludeFolder url="file://$MODULE_DIR$/build/incremental" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
|
<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/debug" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
<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" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.2.1/jars" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.2.1/jars" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/22.2.1/jars" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/22.2.1/jars" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.2.1/jars" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.2.1/jars" />
|
||||||
@ -84,17 +83,23 @@
|
|||||||
<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.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/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/incremental" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-support" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/restart-dex" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/libs" />
|
<excludeFolder url="file://$MODULE_DIR$/build/libs" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/manifests" />
|
<excludeFolder url="file://$MODULE_DIR$/build/manifests" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/ndk" />
|
<excludeFolder url="file://$MODULE_DIR$/build/ndk" />
|
||||||
@ -112,12 +117,12 @@
|
|||||||
<orderEntry type="library" exported="" name="android-easing-1.0.3" 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="imagezoom-1.0.5" level="project" />
|
||||||
<orderEntry type="library" exported="" name="support-v4-22.2.1" level="project" />
|
<orderEntry type="library" exported="" name="support-v4-22.2.1" level="project" />
|
||||||
<orderEntry type="library" exported="" name="eventbus-2.4.0" level="project" />
|
|
||||||
<orderEntry type="library" exported="" name="recyclerview-v7-22.2.1" level="project" />
|
<orderEntry type="library" exported="" name="recyclerview-v7-22.2.1" level="project" />
|
||||||
<orderEntry type="library" exported="" name="android-query.0.26.8" 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="tokenautocomplete-1.2.1" level="project" />
|
||||||
<orderEntry type="library" exported="" name="support-annotations-22.2.1" level="project" />
|
<orderEntry type="library" exported="" name="support-annotations-22.2.1" level="project" />
|
||||||
<orderEntry type="library" exported="" name="appcompat-v7-22.2.1" level="project" />
|
<orderEntry type="library" exported="" name="appcompat-v7-22.2.1" level="project" />
|
||||||
<orderEntry type="library" exported="" name="android-async-http-1.4.6" level="project" />
|
<orderEntry type="library" exported="" name="android-async-http-1.4.6" level="project" />
|
||||||
|
<orderEntry type="library" exported="" name="eventbus-2.4.1" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -1,20 +1,20 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:1.3.0'
|
classpath 'com.android.tools.build:gradle:2.0.0-alpha1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
jcenter()
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 22
|
compileSdkVersion 22
|
||||||
buildToolsVersion "23.0.2"
|
buildToolsVersion '23.0.2'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
@ -54,6 +54,6 @@ dependencies {
|
|||||||
compile 'com.android.support:recyclerview-v7:22.+'
|
compile 'com.android.support:recyclerview-v7:22.+'
|
||||||
compile 'com.loopj.android:android-async-http:1.4.6'
|
compile 'com.loopj.android:android-async-http:1.4.6'
|
||||||
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
|
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
|
||||||
compile 'de.greenrobot:eventbus:2.4.0'
|
compile 'de.greenrobot:eventbus:2.4.1'
|
||||||
compile 'com.shamanland:fab:0.0.6'
|
compile 'com.shamanland:fab:0.0.6'
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ public class MainApplication extends Application {
|
|||||||
JSONObject json = PreferenceUtil.getCachedJson(getApplicationContext(), PreferenceUtil.PREF_CACHED_USER_INFO_JSON);
|
JSONObject json = PreferenceUtil.getCachedJson(getApplicationContext(), PreferenceUtil.PREF_CACHED_USER_INFO_JSON);
|
||||||
ApplicationContext.getInstance().setUserInfo(getApplicationContext(), 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
|
// TODO Provide documents to intent action get content
|
||||||
|
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
@ -28,6 +27,7 @@ public class MainApplication extends Application {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLowMemory() {
|
public void onLowMemory() {
|
||||||
|
super.onLowMemory();
|
||||||
BitmapAjaxCallback.clearCache();
|
BitmapAjaxCallback.clearCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,15 @@ import android.support.v7.app.AppCompatActivity;
|
|||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
|
import android.view.ContextMenu;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@ -31,7 +35,10 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import com.sismics.docs.R;
|
import com.sismics.docs.R;
|
||||||
import com.sismics.docs.adapter.AclListAdapter;
|
import com.sismics.docs.adapter.AclListAdapter;
|
||||||
|
import com.sismics.docs.adapter.CommentListAdapter;
|
||||||
import com.sismics.docs.adapter.FilePagerAdapter;
|
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.DocumentDeleteEvent;
|
||||||
import com.sismics.docs.event.DocumentEditEvent;
|
import com.sismics.docs.event.DocumentEditEvent;
|
||||||
import com.sismics.docs.event.DocumentFullscreenEvent;
|
import com.sismics.docs.event.DocumentFullscreenEvent;
|
||||||
@ -40,6 +47,7 @@ import com.sismics.docs.event.FileDeleteEvent;
|
|||||||
import com.sismics.docs.fragment.DocShareFragment;
|
import com.sismics.docs.fragment.DocShareFragment;
|
||||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||||
import com.sismics.docs.model.application.ApplicationContext;
|
import com.sismics.docs.model.application.ApplicationContext;
|
||||||
|
import com.sismics.docs.resource.CommentResource;
|
||||||
import com.sismics.docs.resource.DocumentResource;
|
import com.sismics.docs.resource.DocumentResource;
|
||||||
import com.sismics.docs.resource.FileResource;
|
import com.sismics.docs.resource.FileResource;
|
||||||
import com.sismics.docs.service.FileUploadService;
|
import com.sismics.docs.service.FileUploadService;
|
||||||
@ -83,6 +91,11 @@ public class DocumentViewActivity extends AppCompatActivity {
|
|||||||
*/
|
*/
|
||||||
private FilePagerAdapter filePagerAdapter;
|
private FilePagerAdapter filePagerAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment list adapter.
|
||||||
|
*/
|
||||||
|
private CommentListAdapter commentListAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Document displayed.
|
* Document displayed.
|
||||||
*/
|
*/
|
||||||
@ -241,6 +254,39 @@ public class DocumentViewActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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 JsonHttpResponseHandler() {
|
||||||
|
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||||
|
EventBus.getDefault().post(new CommentAddEvent(response));
|
||||||
|
commentEditText.setText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||||
|
Toast.makeText(DocumentViewActivity.this, R.string.comment_add_failure, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Grab the comments
|
||||||
|
updateComments();
|
||||||
|
|
||||||
// Grab the attached files
|
// Grab the attached files
|
||||||
updateFiles();
|
updateFiles();
|
||||||
|
|
||||||
@ -268,6 +314,15 @@ public class DocumentViewActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
return true;
|
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:
|
case R.id.download_file:
|
||||||
downloadCurrentFile();
|
downloadCurrentFile();
|
||||||
return true;
|
return true;
|
||||||
@ -507,6 +562,36 @@ public class DocumentViewActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A comment add event has been fired.
|
||||||
|
*
|
||||||
|
* @param event Comment add event
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
if (document == null) return;
|
if (document == null) return;
|
||||||
@ -572,6 +657,89 @@ public class DocumentViewActivity extends AppCompatActivity {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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 JsonHttpResponseHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||||
|
EventBus.getDefault().post(new CommentDeleteEvent(commentId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||||
|
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 JsonHttpResponseHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||||
|
JSONArray comments = response.optJSONArray("comments");
|
||||||
|
commentListAdapter = new CommentListAdapter(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 onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||||
|
emptyView.setText(R.string.error_loading_comments);
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
listView.setVisibility(View.GONE);
|
||||||
|
emptyView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh files list.
|
* Refresh files list.
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
package com.sismics.docs.adapter;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
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.androidquery.AQuery;
|
||||||
|
import com.androidquery.callback.BitmapAjaxCallback;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment list adapter.
|
||||||
|
*
|
||||||
|
* @author bgamard.
|
||||||
|
*/
|
||||||
|
public class CommentListAdapter extends BaseAdapter {
|
||||||
|
/**
|
||||||
|
* AQuery.
|
||||||
|
*/
|
||||||
|
private AQuery aq;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tags.
|
||||||
|
*/
|
||||||
|
private List<JSONObject> commentList = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment list adapter.
|
||||||
|
*
|
||||||
|
* @param commentsArray Comments
|
||||||
|
*/
|
||||||
|
public CommentListAdapter(JSONArray commentsArray) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aq == null) {
|
||||||
|
aq = new AQuery(view);
|
||||||
|
} else {
|
||||||
|
aq.recycle(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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";
|
||||||
|
if (aq.shouldDelay(position, view, parent, gravatarUrl)) {
|
||||||
|
aq.id(gravatarImageView).image((Bitmap) null);
|
||||||
|
} else {
|
||||||
|
aq.id(gravatarImageView).image(new BitmapAjaxCallback()
|
||||||
|
.url(gravatarUrl)
|
||||||
|
.animation(AQuery.FADE_IN_NETWORK)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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,66 @@
|
|||||||
|
package com.sismics.docs.resource;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.loopj.android.http.RequestParams;
|
||||||
|
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access to /comment API.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class CommentResource extends BaseResource {
|
||||||
|
/**
|
||||||
|
* GET /comment/id.
|
||||||
|
*
|
||||||
|
* @param context Context
|
||||||
|
* @param documentId Document ID
|
||||||
|
* @param responseHandler Callback
|
||||||
|
*/
|
||||||
|
public static void list(Context context, String documentId, JsonHttpResponseHandler responseHandler) {
|
||||||
|
init(context);
|
||||||
|
|
||||||
|
client.get(getApiUrl(context) + "/comment/" + documentId, responseHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT /comment.
|
||||||
|
*
|
||||||
|
* @param context Context
|
||||||
|
* @param documentId Document ID
|
||||||
|
* @param content Comment content
|
||||||
|
* @param responseHandler Callback
|
||||||
|
*/
|
||||||
|
public static void add(Context context, String documentId, String content, JsonHttpResponseHandler responseHandler) {
|
||||||
|
init(context);
|
||||||
|
|
||||||
|
RequestParams params = new RequestParams();
|
||||||
|
params.put("id", documentId);
|
||||||
|
params.put("content", content);
|
||||||
|
client.put(getApiUrl(context) + "/comment", params, responseHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE /comment/id.
|
||||||
|
*
|
||||||
|
* @param context Context
|
||||||
|
* @param commentId Comment ID
|
||||||
|
* @param responseHandler Callback
|
||||||
|
*/
|
||||||
|
public static void remove(Context context, String commentId, JsonHttpResponseHandler responseHandler) {
|
||||||
|
init(context);
|
||||||
|
|
||||||
|
client.delete(getApiUrl(context) + "/comment/" + commentId, responseHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel pending requests.
|
||||||
|
*
|
||||||
|
* @param context Context
|
||||||
|
*/
|
||||||
|
public static void cancel(Context context) {
|
||||||
|
client.cancelRequests(context, true);
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 296 B |
Binary file not shown.
After Width: | Height: | Size: 448 B |
Binary file not shown.
After Width: | Height: | Size: 359 B |
Binary file not shown.
After Width: | Height: | Size: 565 B |
57
docs-android/app/src/main/res/layout/comment_list_item.xml
Normal file
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,6 +37,109 @@
|
|||||||
|
|
||||||
</RelativeLayout>
|
</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_black_24dp"
|
||||||
|
android:drawableLeft="@drawable/ic_comment_black_24dp"
|
||||||
|
android:drawablePadding="6dp"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/primary_text_default_material_light"
|
||||||
|
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 -->
|
<!-- Right drawer -->
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -9,6 +9,12 @@
|
|||||||
android:title="@string/toggle_informations">
|
android:title="@string/toggle_informations">
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/comments"
|
||||||
|
app:showAsAction="collapseActionView"
|
||||||
|
android:title="@string/comments">
|
||||||
|
</item>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/download_file"
|
android:id="@+id/download_file"
|
||||||
app:showAsAction="collapseActionView"
|
app:showAsAction="collapseActionView"
|
||||||
|
@ -107,6 +107,16 @@
|
|||||||
<string name="all_languages">All languages</string>
|
<string name="all_languages">All languages</string>
|
||||||
<string name="toggle_informations">Toggle informations</string>
|
<string name="toggle_informations">Toggle informations</string>
|
||||||
<string name="who_can_access">Who can access</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>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#Wed Nov 26 21:58:48 CET 2014
|
#Mon Nov 23 20:12:30 CET 2015
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
update T_CONFIG set CFG_VALUE_C = 'RAM' where CFG_ID_C = 'LUCENE_DIRECTORY_STORAGE';
|
@ -101,6 +101,8 @@ public class UserDao {
|
|||||||
|
|
||||||
// Update the user
|
// Update the user
|
||||||
userFromDb.setEmail(user.getEmail());
|
userFromDb.setEmail(user.getEmail());
|
||||||
|
userFromDb.setStorageQuota(user.getStorageQuota());
|
||||||
|
userFromDb.setStorageCurrent(user.getStorageCurrent());
|
||||||
|
|
||||||
// Create audit log
|
// Create audit log
|
||||||
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE);
|
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE);
|
||||||
@ -226,7 +228,7 @@ public class UserDao {
|
|||||||
Map<String, Object> parameterMap = new HashMap<String, Object>();
|
Map<String, Object> parameterMap = new HashMap<String, Object>();
|
||||||
List<String> criteriaList = new ArrayList<String>();
|
List<String> criteriaList = new ArrayList<String>();
|
||||||
|
|
||||||
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");
|
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 ");
|
sb.append(" from T_USER u ");
|
||||||
|
|
||||||
// Add search criterias
|
// Add search criterias
|
||||||
@ -255,6 +257,8 @@ public class UserDao {
|
|||||||
userDto.setUsername((String) o[i++]);
|
userDto.setUsername((String) o[i++]);
|
||||||
userDto.setEmail((String) o[i++]);
|
userDto.setEmail((String) o[i++]);
|
||||||
userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
||||||
|
userDto.setStorageCurrent(((Number) o[i++]).longValue());
|
||||||
|
userDto.setStorageQuota(((Number) o[i++]).longValue());
|
||||||
userDtoList.add(userDto);
|
userDtoList.add(userDto);
|
||||||
}
|
}
|
||||||
paginatedList.setResultList(userDtoList);
|
paginatedList.setResultList(userDtoList);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.sismics.docs.core.dao.jpa.dto;
|
package com.sismics.docs.core.dao.jpa.dto;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User DTO.
|
* User DTO.
|
||||||
*
|
*
|
||||||
@ -26,6 +27,16 @@ public class UserDto {
|
|||||||
*/
|
*/
|
||||||
private Long createTimestamp;
|
private Long createTimestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage quota.
|
||||||
|
*/
|
||||||
|
private Long storageQuota;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage current usage.
|
||||||
|
*/
|
||||||
|
private Long storageCurrent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter of id.
|
* Getter of id.
|
||||||
*
|
*
|
||||||
@ -88,6 +99,22 @@ public class UserDto {
|
|||||||
public Long getCreateTimestamp() {
|
public Long getCreateTimestamp() {
|
||||||
return createTimestamp;
|
return createTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getStorageQuota() {
|
||||||
|
return storageQuota;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStorageQuota(Long storageQuota) {
|
||||||
|
this.storageQuota = storageQuota;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getStorageCurrent() {
|
||||||
|
return storageCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStorageCurrent(Long storageCurrent) {
|
||||||
|
this.storageCurrent = storageCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setter of createTimestamp.
|
* Setter of createTimestamp.
|
||||||
|
@ -54,6 +54,18 @@ public class User implements Loggable {
|
|||||||
@Column(name = "USE_EMAIL_C", nullable = false, length = 100)
|
@Column(name = "USE_EMAIL_C", nullable = false, length = 100)
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage quota.
|
||||||
|
*/
|
||||||
|
@Column(name = "USE_STORAGEQUOTA_N", nullable = false)
|
||||||
|
private Long storageQuota;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage current usage.
|
||||||
|
*/
|
||||||
|
@Column(name = "USE_STORAGECURRENT_N", nullable = false)
|
||||||
|
private Long storageCurrent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creation date.
|
* Creation date.
|
||||||
*/
|
*/
|
||||||
@ -66,149 +78,87 @@ public class User implements Loggable {
|
|||||||
@Column(name = "USE_DELETEDATE_D")
|
@Column(name = "USE_DELETEDATE_D")
|
||||||
private Date deleteDate;
|
private Date deleteDate;
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter of id.
|
|
||||||
*
|
|
||||||
* @return id
|
|
||||||
*/
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter of id.
|
|
||||||
*
|
|
||||||
* @param id id
|
|
||||||
*/
|
|
||||||
public void setId(String id) {
|
public void setId(String id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter of roleId.
|
|
||||||
*
|
|
||||||
* @return roleId
|
|
||||||
*/
|
|
||||||
public String getRoleId() {
|
public String getRoleId() {
|
||||||
return roleId;
|
return roleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter of roleId.
|
|
||||||
*
|
|
||||||
* @param roleId roleId
|
|
||||||
*/
|
|
||||||
public void setRoleId(String roleId) {
|
public void setRoleId(String roleId) {
|
||||||
this.roleId = roleId;
|
this.roleId = roleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter of username.
|
|
||||||
*
|
|
||||||
* @return username
|
|
||||||
*/
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter of username.
|
|
||||||
*
|
|
||||||
* @param username username
|
|
||||||
*/
|
|
||||||
public void setUsername(String username) {
|
public void setUsername(String username) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter of password.
|
|
||||||
*
|
|
||||||
* @return password
|
|
||||||
*/
|
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter of password.
|
|
||||||
*
|
|
||||||
* @param password password
|
|
||||||
*/
|
|
||||||
public void setPassword(String password) {
|
public void setPassword(String password) {
|
||||||
this.password = password;
|
this.password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter of email.
|
|
||||||
*
|
|
||||||
* @return email
|
|
||||||
*/
|
|
||||||
public String getEmail() {
|
public String getEmail() {
|
||||||
return email;
|
return email;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter of email.
|
|
||||||
*
|
|
||||||
* @param email email
|
|
||||||
*/
|
|
||||||
public void setEmail(String email) {
|
public void setEmail(String email) {
|
||||||
this.email = email;
|
this.email = email;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter of createDate.
|
|
||||||
*
|
|
||||||
* @return createDate
|
|
||||||
*/
|
|
||||||
public Date getCreateDate() {
|
public Date getCreateDate() {
|
||||||
return createDate;
|
return createDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter of createDate.
|
|
||||||
*
|
|
||||||
* @param createDate createDate
|
|
||||||
*/
|
|
||||||
public void setCreateDate(Date createDate) {
|
public void setCreateDate(Date createDate) {
|
||||||
this.createDate = createDate;
|
this.createDate = createDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter of deleteDate.
|
|
||||||
*
|
|
||||||
* @return deleteDate
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Date getDeleteDate() {
|
public Date getDeleteDate() {
|
||||||
return deleteDate;
|
return deleteDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter of deleteDate.
|
|
||||||
*
|
|
||||||
* @param deleteDate deleteDate
|
|
||||||
*/
|
|
||||||
public void setDeleteDate(Date deleteDate) {
|
public void setDeleteDate(Date deleteDate) {
|
||||||
this.deleteDate = deleteDate;
|
this.deleteDate = deleteDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter de privateKey.
|
|
||||||
* @return privateKey
|
|
||||||
*/
|
|
||||||
public String getPrivateKey() {
|
public String getPrivateKey() {
|
||||||
return privateKey;
|
return privateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter de privateKey.
|
|
||||||
* @param privateKey privateKey
|
|
||||||
*/
|
|
||||||
public void setPrivateKey(String privateKey) {
|
public void setPrivateKey(String privateKey) {
|
||||||
this.privateKey = privateKey;
|
this.privateKey = privateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getStorageQuota() {
|
||||||
|
return storageQuota;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStorageQuota(Long storageQuota) {
|
||||||
|
this.storageQuota = storageQuota;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getStorageCurrent() {
|
||||||
|
return storageCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStorageCurrent(Long storageCurrent) {
|
||||||
|
this.storageCurrent = storageCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return MoreObjects.toStringHelper(this)
|
return MoreObjects.toStringHelper(this)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.sismics.docs.core.service;
|
package com.sismics.docs.core.service;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.lucene.index.DirectoryReader;
|
import org.apache.lucene.index.DirectoryReader;
|
||||||
@ -56,10 +56,10 @@ public class IndexingService extends AbstractScheduledService {
|
|||||||
directory = new RAMDirectory();
|
directory = new RAMDirectory();
|
||||||
log.info("Using RAM Lucene storage");
|
log.info("Using RAM Lucene storage");
|
||||||
} else if (luceneStorageConfig.equals(Constants.LUCENE_DIRECTORY_STORAGE_FILE)) {
|
} else if (luceneStorageConfig.equals(Constants.LUCENE_DIRECTORY_STORAGE_FILE)) {
|
||||||
File luceneDirectory = DirectoryUtil.getLuceneDirectory();
|
Path luceneDirectory = DirectoryUtil.getLuceneDirectory();
|
||||||
log.info("Using file Lucene storage: {}", luceneDirectory);
|
log.info("Using file Lucene storage: {}", luceneDirectory);
|
||||||
try {
|
try {
|
||||||
directory = new SimpleFSDirectory(luceneDirectory, new SimpleFSLockFactory());
|
directory = new SimpleFSDirectory(luceneDirectory.toFile(), new SimpleFSLockFactory());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error initializing Lucene index", e);
|
log.error("Error initializing Lucene index", e);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package com.sismics.docs.core.util;
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
@ -17,27 +20,31 @@ public class DirectoryUtil {
|
|||||||
*
|
*
|
||||||
* @return Base data directory
|
* @return Base data directory
|
||||||
*/
|
*/
|
||||||
public static File getBaseDataDirectory() {
|
public static Path getBaseDataDirectory() {
|
||||||
File baseDataDir = null;
|
Path baseDataDir = null;
|
||||||
if (StringUtils.isNotBlank(EnvironmentUtil.getDocsHome())) {
|
if (StringUtils.isNotBlank(EnvironmentUtil.getDocsHome())) {
|
||||||
// If the docs.home property is set then use it
|
// If the docs.home property is set then use it
|
||||||
baseDataDir = new File(EnvironmentUtil.getDocsHome());
|
baseDataDir = Paths.get(EnvironmentUtil.getDocsHome());
|
||||||
} else if (EnvironmentUtil.isUnitTest()) {
|
} else if (EnvironmentUtil.isUnitTest()) {
|
||||||
// For unit testing, use a temporary directory
|
// For unit testing, use a temporary directory
|
||||||
baseDataDir = new File(System.getProperty("java.io.tmpdir"));
|
baseDataDir = Paths.get(System.getProperty("java.io.tmpdir"));
|
||||||
} else {
|
} else {
|
||||||
// We are in a webapp environment and nothing is specified, use the default directory for this OS
|
// We are in a webapp environment and nothing is specified, use the default directory for this OS
|
||||||
if (EnvironmentUtil.isUnix()) {
|
if (EnvironmentUtil.isUnix()) {
|
||||||
baseDataDir = new File("/var/docs");
|
baseDataDir = Paths.get("/var/docs");
|
||||||
} if (EnvironmentUtil.isWindows()) {
|
} if (EnvironmentUtil.isWindows()) {
|
||||||
baseDataDir = new File(EnvironmentUtil.getWindowsAppData() + "\\Sismics\\Docs");
|
baseDataDir = Paths.get(EnvironmentUtil.getWindowsAppData() + "\\Sismics\\Docs");
|
||||||
} else if (EnvironmentUtil.isMacOs()) {
|
} else if (EnvironmentUtil.isMacOs()) {
|
||||||
baseDataDir = new File(EnvironmentUtil.getMacOsUserHome() + "/Library/Sismics/Docs");
|
baseDataDir = Paths.get(EnvironmentUtil.getMacOsUserHome() + "/Library/Sismics/Docs");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (baseDataDir != null && !baseDataDir.isDirectory()) {
|
if (baseDataDir != null && !Files.isDirectory(baseDataDir)) {
|
||||||
baseDataDir.mkdirs();
|
try {
|
||||||
|
Files.createDirectories(baseDataDir);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseDataDir;
|
return baseDataDir;
|
||||||
@ -48,7 +55,7 @@ public class DirectoryUtil {
|
|||||||
*
|
*
|
||||||
* @return Database directory.
|
* @return Database directory.
|
||||||
*/
|
*/
|
||||||
public static File getDbDirectory() {
|
public static Path getDbDirectory() {
|
||||||
return getDataSubDirectory("db");
|
return getDataSubDirectory("db");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +64,7 @@ public class DirectoryUtil {
|
|||||||
*
|
*
|
||||||
* @return Lucene indexes directory.
|
* @return Lucene indexes directory.
|
||||||
*/
|
*/
|
||||||
public static File getLuceneDirectory() {
|
public static Path getLuceneDirectory() {
|
||||||
return getDataSubDirectory("lucene");
|
return getDataSubDirectory("lucene");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +73,7 @@ public class DirectoryUtil {
|
|||||||
*
|
*
|
||||||
* @return Storage directory.
|
* @return Storage directory.
|
||||||
*/
|
*/
|
||||||
public static File getStorageDirectory() {
|
public static Path getStorageDirectory() {
|
||||||
return getDataSubDirectory("storage");
|
return getDataSubDirectory("storage");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +82,7 @@ public class DirectoryUtil {
|
|||||||
*
|
*
|
||||||
* @return Log directory.
|
* @return Log directory.
|
||||||
*/
|
*/
|
||||||
public static File getLogDirectory() {
|
public static Path getLogDirectory() {
|
||||||
return getDataSubDirectory("log");
|
return getDataSubDirectory("log");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,11 +91,15 @@ public class DirectoryUtil {
|
|||||||
*
|
*
|
||||||
* @return Subdirectory
|
* @return Subdirectory
|
||||||
*/
|
*/
|
||||||
private static File getDataSubDirectory(String subdirectory) {
|
private static Path getDataSubDirectory(String subdirectory) {
|
||||||
File baseDataDir = getBaseDataDirectory();
|
Path baseDataDir = getBaseDataDirectory();
|
||||||
File directory = new File(baseDataDir.getPath() + File.separator + subdirectory);
|
Path directory = baseDataDir.resolve(subdirectory);
|
||||||
if (!directory.isDirectory()) {
|
if (!Files.isDirectory(directory)) {
|
||||||
directory.mkdirs();
|
try {
|
||||||
|
Files.createDirectories(directory);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package com.sismics.docs.core.util;
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.CipherInputStream;
|
import javax.crypto.CipherInputStream;
|
||||||
@ -132,7 +130,7 @@ public class FileUtil {
|
|||||||
*/
|
*/
|
||||||
public static void save(InputStream inputStream, File file, String privateKey) throws Exception {
|
public static void save(InputStream inputStream, File file, String privateKey) throws Exception {
|
||||||
Cipher cipher = EncryptionUtil.getEncryptionCipher(privateKey);
|
Cipher cipher = EncryptionUtil.getEncryptionCipher(privateKey);
|
||||||
Path path = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId());
|
Path path = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||||
Files.copy(new CipherInputStream(inputStream, cipher), path);
|
Files.copy(new CipherInputStream(inputStream, cipher), path);
|
||||||
|
|
||||||
// Generate file variations
|
// Generate file variations
|
||||||
@ -172,21 +170,15 @@ public class FileUtil {
|
|||||||
image.flush();
|
image.flush();
|
||||||
|
|
||||||
// Write "web" encrypted image
|
// Write "web" encrypted image
|
||||||
java.io.File outputFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_web").toFile();
|
Path outputFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_web");
|
||||||
OutputStream outputStream = new CipherOutputStream(new FileOutputStream(outputFile), cipher);
|
try (OutputStream outputStream = new CipherOutputStream(Files.newOutputStream(outputFile), cipher)) {
|
||||||
try {
|
|
||||||
ImageUtil.writeJpeg(web, outputStream);
|
ImageUtil.writeJpeg(web, outputStream);
|
||||||
} finally {
|
|
||||||
outputStream.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write "thumb" encrypted image
|
// Write "thumb" encrypted image
|
||||||
outputFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_thumb").toFile();
|
outputFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_thumb");
|
||||||
outputStream = new CipherOutputStream(new FileOutputStream(outputFile), cipher);
|
try (OutputStream outputStream = new CipherOutputStream(Files.newOutputStream(outputFile), cipher)) {
|
||||||
try {
|
|
||||||
ImageUtil.writeJpeg(thumbnail, outputStream);
|
ImageUtil.writeJpeg(thumbnail, outputStream);
|
||||||
} finally {
|
|
||||||
outputStream.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,20 +187,21 @@ public class FileUtil {
|
|||||||
* Remove a file from the storage filesystem.
|
* Remove a file from the storage filesystem.
|
||||||
*
|
*
|
||||||
* @param file File to delete
|
* @param file File to delete
|
||||||
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public static void delete(File file) {
|
public static void delete(File file) throws IOException {
|
||||||
java.io.File storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId()).toFile();
|
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||||
java.io.File webFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_web").toFile();
|
Path webFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_web");
|
||||||
java.io.File thumbnailFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_thumb").toFile();
|
Path thumbnailFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_thumb");
|
||||||
|
|
||||||
if (storedFile.exists()) {
|
if (Files.exists(storedFile)) {
|
||||||
storedFile.delete();
|
Files.delete(storedFile);
|
||||||
}
|
}
|
||||||
if (webFile.exists()) {
|
if (Files.exists(webFile)) {
|
||||||
webFile.delete();
|
Files.delete(webFile);
|
||||||
}
|
}
|
||||||
if (thumbnailFile.exists()) {
|
if (Files.exists(thumbnailFile)) {
|
||||||
thumbnailFile.delete();
|
Files.delete(thumbnailFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
package com.sismics.docs.core.util.math;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Classe utilitaire pour les calculs
|
|
||||||
*
|
|
||||||
* @author bgamard
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class MathUtil {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Arrondi à 2 décimales près
|
|
||||||
*
|
|
||||||
* @param d Nombre à arrondir
|
|
||||||
* @return Nombre arrondi
|
|
||||||
*/
|
|
||||||
public static Double round(Double d) {
|
|
||||||
return Math.round(d * 100.0) / 100.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contraint une valeur entre min et max.
|
|
||||||
*
|
|
||||||
* @param value Valeur
|
|
||||||
* @param min Minimum
|
|
||||||
* @param max Maximum
|
|
||||||
* @return Valeur contrainte
|
|
||||||
*/
|
|
||||||
public static double clip(double value, double min, double max) {
|
|
||||||
if (value < min) {
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
if (value > max) {
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interpole une valeur entre deux points.
|
|
||||||
*
|
|
||||||
* @param x Valeur à interpoler
|
|
||||||
* @param x1 Point 1 (x)
|
|
||||||
* @param y1 Point 1 (y)
|
|
||||||
* @param x2 Point 2 (x)
|
|
||||||
* @param y2 Point 2 (y)
|
|
||||||
* @return Valeur interpolée
|
|
||||||
*/
|
|
||||||
public static double interpolate(double x, double x1, double y1, double x2, double y2) {
|
|
||||||
double alpha = (x - x1) / (x2 - x1);
|
|
||||||
|
|
||||||
return y1 * (1 - alpha) + y2 * alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retourne un Double depuis un Number.
|
|
||||||
*
|
|
||||||
* @param number Number
|
|
||||||
* @return Double
|
|
||||||
*/
|
|
||||||
public static Double getDoubleFromNumber(Number number) {
|
|
||||||
if (number == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return number.doubleValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retourne un Integer depuis un Number.
|
|
||||||
*
|
|
||||||
* @param number Number
|
|
||||||
* @return Integer
|
|
||||||
*/
|
|
||||||
public static Integer getIntegerFromNumber(Number number) {
|
|
||||||
if (number == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return number.intValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retourne un Long depuis un Number.
|
|
||||||
*
|
|
||||||
* @param number Number
|
|
||||||
* @return Long
|
|
||||||
*/
|
|
||||||
public static Long getLongFromNumber(Number number) {
|
|
||||||
if (number == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return number.longValue();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,16 @@
|
|||||||
package com.sismics.util.jpa;
|
package com.sismics.util.jpa;
|
||||||
|
|
||||||
import com.sismics.docs.core.util.DirectoryUtil;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManagerFactory;
|
||||||
|
import javax.persistence.Persistence;
|
||||||
|
|
||||||
import org.hibernate.cfg.Environment;
|
import org.hibernate.cfg.Environment;
|
||||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
@ -8,15 +18,7 @@ import org.hibernate.service.ServiceRegistryBuilder;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.persistence.EntityManagerFactory;
|
import com.sismics.docs.core.util.DirectoryUtil;
|
||||||
import javax.persistence.Persistence;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entity manager factory.
|
* Entity manager factory.
|
||||||
@ -79,8 +81,8 @@ public final class EMF {
|
|||||||
log.info("Configuring EntityManager from environment parameters");
|
log.info("Configuring EntityManager from environment parameters");
|
||||||
Map<Object, Object> props = new HashMap<Object, Object>();
|
Map<Object, Object> props = new HashMap<Object, Object>();
|
||||||
props.put("hibernate.connection.driver_class", "org.h2.Driver");
|
props.put("hibernate.connection.driver_class", "org.h2.Driver");
|
||||||
File dbDirectory = DirectoryUtil.getDbDirectory();
|
Path dbDirectory = DirectoryUtil.getDbDirectory();
|
||||||
String dbFile = dbDirectory.getAbsoluteFile() + File.separator + "docs";
|
String dbFile = dbDirectory.resolve("docs").toAbsolutePath().toString();
|
||||||
props.put("hibernate.connection.url", "jdbc:h2:file:" + dbFile + ";CACHE_SIZE=65536");
|
props.put("hibernate.connection.url", "jdbc:h2:file:" + dbFile + ";CACHE_SIZE=65536");
|
||||||
props.put("hibernate.connection.username", "sa");
|
props.put("hibernate.connection.username", "sa");
|
||||||
props.put("hibernate.hbm2ddl.auto", "none");
|
props.put("hibernate.hbm2ddl.auto", "none");
|
||||||
|
@ -1 +1 @@
|
|||||||
db.version=3
|
db.version=4
|
@ -0,0 +1,3 @@
|
|||||||
|
alter table T_USER add column USE_STORAGEQUOTA_N bigint not null default 10000000000;
|
||||||
|
alter table T_USER add column USE_STORAGECURRENT_N bigint not null default 0;
|
||||||
|
update T_CONFIG set CFG_VALUE_C = '4' where CFG_ID_C = 'DB_VERSION';
|
@ -20,6 +20,8 @@ public class TestJpa extends BaseTransactionalTest {
|
|||||||
user.setUsername("username");
|
user.setUsername("username");
|
||||||
user.setEmail("toto@docs.com");
|
user.setEmail("toto@docs.com");
|
||||||
user.setRoleId("admin");
|
user.setRoleId("admin");
|
||||||
|
user.setStorageCurrent(0l);
|
||||||
|
user.setStorageQuota(10l);
|
||||||
user.setPrivateKey("AwesomePrivateKey");
|
user.setPrivateKey("AwesomePrivateKey");
|
||||||
String id = userDao.create(user);
|
String id = userDao.create(user);
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
<org.apache.maven.plugins.maven-release-plugin.version>2.5.2</org.apache.maven.plugins.maven-release-plugin.version>
|
<org.apache.maven.plugins.maven-release-plugin.version>2.5.2</org.apache.maven.plugins.maven-release-plugin.version>
|
||||||
<org.apache.maven.plugins.maven-resources-plugin.version>2.7</org.apache.maven.plugins.maven-resources-plugin.version>
|
<org.apache.maven.plugins.maven-resources-plugin.version>2.7</org.apache.maven.plugins.maven-resources-plugin.version>
|
||||||
<org.apache.maven.plugins.maven-war-plugin.version>2.6</org.apache.maven.plugins.maven-war-plugin.version>
|
<org.apache.maven.plugins.maven-war-plugin.version>2.6</org.apache.maven.plugins.maven-war-plugin.version>
|
||||||
|
<org.apache.maven.plugins.maven-surefire-plugin.version>2.18.1</org.apache.maven.plugins.maven-surefire-plugin.version>
|
||||||
<org.eclipse.jetty.jetty-maven-plugin.version>9.2.13.v20150730</org.eclipse.jetty.jetty-maven-plugin.version>
|
<org.eclipse.jetty.jetty-maven-plugin.version>9.2.13.v20150730</org.eclipse.jetty.jetty-maven-plugin.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@ -108,12 +109,17 @@
|
|||||||
<version>${org.apache.maven.plugins.maven-war-plugin.version}</version>
|
<version>${org.apache.maven.plugins.maven-war-plugin.version}</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>${org.apache.maven.plugins.maven-surefire-plugin.version}</version>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-maven-plugin</artifactId>
|
<artifactId>jetty-maven-plugin</artifactId>
|
||||||
<version>${org.eclipse.jetty.jetty-maven-plugin.version}</version>
|
<version>${org.eclipse.jetty.jetty-maven-plugin.version}</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
@ -155,7 +155,23 @@ public class ValidationUtil {
|
|||||||
try {
|
try {
|
||||||
return Integer.valueOf(s);
|
return Integer.valueOf(s);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw new ClientException("Validation Error", MessageFormat.format("{0} is not a number", name));
|
throw new ClientException("ValidationError", MessageFormat.format("{0} is not a number", name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the string is a number.
|
||||||
|
*
|
||||||
|
* @param s String to validate
|
||||||
|
* @param name Name of the parameter
|
||||||
|
* @return Parsed number
|
||||||
|
* @throws ClientException
|
||||||
|
*/
|
||||||
|
public static Long validateLong(String s, String name) throws ClientException {
|
||||||
|
try {
|
||||||
|
return Long.valueOf(s);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new ClientException("ValidationError", MessageFormat.format("{0} is not a number", name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.sismics.util.filter;
|
package com.sismics.util.filter;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
@ -44,20 +43,19 @@ public class RequestContextFilter implements Filter {
|
|||||||
if (!filterConfig.getServletContext().getServerInfo().startsWith("Grizzly")) {
|
if (!filterConfig.getServletContext().getServerInfo().startsWith("Grizzly")) {
|
||||||
EnvironmentUtil.setWebappContext(true);
|
EnvironmentUtil.setWebappContext(true);
|
||||||
}
|
}
|
||||||
File baseDataDirectory = null;
|
|
||||||
try {
|
try {
|
||||||
baseDataDirectory = DirectoryUtil.getBaseDataDirectory();
|
if (log.isInfoEnabled()) {
|
||||||
|
log.info(MessageFormat.format("Using base data directory: {0}", DirectoryUtil.getBaseDataDirectory()));
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error initializing base data directory", e);
|
log.error("Error initializing base data directory", e);
|
||||||
}
|
}
|
||||||
if (log.isInfoEnabled()) {
|
|
||||||
log.info(MessageFormat.format("Using base data directory: {0}", baseDataDirectory.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize file logger
|
// Initialize file logger
|
||||||
RollingFileAppender fileAppender = new RollingFileAppender();
|
RollingFileAppender fileAppender = new RollingFileAppender();
|
||||||
fileAppender.setName("FILE");
|
fileAppender.setName("FILE");
|
||||||
fileAppender.setFile(DirectoryUtil.getLogDirectory() + File.separator + "docs.log");
|
fileAppender.setFile(DirectoryUtil.getLogDirectory().resolve("docs.log").toString());
|
||||||
fileAppender.setLayout(new PatternLayout("%d{DATE} %p %l %m %n"));
|
fileAppender.setLayout(new PatternLayout("%d{DATE} %p %l %m %n"));
|
||||||
fileAppender.setThreshold(Level.INFO);
|
fileAppender.setThreshold(Level.INFO);
|
||||||
fileAppender.setAppend(true);
|
fileAppender.setAppend(true);
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package com.sismics.docs.rest;
|
package com.sismics.docs.rest;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URLDecoder;
|
|
||||||
|
|
||||||
import javax.ws.rs.core.Application;
|
import javax.ws.rs.core.Application;
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
@ -63,8 +61,7 @@ public abstract class BaseJerseyTest extends JerseyTest {
|
|||||||
|
|
||||||
clientUtil = new ClientUtil(target());
|
clientUtil = new ClientUtil(target());
|
||||||
|
|
||||||
String httpRoot = URLDecoder.decode(new File(getClass().getResource("/").getFile()).getAbsolutePath(), "utf-8");
|
httpServer = HttpServer.createSimpleServer(getClass().getResource("/").getFile(), "localhost", getPort());
|
||||||
httpServer = HttpServer.createSimpleServer(httpRoot, "localhost", getPort());
|
|
||||||
WebappContext context = new WebappContext("GrizzlyContext", "/docs");
|
WebappContext context = new WebappContext("GrizzlyContext", "/docs");
|
||||||
context.addFilter("requestContextFilter", RequestContextFilter.class)
|
context.addFilter("requestContextFilter", RequestContextFilter.class)
|
||||||
.addMappingForUrlPatterns(null, "/*");
|
.addMappingForUrlPatterns(null, "/*");
|
||||||
|
@ -40,7 +40,7 @@ public class ClientUtil {
|
|||||||
form.param("username", username);
|
form.param("username", username);
|
||||||
form.param("email", username + "@docs.com");
|
form.param("email", username + "@docs.com");
|
||||||
form.param("password", "12345678");
|
form.param("password", "12345678");
|
||||||
form.param("time_zone", "Asia/Tokyo");
|
form.param("storage_quota", "1000000"); // 1MB quota
|
||||||
resource.path("/user").request()
|
resource.path("/user").request()
|
||||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminAuthenticationToken)
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminAuthenticationToken)
|
||||||
.put(Entity.form(form), JsonObject.class);
|
.put(Entity.form(form), JsonObject.class);
|
||||||
|
@ -174,6 +174,15 @@
|
|||||||
</webApp>
|
</webApp>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<forkCount>1</forkCount>
|
||||||
|
<reuseForks>false</reuseForks>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=3
|
db.version=4
|
@ -1,5 +1,8 @@
|
|||||||
package com.sismics.docs.rest.resource;
|
package com.sismics.docs.rest.resource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -19,8 +22,10 @@ import org.apache.log4j.Appender;
|
|||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import com.sismics.docs.core.dao.jpa.FileDao;
|
import com.sismics.docs.core.dao.jpa.FileDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||||
import com.sismics.docs.core.model.context.AppContext;
|
import com.sismics.docs.core.model.context.AppContext;
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
|
import com.sismics.docs.core.model.jpa.User;
|
||||||
import com.sismics.docs.core.util.ConfigUtil;
|
import com.sismics.docs.core.util.ConfigUtil;
|
||||||
import com.sismics.docs.core.util.DirectoryUtil;
|
import com.sismics.docs.core.util.DirectoryUtil;
|
||||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||||
@ -165,13 +170,65 @@ public class AppResource extends BaseResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if each stored file is valid
|
// Check if each stored file is valid
|
||||||
java.io.File[] storedFileList = DirectoryUtil.getStorageDirectory().listFiles();
|
try (DirectoryStream<java.nio.file.Path> storedFileList = Files.newDirectoryStream(DirectoryUtil.getStorageDirectory())) {
|
||||||
for (java.io.File storedFile : storedFileList) {
|
for (java.nio.file.Path storedFile : storedFileList) {
|
||||||
String fileName = storedFile.getName();
|
String fileName = storedFile.getFileName().toString();
|
||||||
String[] fileNameArray = fileName.split("_");
|
String[] fileNameArray = fileName.split("_");
|
||||||
if (!fileMap.containsKey(fileNameArray[0])) {
|
if (!fileMap.containsKey(fileNameArray[0])) {
|
||||||
storedFile.delete();
|
Files.delete(storedFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ServerException("FileError", "Error deleting orphan files", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always return OK
|
||||||
|
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||||
|
.add("status", "ok");
|
||||||
|
return Response.ok().entity(response.build()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recompute the quota for each user.
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("batch/recompute_quota")
|
||||||
|
public Response batchRecomputeQuota() {
|
||||||
|
if (!authenticate()) {
|
||||||
|
throw new ForbiddenClientException();
|
||||||
|
}
|
||||||
|
checkBaseFunction(BaseFunction.ADMIN);
|
||||||
|
|
||||||
|
// Get all files
|
||||||
|
FileDao fileDao = new FileDao();
|
||||||
|
List<File> fileList = fileDao.findAll();
|
||||||
|
|
||||||
|
// Count each file for the corresponding user quota
|
||||||
|
UserDao userDao = new UserDao();
|
||||||
|
Map<String, User> userMap = new HashMap<>();
|
||||||
|
for (File file : fileList) {
|
||||||
|
java.nio.file.Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||||
|
User user = null;
|
||||||
|
if (userMap.containsKey(file.getUserId())) {
|
||||||
|
user = userMap.get(file.getUserId());
|
||||||
|
} else {
|
||||||
|
user = userDao.getById(file.getUserId());
|
||||||
|
user.setStorageCurrent(0l);
|
||||||
|
userMap.put(user.getId(), user);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
user.setStorageCurrent(user.getStorageCurrent() + Files.size(storedFile));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ServerException("MissingFile", "File does not exist", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save all users
|
||||||
|
for (User user : userMap.values()) {
|
||||||
|
userDao.update(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always return OK
|
// Always return OK
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package com.sismics.docs.rest.resource;
|
package com.sismics.docs.rest.resource;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
@ -123,6 +123,11 @@ public class FileResource extends BaseResource {
|
|||||||
throw new ClientException("InvalidFileType", "File type not recognized");
|
throw new ClientException("InvalidFileType", "File type not recognized");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate quota
|
||||||
|
if (user.getStorageCurrent() + fileData.length > user.getStorageQuota()) {
|
||||||
|
throw new ClientException("QuotaReached", "Quota limit reached");
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get files of this document
|
// Get files of this document
|
||||||
FileDao fileDao = new FileDao();
|
FileDao fileDao = new FileDao();
|
||||||
@ -144,6 +149,10 @@ public class FileResource extends BaseResource {
|
|||||||
// Save the file
|
// Save the file
|
||||||
FileUtil.save(fileInputStream, file, user.getPrivateKey());
|
FileUtil.save(fileInputStream, file, user.getPrivateKey());
|
||||||
|
|
||||||
|
// Update the user quota
|
||||||
|
user.setStorageCurrent(user.getStorageCurrent() + fileData.length);
|
||||||
|
userDao.update(user);
|
||||||
|
|
||||||
// Raise a new file created event if we have a document
|
// Raise a new file created event if we have a document
|
||||||
if (documentId != null) {
|
if (documentId != null) {
|
||||||
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
|
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
|
||||||
@ -156,7 +165,8 @@ public class FileResource extends BaseResource {
|
|||||||
// Always return OK
|
// Always return OK
|
||||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||||
.add("status", "ok")
|
.add("status", "ok")
|
||||||
.add("id", fileId);
|
.add("id", fileId)
|
||||||
|
.add("size", fileData.length);
|
||||||
return Response.ok().entity(response.build()).build();
|
return Response.ok().entity(response.build()).build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ServerException("FileError", "Error adding a file", e);
|
throw new ServerException("FileError", "Error adding a file", e);
|
||||||
@ -206,8 +216,8 @@ public class FileResource extends BaseResource {
|
|||||||
|
|
||||||
// Raise a new file created event (it wasn't sent during file creation)
|
// Raise a new file created event (it wasn't sent during file creation)
|
||||||
try {
|
try {
|
||||||
java.io.File storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), id).toFile();
|
java.nio.file.Path storedFile = DirectoryUtil.getStorageDirectory().resolve(id);
|
||||||
InputStream fileInputStream = new FileInputStream(storedfile);
|
InputStream fileInputStream = Files.newInputStream(storedFile);
|
||||||
final InputStream responseInputStream = EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey());
|
final InputStream responseInputStream = EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey());
|
||||||
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
|
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
|
||||||
fileCreatedAsyncEvent.setDocument(document);
|
fileCreatedAsyncEvent.setDocument(document);
|
||||||
@ -294,11 +304,16 @@ public class FileResource extends BaseResource {
|
|||||||
|
|
||||||
JsonArrayBuilder files = Json.createArrayBuilder();
|
JsonArrayBuilder files = Json.createArrayBuilder();
|
||||||
for (File fileDb : fileList) {
|
for (File fileDb : fileList) {
|
||||||
files.add(Json.createObjectBuilder()
|
try {
|
||||||
.add("id", fileDb.getId())
|
files.add(Json.createObjectBuilder()
|
||||||
.add("mimetype", fileDb.getMimeType())
|
.add("id", fileDb.getId())
|
||||||
.add("document_id", JsonUtil.nullable(fileDb.getDocumentId()))
|
.add("mimetype", fileDb.getMimeType())
|
||||||
.add("create_date", fileDb.getCreateDate().getTime()));
|
.add("document_id", JsonUtil.nullable(fileDb.getDocumentId()))
|
||||||
|
.add("create_date", fileDb.getCreateDate().getTime())
|
||||||
|
.add("size", Files.size(DirectoryUtil.getStorageDirectory().resolve(fileDb.getId()))));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ServerException("FileError", "Unable to get the size of " + fileDb.getId(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||||
@ -341,6 +356,17 @@ public class FileResource extends BaseResource {
|
|||||||
// Delete the file
|
// Delete the file
|
||||||
fileDao.delete(file.getId());
|
fileDao.delete(file.getId());
|
||||||
|
|
||||||
|
// Update the user quota
|
||||||
|
UserDao userDao = new UserDao();
|
||||||
|
User user = userDao.getById(principal.getId());
|
||||||
|
java.nio.file.Path storedFile = DirectoryUtil.getStorageDirectory().resolve(id);
|
||||||
|
try {
|
||||||
|
user.setStorageCurrent(user.getStorageCurrent() - Files.size(storedFile));
|
||||||
|
userDao.update(user);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// The file doesn't exists on disk, which is weird, but not fatal
|
||||||
|
}
|
||||||
|
|
||||||
// Raise a new file deleted event
|
// Raise a new file deleted event
|
||||||
FileDeletedAsyncEvent fileDeletedAsyncEvent = new FileDeletedAsyncEvent();
|
FileDeletedAsyncEvent fileDeletedAsyncEvent = new FileDeletedAsyncEvent();
|
||||||
fileDeletedAsyncEvent.setFile(file);
|
fileDeletedAsyncEvent.setFile(file);
|
||||||
@ -396,30 +422,33 @@ public class FileResource extends BaseResource {
|
|||||||
|
|
||||||
|
|
||||||
// Get the stored file
|
// Get the stored file
|
||||||
java.io.File storedfile;
|
java.nio.file.Path storedFile;
|
||||||
String mimeType;
|
String mimeType;
|
||||||
boolean decrypt = false;
|
boolean decrypt = false;
|
||||||
if (size != null) {
|
if (size != null) {
|
||||||
storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), fileId + "_" + size).toFile();
|
storedFile = DirectoryUtil.getStorageDirectory().resolve(fileId + "_" + size);
|
||||||
mimeType = MimeType.IMAGE_JPEG; // Thumbnails are JPEG
|
mimeType = MimeType.IMAGE_JPEG; // Thumbnails are JPEG
|
||||||
decrypt = true; // Thumbnails are encrypted
|
decrypt = true; // Thumbnails are encrypted
|
||||||
if (!storedfile.exists()) {
|
if (!Files.exists(storedFile)) {
|
||||||
storedfile = new java.io.File(getClass().getResource("/image/file.png").getFile());
|
storedFile = Paths.get(getClass().getResource("/image/file.png").getFile());
|
||||||
mimeType = MimeType.IMAGE_PNG;
|
mimeType = MimeType.IMAGE_PNG;
|
||||||
decrypt = false;
|
decrypt = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), fileId).toFile();
|
storedFile = DirectoryUtil.getStorageDirectory().resolve(fileId);
|
||||||
mimeType = file.getMimeType();
|
mimeType = file.getMimeType();
|
||||||
decrypt = true; // Original files are encrypted
|
decrypt = true; // Original files are encrypted
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream the output and decrypt it if necessary
|
// Stream the output and decrypt it if necessary
|
||||||
StreamingOutput stream;
|
StreamingOutput stream;
|
||||||
|
|
||||||
// A file is always encrypted by the creator of it
|
// A file is always encrypted by the creator of it
|
||||||
User user = userDao.getById(file.getUserId());
|
User user = userDao.getById(file.getUserId());
|
||||||
|
|
||||||
|
// Write the decrypted file to the output
|
||||||
try {
|
try {
|
||||||
InputStream fileInputStream = new FileInputStream(storedfile);
|
InputStream fileInputStream = Files.newInputStream(storedFile);
|
||||||
final InputStream responseInputStream = decrypt ?
|
final InputStream responseInputStream = decrypt ?
|
||||||
EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey()) : fileInputStream;
|
EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey()) : fileInputStream;
|
||||||
|
|
||||||
@ -484,8 +513,8 @@ public class FileResource extends BaseResource {
|
|||||||
// Add each file to the ZIP stream
|
// Add each file to the ZIP stream
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (File file : fileList) {
|
for (File file : fileList) {
|
||||||
java.io.File storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId()).toFile();
|
java.nio.file.Path storedfile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||||
InputStream fileInputStream = new FileInputStream(storedfile);
|
InputStream fileInputStream = Files.newInputStream(storedfile);
|
||||||
|
|
||||||
// Add the decrypted file to the ZIP stream
|
// Add the decrypted file to the ZIP stream
|
||||||
// Files are encrypted by the creator of them
|
// Files are encrypted by the creator of them
|
||||||
|
@ -64,7 +64,8 @@ public class UserResource extends BaseResource {
|
|||||||
public Response register(
|
public Response register(
|
||||||
@FormParam("username") String username,
|
@FormParam("username") String username,
|
||||||
@FormParam("password") String password,
|
@FormParam("password") String password,
|
||||||
@FormParam("email") String email) {
|
@FormParam("email") String email,
|
||||||
|
@FormParam("storage_quota") String storageQuotaStr) {
|
||||||
|
|
||||||
if (!authenticate()) {
|
if (!authenticate()) {
|
||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
@ -76,6 +77,7 @@ public class UserResource extends BaseResource {
|
|||||||
ValidationUtil.validateAlphanumeric(username, "username");
|
ValidationUtil.validateAlphanumeric(username, "username");
|
||||||
password = ValidationUtil.validateLength(password, "password", 8, 50);
|
password = ValidationUtil.validateLength(password, "password", 8, 50);
|
||||||
email = ValidationUtil.validateLength(email, "email", 3, 50);
|
email = ValidationUtil.validateLength(email, "email", 3, 50);
|
||||||
|
Long storageQuota = ValidationUtil.validateLong(storageQuotaStr, "storage_quota");
|
||||||
ValidationUtil.validateEmail(email, "email");
|
ValidationUtil.validateEmail(email, "email");
|
||||||
|
|
||||||
// Create the user
|
// Create the user
|
||||||
@ -84,6 +86,8 @@ public class UserResource extends BaseResource {
|
|||||||
user.setUsername(username);
|
user.setUsername(username);
|
||||||
user.setPassword(password);
|
user.setPassword(password);
|
||||||
user.setEmail(email);
|
user.setEmail(email);
|
||||||
|
user.setStorageQuota(storageQuota);
|
||||||
|
user.setStorageCurrent(0l);
|
||||||
try {
|
try {
|
||||||
user.setPrivateKey(EncryptionUtil.generatePrivateKey());
|
user.setPrivateKey(EncryptionUtil.generatePrivateKey());
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
@ -135,9 +139,9 @@ public class UserResource extends BaseResource {
|
|||||||
if (email != null) {
|
if (email != null) {
|
||||||
user.setEmail(email);
|
user.setEmail(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
user = userDao.update(user);
|
user = userDao.update(user);
|
||||||
|
|
||||||
|
// Change the password
|
||||||
if (StringUtils.isNotBlank(password)) {
|
if (StringUtils.isNotBlank(password)) {
|
||||||
user.setPassword(password);
|
user.setPassword(password);
|
||||||
userDao.updatePassword(user);
|
userDao.updatePassword(user);
|
||||||
@ -162,7 +166,8 @@ public class UserResource extends BaseResource {
|
|||||||
public Response update(
|
public Response update(
|
||||||
@PathParam("username") String username,
|
@PathParam("username") String username,
|
||||||
@FormParam("password") String password,
|
@FormParam("password") String password,
|
||||||
@FormParam("email") String email) {
|
@FormParam("email") String email,
|
||||||
|
@FormParam("storage_quota") String storageQuotaStr) {
|
||||||
|
|
||||||
if (!authenticate()) {
|
if (!authenticate()) {
|
||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
@ -184,11 +189,14 @@ public class UserResource extends BaseResource {
|
|||||||
if (email != null) {
|
if (email != null) {
|
||||||
user.setEmail(email);
|
user.setEmail(email);
|
||||||
}
|
}
|
||||||
|
if (StringUtils.isNotBlank(storageQuotaStr)) {
|
||||||
|
Long storageQuota = ValidationUtil.validateLong(storageQuotaStr, "storage_quota");
|
||||||
|
user.setStorageQuota(storageQuota);
|
||||||
|
}
|
||||||
user = userDao.update(user);
|
user = userDao.update(user);
|
||||||
|
|
||||||
|
// Change the password
|
||||||
if (StringUtils.isNotBlank(password)) {
|
if (StringUtils.isNotBlank(password)) {
|
||||||
// Change the password
|
|
||||||
user.setPassword(password);
|
user.setPassword(password);
|
||||||
userDao.updatePassword(user);
|
userDao.updatePassword(user);
|
||||||
}
|
}
|
||||||
@ -406,7 +414,9 @@ public class UserResource extends BaseResource {
|
|||||||
UserDao userDao = new UserDao();
|
UserDao userDao = new UserDao();
|
||||||
User user = userDao.getById(principal.getId());
|
User user = userDao.getById(principal.getId());
|
||||||
response.add("username", user.getUsername())
|
response.add("username", user.getUsername())
|
||||||
.add("email", user.getEmail());
|
.add("email", user.getEmail())
|
||||||
|
.add("storage_quota", user.getStorageQuota())
|
||||||
|
.add("storage_current", user.getStorageCurrent());
|
||||||
JsonArrayBuilder baseFunctions = Json.createArrayBuilder();
|
JsonArrayBuilder baseFunctions = Json.createArrayBuilder();
|
||||||
for (String baseFunction : ((UserPrincipal) principal).getBaseFunctionSet()) {
|
for (String baseFunction : ((UserPrincipal) principal).getBaseFunctionSet()) {
|
||||||
baseFunctions.add(baseFunction);
|
baseFunctions.add(baseFunction);
|
||||||
@ -441,7 +451,9 @@ public class UserResource extends BaseResource {
|
|||||||
|
|
||||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||||
.add("username", user.getUsername())
|
.add("username", user.getUsername())
|
||||||
.add("email", user.getEmail());
|
.add("email", user.getEmail())
|
||||||
|
.add("storage_quota", user.getStorageQuota())
|
||||||
|
.add("storage_current", user.getStorageCurrent());
|
||||||
return Response.ok().entity(response.build()).build();
|
return Response.ok().entity(response.build()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -477,6 +489,8 @@ public class UserResource extends BaseResource {
|
|||||||
.add("id", userDto.getId())
|
.add("id", userDto.getId())
|
||||||
.add("username", userDto.getUsername())
|
.add("username", userDto.getUsername())
|
||||||
.add("email", userDto.getEmail())
|
.add("email", userDto.getEmail())
|
||||||
|
.add("storage_quota", userDto.getStorageQuota())
|
||||||
|
.add("storage_current", userDto.getStorageCurrent())
|
||||||
.add("create_date", userDto.getCreateTimestamp()));
|
.add("create_date", userDto.getCreateTimestamp()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Document default controller.
|
* Document default controller.
|
||||||
*/
|
*/
|
||||||
angular.module('docs').controller('DocumentDefault', function($scope, $state, Restangular, $upload) {
|
angular.module('docs').controller('DocumentDefault', function($scope, $rootScope, $state, Restangular, $upload) {
|
||||||
// Load app data
|
// Load app data
|
||||||
Restangular.one('app').get().then(function(data) {
|
Restangular.one('app').get().then(function(data) {
|
||||||
$scope.app = data;
|
$scope.app = data;
|
||||||
@ -73,7 +73,18 @@ angular.module('docs').controller('DocumentDefault', function($scope, $state, Re
|
|||||||
newfile.progress = parseInt(100.0 * e.loaded / e.total);
|
newfile.progress = parseInt(100.0 * e.loaded / e.total);
|
||||||
})
|
})
|
||||||
.success(function (data) {
|
.success(function (data) {
|
||||||
|
// Update local model with real data
|
||||||
newfile.id = data.id;
|
newfile.id = data.id;
|
||||||
|
newfile.size = data.size;
|
||||||
|
|
||||||
|
// New file uploaded, increase used quota
|
||||||
|
$rootScope.userInfo.storage_current += data.size;
|
||||||
|
})
|
||||||
|
.error(function (data) {
|
||||||
|
newfile.status = 'Upload error';
|
||||||
|
if (data.type == 'QuotaReached') {
|
||||||
|
newfile.status += ' - Quota reached';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,7 +101,11 @@ angular.module('docs').controller('DocumentDefault', function($scope, $state, Re
|
|||||||
$scope.deleteFile = function ($event, file) {
|
$scope.deleteFile = function ($event, file) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
|
|
||||||
Restangular.one('file', file.id).remove().then(function () {
|
Restangular.one('file', file.id).remove().then(function() {
|
||||||
|
// File deleted, decrease used quota
|
||||||
|
$rootScope.userInfo.storage_current -= file.size;
|
||||||
|
|
||||||
|
// Update local data
|
||||||
$scope.loadFiles();
|
$scope.loadFiles();
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
|
@ -142,8 +142,8 @@ angular.module('docs').controller('DocumentEdit', function($rootScope, $scope, $
|
|||||||
success: function(response) {
|
success: function(response) {
|
||||||
deferred.resolve(response);
|
deferred.resolve(response);
|
||||||
},
|
},
|
||||||
error: function(jqXHR, textStatus, errorThrown) {
|
error: function(jqXHR) {
|
||||||
deferred.reject(errorThrown);
|
deferred.reject(jqXHR);
|
||||||
},
|
},
|
||||||
xhr: function() {
|
xhr: function() {
|
||||||
var myXhr = $.ajaxSettings.xhr();
|
var myXhr = $.ajaxSettings.xhr();
|
||||||
@ -155,8 +155,23 @@ angular.module('docs').controller('DocumentEdit', function($rootScope, $scope, $
|
|||||||
|
|
||||||
// Update progress bar and title on progress
|
// Update progress bar and title on progress
|
||||||
var startProgress = $scope.fileProgress;
|
var startProgress = $scope.fileProgress;
|
||||||
deferred.promise.then(null, null, function(e) {
|
deferred.promise.then(function(data) {
|
||||||
var done = 1 - (e.total - e.position) / e.total;
|
// New file uploaded, increase used quota
|
||||||
|
$rootScope.userInfo.storage_current += data.size;
|
||||||
|
}, function(data) {
|
||||||
|
// Error uploading a file, we stop here
|
||||||
|
$scope.alerts.unshift({
|
||||||
|
type: 'danger',
|
||||||
|
msg: 'Document successfully ' + ($scope.isEdit() ? 'edited' : 'added') + ' but some files cannot be uploaded'
|
||||||
|
+ (data.responseJSON.type == 'QuotaReached' ? ' - Quota reached' : '')
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset view and title
|
||||||
|
$scope.fileIsUploading = false;
|
||||||
|
$scope.fileProgress = 0;
|
||||||
|
$rootScope.pageTitle = 'Sismics Docs';
|
||||||
|
}, function(e) {
|
||||||
|
var done = 1 - (e.total - e.loaded) / e.total;
|
||||||
var chunk = 100 / _.size($scope.newFiles);
|
var chunk = 100 / _.size($scope.newFiles);
|
||||||
$scope.fileProgress = startProgress + done * chunk;
|
$scope.fileProgress = startProgress + done * chunk;
|
||||||
$rootScope.pageTitle = Math.round($scope.fileProgress) + '% - Sismics Docs';
|
$rootScope.pageTitle = Math.round($scope.fileProgress) + '% - Sismics Docs';
|
||||||
@ -170,7 +185,7 @@ angular.module('docs').controller('DocumentEdit', function($rootScope, $scope, $
|
|||||||
var then = function() {
|
var then = function() {
|
||||||
key++;
|
key++;
|
||||||
if ($scope.newFiles[key]) {
|
if ($scope.newFiles[key]) {
|
||||||
sendFile(key).then(then); // TODO Handle upload error
|
sendFile(key).then(then);
|
||||||
} else {
|
} else {
|
||||||
$scope.fileIsUploading = false;
|
$scope.fileIsUploading = false;
|
||||||
$scope.fileProgress = 0;
|
$scope.fileProgress = 0;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Document view content controller.
|
* Document view content controller.
|
||||||
*/
|
*/
|
||||||
angular.module('docs').controller('DocumentViewContent', function ($scope, $stateParams, Restangular, $dialog, $state, $upload) {
|
angular.module('docs').controller('DocumentViewContent', function ($scope, $rootScope, $stateParams, Restangular, $dialog, $state, $upload) {
|
||||||
/**
|
/**
|
||||||
* Configuration for file sorting.
|
* Configuration for file sorting.
|
||||||
*/
|
*/
|
||||||
@ -55,6 +55,10 @@ angular.module('docs').controller('DocumentViewContent', function ($scope, $stat
|
|||||||
$dialog.messageBox(title, msg, btns, function (result) {
|
$dialog.messageBox(title, msg, btns, function (result) {
|
||||||
if (result == 'ok') {
|
if (result == 'ok') {
|
||||||
Restangular.one('file', file.id).remove().then(function () {
|
Restangular.one('file', file.id).remove().then(function () {
|
||||||
|
// File deleted, decrease used quota
|
||||||
|
$rootScope.userInfo.storage_current -= file.size;
|
||||||
|
|
||||||
|
// Update local data
|
||||||
$scope.loadFiles();
|
$scope.loadFiles();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -109,11 +113,22 @@ angular.module('docs').controller('DocumentViewContent', function ($scope, $stat
|
|||||||
id: $stateParams.id
|
id: $stateParams.id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.progress(function (e) {
|
.progress(function(e) {
|
||||||
newfile.progress = parseInt(100.0 * e.loaded / e.total);
|
newfile.progress = parseInt(100.0 * e.loaded / e.total);
|
||||||
})
|
})
|
||||||
.success(function (data) {
|
.success(function(data) {
|
||||||
|
// Update local model with real data
|
||||||
newfile.id = data.id;
|
newfile.id = data.id;
|
||||||
|
newfile.size = data.size;
|
||||||
|
|
||||||
|
// New file uploaded, increase used quota
|
||||||
|
$rootScope.userInfo.storage_current += data.size;
|
||||||
|
})
|
||||||
|
.error(function (data) {
|
||||||
|
newfile.status = 'Upload error';
|
||||||
|
if (data.type == 'QuotaReached') {
|
||||||
|
newfile.status += ' - Quota reached';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
@ -16,6 +16,7 @@ angular.module('docs').controller('SettingsUserEdit', function($scope, $dialog,
|
|||||||
*/
|
*/
|
||||||
if ($scope.isEdit()) {
|
if ($scope.isEdit()) {
|
||||||
Restangular.one('user', $stateParams.username).get().then(function(data) {
|
Restangular.one('user', $stateParams.username).get().then(function(data) {
|
||||||
|
data.storage_quota /= 1000000;
|
||||||
$scope.user = data;
|
$scope.user = data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -25,15 +26,17 @@ angular.module('docs').controller('SettingsUserEdit', function($scope, $dialog,
|
|||||||
*/
|
*/
|
||||||
$scope.edit = function() {
|
$scope.edit = function() {
|
||||||
var promise = null;
|
var promise = null;
|
||||||
|
var user = angular.copy($scope.user);
|
||||||
|
user.storage_quota *= 1000000;
|
||||||
|
|
||||||
if ($scope.isEdit()) {
|
if ($scope.isEdit()) {
|
||||||
promise = Restangular
|
promise = Restangular
|
||||||
.one('user', $stateParams.username)
|
.one('user', $stateParams.username)
|
||||||
.post('', $scope.user);
|
.post('', user);
|
||||||
} else {
|
} else {
|
||||||
promise = Restangular
|
promise = Restangular
|
||||||
.one('user')
|
.one('user')
|
||||||
.put($scope.user);
|
.put(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.then(function() {
|
promise.then(function() {
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
<!-- endref -->
|
<!-- endref -->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-default" role="navigation" ng-controller="Navigation">
|
<nav class="navbar navbar-inverse" role="navigation" ng-controller="Navigation">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
<button type="button" class="navbar-toggle"
|
<button type="button" class="navbar-toggle"
|
||||||
ng-init="isCollapsed = true"
|
ng-init="isCollapsed = true"
|
||||||
@ -110,7 +110,10 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#/settings/account" title="Logged in as {{ userInfo.username }}">{{ userInfo.username }}</a>
|
<a href="#/settings/account" title="Logged in as {{ userInfo.username }}">
|
||||||
|
<span class="glyphicon glyphicon-user"></span>
|
||||||
|
{{ userInfo.username }}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li ng-class="{active: $uiRoute}" ui-route="/settings.*">
|
<li ng-class="{active: $uiRoute}" ui-route="/settings.*">
|
||||||
<a href="#/settings/account">
|
<a href="#/settings/account">
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<div ng-show="app">
|
<div ng-show="app">
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<h3>Quick upload</h3>
|
<h3><span class="glyphicon glyphicon-cloud-upload"></span> Quick upload</h3>
|
||||||
<div class="row upload-zone" ng-model="dropFiles" ng-file-drop drag-over-class="bg-success"
|
<div class="row upload-zone" ng-model="dropFiles" ng-file-drop drag-over-class="bg-success"
|
||||||
ng-multiple="true" allow-dir="false" accept="image/*,application/pdf,application/zip" ng-file-change="fileDropped($files, $event, $rejectedFiles)">
|
ng-multiple="true" allow-dir="false" accept="image/*,application/pdf,application/zip" ng-file-change="fileDropped($files, $event, $rejectedFiles)">
|
||||||
<div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 text-center" ng-repeat="file in files">
|
<div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 text-center" ng-repeat="file in files">
|
||||||
@ -44,8 +44,8 @@
|
|||||||
|
|
||||||
<div ui-view="file"></div>
|
<div ui-view="file"></div>
|
||||||
|
|
||||||
<div class="well">
|
<div>
|
||||||
<h3>Latest activity</h3>
|
<h3><span class="glyphicon glyphicon-tasks"></span> Latest activity</h3>
|
||||||
<audit-log logs="logs" />
|
<audit-log logs="logs" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -73,6 +73,11 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="pull-left" title="To upgrade your quota, ask your administrator">
|
||||||
|
{{ userInfo.storage_current / 1000000 | number: 0 }}MB ({{ userInfo.storage_current / userInfo.storage_quota * 100 | number: 1 }}%)
|
||||||
|
used on {{ userInfo.storage_quota / 1000000 | number: 0 }}MB
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
{{ totalDocuments }} document{{ totalDocuments > 1 ? 's' : '' }} found
|
{{ totalDocuments }} document{{ totalDocuments > 1 ? 's' : '' }} found
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,6 +37,21 @@
|
|||||||
<span class="help-block" ng-show="editUserForm.email.$error.maxlength">Too long</span>
|
<span class="help-block" ng-show="editUserForm.email.$error.maxlength">Too long</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" ng-class="{ 'has-error': !editUserForm.storage_quota.$valid, success: editUserForm.storage_quota.$valid }">
|
||||||
|
<label class="col-sm-2 control-label" for="inputQuota">Storage quota</label>
|
||||||
|
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<div class="input-group">
|
||||||
|
<input name="storage_quota" type="number" id="inputQuota" required class="form-control"
|
||||||
|
ng-pattern="/[0-9]*/" placeholder="Storage quota (in MB)" ng-model="user.storage_quota"/>
|
||||||
|
<div class="input-group-addon">MB</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<span class="help-block" ng-show="editUserForm.storage_quota.$error.pattern">Number required</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group" ng-class="{ 'has-error': !editUserForm.password.$valid, success: editUserForm.password.$valid }">
|
<div class="form-group" ng-class="{ 'has-error': !editUserForm.password.$valid, success: editUserForm.password.$valid }">
|
||||||
<label class="col-sm-2 control-label" for="inputPassword">Password</label>
|
<label class="col-sm-2 control-label" for="inputPassword">Password</label>
|
||||||
|
|
||||||
|
@ -7,6 +7,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Navbar color
|
||||||
|
.navbar {
|
||||||
|
background-color: #263238;
|
||||||
|
}
|
||||||
|
|
||||||
// Documents list
|
// Documents list
|
||||||
.table-documents {
|
.table-documents {
|
||||||
thead th {
|
thead th {
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=3
|
db.version=4
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=3
|
db.version=4
|
@ -53,6 +53,12 @@ public class TestAppResource extends BaseJerseyTest {
|
|||||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminAuthenticationToken)
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminAuthenticationToken)
|
||||||
.post(Entity.form(new Form()));
|
.post(Entity.form(new Form()));
|
||||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||||
|
|
||||||
|
// Recompute quota
|
||||||
|
response = target().path("/app/batch/recompute_quota").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminAuthenticationToken)
|
||||||
|
.post(Entity.form(new Form()));
|
||||||
|
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.sismics.docs.rest;
|
package com.sismics.docs.rest;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.json.JsonArray;
|
import javax.json.JsonArray;
|
||||||
@ -237,9 +236,9 @@ public class TestDocumentResource extends BaseJerseyTest {
|
|||||||
Assert.assertEquals("ok", json.getString("status"));
|
Assert.assertEquals("ok", json.getString("status"));
|
||||||
|
|
||||||
// Check that the associated files are deleted from FS
|
// Check that the associated files are deleted from FS
|
||||||
java.io.File storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id).toFile();
|
java.io.File storedFile = DirectoryUtil.getStorageDirectory().resolve(file1Id).toFile();
|
||||||
java.io.File webFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_web").toFile();
|
java.io.File webFile = DirectoryUtil.getStorageDirectory().resolve(file1Id + "_web").toFile();
|
||||||
java.io.File thumbnailFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_thumb").toFile();
|
java.io.File thumbnailFile = DirectoryUtil.getStorageDirectory().resolve(file1Id + "_thumb").toFile();
|
||||||
Assert.assertFalse(storedFile.exists());
|
Assert.assertFalse(storedFile.exists());
|
||||||
Assert.assertFalse(webFile.exists());
|
Assert.assertFalse(webFile.exists());
|
||||||
Assert.assertFalse(thumbnailFile.exists());
|
Assert.assertFalse(thumbnailFile.exists());
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.sismics.docs.rest;
|
package com.sismics.docs.rest;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.json.JsonArray;
|
import javax.json.JsonArray;
|
||||||
@ -68,6 +68,7 @@ public class TestFileResource extends BaseJerseyTest {
|
|||||||
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
|
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
|
||||||
file1Id = json.getString("id");
|
file1Id = json.getString("id");
|
||||||
Assert.assertNotNull(file1Id);
|
Assert.assertNotNull(file1Id);
|
||||||
|
Assert.assertEquals(163510l, json.getJsonNumber("size").longValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,8 +122,8 @@ public class TestFileResource extends BaseJerseyTest {
|
|||||||
Assert.assertTrue(fileBytes.length > 0);
|
Assert.assertTrue(fileBytes.length > 0);
|
||||||
|
|
||||||
// Check that the files are not readable directly from FS
|
// Check that the files are not readable directly from FS
|
||||||
java.io.File storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id).toFile();
|
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file1Id);
|
||||||
try (InputStream storedFileInputStream = new BufferedInputStream(new FileInputStream(storedFile))) {
|
try (InputStream storedFileInputStream = new BufferedInputStream(Files.newInputStream(storedFile))) {
|
||||||
Assert.assertNull(MimeTypeUtil.guessMimeType(storedFileInputStream));
|
Assert.assertNull(MimeTypeUtil.guessMimeType(storedFileInputStream));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +136,7 @@ public class TestFileResource extends BaseJerseyTest {
|
|||||||
JsonArray files = json.getJsonArray("files");
|
JsonArray files = json.getJsonArray("files");
|
||||||
Assert.assertEquals(2, files.size());
|
Assert.assertEquals(2, files.size());
|
||||||
Assert.assertEquals(file1Id, files.getJsonObject(0).getString("id"));
|
Assert.assertEquals(file1Id, files.getJsonObject(0).getString("id"));
|
||||||
|
Assert.assertEquals(163510l, files.getJsonObject(0).getJsonNumber("size").longValue());
|
||||||
Assert.assertEquals(file2Id, files.getJsonObject(1).getString("id"));
|
Assert.assertEquals(file2Id, files.getJsonObject(1).getString("id"));
|
||||||
|
|
||||||
// Reorder files
|
// Reorder files
|
||||||
@ -179,12 +181,12 @@ public class TestFileResource extends BaseJerseyTest {
|
|||||||
Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus()));
|
Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus()));
|
||||||
|
|
||||||
// Check that files are deleted from FS
|
// Check that files are deleted from FS
|
||||||
storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id).toFile();
|
storedFile = DirectoryUtil.getStorageDirectory().resolve(file1Id);
|
||||||
java.io.File webFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_web").toFile();
|
Path webFile = DirectoryUtil.getStorageDirectory().resolve(file1Id + "_web");
|
||||||
java.io.File thumbnailFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_thumb").toFile();
|
Path thumbnailFile = DirectoryUtil.getStorageDirectory().resolve(file1Id + "_thumb");
|
||||||
Assert.assertFalse(storedFile.exists());
|
Assert.assertFalse(Files.exists(storedFile));
|
||||||
Assert.assertFalse(webFile.exists());
|
Assert.assertFalse(Files.exists(webFile));
|
||||||
Assert.assertFalse(thumbnailFile.exists());
|
Assert.assertFalse(Files.exists(thumbnailFile));
|
||||||
|
|
||||||
// Get all files from a document
|
// Get all files from a document
|
||||||
json = target().path("/file/list")
|
json = target().path("/file/list")
|
||||||
@ -198,7 +200,7 @@ public class TestFileResource extends BaseJerseyTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOrphanFile() throws Exception {
|
public void testOrphanFile() throws Exception {
|
||||||
// Login file1
|
// Login file2
|
||||||
clientUtil.createUser("file2");
|
clientUtil.createUser("file2");
|
||||||
String file2AuthenticationToken = clientUtil.login("file2");
|
String file2AuthenticationToken = clientUtil.login("file2");
|
||||||
|
|
||||||
@ -280,4 +282,97 @@ public class TestFileResource extends BaseJerseyTest {
|
|||||||
.delete(JsonObject.class);
|
.delete(JsonObject.class);
|
||||||
Assert.assertEquals("ok", json.getString("status"));
|
Assert.assertEquals("ok", json.getString("status"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQuota() throws Exception {
|
||||||
|
// Login file_quota
|
||||||
|
clientUtil.createUser("file_quota");
|
||||||
|
String fileQuotaAuthenticationToken = clientUtil.login("file_quota");
|
||||||
|
|
||||||
|
// Add a file (292641 bytes large)
|
||||||
|
String file1Id = null;
|
||||||
|
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
|
||||||
|
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
|
||||||
|
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
|
||||||
|
JsonObject json = target()
|
||||||
|
.register(MultiPartFeature.class)
|
||||||
|
.path("/file").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||||
|
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
|
||||||
|
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
|
||||||
|
file1Id = json.getString("id");
|
||||||
|
Assert.assertNotNull(file1Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check current quota
|
||||||
|
JsonObject json = target().path("/user").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||||
|
.get(JsonObject.class);
|
||||||
|
Assert.assertEquals(292641l, json.getJsonNumber("storage_current").longValue());
|
||||||
|
|
||||||
|
// Add a file (292641 bytes large)
|
||||||
|
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
|
||||||
|
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
|
||||||
|
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
|
||||||
|
target()
|
||||||
|
.register(MultiPartFeature.class)
|
||||||
|
.path("/file").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||||
|
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
|
||||||
|
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check current quota
|
||||||
|
json = target().path("/user").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||||
|
.get(JsonObject.class);
|
||||||
|
Assert.assertEquals(585282l, json.getJsonNumber("storage_current").longValue());
|
||||||
|
|
||||||
|
// Add a file (292641 bytes large)
|
||||||
|
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
|
||||||
|
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
|
||||||
|
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
|
||||||
|
target()
|
||||||
|
.register(MultiPartFeature.class)
|
||||||
|
.path("/file").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||||
|
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
|
||||||
|
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check current quota
|
||||||
|
json = target().path("/user").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||||
|
.get(JsonObject.class);
|
||||||
|
Assert.assertEquals(877923l, json.getJsonNumber("storage_current").longValue());
|
||||||
|
|
||||||
|
// Add a file (292641 bytes large)
|
||||||
|
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
|
||||||
|
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
|
||||||
|
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
|
||||||
|
Response response = target()
|
||||||
|
.register(MultiPartFeature.class)
|
||||||
|
.path("/file").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||||
|
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
|
||||||
|
MediaType.MULTIPART_FORM_DATA_TYPE));
|
||||||
|
Assert.assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes a file
|
||||||
|
json = target().path("/file/" + file1Id).request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||||
|
.delete(JsonObject.class);
|
||||||
|
Assert.assertEquals("ok", json.getString("status"));
|
||||||
|
|
||||||
|
// Check current quota
|
||||||
|
json = target().path("/user").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||||
|
.get(JsonObject.class);
|
||||||
|
Assert.assertEquals(585282l, json.getJsonNumber("storage_current").longValue());
|
||||||
|
}
|
||||||
}
|
}
|
@ -48,6 +48,13 @@ public class TestUserResource extends BaseJerseyTest {
|
|||||||
.get(JsonObject.class);
|
.get(JsonObject.class);
|
||||||
JsonArray users = json.getJsonArray("users");
|
JsonArray users = json.getJsonArray("users");
|
||||||
Assert.assertTrue(users.size() > 0);
|
Assert.assertTrue(users.size() > 0);
|
||||||
|
JsonObject user = users.getJsonObject(0);
|
||||||
|
Assert.assertNotNull(user.getString("id"));
|
||||||
|
Assert.assertNotNull(user.getString("username"));
|
||||||
|
Assert.assertNotNull(user.getString("email"));
|
||||||
|
Assert.assertNotNull(user.getJsonNumber("storage_quota"));
|
||||||
|
Assert.assertNotNull(user.getJsonNumber("storage_current"));
|
||||||
|
Assert.assertNotNull(user.getJsonNumber("create_date"));
|
||||||
|
|
||||||
// Create a user KO (login length validation)
|
// Create a user KO (login length validation)
|
||||||
Response response = target().path("/user").request()
|
Response response = target().path("/user").request()
|
||||||
@ -55,7 +62,8 @@ public class TestUserResource extends BaseJerseyTest {
|
|||||||
.put(Entity.form(new Form()
|
.put(Entity.form(new Form()
|
||||||
.param("username", " bb ")
|
.param("username", " bb ")
|
||||||
.param("email", "bob@docs.com")
|
.param("email", "bob@docs.com")
|
||||||
.param("password", "12345678")));
|
.param("password", "12345678")
|
||||||
|
.param("storage_quota", "10")));
|
||||||
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
||||||
json = response.readEntity(JsonObject.class);
|
json = response.readEntity(JsonObject.class);
|
||||||
Assert.assertEquals("ValidationError", json.getString("type"));
|
Assert.assertEquals("ValidationError", json.getString("type"));
|
||||||
@ -67,11 +75,25 @@ public class TestUserResource extends BaseJerseyTest {
|
|||||||
.put(Entity.form(new Form()
|
.put(Entity.form(new Form()
|
||||||
.param("username", "bob-")
|
.param("username", "bob-")
|
||||||
.param("email", "bob@docs.com")
|
.param("email", "bob@docs.com")
|
||||||
.param("password", "12345678")));
|
.param("password", "12345678")
|
||||||
|
.param("storage_quota", "10")));
|
||||||
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
||||||
json = response.readEntity(JsonObject.class);
|
json = response.readEntity(JsonObject.class);
|
||||||
Assert.assertEquals("ValidationError", json.getString("type"));
|
Assert.assertEquals("ValidationError", json.getString("type"));
|
||||||
Assert.assertTrue(json.getString("message"), json.getString("message").contains("alphanumeric"));
|
Assert.assertTrue(json.getString("message"), json.getString("message").contains("alphanumeric"));
|
||||||
|
|
||||||
|
// Create a user KO (invalid quota)
|
||||||
|
response = target().path("/user").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminAuthenticationToken)
|
||||||
|
.put(Entity.form(new Form()
|
||||||
|
.param("username", "bob")
|
||||||
|
.param("email", "bob@docs.com")
|
||||||
|
.param("password", "12345678")
|
||||||
|
.param("storage_quota", "nope")));
|
||||||
|
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
||||||
|
json = response.readEntity(JsonObject.class);
|
||||||
|
Assert.assertEquals("ValidationError", json.getString("type"));
|
||||||
|
Assert.assertTrue(json.getString("message"), json.getString("message").contains("number"));
|
||||||
|
|
||||||
// Create a user KO (email format validation)
|
// Create a user KO (email format validation)
|
||||||
response = target().path("/user").request()
|
response = target().path("/user").request()
|
||||||
@ -79,7 +101,8 @@ public class TestUserResource extends BaseJerseyTest {
|
|||||||
.put(Entity.form(new Form()
|
.put(Entity.form(new Form()
|
||||||
.param("username", "bob")
|
.param("username", "bob")
|
||||||
.param("email", "bobdocs.com")
|
.param("email", "bobdocs.com")
|
||||||
.param("password", "12345678")));
|
.param("password", "12345678")
|
||||||
|
.param("storage_quota", "10")));
|
||||||
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
||||||
json = response.readEntity(JsonObject.class);
|
json = response.readEntity(JsonObject.class);
|
||||||
Assert.assertEquals("ValidationError", json.getString("type"));
|
Assert.assertEquals("ValidationError", json.getString("type"));
|
||||||
@ -89,7 +112,8 @@ public class TestUserResource extends BaseJerseyTest {
|
|||||||
Form form = new Form()
|
Form form = new Form()
|
||||||
.param("username", " bob ")
|
.param("username", " bob ")
|
||||||
.param("email", " bob@docs.com ")
|
.param("email", " bob@docs.com ")
|
||||||
.param("password", " 12345678 ");
|
.param("password", " 12345678 ")
|
||||||
|
.param("storage_quota", "10");
|
||||||
json = target().path("/user").request()
|
json = target().path("/user").request()
|
||||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminAuthenticationToken)
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminAuthenticationToken)
|
||||||
.put(Entity.form(form), JsonObject.class);
|
.put(Entity.form(form), JsonObject.class);
|
||||||
@ -154,6 +178,8 @@ public class TestUserResource extends BaseJerseyTest {
|
|||||||
.get(JsonObject.class);
|
.get(JsonObject.class);
|
||||||
Assert.assertEquals("alice@docs.com", json.getString("email"));
|
Assert.assertEquals("alice@docs.com", json.getString("email"));
|
||||||
Assert.assertFalse(json.getBoolean("is_default_password"));
|
Assert.assertFalse(json.getBoolean("is_default_password"));
|
||||||
|
Assert.assertEquals(0l, json.getJsonNumber("storage_current").longValue());
|
||||||
|
Assert.assertEquals(1000000l, json.getJsonNumber("storage_quota").longValue());
|
||||||
|
|
||||||
// Check bob user information
|
// Check bob user information
|
||||||
json = target().path("/user").request()
|
json = target().path("/user").request()
|
||||||
@ -219,6 +245,8 @@ public class TestUserResource extends BaseJerseyTest {
|
|||||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminAuthenticationToken)
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminAuthenticationToken)
|
||||||
.get(JsonObject.class);
|
.get(JsonObject.class);
|
||||||
Assert.assertTrue(json.getBoolean("is_default_password"));
|
Assert.assertTrue(json.getBoolean("is_default_password"));
|
||||||
|
Assert.assertEquals(0l, json.getJsonNumber("storage_current").longValue());
|
||||||
|
Assert.assertEquals(10000000000l, json.getJsonNumber("storage_quota").longValue());
|
||||||
|
|
||||||
// User admin updates his information
|
// User admin updates his information
|
||||||
json = target().path("/user").request()
|
json = target().path("/user").request()
|
||||||
|
Loading…
Reference in New Issue
Block a user