diff --git a/README.md b/README.md
index 15ebe71a..9caba2a6 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,7 @@ Features
- Tag system with relations
- Multi-users ACL system
- Audit log
+- Comments
- Document sharing by URL
- RESTful Web API
- Fully featured Android client
diff --git a/docs-android/app/app.iml b/docs-android/app/app.iml
index afd2300c..704ef345 100644
--- a/docs-android/app/app.iml
+++ b/docs-android/app/app.iml
@@ -71,12 +71,11 @@
-
+
-
+
-
@@ -84,17 +83,23 @@
-
-
-
+
+
+
+
+
-
+
+
+
+
+
@@ -112,12 +117,12 @@
-
+
\ No newline at end of file
diff --git a/docs-android/app/build.gradle b/docs-android/app/build.gradle
index e162b17a..44f07f11 100644
--- a/docs-android/app/build.gradle
+++ b/docs-android/app/build.gradle
@@ -1,20 +1,20 @@
buildscript {
repositories {
- mavenCentral()
+ jcenter()
}
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'
repositories {
- mavenCentral()
+ jcenter()
}
android {
compileSdkVersion 22
- buildToolsVersion "23.0.2"
+ buildToolsVersion '23.0.2'
defaultConfig {
minSdkVersion 14
@@ -54,6 +54,6 @@ dependencies {
compile 'com.android.support:recyclerview-v7:22.+'
compile 'com.loopj.android:android-async-http:1.4.6'
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'
}
diff --git a/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java b/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java
index 340abdbf..6d636c78 100644
--- a/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java
+++ b/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java
@@ -20,7 +20,6 @@ public class MainApplication extends Application {
JSONObject json = PreferenceUtil.getCachedJson(getApplicationContext(), PreferenceUtil.PREF_CACHED_USER_INFO_JSON);
ApplicationContext.getInstance().setUserInfo(getApplicationContext(), json);
- // TODO google docs app: right drawer with all actions, with acls, with deep metadatas
// TODO Provide documents to intent action get content
super.onCreate();
@@ -28,6 +27,7 @@ public class MainApplication extends Application {
@Override
public void onLowMemory() {
+ super.onLowMemory();
BitmapAjaxCallback.clearCache();
}
}
diff --git a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java
index ea2e8cbe..d6bc8f12 100644
--- a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java
+++ b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java
@@ -19,11 +19,15 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.text.format.DateFormat;
+import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.widget.AdapterView;
import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
@@ -31,7 +35,10 @@ import android.widget.Toast;
import com.sismics.docs.R;
import com.sismics.docs.adapter.AclListAdapter;
+import com.sismics.docs.adapter.CommentListAdapter;
import com.sismics.docs.adapter.FilePagerAdapter;
+import com.sismics.docs.event.CommentAddEvent;
+import com.sismics.docs.event.CommentDeleteEvent;
import com.sismics.docs.event.DocumentDeleteEvent;
import com.sismics.docs.event.DocumentEditEvent;
import com.sismics.docs.event.DocumentFullscreenEvent;
@@ -40,6 +47,7 @@ import com.sismics.docs.event.FileDeleteEvent;
import com.sismics.docs.fragment.DocShareFragment;
import com.sismics.docs.listener.JsonHttpResponseHandler;
import com.sismics.docs.model.application.ApplicationContext;
+import com.sismics.docs.resource.CommentResource;
import com.sismics.docs.resource.DocumentResource;
import com.sismics.docs.resource.FileResource;
import com.sismics.docs.service.FileUploadService;
@@ -83,6 +91,11 @@ public class DocumentViewActivity extends AppCompatActivity {
*/
private FilePagerAdapter filePagerAdapter;
+ /**
+ * Comment list adapter.
+ */
+ private CommentListAdapter commentListAdapter;
+
/**
* 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
updateFiles();
@@ -268,6 +314,15 @@ public class DocumentViewActivity extends AppCompatActivity {
}
return true;
+ case R.id.comments:
+ drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
+ drawerLayout.closeDrawer(GravityCompat.START);
+ } else {
+ drawerLayout.openDrawer(GravityCompat.START);
+ }
+ return true;
+
case R.id.download_file:
downloadCurrentFile();
return true;
@@ -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
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
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.
*/
diff --git a/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java b/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java
new file mode 100644
index 00000000..3a11585c
--- /dev/null
+++ b/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java
@@ -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 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;
+ }
+ }
+ }
+}
diff --git a/docs-android/app/src/main/java/com/sismics/docs/event/CommentAddEvent.java b/docs-android/app/src/main/java/com/sismics/docs/event/CommentAddEvent.java
new file mode 100644
index 00000000..374fda6d
--- /dev/null
+++ b/docs-android/app/src/main/java/com/sismics/docs/event/CommentAddEvent.java
@@ -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;
+ }
+}
diff --git a/docs-android/app/src/main/java/com/sismics/docs/event/CommentDeleteEvent.java b/docs-android/app/src/main/java/com/sismics/docs/event/CommentDeleteEvent.java
new file mode 100644
index 00000000..4d25863d
--- /dev/null
+++ b/docs-android/app/src/main/java/com/sismics/docs/event/CommentDeleteEvent.java
@@ -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;
+ }
+}
diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java b/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java
new file mode 100644
index 00000000..6e545956
--- /dev/null
+++ b/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java
@@ -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);
+ }
+}
diff --git a/docs-android/app/src/main/res/drawable-xhdpi/ic_comment_black_24dp.png b/docs-android/app/src/main/res/drawable-xhdpi/ic_comment_black_24dp.png
new file mode 100644
index 00000000..412de9b0
Binary files /dev/null and b/docs-android/app/src/main/res/drawable-xhdpi/ic_comment_black_24dp.png differ
diff --git a/docs-android/app/src/main/res/drawable-xhdpi/ic_send_grey600_24dp.png b/docs-android/app/src/main/res/drawable-xhdpi/ic_send_grey600_24dp.png
new file mode 100644
index 00000000..e1380462
Binary files /dev/null and b/docs-android/app/src/main/res/drawable-xhdpi/ic_send_grey600_24dp.png differ
diff --git a/docs-android/app/src/main/res/drawable-xxhdpi/ic_comment_black_24dp.png b/docs-android/app/src/main/res/drawable-xxhdpi/ic_comment_black_24dp.png
new file mode 100644
index 00000000..382ee7a1
Binary files /dev/null and b/docs-android/app/src/main/res/drawable-xxhdpi/ic_comment_black_24dp.png differ
diff --git a/docs-android/app/src/main/res/drawable-xxhdpi/ic_send_grey600_24dp.png b/docs-android/app/src/main/res/drawable-xxhdpi/ic_send_grey600_24dp.png
new file mode 100644
index 00000000..2c7a8026
Binary files /dev/null and b/docs-android/app/src/main/res/drawable-xxhdpi/ic_send_grey600_24dp.png differ
diff --git a/docs-android/app/src/main/res/layout/comment_list_item.xml b/docs-android/app/src/main/res/layout/comment_list_item.xml
new file mode 100644
index 00000000..eb444391
--- /dev/null
+++ b/docs-android/app/src/main/res/layout/comment_list_item.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs-android/app/src/main/res/layout/document_view_activity.xml b/docs-android/app/src/main/res/layout/document_view_activity.xml
index 19744f59..a0609e89 100644
--- a/docs-android/app/src/main/res/layout/document_view_activity.xml
+++ b/docs-android/app/src/main/res/layout/document_view_activity.xml
@@ -37,6 +37,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
- All languages
Toggle informations
Who can access
+ Comments
+ No comments
+ Error loading comments
+ Send
+ Add a comment
+ Error adding a comment
+ Adding a comment
+ Delete comment
+ Deleting comment
+ Error deleting comment
diff --git a/docs-android/gradle/wrapper/gradle-wrapper.properties b/docs-android/gradle/wrapper/gradle-wrapper.properties
index 695bbdaa..476556cf 100644
--- a/docs-android/gradle/wrapper/gradle-wrapper.properties
+++ b/docs-android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Nov 26 21:58:48 CET 2014
+#Mon Nov 23 20:12:30 CET 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/docs-core/src/dev/resources/db/update/dbupdate-000-1.sql b/docs-core/src/dev/resources/db/update/dbupdate-000-1.sql
index e69de29b..afec483c 100644
--- a/docs-core/src/dev/resources/db/update/dbupdate-000-1.sql
+++ b/docs-core/src/dev/resources/db/update/dbupdate-000-1.sql
@@ -0,0 +1 @@
+update T_CONFIG set CFG_VALUE_C = 'RAM' where CFG_ID_C = 'LUCENE_DIRECTORY_STORAGE';
\ No newline at end of file
diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java
index 3a6c0154..a0d20c1c 100644
--- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java
+++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java
@@ -101,6 +101,8 @@ public class UserDao {
// Update the user
userFromDb.setEmail(user.getEmail());
+ userFromDb.setStorageQuota(user.getStorageQuota());
+ userFromDb.setStorageCurrent(user.getStorageCurrent());
// Create audit log
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE);
@@ -226,7 +228,7 @@ public class UserDao {
Map parameterMap = new HashMap();
List criteriaList = new ArrayList();
- StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3");
+ StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3, u.USE_STORAGECURRENT_N as c4, u.USE_STORAGEQUOTA_N as c5");
sb.append(" from T_USER u ");
// Add search criterias
@@ -255,6 +257,8 @@ public class UserDao {
userDto.setUsername((String) o[i++]);
userDto.setEmail((String) o[i++]);
userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
+ userDto.setStorageCurrent(((Number) o[i++]).longValue());
+ userDto.setStorageQuota(((Number) o[i++]).longValue());
userDtoList.add(userDto);
}
paginatedList.setResultList(userDtoList);
diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/UserDto.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/UserDto.java
index 956d0c90..2dda296e 100644
--- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/UserDto.java
+++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/UserDto.java
@@ -1,5 +1,6 @@
package com.sismics.docs.core.dao.jpa.dto;
+
/**
* User DTO.
*
@@ -26,6 +27,16 @@ public class UserDto {
*/
private Long createTimestamp;
+ /**
+ * Storage quota.
+ */
+ private Long storageQuota;
+
+ /**
+ * Storage current usage.
+ */
+ private Long storageCurrent;
+
/**
* Getter of id.
*
@@ -88,6 +99,22 @@ public class UserDto {
public Long getCreateTimestamp() {
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.
diff --git a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/User.java b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/User.java
index eb51877b..5476a4ab 100644
--- a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/User.java
+++ b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/User.java
@@ -54,6 +54,18 @@ public class User implements Loggable {
@Column(name = "USE_EMAIL_C", nullable = false, length = 100)
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.
*/
@@ -66,149 +78,87 @@ public class User implements Loggable {
@Column(name = "USE_DELETEDATE_D")
private Date deleteDate;
- /**
- * Getter of id.
- *
- * @return id
- */
public String getId() {
return id;
}
- /**
- * Setter of id.
- *
- * @param id id
- */
public void setId(String id) {
this.id = id;
}
- /**
- * Getter of roleId.
- *
- * @return roleId
- */
public String getRoleId() {
return roleId;
}
- /**
- * Setter of roleId.
- *
- * @param roleId roleId
- */
public void setRoleId(String roleId) {
this.roleId = roleId;
}
- /**
- * Getter of username.
- *
- * @return username
- */
public String getUsername() {
return username;
}
- /**
- * Setter of username.
- *
- * @param username username
- */
public void setUsername(String username) {
this.username = username;
}
- /**
- * Getter of password.
- *
- * @return password
- */
public String getPassword() {
return password;
}
- /**
- * Setter of password.
- *
- * @param password password
- */
public void setPassword(String password) {
this.password = password;
}
- /**
- * Getter of email.
- *
- * @return email
- */
public String getEmail() {
return email;
}
- /**
- * Setter of email.
- *
- * @param email email
- */
public void setEmail(String email) {
this.email = email;
}
- /**
- * Getter of createDate.
- *
- * @return createDate
- */
public Date getCreateDate() {
return createDate;
}
- /**
- * Setter of createDate.
- *
- * @param createDate createDate
- */
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
- /**
- * Getter of deleteDate.
- *
- * @return deleteDate
- */
@Override
public Date getDeleteDate() {
return deleteDate;
}
- /**
- * Setter of deleteDate.
- *
- * @param deleteDate deleteDate
- */
public void setDeleteDate(Date deleteDate) {
this.deleteDate = deleteDate;
}
- /**
- * Getter de privateKey.
- * @return privateKey
- */
public String getPrivateKey() {
return privateKey;
}
- /**
- * Setter de privateKey.
- * @param privateKey privateKey
- */
public void setPrivateKey(String 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
public String toString() {
return MoreObjects.toStringHelper(this)
diff --git a/docs-core/src/main/java/com/sismics/docs/core/service/IndexingService.java b/docs-core/src/main/java/com/sismics/docs/core/service/IndexingService.java
index d5988d3c..3efd055b 100644
--- a/docs-core/src/main/java/com/sismics/docs/core/service/IndexingService.java
+++ b/docs-core/src/main/java/com/sismics/docs/core/service/IndexingService.java
@@ -1,7 +1,7 @@
package com.sismics.docs.core.service;
-import java.io.File;
import java.io.IOException;
+import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import org.apache.lucene.index.DirectoryReader;
@@ -56,10 +56,10 @@ public class IndexingService extends AbstractScheduledService {
directory = new RAMDirectory();
log.info("Using RAM Lucene storage");
} else if (luceneStorageConfig.equals(Constants.LUCENE_DIRECTORY_STORAGE_FILE)) {
- File luceneDirectory = DirectoryUtil.getLuceneDirectory();
+ Path luceneDirectory = DirectoryUtil.getLuceneDirectory();
log.info("Using file Lucene storage: {}", luceneDirectory);
try {
- directory = new SimpleFSDirectory(luceneDirectory, new SimpleFSLockFactory());
+ directory = new SimpleFSDirectory(luceneDirectory.toFile(), new SimpleFSLockFactory());
} catch (IOException e) {
log.error("Error initializing Lucene index", e);
}
diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/DirectoryUtil.java b/docs-core/src/main/java/com/sismics/docs/core/util/DirectoryUtil.java
index dfbab290..7f947157 100644
--- a/docs-core/src/main/java/com/sismics/docs/core/util/DirectoryUtil.java
+++ b/docs-core/src/main/java/com/sismics/docs/core/util/DirectoryUtil.java
@@ -1,6 +1,9 @@
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;
@@ -17,27 +20,31 @@ public class DirectoryUtil {
*
* @return Base data directory
*/
- public static File getBaseDataDirectory() {
- File baseDataDir = null;
+ public static Path getBaseDataDirectory() {
+ Path baseDataDir = null;
if (StringUtils.isNotBlank(EnvironmentUtil.getDocsHome())) {
// If the docs.home property is set then use it
- baseDataDir = new File(EnvironmentUtil.getDocsHome());
+ baseDataDir = Paths.get(EnvironmentUtil.getDocsHome());
} else if (EnvironmentUtil.isUnitTest()) {
// For unit testing, use a temporary directory
- baseDataDir = new File(System.getProperty("java.io.tmpdir"));
+ baseDataDir = Paths.get(System.getProperty("java.io.tmpdir"));
} else {
// We are in a webapp environment and nothing is specified, use the default directory for this OS
if (EnvironmentUtil.isUnix()) {
- baseDataDir = new File("/var/docs");
+ baseDataDir = Paths.get("/var/docs");
} if (EnvironmentUtil.isWindows()) {
- baseDataDir = new File(EnvironmentUtil.getWindowsAppData() + "\\Sismics\\Docs");
+ baseDataDir = Paths.get(EnvironmentUtil.getWindowsAppData() + "\\Sismics\\Docs");
} 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()) {
- baseDataDir.mkdirs();
+ if (baseDataDir != null && !Files.isDirectory(baseDataDir)) {
+ try {
+ Files.createDirectories(baseDataDir);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
return baseDataDir;
@@ -48,7 +55,7 @@ public class DirectoryUtil {
*
* @return Database directory.
*/
- public static File getDbDirectory() {
+ public static Path getDbDirectory() {
return getDataSubDirectory("db");
}
@@ -57,7 +64,7 @@ public class DirectoryUtil {
*
* @return Lucene indexes directory.
*/
- public static File getLuceneDirectory() {
+ public static Path getLuceneDirectory() {
return getDataSubDirectory("lucene");
}
@@ -66,7 +73,7 @@ public class DirectoryUtil {
*
* @return Storage directory.
*/
- public static File getStorageDirectory() {
+ public static Path getStorageDirectory() {
return getDataSubDirectory("storage");
}
@@ -75,7 +82,7 @@ public class DirectoryUtil {
*
* @return Log directory.
*/
- public static File getLogDirectory() {
+ public static Path getLogDirectory() {
return getDataSubDirectory("log");
}
@@ -84,11 +91,15 @@ public class DirectoryUtil {
*
* @return Subdirectory
*/
- private static File getDataSubDirectory(String subdirectory) {
- File baseDataDir = getBaseDataDirectory();
- File directory = new File(baseDataDir.getPath() + File.separator + subdirectory);
- if (!directory.isDirectory()) {
- directory.mkdirs();
+ private static Path getDataSubDirectory(String subdirectory) {
+ Path baseDataDir = getBaseDataDirectory();
+ Path directory = baseDataDir.resolve(subdirectory);
+ if (!Files.isDirectory(directory)) {
+ try {
+ Files.createDirectories(directory);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
return directory;
}
diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/FileUtil.java b/docs-core/src/main/java/com/sismics/docs/core/util/FileUtil.java
index 40e04e15..d836e94b 100644
--- a/docs-core/src/main/java/com/sismics/docs/core/util/FileUtil.java
+++ b/docs-core/src/main/java/com/sismics/docs/core/util/FileUtil.java
@@ -1,13 +1,11 @@
package com.sismics.docs.core.util;
import java.awt.image.BufferedImage;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
@@ -132,7 +130,7 @@ public class FileUtil {
*/
public static void save(InputStream inputStream, File file, String privateKey) throws Exception {
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);
// Generate file variations
@@ -172,21 +170,15 @@ public class FileUtil {
image.flush();
// Write "web" encrypted image
- java.io.File outputFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_web").toFile();
- OutputStream outputStream = new CipherOutputStream(new FileOutputStream(outputFile), cipher);
- try {
+ Path outputFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_web");
+ try (OutputStream outputStream = new CipherOutputStream(Files.newOutputStream(outputFile), cipher)) {
ImageUtil.writeJpeg(web, outputStream);
- } finally {
- outputStream.close();
}
// Write "thumb" encrypted image
- outputFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_thumb").toFile();
- outputStream = new CipherOutputStream(new FileOutputStream(outputFile), cipher);
- try {
+ outputFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_thumb");
+ try (OutputStream outputStream = new CipherOutputStream(Files.newOutputStream(outputFile), cipher)) {
ImageUtil.writeJpeg(thumbnail, outputStream);
- } finally {
- outputStream.close();
}
}
}
@@ -195,20 +187,21 @@ public class FileUtil {
* Remove a file from the storage filesystem.
*
* @param file File to delete
+ * @throws IOException
*/
- public static void delete(File file) {
- java.io.File storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId()).toFile();
- java.io.File webFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_web").toFile();
- java.io.File thumbnailFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_thumb").toFile();
+ public static void delete(File file) throws IOException {
+ Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
+ Path webFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_web");
+ Path thumbnailFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_thumb");
- if (storedFile.exists()) {
- storedFile.delete();
+ if (Files.exists(storedFile)) {
+ Files.delete(storedFile);
}
- if (webFile.exists()) {
- webFile.delete();
+ if (Files.exists(webFile)) {
+ Files.delete(webFile);
}
- if (thumbnailFile.exists()) {
- thumbnailFile.delete();
+ if (Files.exists(thumbnailFile)) {
+ Files.delete(thumbnailFile);
}
}
}
diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/math/MathUtil.java b/docs-core/src/main/java/com/sismics/docs/core/util/math/MathUtil.java
deleted file mode 100644
index 435edc19..00000000
--- a/docs-core/src/main/java/com/sismics/docs/core/util/math/MathUtil.java
+++ /dev/null
@@ -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();
- }
-}
diff --git a/docs-core/src/main/java/com/sismics/util/jpa/EMF.java b/docs-core/src/main/java/com/sismics/util/jpa/EMF.java
index 7dbc8746..01287b9e 100644
--- a/docs-core/src/main/java/com/sismics/util/jpa/EMF.java
+++ b/docs-core/src/main/java/com/sismics/util/jpa/EMF.java
@@ -1,6 +1,16 @@
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.internal.util.config.ConfigurationHelper;
import org.hibernate.service.ServiceRegistry;
@@ -8,15 +18,7 @@ import org.hibernate.service.ServiceRegistryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.persistence.EntityManagerFactory;
-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;
+import com.sismics.docs.core.util.DirectoryUtil;
/**
* Entity manager factory.
@@ -79,8 +81,8 @@ public final class EMF {
log.info("Configuring EntityManager from environment parameters");
Map