@ -28,6 +28,7 @@ Features
|
||||
- 256-bit AES encryption
|
||||
- Tag system with relations
|
||||
- Multi-users ACL system
|
||||
- Hierarchical groups
|
||||
- Audit log
|
||||
- Comments
|
||||
- Storage quota per user
|
||||
|
@ -77,10 +77,13 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.1.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/23.1.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.1.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.shamanland/fab/0.0.6/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/animated-vector-drawable/23.2.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.2.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/23.2.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/23.2.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.2.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-vector-drawable/23.2.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/it.sephiroth.android.library.easing/android-easing/1.0.3/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/it.sephiroth.android.library.imagezoom/imagezoom/1.0.5/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
@ -92,19 +95,21 @@
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="support-annotations-23.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-23.2.1" 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="design-23.2.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="okhttp-urlconnection-3.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="picasso-2.5.2" level="project" />
|
||||
<orderEntry type="library" exported="" name="recyclerview-v7-23.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-23.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="fab-0.0.6" level="project" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-23.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="animated-vector-drawable-23.2.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="recyclerview-v7-23.2.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-annotations-23.2.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="okhttp-3.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="okio-1.6.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-vector-drawable-23.2.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="picasso2-okhttp3-downloader-1.0.2" level="project" />
|
||||
<orderEntry type="library" exported="" name="tokenautocomplete-1.2.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-23.2.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="eventbus-3.0.0" level="project" />
|
||||
</component>
|
||||
</module>
|
@ -3,7 +3,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.0.0-beta5'
|
||||
classpath 'com.android.tools.build:gradle:2.1.0-alpha3'
|
||||
}
|
||||
}
|
||||
apply plugin: 'com.android.application'
|
||||
@ -50,11 +50,11 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: '*.jar')
|
||||
compile 'com.android.support:appcompat-v7:23.1.1'
|
||||
compile 'com.android.support:recyclerview-v7:23.1.1'
|
||||
compile 'com.android.support:appcompat-v7:23.2.1'
|
||||
compile 'com.android.support:recyclerview-v7:23.2.1'
|
||||
compile 'com.android.support:design:23.2.1'
|
||||
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
|
||||
compile 'org.greenrobot:eventbus:3.0.0'
|
||||
compile 'com.shamanland:fab:0.0.6'
|
||||
compile 'com.squareup.picasso:picasso:2.5.2'
|
||||
compile 'com.squareup.okhttp3:okhttp:3.1.1'
|
||||
compile "com.squareup.okhttp3:okhttp-urlconnection:3.1.1"
|
||||
|
@ -47,6 +47,16 @@
|
||||
android:name=".activity.DocumentEditActivity"
|
||||
android:label="@string/new_document">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.AuditLogActivity"
|
||||
android:label="@string/latest_activity">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.UserProfileActivity">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.GroupProfileActivity">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.SettingsActivity"
|
||||
android:label="@string/settings">
|
||||
|
@ -0,0 +1,121 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.adapter.AuditLogListAdapter;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.AuditLogResource;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Audit log activity.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class AuditLogActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Check if logged in
|
||||
if (!ApplicationContext.getInstance().isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle activity context
|
||||
if (getIntent() == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Input document ID (optional)
|
||||
final String documentId = getIntent().getStringExtra("documentId");
|
||||
|
||||
// Setup the activity
|
||||
setContentView(R.layout.auditlog_activity);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
}
|
||||
|
||||
// Configure the swipe refresh layout
|
||||
SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
|
||||
swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
|
||||
android.R.color.holo_green_light,
|
||||
android.R.color.holo_orange_light,
|
||||
android.R.color.holo_red_light);
|
||||
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
refreshView(documentId);
|
||||
}
|
||||
});
|
||||
|
||||
// Navigate to user profile on click
|
||||
final ListView auditLogListView = (ListView) findViewById(R.id.auditLogListView);
|
||||
auditLogListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (auditLogListView.getAdapter() == null) {
|
||||
return;
|
||||
}
|
||||
AuditLogListAdapter adapter = (AuditLogListAdapter) auditLogListView.getAdapter();
|
||||
String username = adapter.getItem(position).optString("username");
|
||||
Intent intent = new Intent(AuditLogActivity.this, UserProfileActivity.class);
|
||||
intent.putExtra("username", username);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
// Get audit log list
|
||||
refreshView(documentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the view.
|
||||
*/
|
||||
private void refreshView(String documentId) {
|
||||
final SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
|
||||
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
final ListView auditLogListView = (ListView) findViewById(R.id.auditLogListView);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
auditLogListView.setVisibility(View.GONE);
|
||||
AuditLogResource.list(this, documentId, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject response) {
|
||||
auditLogListView.setAdapter(new AuditLogListAdapter(response.optJSONArray("logs")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
auditLogListView.setVisibility(View.VISIBLE);
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
@ -154,7 +154,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param document Document in JSON format
|
||||
*/
|
||||
private void refreshDocument(JSONObject document) {
|
||||
private void refreshDocument(final JSONObject document) {
|
||||
this.document = document;
|
||||
|
||||
String title = document.optString("title");
|
||||
@ -249,7 +249,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
DialogFragment dialog = DocExportPdfFragment.newInstance(
|
||||
DocumentViewActivity.this.document.optString("id"), DocumentViewActivity.this.document.optString("title"));
|
||||
document.optString("id"), document.optString("title"));
|
||||
dialog.show(getSupportFragmentManager(), "DocExportPdfFragment");
|
||||
}
|
||||
});
|
||||
@ -259,11 +259,22 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
DialogFragment dialog = DocShareFragment.newInstance(DocumentViewActivity.this.document.optString("id"));
|
||||
DialogFragment dialog = DocShareFragment.newInstance(document.optString("id"));
|
||||
dialog.show(getSupportFragmentManager(), "DocShareFragment");
|
||||
}
|
||||
});
|
||||
|
||||
// Action audit log
|
||||
button = (Button) findViewById(R.id.actionAuditLog);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, AuditLogActivity.class);
|
||||
intent.putExtra("documentId", document.optString("id"));
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
// Button add a comment
|
||||
ImageButton imageButton = (ImageButton) findViewById(R.id.addCommentBtn);
|
||||
imageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@ -300,7 +311,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
// Grab the attached files
|
||||
updateFiles();
|
||||
|
||||
// Grab the full document (used for ACLs and writable status)
|
||||
// Grab the full document (used for ACLs, remaining metadata and writable status)
|
||||
updateDocument();
|
||||
}
|
||||
|
||||
@ -630,6 +641,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
menu.findItem(R.id.delete_file).setVisible(writable);
|
||||
}
|
||||
|
||||
// Action only available if the document is writable
|
||||
findViewById(R.id.actionEditDocument).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
||||
findViewById(R.id.actionUploadFile).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
||||
findViewById(R.id.actionSharing).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
||||
@ -637,7 +649,36 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
|
||||
// ACLs
|
||||
ListView aclListView = (ListView) findViewById(R.id.aclListView);
|
||||
aclListView.setAdapter(new AclListAdapter(document.optJSONArray("acls")));
|
||||
final AclListAdapter aclListAdapter = new AclListAdapter(document.optJSONArray("acls"));
|
||||
aclListView.setAdapter(aclListAdapter);
|
||||
aclListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
AclListAdapter.AclItem acl = aclListAdapter.getItem(position);
|
||||
if (acl.getType().equals("USER")) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, UserProfileActivity.class);
|
||||
intent.putExtra("username", acl.getName());
|
||||
startActivity(intent);
|
||||
} else if (acl.getType().equals("GROUP")) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, GroupProfileActivity.class);
|
||||
intent.putExtra("name", acl.getName());
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Remaining metadata
|
||||
TextView creatorTextView = (TextView) findViewById(R.id.creatorTextView);
|
||||
final String creator = document.optString("creator");
|
||||
creatorTextView.setText(creator);
|
||||
creatorTextView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, UserProfileActivity.class);
|
||||
intent.putExtra("username", creator);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,92 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.UserResource;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Group profile activity.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class GroupProfileActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Check if logged in
|
||||
if (!ApplicationContext.getInstance().isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle activity context
|
||||
if (getIntent() == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Input name
|
||||
final String name = getIntent().getStringExtra("name");
|
||||
if (name == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup the activity
|
||||
setTitle(name);
|
||||
setContentView(R.layout.groupprofile_activity);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
}
|
||||
|
||||
// Get the group and populate the view
|
||||
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
final View layoutView = findViewById(R.id.layout);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
layoutView.setVisibility(View.GONE);
|
||||
UserResource.get(this, name, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject json) {
|
||||
TextView membersTextView = (TextView) findViewById(R.id.membersTextView);
|
||||
JSONArray members = json.optJSONArray("members");
|
||||
String output = "";
|
||||
for (int i = 0; i < members.length(); i++) {
|
||||
output += members.optString(i) + "; ";
|
||||
}
|
||||
membersTextView.setText(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
layoutView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
@ -30,7 +30,6 @@ import org.json.JSONObject;
|
||||
* @author bgamard
|
||||
*/
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
|
||||
/**
|
||||
* User interface.
|
||||
*/
|
||||
|
@ -42,7 +42,6 @@ import org.json.JSONObject;
|
||||
*/
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private ActionBarDrawerToggle drawerToggle;
|
||||
private MenuItem searchItem;
|
||||
private DrawerLayout drawerLayout;
|
||||
@ -72,7 +71,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
// between the sliding drawer and the action bar app icon
|
||||
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
|
||||
R.string.drawer_open, R.string.drawer_close);
|
||||
drawerLayout.setDrawerListener(drawerToggle);
|
||||
drawerLayout.addDrawerListener(drawerToggle);
|
||||
|
||||
// Fill the drawer user info
|
||||
JSONObject userInfo = ApplicationContext.getInstance().getUserInfo();
|
||||
@ -137,6 +136,15 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
});
|
||||
|
||||
// Click on Latest activity
|
||||
View auditLogLayout = findViewById(R.id.auditLogLayout);
|
||||
auditLogLayout.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
startActivity(new Intent(MainActivity.this, AuditLogActivity.class));
|
||||
}
|
||||
});
|
||||
|
||||
handleIntent(getIntent());
|
||||
|
||||
EventBus.getDefault().register(this);
|
||||
|
@ -12,7 +12,6 @@ import com.sismics.docs.fragment.SettingsFragment;
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
@ -0,0 +1,91 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.UserResource;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* User profile activity.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class UserProfileActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Check if logged in
|
||||
if (!ApplicationContext.getInstance().isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle activity context
|
||||
if (getIntent() == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Input username
|
||||
final String username = getIntent().getStringExtra("username");
|
||||
if (username == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup the activity
|
||||
setTitle(username);
|
||||
setContentView(R.layout.userprofile_activity);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
}
|
||||
|
||||
// Get the user and populate the view
|
||||
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
final View layoutView = findViewById(R.id.layout);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
layoutView.setVisibility(View.GONE);
|
||||
UserResource.get(this, username, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject json) {
|
||||
TextView emailTextView = (TextView) findViewById(R.id.emailTextView);
|
||||
emailTextView.setText(json.optString("email"));
|
||||
|
||||
TextView quotaTextView = (TextView) findViewById(R.id.quotaTextView);
|
||||
quotaTextView.setText(getString(R.string.storage_display,
|
||||
Math.round(json.optLong("storage_current") / 1000000),
|
||||
Math.round(json.optLong("storage_quota") / 1000000)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
layoutView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
@ -98,11 +98,19 @@ public class AclListAdapter extends BaseAdapter {
|
||||
* An ACL item in the list.
|
||||
* Permissions are grouped together.
|
||||
*/
|
||||
private static class AclItem {
|
||||
public static class AclItem {
|
||||
private String type;
|
||||
private String name;
|
||||
private List<String> permList = new ArrayList<>();
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (type + name).hashCode();
|
||||
|
@ -0,0 +1,93 @@
|
||||
package com.sismics.docs.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Audit log list adapter.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class AuditLogListAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Shares.
|
||||
*/
|
||||
private List<JSONObject> logList;
|
||||
|
||||
/**
|
||||
* Audit log list adapter.
|
||||
*
|
||||
* @param logs Logs
|
||||
*/
|
||||
public AuditLogListAdapter(JSONArray logs) {
|
||||
this.logList = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < logs.length(); i++) {
|
||||
logList.add(logs.optJSONObject(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return logList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getItem(int position) {
|
||||
return logList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getItem(position).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, final ViewGroup parent) {
|
||||
if (view == null) {
|
||||
LayoutInflater vi = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
view = vi.inflate(R.layout.auditlog_list_item, parent, false);
|
||||
}
|
||||
|
||||
// Build message
|
||||
final JSONObject log = getItem(position);
|
||||
StringBuilder message = new StringBuilder(log.optString("class"));
|
||||
switch (log.optString("type")) {
|
||||
case "CREATE": message.append(" created"); break;
|
||||
case "UPDATE": message.append(" updated"); break;
|
||||
case "DELETE": message.append(" deleted"); break;
|
||||
}
|
||||
switch (log.optString("class")) {
|
||||
case "Document":
|
||||
case "Acl":
|
||||
case "Tag":
|
||||
case "User":
|
||||
case "Group":
|
||||
message.append(" : ");
|
||||
message.append(log.optString("message"));
|
||||
break;
|
||||
}
|
||||
|
||||
// Fill the view
|
||||
TextView usernameTextView = (TextView) view.findViewById(R.id.usernameTextView);
|
||||
TextView messageTextView = (TextView) view.findViewById(R.id.messageTextView);
|
||||
usernameTextView.setText(log.optString("username"));
|
||||
messageTextView.setText(message);
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
@ -55,6 +55,7 @@ public class SearchFragment extends DialogFragment {
|
||||
View view = inflater.inflate(R.layout.search_dialog, null);
|
||||
final EditText searchEditText = (EditText) view.findViewById(R.id.searchEditText);
|
||||
final EditText fulltextEditText = (EditText) view.findViewById(R.id.fulltextEditText);
|
||||
final EditText creatorEditText = (EditText) view.findViewById(R.id.creatorEditText);
|
||||
final CheckBox sharedCheckbox = (CheckBox) view.findViewById(R.id.sharedCheckbox);
|
||||
final Spinner languageSpinner = (Spinner) view.findViewById(R.id.languageSpinner);
|
||||
final DatePickerView beforeDatePicker = (DatePickerView) view.findViewById(R.id.beforeDatePicker);
|
||||
@ -89,6 +90,7 @@ public class SearchFragment extends DialogFragment {
|
||||
// Build the simple criterias
|
||||
SearchQueryBuilder queryBuilder = new SearchQueryBuilder()
|
||||
.simpleSearch(searchEditText.getText().toString())
|
||||
.creator(creatorEditText.getText().toString())
|
||||
.shared(sharedCheckbox.isChecked())
|
||||
.language(((LanguageAdapter.Language) languageSpinner.getSelectedItem()).getId())
|
||||
.before(beforeDatePicker.getDate())
|
||||
|
@ -0,0 +1,38 @@
|
||||
package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
|
||||
/**
|
||||
* Access to /auditlog API.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class AuditLogResource extends BaseResource {
|
||||
/**
|
||||
* GET /auditlog.
|
||||
*
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void list(Context context, String documentId, HttpCallback callback) {
|
||||
HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(getApiUrl(context) + "/auditlog")
|
||||
.newBuilder();
|
||||
if (documentId != null) {
|
||||
httpUrlBuilder.addQueryParameter("document", documentId);
|
||||
}
|
||||
Request request = new Request.Builder()
|
||||
.url(httpUrlBuilder.build())
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
}
|
@ -53,6 +53,23 @@ public class UserResource extends BaseResource {
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /user/username.
|
||||
*
|
||||
* @param context Context
|
||||
* param username Username
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void get(Context context, String username, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/user/" + username))
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /user/logout.
|
||||
|
@ -59,6 +59,21 @@ public class SearchQueryBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a creator criteria.
|
||||
*
|
||||
* @param creator Creator criteria
|
||||
* @return The builder
|
||||
*/
|
||||
public SearchQueryBuilder creator(String creator) {
|
||||
if (isValid(creator)) {
|
||||
query.append(SEARCH_SEPARATOR)
|
||||
.append("by:")
|
||||
.append(creator);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a language criteria.
|
||||
*
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 296 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 359 B |
After Width: | Height: | Size: 1.2 KiB |
30
docs-android/app/src/main/res/layout/auditlog_activity.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v4.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/auditLogListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="singleChoice"
|
||||
android:dividerHeight="0dp"
|
||||
android:visibility="gone">
|
||||
</ListView>
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true" />
|
||||
|
||||
</RelativeLayout>
|
60
docs-android/app/src/main/res/layout/auditlog_list_item.xml
Normal file
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/assignImageView"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_assignment_grey600_48dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/usernameTextView"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@+id/assignImageView"
|
||||
android:layout_toEndOf="@+id/assignImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:textColor="#212121"
|
||||
android:text="admin"
|
||||
android:textSize="16sp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/messageTextView"
|
||||
android:layout_below="@+id/usernameTextView"
|
||||
android:layout_toRightOf="@+id/assignImageView"
|
||||
android:layout_toEndOf="@+id/assignImageView"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:textColor="#777777"
|
||||
android:text="Document created : test doc 1"
|
||||
android:textSize="16sp"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="2014-12-02"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:textColor="#777777"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</RelativeLayout>
|
@ -37,7 +37,7 @@
|
||||
android:textSize="16sp"
|
||||
android:layout_centerInParent="true"/>
|
||||
|
||||
<com.shamanland.fab.FloatingActionButton
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/addDocumentButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -48,6 +48,6 @@
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:src="@drawable/ic_add_white_24dp"
|
||||
app:floatingActionButtonColor="#263238"/>
|
||||
app:fabSize="normal"/>
|
||||
|
||||
</RelativeLayout>
|
@ -13,6 +13,7 @@
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:id="@+id/folderImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -22,7 +23,9 @@
|
||||
android:id="@+id/titleTextView"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@+id/folderImageView"
|
||||
android:layout_toEndOf="@+id/folderImageView"
|
||||
android:layout_toLeftOf="@+id/dateTextView"
|
||||
android:layout_toStartOf="@+id/dateTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"
|
||||
|
@ -52,14 +52,14 @@
|
||||
<!-- Comments -->
|
||||
|
||||
<TextView
|
||||
android:drawableStart="@drawable/ic_comment_black_24dp"
|
||||
android:drawableLeft="@drawable/ic_comment_black_24dp"
|
||||
android:drawableStart="@drawable/ic_comment_grey600_24dp"
|
||||
android:drawableLeft="@drawable/ic_comment_grey600_24dp"
|
||||
android:drawablePadding="6dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/primary_text_default_material_light"
|
||||
android:textColor="#de000000"
|
||||
android:text="@string/comments"
|
||||
android:layout_margin="12dp"/>
|
||||
|
||||
@ -173,7 +173,7 @@
|
||||
android:drawableTop="@drawable/ic_create_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/edit_document"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
@ -184,7 +184,7 @@
|
||||
android:drawableTop="@drawable/ic_file_upload_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/upload_file"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
@ -195,7 +195,7 @@
|
||||
android:drawableTop="@drawable/ic_file_download_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/download_document"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
@ -214,9 +214,9 @@
|
||||
android:drawableTop="@drawable/ic_description_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/export_pdf"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionSharing"
|
||||
@ -225,9 +225,20 @@
|
||||
android:drawableTop="@drawable/ic_share_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/share"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionAuditLog"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_assignment_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/activity"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionDelete"
|
||||
@ -236,9 +247,9 @@
|
||||
android:drawableTop="@drawable/ic_delete_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/delete_document"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -280,12 +291,33 @@
|
||||
android:layout_alignParentTop="true"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/creatorLabel"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_below="@+id/createdDateLabel"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/creator"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/creatorTextView"
|
||||
android:layout_toRightOf="@id/creatorLabel"
|
||||
android:layout_toEndOf="@id/creatorLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_below="@+id/createdDateTextView"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagTextView"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/createdDateLabel"
|
||||
android:layout_below="@id/creatorLabel"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:maxLines="1"
|
||||
@ -333,7 +365,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/primary_text_default_material_light"
|
||||
android:textColor="#de000000"
|
||||
android:text="@string/who_can_access"
|
||||
android:layout_margin="12dp"/>
|
||||
|
||||
|
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/membersTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true" />
|
||||
|
||||
</RelativeLayout>
|
@ -117,6 +117,40 @@
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Audit log -->
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/auditLogLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:clickable="true"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/auditLogImageView"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_assignment_grey600_24dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toRightOf="@+id/auditLogImageView"
|
||||
android:layout_toEndOf="@+id/auditLogImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textColor="#212121"
|
||||
android:text="@string/latest_activity"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Separator -->
|
||||
|
||||
<View
|
||||
|
@ -27,6 +27,15 @@
|
||||
android:textSize="18sp"
|
||||
android:hint="@string/fulltext_search"/>
|
||||
|
||||
<!-- Creator -->
|
||||
<EditText
|
||||
android:id="@+id/creatorEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:textSize="18sp"
|
||||
android:hint="@string/creator"/>
|
||||
|
||||
<!-- Language -->
|
||||
<Spinner
|
||||
android:id="@+id/languageSpinner"
|
||||
|
@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:gravity="end"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/email"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/emailTextView"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="16sp"
|
||||
android:text="user1@sismicsdocs.com"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:gravity="end"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/storage_quota"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/quotaTextView"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="16sp"
|
||||
android:text="35/500 MB"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true" />
|
||||
|
||||
</RelativeLayout>
|
@ -99,6 +99,7 @@
|
||||
<string name="title">Title</string>
|
||||
<string name="simple_search">Simple search</string>
|
||||
<string name="fulltext_search">Fulltext search</string>
|
||||
<string name="creator">Creator</string>
|
||||
<string name="after_date">After date</string>
|
||||
<string name="before_date">Before date</string>
|
||||
<string name="search_tags">Search tags</string>
|
||||
@ -115,7 +116,7 @@
|
||||
<string name="comment_delete">Delete comment</string>
|
||||
<string name="deleting_comment">Deleting comment</string>
|
||||
<string name="error_deleting_comment">Error deleting comment</string>
|
||||
<string name="export_pdf">Export PDF</string>
|
||||
<string name="export_pdf">PDF</string>
|
||||
<string name="download">Download</string>
|
||||
<string name="margin">Margin</string>
|
||||
<string name="fit_image_to_page">Fit image to page</string>
|
||||
@ -125,5 +126,10 @@
|
||||
<string name="download_file_title">Sismics Docs file export</string>
|
||||
<string name="download_document_title">Sismics Docs document export</string>
|
||||
<string name="download_pdf_title">Sismics Docs PDF export</string>
|
||||
<string name="latest_activity">Latest activity</string>
|
||||
<string name="activity">Activity</string>
|
||||
<string name="email">E-mail</string>
|
||||
<string name="storage_quota">Storage quota</string>
|
||||
<string name="storage_display">%1$d/%2$d MB</string>
|
||||
|
||||
</resources>
|
||||
|
@ -68,10 +68,12 @@ public class AclDao {
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<AclDto> getBySourceId(String sourceId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select a.ACL_ID_C, a.ACL_PERM_C, a.ACL_TARGETID_C, u.USE_USERNAME_C, s.SHA_NAME_C");
|
||||
StringBuilder sb = new StringBuilder("select a.ACL_ID_C, a.ACL_PERM_C, a.ACL_TARGETID_C, ");
|
||||
sb.append(" u.USE_USERNAME_C, s.SHA_ID_C, s.SHA_NAME_C, g.GRP_NAME_C ");
|
||||
sb.append(" from T_ACL a ");
|
||||
sb.append(" left join T_USER u on u.USE_ID_C = a.ACL_TARGETID_C ");
|
||||
sb.append(" left join T_SHARE s on s.SHA_ID_C = a.ACL_TARGETID_C ");
|
||||
sb.append(" left join T_GROUP g on g.GRP_ID_C = a.ACL_TARGETID_C ");
|
||||
sb.append(" where a.ACL_DELETEDATE_D is null and a.ACL_SOURCEID_C = :sourceId ");
|
||||
|
||||
// Perform the query
|
||||
@ -88,10 +90,21 @@ public class AclDao {
|
||||
aclDto.setPerm(PermType.valueOf((String) o[i++]));
|
||||
aclDto.setTargetId((String) o[i++]);
|
||||
String userName = (String) o[i++];
|
||||
String shareId = (String) o[i++];
|
||||
String shareName = (String) o[i++];
|
||||
aclDto.setTargetName(userName == null ? shareName : userName);
|
||||
aclDto.setTargetType(userName == null ?
|
||||
AclTargetType.SHARE.name() : AclTargetType.USER.name());
|
||||
String groupName = (String) o[i++];
|
||||
if (userName != null) {
|
||||
aclDto.setTargetName(userName);
|
||||
aclDto.setTargetType(AclTargetType.USER.name());
|
||||
}
|
||||
if (shareId != null) { // Use ID because share name is nullable
|
||||
aclDto.setTargetName(shareName);
|
||||
aclDto.setTargetType(AclTargetType.SHARE.name());
|
||||
}
|
||||
if (groupName != null) {
|
||||
aclDto.setTargetName(groupName);
|
||||
aclDto.setTargetType(AclTargetType.GROUP.name());
|
||||
}
|
||||
aclDtoList.add(aclDto);
|
||||
}
|
||||
return aclDtoList;
|
||||
@ -105,12 +118,12 @@ public class AclDao {
|
||||
* @param targetId ACL target entity ID
|
||||
* @return True if the document is accessible
|
||||
*/
|
||||
public boolean checkPermission(String sourceId, PermType perm, String targetId) {
|
||||
public boolean checkPermission(String sourceId, PermType perm, List<String> targetIdList) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select a from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.deleteDate is null");
|
||||
Query q = em.createQuery("select a from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId in (:targetIdList) and a.deleteDate is null");
|
||||
q.setParameter("sourceId", sourceId);
|
||||
q.setParameter("perm", perm);
|
||||
q.setParameter("targetId", targetId);
|
||||
q.setParameter("targetIdList", targetIdList);
|
||||
|
||||
// We have a matching permission
|
||||
if (q.getResultList().size() > 0) {
|
||||
|
@ -54,9 +54,8 @@ public class AuditLogDao {
|
||||
* @param criteria Search criteria
|
||||
* @param sortCriteria Sort criteria
|
||||
* @return List of audit logs
|
||||
* @throws Exception
|
||||
*/
|
||||
public void findByCriteria(PaginatedList<AuditLogDto> paginatedList, AuditLogCriteria criteria, SortCriteria sortCriteria) throws Exception {
|
||||
public void findByCriteria(PaginatedList<AuditLogDto> paginatedList, AuditLogCriteria criteria, SortCriteria sortCriteria) {
|
||||
Map<String, Object> parameterMap = new HashMap<String, Object>();
|
||||
|
||||
StringBuilder baseQuery = new StringBuilder("select l.LOG_ID_C c0, l.LOG_CREATEDATE_D c1, u.USE_USERNAME_C c2, l.LOG_IDENTITY_C c3, l.LOG_CLASSENTITY_C c4, l.LOG_TYPE_C c5, l.LOG_MESSAGE_C c6 from T_AUDIT_LOG l ");
|
||||
|
@ -57,7 +57,7 @@ public class DocumentDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all documents.
|
||||
* Returns the list of all active documents.
|
||||
*
|
||||
* @return List of documents
|
||||
*/
|
||||
@ -69,7 +69,7 @@ public class DocumentDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all documents from a user.
|
||||
* Returns the list of all active documents from a user.
|
||||
*
|
||||
* @param userId User ID
|
||||
* @return List of documents
|
||||
@ -83,21 +83,29 @@ public class DocumentDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an active document.
|
||||
* Returns an active document with permission checking.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @param perm Permission needed
|
||||
* @param userId User ID
|
||||
* @return Document
|
||||
*/
|
||||
public DocumentDto getDocument(String id) {
|
||||
public DocumentDto getDocument(String id, PermType perm, List<String> targetIdList) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select d.DOC_ID_C, d.DOC_TITLE_C, d.DOC_DESCRIPTION_C, d.DOC_SUBJECT_C, d.DOC_IDENTIFIER_C, d.DOC_PUBLISHER_C, d.DOC_FORMAT_C, d.DOC_SOURCE_C, d.DOC_TYPE_C, d.DOC_COVERAGE_C, d.DOC_RIGHTS_C, d.DOC_CREATEDATE_D, d.DOC_LANGUAGE_C, ");
|
||||
StringBuilder sb = new StringBuilder("select distinct d.DOC_ID_C, d.DOC_TITLE_C, d.DOC_DESCRIPTION_C, d.DOC_SUBJECT_C, d.DOC_IDENTIFIER_C, d.DOC_PUBLISHER_C, d.DOC_FORMAT_C, d.DOC_SOURCE_C, d.DOC_TYPE_C, d.DOC_COVERAGE_C, d.DOC_RIGHTS_C, d.DOC_CREATEDATE_D, d.DOC_LANGUAGE_C, ");
|
||||
sb.append(" (select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null), ");
|
||||
sb.append(" (select count(f.FIL_ID_C) from T_FILE f where f.FIL_DELETEDATE_D is null and f.FIL_IDDOC_C = d.DOC_ID_C), ");
|
||||
sb.append(" u.USE_USERNAME_C ");
|
||||
sb.append(" from T_DOCUMENT d, T_USER u ");
|
||||
sb.append(" where d.DOC_IDUSER_C = u.USE_ID_C and d.DOC_ID_C = :id and d.DOC_DELETEDATE_D is null ");
|
||||
sb.append(" from T_DOCUMENT d ");
|
||||
sb.append(" join T_USER u on d.DOC_IDUSER_C = u.USE_ID_C ");
|
||||
sb.append(" left join T_ACL a on a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_TARGETID_C in (:targetIdList) and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null ");
|
||||
sb.append(" where d.DOC_ID_C = :id and a.ACL_ID_C is not null and d.DOC_DELETEDATE_D is null ");
|
||||
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("id", id);
|
||||
q.setParameter("perm", perm.name());
|
||||
q.setParameter("targetIdList", targetIdList);
|
||||
|
||||
Object[] o = null;
|
||||
try {
|
||||
o = (Object[]) q.getSingleResult();
|
||||
@ -126,30 +134,6 @@ public class DocumentDao {
|
||||
return documentDto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an active document.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @param perm Permission needed
|
||||
* @param userId User ID
|
||||
* @return Document
|
||||
*/
|
||||
public Document getDocument(String id, PermType perm, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select d.* from T_DOCUMENT d ");
|
||||
sb.append(" join T_ACL a on a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_TARGETID_C = :userId and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null ");
|
||||
sb.append(" where d.DOC_ID_C = :id and d.DOC_DELETEDATE_D is null");
|
||||
Query q = em.createNativeQuery(sb.toString(), Document.class);
|
||||
q.setParameter("id", id);
|
||||
q.setParameter("perm", perm.name());
|
||||
q.setParameter("userId", userId);
|
||||
try {
|
||||
return (Document) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a document.
|
||||
*
|
||||
@ -184,20 +168,27 @@ public class DocumentDao {
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
q = em.createQuery("update Relation r set r.deleteDate = :dateNow where (r.fromDocumentId = :documentId or r.toDocumentId = :documentId) and r.deleteDate is not null");
|
||||
q.setParameter("documentId", id);
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(documentDb, AuditLogType.DELETE, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a document by its ID.
|
||||
* Gets an active document by its ID.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @return Document
|
||||
*/
|
||||
public Document getById(String id) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select d from Document d where d.id = :id and d.deleteDate is null");
|
||||
q.setParameter("id", id);
|
||||
try {
|
||||
return em.find(Document.class, id);
|
||||
return (Document) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
@ -216,16 +207,16 @@ public class DocumentDao {
|
||||
Map<String, Object> parameterMap = new HashMap<String, Object>();
|
||||
List<String> criteriaList = new ArrayList<String>();
|
||||
|
||||
StringBuilder sb = new StringBuilder("select d.DOC_ID_C c0, d.DOC_TITLE_C c1, d.DOC_DESCRIPTION_C c2, d.DOC_CREATEDATE_D c3, d.DOC_LANGUAGE_C c4, ");
|
||||
StringBuilder sb = new StringBuilder("select distinct d.DOC_ID_C c0, d.DOC_TITLE_C c1, d.DOC_DESCRIPTION_C c2, d.DOC_CREATEDATE_D c3, d.DOC_LANGUAGE_C c4, ");
|
||||
sb.append(" (select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null) c5, ");
|
||||
sb.append(" (select count(f.FIL_ID_C) from T_FILE f where f.FIL_DELETEDATE_D is null and f.FIL_IDDOC_C = d.DOC_ID_C) c6 ");
|
||||
sb.append(" from T_DOCUMENT d ");
|
||||
|
||||
// Adds search criteria
|
||||
if (criteria.getUserId() != null) {
|
||||
if (criteria.getTargetIdList() != null) {
|
||||
// Read permission is enough for searching
|
||||
sb.append(" join T_ACL a on a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_TARGETID_C = :userId and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null ");
|
||||
parameterMap.put("userId", criteria.getUserId());
|
||||
sb.append(" join T_ACL a on a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_TARGETID_C in (:targetIdList) and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null ");
|
||||
parameterMap.put("targetIdList", criteria.getTargetIdList());
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(criteria.getSearch()) || !Strings.isNullOrEmpty(criteria.getFullSearch())) {
|
||||
LuceneDao luceneDao = new LuceneDao();
|
||||
|
@ -0,0 +1,283 @@
|
||||
package com.sismics.docs.core.dao.jpa;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.sismics.docs.core.constant.AuditLogType;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.dto.GroupDto;
|
||||
import com.sismics.docs.core.model.jpa.Group;
|
||||
import com.sismics.docs.core.model.jpa.UserGroup;
|
||||
import com.sismics.docs.core.util.AuditLogUtil;
|
||||
import com.sismics.docs.core.util.jpa.QueryParam;
|
||||
import com.sismics.docs.core.util.jpa.QueryUtil;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
/**
|
||||
* Group DAO.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class GroupDao {
|
||||
/**
|
||||
* Returns a group by name.
|
||||
*
|
||||
* @param name Name
|
||||
* @return Group
|
||||
*/
|
||||
public Group getActiveByName(String name) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select g from Group g where g.name = :name and g.deleteDate is null");
|
||||
q.setParameter("name", name);
|
||||
try {
|
||||
return (Group) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group by ID.
|
||||
*
|
||||
* @param id Group ID
|
||||
* @return Group
|
||||
*/
|
||||
public Group getActiveById(String id) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select g from Group g where g.id = :id and g.deleteDate is null");
|
||||
q.setParameter("id", id);
|
||||
try {
|
||||
return (Group) q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new group.
|
||||
*
|
||||
* @param group Group
|
||||
* @param userId User ID
|
||||
* @return New ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public String create(Group group, String userId) {
|
||||
// Create the UUID
|
||||
group.setId(UUID.randomUUID().toString());
|
||||
|
||||
// Create the group
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
em.persist(group);
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(group, AuditLogType.CREATE, userId);
|
||||
|
||||
return group.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a group.
|
||||
*
|
||||
* @param groupId Group ID
|
||||
* @param userId User ID
|
||||
*/
|
||||
public void delete(String groupId, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the group
|
||||
Query q = em.createQuery("select g from Group g where g.id = :id and g.deleteDate is null");
|
||||
q.setParameter("id", groupId);
|
||||
Group groupDb = (Group) q.getSingleResult();
|
||||
|
||||
// Delete the group
|
||||
Date dateNow = new Date();
|
||||
groupDb.setDeleteDate(dateNow);
|
||||
|
||||
// Delete linked data
|
||||
q = em.createQuery("update UserGroup ug set ug.deleteDate = :dateNow where ug.groupId = :groupId and ug.deleteDate is not null");
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.setParameter("groupId", groupId);
|
||||
q.executeUpdate();
|
||||
|
||||
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.targetId = :groupId and a.deleteDate is null");
|
||||
q.setParameter("groupId", groupDb.getId());
|
||||
q.setParameter("dateNow", dateNow);
|
||||
q.executeUpdate();
|
||||
|
||||
q = em.createQuery("update Group g set g.parentId = null where g.parentId = :groupId and g.deleteDate is null");
|
||||
q.setParameter("groupId", groupDb.getId());
|
||||
q.executeUpdate();
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(groupDb, AuditLogType.DELETE, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an user to a group.
|
||||
*
|
||||
* @param group Group
|
||||
* @return New ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public String addMember(UserGroup userGroup) {
|
||||
// Create the UUID
|
||||
userGroup.setId(UUID.randomUUID().toString());
|
||||
|
||||
// Create the user group
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
em.persist(userGroup);
|
||||
|
||||
return userGroup.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an user from a group.
|
||||
*
|
||||
* @param groupId Group ID
|
||||
* @param userId User ID
|
||||
*/
|
||||
public void removeMember(String groupId, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the user group
|
||||
Query q = em.createQuery("select ug from UserGroup ug where ug.groupId = :groupId and ug.userId = :userId and ug.deleteDate is null");
|
||||
q.setParameter("groupId", groupId);
|
||||
q.setParameter("userId", userId);
|
||||
UserGroup userGroupDb = (UserGroup) q.getSingleResult();
|
||||
|
||||
// Delete the user group
|
||||
Date dateNow = new Date();
|
||||
userGroupDb.setDeleteDate(dateNow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all groups.
|
||||
*
|
||||
* @param criteria Search criteria
|
||||
* @param sortCriteria Sort criteria
|
||||
* @return List of groups
|
||||
*/
|
||||
public List<GroupDto> findByCriteria(GroupCriteria criteria, SortCriteria sortCriteria) {
|
||||
Map<String, Object> parameterMap = new HashMap<String, Object>();
|
||||
List<String> criteriaList = new ArrayList<String>();
|
||||
|
||||
StringBuilder sb = new StringBuilder("select g.GRP_ID_C as c0, g.GRP_NAME_C as c1, g.GRP_IDPARENT_C as c2, gp.GRP_NAME_C as c3, g.GRP_IDROLE_C ");
|
||||
if (criteria.getUserId() != null) {
|
||||
sb.append(" , ug.UGP_ID_C ");
|
||||
}
|
||||
sb.append(" from T_GROUP g ");
|
||||
sb.append(" left join T_GROUP gp on g.GRP_IDPARENT_C = gp.GRP_ID_C ");
|
||||
|
||||
// Add search criterias
|
||||
if (criteria.getSearch() != null) {
|
||||
criteriaList.add("lower(g.GRP_NAME_C) like lower(:search)");
|
||||
parameterMap.put("search", "%" + criteria.getSearch() + "%");
|
||||
}
|
||||
if (criteria.getUserId() != null) {
|
||||
// Left join and post-filtering for recursive groups
|
||||
sb.append((criteria.isRecursive() ? " left " : "")
|
||||
+ " join T_USER_GROUP ug on ug.UGP_IDGROUP_C = g.GRP_ID_C and ug.UGP_IDUSER_C = :userId and ug.UGP_DELETEDATE_D is null ");
|
||||
parameterMap.put("userId", criteria.getUserId());
|
||||
}
|
||||
|
||||
criteriaList.add("g.GRP_DELETEDATE_D is null");
|
||||
|
||||
if (!criteriaList.isEmpty()) {
|
||||
sb.append(" where ");
|
||||
sb.append(Joiner.on(" and ").join(criteriaList));
|
||||
}
|
||||
|
||||
// Perform the search
|
||||
QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object[]> l = QueryUtil.getNativeQuery(queryParam).getResultList();
|
||||
|
||||
// Assemble results
|
||||
List<GroupDto> groupDtoList = new ArrayList<>();
|
||||
List<GroupDto> userGroupDtoList = new ArrayList<>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
GroupDto groupDto = new GroupDto()
|
||||
.setId((String) o[i++])
|
||||
.setName((String) o[i++])
|
||||
.setParentId((String) o[i++])
|
||||
.setParentName((String) o[i++])
|
||||
.setRoleId((String) o[i++]);
|
||||
groupDtoList.add(groupDto);
|
||||
if (criteria.getUserId() != null && o[i++] != null) {
|
||||
userGroupDtoList.add(groupDto);
|
||||
}
|
||||
}
|
||||
|
||||
// Post-query filtering for recursive groups
|
||||
if (criteria.getUserId() != null && criteria.isRecursive()) {
|
||||
Set<GroupDto> filteredGroupDtoSet = new HashSet<>();
|
||||
for (GroupDto userGroupDto : userGroupDtoList) {
|
||||
filteredGroupDtoSet.add(userGroupDto); // Direct group
|
||||
findGroupParentHierarchy(filteredGroupDtoSet, groupDtoList, userGroupDto, 0); // Indirect groups
|
||||
}
|
||||
groupDtoList = new ArrayList<>(filteredGroupDtoSet);
|
||||
}
|
||||
|
||||
return groupDtoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively search group's parents.
|
||||
*
|
||||
* @param parentGroupDtoSet Resulting parents
|
||||
* @param groupDtoList All groups
|
||||
* @param userGroupDto Reference group to search from
|
||||
* @param depth Depth
|
||||
*/
|
||||
private void findGroupParentHierarchy(Set<GroupDto> parentGroupDtoSet, List<GroupDto> groupDtoList, GroupDto userGroupDto, int depth) {
|
||||
if (userGroupDto.getParentId() == null || depth == 10) { // Max depth 10 to avoid infinite loop
|
||||
return;
|
||||
}
|
||||
|
||||
for (GroupDto groupDto : groupDtoList) {
|
||||
if (groupDto.getId().equals(userGroupDto.getParentId())) {
|
||||
parentGroupDtoSet.add(groupDto); // Add parent
|
||||
findGroupParentHierarchy(parentGroupDtoSet, groupDtoList, groupDto, depth + 1); // Find parent's parents
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a group.
|
||||
*
|
||||
* @param group Group to update
|
||||
* @param userId User ID
|
||||
* @return Updated group
|
||||
*/
|
||||
public Group update(Group group, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the group
|
||||
Query q = em.createQuery("select g from Group g where g.id = :id and g.deleteDate is null");
|
||||
q.setParameter("id", group.getId());
|
||||
Group groupFromDb = (Group) q.getSingleResult();
|
||||
|
||||
// Update the group
|
||||
groupFromDb.setName(group.getName());
|
||||
groupFromDb.setParentId(group.getParentId());
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(groupFromDb, AuditLogType.UPDATE, userId);
|
||||
|
||||
return groupFromDb;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
package com.sismics.docs.core.dao.jpa;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import com.sismics.docs.core.dao.jpa.dto.RelationDto;
|
||||
import com.sismics.docs.core.model.jpa.Relation;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
/**
|
||||
* Relation DAO.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class RelationDao {
|
||||
/**
|
||||
* Get all relations from/to a document.
|
||||
*
|
||||
* @param documentId Document ID
|
||||
* @return List of relations
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<RelationDto> getByDocumentId(String documentId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select d.DOC_ID_C, d.DOC_TITLE_C, r.REL_IDDOCFROM_C ");
|
||||
sb.append(" from T_RELATION r ");
|
||||
sb.append(" join T_DOCUMENT d on d.DOC_ID_C = r.REL_IDDOCFROM_C and r.REL_IDDOCFROM_C != :documentId or d.DOC_ID_C = r.REL_IDDOCTO_C and r.REL_IDDOCTO_C != :documentId ");
|
||||
sb.append(" where (r.REL_IDDOCFROM_C = :documentId or r.REL_IDDOCTO_C = :documentId) ");
|
||||
sb.append(" and r.REL_DELETEDATE_D is null ");
|
||||
sb.append(" order by d.DOC_TITLE_C ");
|
||||
|
||||
// Perform the query
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("documentId", documentId);
|
||||
List<Object[]> l = q.getResultList();
|
||||
|
||||
// Assemble results
|
||||
List<RelationDto> relationDtoList = new ArrayList<RelationDto>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
RelationDto relationDto = new RelationDto();
|
||||
relationDto.setId((String) o[i++]);
|
||||
relationDto.setTitle((String) o[i++]);
|
||||
String fromDocId = (String) o[i++];
|
||||
relationDto.setSource(documentId.equals(fromDocId));
|
||||
relationDtoList.add(relationDto);
|
||||
}
|
||||
return relationDtoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update relations on a document.
|
||||
*
|
||||
* @param documentId Document ID
|
||||
* @param documentIdSet Set of document ID
|
||||
*/
|
||||
public void updateRelationList(String documentId, Set<String> documentIdSet) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get current relations from this document
|
||||
Query q = em.createQuery("select r from Relation r where r.fromDocumentId = :documentId and r.deleteDate is null");
|
||||
q.setParameter("documentId", documentId);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Relation> relationList = q.getResultList();
|
||||
|
||||
// Deleting relations no longer there
|
||||
for (Relation relation : relationList) {
|
||||
if (!documentIdSet.contains(relation.getToDocumentId())) {
|
||||
relation.setDeleteDate(new Date());
|
||||
}
|
||||
}
|
||||
|
||||
// Adding new relations
|
||||
for (String targetDocId : documentIdSet) {
|
||||
boolean found = false;
|
||||
for (Relation relation : relationList) {
|
||||
if (relation.getToDocumentId().equals(targetDocId)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Relation relation = new Relation();
|
||||
relation.setId(UUID.randomUUID().toString());
|
||||
relation.setFromDocumentId(documentId);
|
||||
relation.setToDocumentId(targetDocId);
|
||||
em.persist(relation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,17 +16,17 @@ public class RoleBaseFunctionDao {
|
||||
/**
|
||||
* Find the set of base functions of a role.
|
||||
*
|
||||
* @param roleId Role ID
|
||||
* @param roleIdSet Set of role ID
|
||||
* @return Set of base functions
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set<String> findByRoleId(String roleId) {
|
||||
public Set<String> findByRoleId(Set<String> roleIdSet) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select rbf.RBF_IDBASEFUNCTION_C from T_ROLE_BASE_FUNCTION rbf, T_ROLE r");
|
||||
sb.append(" where rbf.RBF_IDROLE_C = :roleId and rbf.RBF_DELETEDATE_D is null");
|
||||
sb.append(" where rbf.RBF_IDROLE_C in (:roleIdSet) and rbf.RBF_DELETEDATE_D is null");
|
||||
sb.append(" and r.ROL_ID_C = rbf.RBF_IDROLE_C and r.ROL_DELETEDATE_D is null");
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("roleId", roleId);
|
||||
q.setParameter("roleIdSet", roleIdSet);
|
||||
return Sets.newHashSet(q.getResultList());
|
||||
}
|
||||
}
|
||||
|
@ -20,9 +20,8 @@ import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
import com.sismics.docs.core.util.AuditLogUtil;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||
import com.sismics.docs.core.util.jpa.QueryParam;
|
||||
import com.sismics.docs.core.util.jpa.QueryUtil;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
@ -265,10 +264,11 @@ public class UserDao {
|
||||
/**
|
||||
* Returns the list of all users.
|
||||
*
|
||||
* @param paginatedList List of users (updated by side effects)
|
||||
* @param criteria Search criteria
|
||||
* @param sortCriteria Sort criteria
|
||||
* @return List of users
|
||||
*/
|
||||
public void findByCriteria(PaginatedList<UserDto> paginatedList, UserCriteria criteria, SortCriteria sortCriteria) {
|
||||
public List<UserDto> findByCriteria(UserCriteria criteria, SortCriteria sortCriteria) {
|
||||
Map<String, Object> parameterMap = new HashMap<String, Object>();
|
||||
List<String> criteriaList = new ArrayList<String>();
|
||||
|
||||
@ -281,6 +281,11 @@ public class UserDao {
|
||||
parameterMap.put("search", "%" + criteria.getSearch() + "%");
|
||||
}
|
||||
|
||||
if (criteria.getGroupId() != null) {
|
||||
sb.append(" join T_USER_GROUP ug on ug.UGP_IDUSER_C = u.USE_ID_C and ug.UGP_IDGROUP_C = :groupId and ug.UGP_DELETEDATE_D is null ");
|
||||
parameterMap.put("groupId", criteria.getGroupId());
|
||||
}
|
||||
|
||||
criteriaList.add("u.USE_DELETEDATE_D is null");
|
||||
|
||||
if (!criteriaList.isEmpty()) {
|
||||
@ -289,8 +294,9 @@ public class UserDao {
|
||||
}
|
||||
|
||||
// Perform the search
|
||||
QueryParam queryParam = new QueryParam(sb.toString(), parameterMap);
|
||||
List<Object[]> l = PaginatedLists.executePaginatedQuery(paginatedList, queryParam, sortCriteria);
|
||||
QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object[]> l = QueryUtil.getNativeQuery(queryParam).getResultList();
|
||||
|
||||
// Assemble results
|
||||
List<UserDto> userDtoList = new ArrayList<UserDto>();
|
||||
@ -305,6 +311,6 @@ public class UserDao {
|
||||
userDto.setStorageQuota(((Number) o[i++]).longValue());
|
||||
userDtoList.add(userDto);
|
||||
}
|
||||
paginatedList.setResultList(userDtoList);
|
||||
return userDtoList;
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ public class VocabularyDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all vocabulary entries sharing a single name
|
||||
* Get all vocabulary entries sharing a single name.
|
||||
*
|
||||
* @param name Name
|
||||
* @return Vocabulary entries
|
||||
|
@ -11,9 +11,9 @@ import java.util.List;
|
||||
*/
|
||||
public class DocumentCriteria {
|
||||
/**
|
||||
* User ID.
|
||||
* ACL target ID list.
|
||||
*/
|
||||
private String userId;
|
||||
private List<String> targetIdList;
|
||||
|
||||
/**
|
||||
* Search query.
|
||||
@ -55,12 +55,12 @@ public class DocumentCriteria {
|
||||
*/
|
||||
private String creatorId;
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
public List<String> getTargetIdList() {
|
||||
return targetIdList;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
public void setTargetIdList(List<String> targetIdList) {
|
||||
this.targetIdList = targetIdList;
|
||||
}
|
||||
|
||||
public String getSearch() {
|
||||
|
@ -0,0 +1,50 @@
|
||||
package com.sismics.docs.core.dao.jpa.criteria;
|
||||
|
||||
/**
|
||||
* Group criteria.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class GroupCriteria {
|
||||
/**
|
||||
* Search query.
|
||||
*/
|
||||
private String search;
|
||||
|
||||
/**
|
||||
* User ID.
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* Retrieve user groups recursively.
|
||||
*/
|
||||
private boolean recursive = false;
|
||||
|
||||
public String getSearch() {
|
||||
return search;
|
||||
}
|
||||
|
||||
public GroupCriteria setSearch(String search) {
|
||||
this.search = search;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public GroupCriteria setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isRecursive() {
|
||||
return recursive;
|
||||
}
|
||||
|
||||
public GroupCriteria setRecursive(boolean recursive) {
|
||||
this.recursive = recursive;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package com.sismics.docs.core.dao.jpa.criteria;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* User criteria.
|
||||
*
|
||||
@ -12,6 +10,11 @@ public class UserCriteria {
|
||||
* Search query.
|
||||
*/
|
||||
private String search;
|
||||
|
||||
/**
|
||||
* Group ID.
|
||||
*/
|
||||
private String groupId;
|
||||
|
||||
public String getSearch() {
|
||||
return search;
|
||||
@ -21,4 +24,13 @@ public class UserCriteria {
|
||||
this.search = search;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public UserCriteria setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,88 @@
|
||||
package com.sismics.docs.core.dao.jpa.dto;
|
||||
|
||||
/**
|
||||
* Group DTO.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class GroupDto {
|
||||
/**
|
||||
* Group ID.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Name.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Parent ID.
|
||||
*/
|
||||
private String parentId;
|
||||
|
||||
/**
|
||||
* Parent name.
|
||||
*/
|
||||
private String parentName;
|
||||
|
||||
/**
|
||||
* Role ID.
|
||||
*/
|
||||
private String roleId;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public GroupDto setId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public GroupDto setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public GroupDto setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getParentName() {
|
||||
return parentName;
|
||||
}
|
||||
|
||||
public GroupDto setParentName(String parentName) {
|
||||
this.parentName = parentName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public GroupDto setRoleId(String roleId) {
|
||||
this.roleId = roleId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return id.equals(((GroupDto) obj).getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.sismics.docs.core.dao.jpa.dto;
|
||||
|
||||
/**
|
||||
* Tag DTO.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class RelationDto {
|
||||
/**
|
||||
* Document ID.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Document title.
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* True if the document is the source of the relation.
|
||||
*/
|
||||
private boolean source;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public boolean isSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource(boolean source) {
|
||||
this.source = source;
|
||||
}
|
||||
}
|
@ -26,74 +26,34 @@ public class TagDto {
|
||||
*/
|
||||
private String parentId;
|
||||
|
||||
/**
|
||||
* Getter of id.
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of id.
|
||||
*
|
||||
* @param id id
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of name.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of name.
|
||||
*
|
||||
* @param name name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of color.
|
||||
*
|
||||
* @return the color
|
||||
*/
|
||||
public String getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of color.
|
||||
*
|
||||
* @param color color
|
||||
*/
|
||||
public void setColor(String color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of parentId.
|
||||
*
|
||||
* @return the parentId
|
||||
*/
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of parentId.
|
||||
*
|
||||
* @param color parentId
|
||||
*/
|
||||
public void setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.sismics.docs.core.dao.jpa.dto;
|
||||
|
||||
|
||||
/**
|
||||
* User DTO.
|
||||
*
|
||||
|
@ -1,9 +1,7 @@
|
||||
package com.sismics.docs.core.dao.lucene;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
||||
@ -46,16 +44,14 @@ public class LuceneDao {
|
||||
indexWriter.deleteAll();
|
||||
|
||||
// Add all documents
|
||||
Map<String, Document> documentMap = new HashMap<>();
|
||||
for (Document document : documentList) {
|
||||
org.apache.lucene.document.Document luceneDocument = getDocumentFromDocument(document);
|
||||
indexWriter.addDocument(luceneDocument);
|
||||
documentMap.put(document.getId(), document);
|
||||
}
|
||||
|
||||
// Add all files
|
||||
for (File file : fileList) {
|
||||
org.apache.lucene.document.Document luceneDocument = getDocumentFromFile(file, documentMap.get(file.getDocumentId()));
|
||||
org.apache.lucene.document.Document luceneDocument = getDocumentFromFile(file);
|
||||
indexWriter.addDocument(luceneDocument);
|
||||
}
|
||||
}
|
||||
@ -81,13 +77,12 @@ public class LuceneDao {
|
||||
* Add file to the index.
|
||||
*
|
||||
* @param file File to add
|
||||
* @param document Document linked to the file
|
||||
*/
|
||||
public void createFile(final File file, final Document document) {
|
||||
public void createFile(final File file) {
|
||||
LuceneUtil.handle(new LuceneRunnable() {
|
||||
@Override
|
||||
public void run(IndexWriter indexWriter) throws Exception {
|
||||
org.apache.lucene.document.Document luceneDocument = getDocumentFromFile(file, document);
|
||||
org.apache.lucene.document.Document luceneDocument = getDocumentFromFile(file);
|
||||
indexWriter.addDocument(luceneDocument);
|
||||
}
|
||||
});
|
||||
@ -112,13 +107,12 @@ public class LuceneDao {
|
||||
* Update file index.
|
||||
*
|
||||
* @param file Updated file
|
||||
* @param document Document linked to the file
|
||||
*/
|
||||
public void updateFile(final File file, final Document document) {
|
||||
public void updateFile(final File file) {
|
||||
LuceneUtil.handle(new LuceneRunnable() {
|
||||
@Override
|
||||
public void run(IndexWriter indexWriter) throws Exception {
|
||||
org.apache.lucene.document.Document luceneDocument = getDocumentFromFile(file, document);
|
||||
org.apache.lucene.document.Document luceneDocument = getDocumentFromFile(file);
|
||||
indexWriter.updateDocument(new Term("id", file.getId()), luceneDocument);
|
||||
}
|
||||
});
|
||||
@ -200,7 +194,7 @@ public class LuceneDao {
|
||||
/**
|
||||
* Build Lucene document from database document.
|
||||
*
|
||||
* @param document Document
|
||||
* @param documentDto Document
|
||||
* @return Document
|
||||
*/
|
||||
private org.apache.lucene.document.Document getDocumentFromDocument(Document document) {
|
||||
@ -243,10 +237,9 @@ public class LuceneDao {
|
||||
* Build Lucene document from file.
|
||||
*
|
||||
* @param file File
|
||||
* @param document Document linked to the file
|
||||
* @return Document
|
||||
*/
|
||||
private org.apache.lucene.document.Document getDocumentFromFile(File file, Document document) {
|
||||
private org.apache.lucene.document.Document getDocumentFromFile(File file) {
|
||||
org.apache.lucene.document.Document luceneDocument = new org.apache.lucene.document.Document();
|
||||
luceneDocument.add(new StringField("id", file.getId(), Field.Store.YES));
|
||||
luceneDocument.add(new StringField("doctype", "file", Field.Store.YES));
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.sismics.docs.core.event;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
|
||||
/**
|
||||
* Document deleted event.
|
||||
@ -10,32 +9,22 @@ import com.sismics.docs.core.model.jpa.Document;
|
||||
*/
|
||||
public class DocumentDeletedAsyncEvent extends UserEvent {
|
||||
/**
|
||||
* Created document.
|
||||
* Document ID.
|
||||
*/
|
||||
private Document document;
|
||||
private String documentId;
|
||||
|
||||
/**
|
||||
* Getter of document.
|
||||
*
|
||||
* @return the document
|
||||
*/
|
||||
public Document getDocument() {
|
||||
return document;
|
||||
public String getDocumentId() {
|
||||
return documentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of document.
|
||||
*
|
||||
* @param document document
|
||||
*/
|
||||
public void setDocument(Document document) {
|
||||
this.document = document;
|
||||
public void setDocumentId(String documentId) {
|
||||
this.documentId = documentId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("document", document)
|
||||
.add("documentId", documentId)
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package com.sismics.docs.core.event;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
|
||||
/**
|
||||
* Document updated event.
|
||||
@ -10,32 +9,22 @@ import com.sismics.docs.core.model.jpa.Document;
|
||||
*/
|
||||
public class DocumentUpdatedAsyncEvent extends UserEvent {
|
||||
/**
|
||||
* Created document.
|
||||
* Document ID.
|
||||
*/
|
||||
private Document document;
|
||||
private String documentId;
|
||||
|
||||
/**
|
||||
* Getter of document.
|
||||
*
|
||||
* @return the document
|
||||
*/
|
||||
public Document getDocument() {
|
||||
return document;
|
||||
public String getDocumentId() {
|
||||
return documentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of document.
|
||||
*
|
||||
* @param document document
|
||||
*/
|
||||
public void setDocument(Document document) {
|
||||
this.document = document;
|
||||
public void setDocumentId(String documentId) {
|
||||
this.documentId = documentId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("document", document)
|
||||
.add("documentId", documentId)
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package com.sismics.docs.core.event;
|
||||
import java.io.InputStream;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
|
||||
/**
|
||||
@ -18,9 +17,9 @@ public class FileCreatedAsyncEvent extends UserEvent {
|
||||
private File file;
|
||||
|
||||
/**
|
||||
* Document linked to the file.
|
||||
* Language of the file.
|
||||
*/
|
||||
private Document document;
|
||||
private String language;
|
||||
|
||||
/**
|
||||
* Unencrypted input stream containing the file.
|
||||
@ -42,12 +41,12 @@ public class FileCreatedAsyncEvent extends UserEvent {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public Document getDocument() {
|
||||
return document;
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public void setDocument(Document document) {
|
||||
this.document = document;
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
@ -70,7 +69,7 @@ public class FileCreatedAsyncEvent extends UserEvent {
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("file", file)
|
||||
.add("document", document)
|
||||
.add("language", language)
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -14,20 +14,10 @@ public class FileDeletedAsyncEvent extends UserEvent {
|
||||
*/
|
||||
private File file;
|
||||
|
||||
/**
|
||||
* Getter of file.
|
||||
*
|
||||
* @return the file
|
||||
*/
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of file.
|
||||
*
|
||||
* @param file file
|
||||
*/
|
||||
public void setFile(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
@ -32,6 +32,6 @@ public class DocumentDeletedAsyncListener {
|
||||
|
||||
// Update Lucene index
|
||||
LuceneDao luceneDao = new LuceneDao();
|
||||
luceneDao.deleteDocument(documentDeletedAsyncEvent.getDocument().getId());
|
||||
luceneDao.deleteDocument(documentDeletedAsyncEvent.getDocumentId());
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.sismics.docs.core.dao.jpa.ContributorDao;
|
||||
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||
import com.sismics.docs.core.dao.lucene.LuceneDao;
|
||||
import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent;
|
||||
import com.sismics.docs.core.model.jpa.Contributor;
|
||||
@ -35,12 +36,17 @@ public class DocumentUpdatedAsyncListener {
|
||||
log.info("Document updated event: " + event.toString());
|
||||
}
|
||||
|
||||
// Update contributors list
|
||||
TransactionUtil.handle(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Update Lucene index
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
LuceneDao luceneDao = new LuceneDao();
|
||||
luceneDao.updateDocument(documentDao.getById(event.getDocumentId()));
|
||||
|
||||
// Update contributors list
|
||||
ContributorDao contributorDao = new ContributorDao();
|
||||
List<Contributor> contributorList = contributorDao.findByDocumentId(event.getDocument().getId());
|
||||
List<Contributor> contributorList = contributorDao.findByDocumentId(event.getDocumentId());
|
||||
|
||||
// Check if the user firing this event is not already a contributor
|
||||
for (Contributor contributor : contributorList) {
|
||||
@ -52,14 +58,10 @@ public class DocumentUpdatedAsyncListener {
|
||||
|
||||
// Add a new contributor
|
||||
Contributor contributor = new Contributor();
|
||||
contributor.setDocumentId(event.getDocument().getId());
|
||||
contributor.setDocumentId(event.getDocumentId());
|
||||
contributor.setUserId(event.getUserId());
|
||||
contributorDao.create(contributor);
|
||||
}
|
||||
});
|
||||
|
||||
// Update Lucene index
|
||||
LuceneDao luceneDao = new LuceneDao();
|
||||
luceneDao.updateDocument(event.getDocument());
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public class FileCreatedAsyncListener {
|
||||
|
||||
// Extract text content from the file
|
||||
long startTime = System.currentTimeMillis();
|
||||
final String content = FileUtil.extractContent(fileCreatedAsyncEvent.getDocument(), file,
|
||||
final String content = FileUtil.extractContent(fileCreatedAsyncEvent.getLanguage(), file,
|
||||
fileCreatedAsyncEvent.getInputStream(), fileCreatedAsyncEvent.getPdfInputStream());
|
||||
fileCreatedAsyncEvent.getInputStream().close();
|
||||
if (fileCreatedAsyncEvent.getPdfInputStream() != null) {
|
||||
@ -66,6 +66,6 @@ public class FileCreatedAsyncListener {
|
||||
|
||||
// Update Lucene index
|
||||
LuceneDao luceneDao = new LuceneDao();
|
||||
luceneDao.createFile(fileCreatedAsyncEvent.getFile(), fileCreatedAsyncEvent.getDocument());
|
||||
luceneDao.createFile(fileCreatedAsyncEvent.getFile());
|
||||
}
|
||||
}
|
||||
|
@ -30,20 +30,20 @@ public class Acl implements Loggable {
|
||||
/**
|
||||
* ACL permission.
|
||||
*/
|
||||
@Column(name = "ACL_PERM_C", length = 30)
|
||||
@Column(name = "ACL_PERM_C", length = 30, nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private PermType perm;
|
||||
|
||||
/**
|
||||
* ACL source ID.
|
||||
*/
|
||||
@Column(name = "ACL_SOURCEID_C", length = 36)
|
||||
@Column(name = "ACL_SOURCEID_C", length = 36, nullable = false)
|
||||
private String sourceId;
|
||||
|
||||
/**
|
||||
* ACL target ID.
|
||||
*/
|
||||
@Column(name = "ACL_TARGETID_C", length = 36)
|
||||
@Column(name = "ACL_TARGETID_C", length = 36, nullable = false)
|
||||
private String targetId;
|
||||
|
||||
/**
|
||||
|
@ -13,7 +13,7 @@ import com.google.common.base.MoreObjects;
|
||||
* @author bgamard
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "T_Contributor")
|
||||
@Table(name = "T_CONTRIBUTOR")
|
||||
public class Contributor {
|
||||
/**
|
||||
* Contributor ID.
|
||||
|
@ -33,13 +33,13 @@ public class DocumentTag implements Serializable {
|
||||
/**
|
||||
* Document ID.
|
||||
*/
|
||||
@Column(name = "DOT_IDDOCUMENT_C", length = 36)
|
||||
@Column(name = "DOT_IDDOCUMENT_C", nullable = false, length = 36)
|
||||
private String documentId;
|
||||
|
||||
/**
|
||||
* Tag ID.
|
||||
*/
|
||||
@Column(name = "DOT_IDTAG_C", length = 36)
|
||||
@Column(name = "DOT_IDTAG_C", nullable = false, length = 36)
|
||||
private String tagId;
|
||||
|
||||
/**
|
||||
@ -83,6 +83,7 @@ public class DocumentTag implements Serializable {
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("id", id)
|
||||
.add("documentId", documentId)
|
||||
.add("tagId", tagId)
|
||||
.toString();
|
||||
|
@ -0,0 +1,111 @@
|
||||
package com.sismics.docs.core.model.jpa;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
/**
|
||||
* Group entity.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "T_GROUP")
|
||||
public class Group implements Loggable {
|
||||
/**
|
||||
* Group ID.
|
||||
*/
|
||||
@Id
|
||||
@Column(name = "GRP_ID_C", nullable = false, length = 36)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Vocabulary value.
|
||||
*/
|
||||
@Column(name = "GRP_IDPARENT_C", length = 36)
|
||||
private String parentId;
|
||||
|
||||
/**
|
||||
* Group name.
|
||||
*/
|
||||
@Column(name = "GRP_NAME_C", nullable = false, length = 50)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Role ID.
|
||||
*/
|
||||
@Column(name = "GRP_IDROLE_C", length = 36)
|
||||
private String roleId;
|
||||
|
||||
/**
|
||||
* Deletion date.
|
||||
*/
|
||||
@Column(name = "GRP_DELETEDATE_D")
|
||||
private Date deleteDate;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Group setId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public Group setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Group setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getDeleteDate() {
|
||||
return deleteDate;
|
||||
}
|
||||
|
||||
public Group setDeleteDate(Date deleteDate) {
|
||||
this.deleteDate = deleteDate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public Group setRoleId(String roleId) {
|
||||
this.roleId = roleId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("id", id)
|
||||
.add("roleId", roleId)
|
||||
.add("parentId", parentId)
|
||||
.add("name", name)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toMessage() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package com.sismics.docs.core.model.jpa;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
/**
|
||||
* Relation entity.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "T_RELATION")
|
||||
public class Relation {
|
||||
/**
|
||||
* Relation ID.
|
||||
*/
|
||||
@Id
|
||||
@Column(name = "REL_ID_C", length = 36)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Source document ID.
|
||||
*/
|
||||
@Column(name = "REL_IDDOCFROM_C", length = 36, nullable = false)
|
||||
private String fromDocumentId;
|
||||
|
||||
/**
|
||||
* Destination document ID.
|
||||
*/
|
||||
@Column(name = "REL_IDDOCTO_C", length = 36, nullable = false)
|
||||
private String toDocumentId;
|
||||
|
||||
/**
|
||||
* Deletion date.
|
||||
*/
|
||||
@Column(name = "REL_DELETEDATE_D")
|
||||
private Date deleteDate;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getFromDocumentId() {
|
||||
return fromDocumentId;
|
||||
}
|
||||
|
||||
public void setFromDocumentId(String fromDocumentId) {
|
||||
this.fromDocumentId = fromDocumentId;
|
||||
}
|
||||
|
||||
public String getToDocumentId() {
|
||||
return toDocumentId;
|
||||
}
|
||||
|
||||
public void setToDocumentId(String toDocumentId) {
|
||||
this.toDocumentId = toDocumentId;
|
||||
}
|
||||
|
||||
public Date getDeleteDate() {
|
||||
return deleteDate;
|
||||
}
|
||||
|
||||
public void setDeleteDate(Date deleteDate) {
|
||||
this.deleteDate = deleteDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("id", id)
|
||||
.add("fromDocumentId", fromDocumentId)
|
||||
.add("toDocumentId", toDocumentId)
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package com.sismics.docs.core.model.jpa;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
/**
|
||||
* Link between an user and a group.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "T_USER_GROUP")
|
||||
public class UserGroup implements Serializable {
|
||||
/**
|
||||
* Serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* User group ID.
|
||||
*/
|
||||
@Id
|
||||
@Column(name = "UGP_ID_C", length = 36)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* User ID.
|
||||
*/
|
||||
@Column(name = "UGP_IDUSER_C", nullable = false, length = 36)
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* Group ID.
|
||||
*/
|
||||
@Column(name = "UGP_IDGROUP_C", nullable = false, length = 36)
|
||||
private String groupId;
|
||||
|
||||
/**
|
||||
* Deletion date.
|
||||
*/
|
||||
@Column(name = "UGP_DELETEDATE_D")
|
||||
private Date deleteDate;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public Date getDeleteDate() {
|
||||
return deleteDate;
|
||||
}
|
||||
|
||||
public void setDeleteDate(Date deleteDate) {
|
||||
this.deleteDate = deleteDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("id", id)
|
||||
.add("userId", userId)
|
||||
.add("groupId", groupId)
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -18,7 +18,6 @@ import org.imgscalr.Scalr.Mode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.tess4j.Tesseract;
|
||||
import com.sismics.util.ImageUtil;
|
||||
@ -37,17 +36,17 @@ public class FileUtil {
|
||||
/**
|
||||
* Extract content from a file.
|
||||
*
|
||||
* @param document Document linked to the file
|
||||
* @param language Language to extract
|
||||
* @param file File to extract
|
||||
* @param inputStream Unencrypted input stream
|
||||
* @param pdfInputStream Unencrypted PDF input stream
|
||||
* @return Content extract
|
||||
*/
|
||||
public static String extractContent(Document document, File file, InputStream inputStream, InputStream pdfInputStream) {
|
||||
public static String extractContent(String language, File file, InputStream inputStream, InputStream pdfInputStream) {
|
||||
String content = null;
|
||||
|
||||
if (ImageUtil.isImage(file.getMimeType())) {
|
||||
content = ocrFile(inputStream, document);
|
||||
content = ocrFile(inputStream, language);
|
||||
} else if (pdfInputStream != null) {
|
||||
content = PdfUtil.extractPdf(pdfInputStream);
|
||||
}
|
||||
@ -59,10 +58,10 @@ public class FileUtil {
|
||||
* Optical character recognition on a stream.
|
||||
*
|
||||
* @param inputStream Unencrypted input stream
|
||||
* @param document Document linked to the file
|
||||
* @param language Language to OCR
|
||||
* @return Content extracted
|
||||
*/
|
||||
private static String ocrFile(InputStream inputStream, Document document) {
|
||||
private static String ocrFile(InputStream inputStream, String language) {
|
||||
Tesseract instance = Tesseract.getInstance();
|
||||
String content = null;
|
||||
BufferedImage image = null;
|
||||
@ -80,7 +79,7 @@ public class FileUtil {
|
||||
// OCR the file
|
||||
try {
|
||||
log.info("Starting OCR with TESSDATA_PREFIX=" + System.getenv("TESSDATA_PREFIX") + ";LC_NUMERIC=" + System.getenv("LC_NUMERIC"));
|
||||
instance.setLanguage(document.getLanguage());
|
||||
instance.setLanguage(language);
|
||||
content = instance.doOCR(image);
|
||||
} catch (Throwable e) {
|
||||
log.error("Error while OCR-izing the image", e);
|
||||
|
@ -7,6 +7,8 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
@ -17,6 +19,7 @@ import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
@ -29,8 +32,11 @@ import org.odftoolkit.odfdom.doc.OdfTextDocument;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.io.Closer;
|
||||
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.docs.core.util.pdf.PdfPage;
|
||||
import com.sismics.util.ImageUtil;
|
||||
import com.sismics.util.mime.MimeType;
|
||||
|
||||
@ -141,25 +147,67 @@ public class PdfUtil {
|
||||
/**
|
||||
* Convert a document and its files to a merged PDF file.
|
||||
*
|
||||
* @param documentDto Document DTO
|
||||
* @param fileList List of files
|
||||
* @param fitImageToPage Fit images to the page
|
||||
* @param metadata Add a page with metadata
|
||||
* @param margin Margins in millimeters
|
||||
* @return PDF input stream
|
||||
* @throws IOException
|
||||
*/
|
||||
public static InputStream convertToPdf(List<File> fileList, boolean fitImageToPage, int margin) throws Exception {
|
||||
// TODO PDF Export: Option to add a front page with:
|
||||
// document title, document description, creator, date created, language,
|
||||
// list of all files (and information if it is in this document or not)
|
||||
// TODO PDF Export: Option to add the comments
|
||||
|
||||
// Create a blank PDF
|
||||
public static InputStream convertToPdf(DocumentDto documentDto, List<File> fileList,
|
||||
boolean fitImageToPage, boolean metadata, int margin) throws Exception {
|
||||
// Setup PDFBox
|
||||
Closer closer = Closer.create();
|
||||
MemoryUsageSetting memUsageSettings = MemoryUsageSetting.setupMixed(1000000); // 1MB max memory usage
|
||||
memUsageSettings.setTempDir(new java.io.File(System.getProperty("java.io.tmpdir"))); // To OS temp
|
||||
float mmPerInch = 1 / (10 * 2.54f) * 72f;
|
||||
|
||||
// Create a blank PDF
|
||||
try (PDDocument doc = new PDDocument(memUsageSettings)) {
|
||||
// Add metadata
|
||||
if (metadata) {
|
||||
PDPage page = new PDPage();
|
||||
doc.addPage(page);
|
||||
try (PdfPage pdfPage = new PdfPage(doc, page, margin * mmPerInch, PDType1Font.HELVETICA, 12)) {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
pdfPage.addText(documentDto.getTitle(), true, PDType1Font.HELVETICA_BOLD, 16)
|
||||
.newLine()
|
||||
.addText("Created by " + documentDto.getCreator()
|
||||
+ " on " + dateFormat.format(new Date(documentDto.getCreateTimestamp())), true)
|
||||
.newLine()
|
||||
.addText(documentDto.getDescription())
|
||||
.newLine();
|
||||
if (!Strings.isNullOrEmpty(documentDto.getSubject())) {
|
||||
pdfPage.addText("Subject: " + documentDto.getSubject());
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(documentDto.getIdentifier())) {
|
||||
pdfPage.addText("Identifier: " + documentDto.getIdentifier());
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(documentDto.getPublisher())) {
|
||||
pdfPage.addText("Publisher: " + documentDto.getPublisher());
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(documentDto.getFormat())) {
|
||||
pdfPage.addText("Format: " + documentDto.getFormat());
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(documentDto.getSource())) {
|
||||
pdfPage.addText("Source: " + documentDto.getSource());
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(documentDto.getType())) {
|
||||
pdfPage.addText("Type: " + documentDto.getType());
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(documentDto.getCoverage())) {
|
||||
pdfPage.addText("Coverage: " + documentDto.getCoverage());
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(documentDto.getRights())) {
|
||||
pdfPage.addText("Rights: " + documentDto.getRights());
|
||||
}
|
||||
pdfPage.addText("Language: " + documentDto.getLanguage())
|
||||
.newLine()
|
||||
.addText("Files in this document : " + fileList.size(), false, PDType1Font.HELVETICA_BOLD, 12);
|
||||
}
|
||||
}
|
||||
|
||||
// Add files
|
||||
for (File file : fileList) {
|
||||
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||
|
@ -105,13 +105,7 @@ public class PaginatedLists {
|
||||
* @return List of results
|
||||
*/
|
||||
public static <E> List<Object[]> executePaginatedQuery(PaginatedList<E> paginatedList, QueryParam queryParam, SortCriteria sortCriteria) {
|
||||
StringBuilder sb = new StringBuilder(queryParam.getQueryString());
|
||||
sb.append(" order by c");
|
||||
sb.append(sortCriteria.getColumn());
|
||||
sb.append(sortCriteria.isAsc() ? " asc" : " desc");
|
||||
|
||||
QueryParam sortedQueryParam = new QueryParam(sb.toString(), queryParam.getParameterMap());
|
||||
|
||||
QueryParam sortedQueryParam = QueryUtil.getSortedQueryParam(queryParam, sortCriteria);
|
||||
executeCountQuery(paginatedList, sortedQueryParam);
|
||||
return executeResultQuery(paginatedList, sortedQueryParam);
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
package com.sismics.docs.core.util.jpa;
|
||||
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
/**
|
||||
* Query utilities.
|
||||
@ -27,4 +28,22 @@ public class QueryUtil {
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sorted query parameters.
|
||||
*
|
||||
* @param queryParam Query parameters
|
||||
* @param sortCriteria Sort criteria
|
||||
* @return Sorted query parameters
|
||||
*/
|
||||
public static QueryParam getSortedQueryParam(QueryParam queryParam, SortCriteria sortCriteria) {
|
||||
StringBuilder sb = new StringBuilder(queryParam.getQueryString());
|
||||
if (sortCriteria != null) {
|
||||
sb.append(" order by c");
|
||||
sb.append(sortCriteria.getColumn());
|
||||
sb.append(sortCriteria.isAsc() ? " asc" : " desc");
|
||||
}
|
||||
|
||||
return new QueryParam(sb.toString(), queryParam.getParameterMap());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,153 @@
|
||||
package com.sismics.docs.core.util.pdf;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||
|
||||
/**
|
||||
* Wrapper around PDFBox for high level abstraction of PDF writing.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class PdfPage implements Closeable {
|
||||
private PDPage pdPage;
|
||||
private PDPageContentStream pdContent;
|
||||
private float margin;
|
||||
private PDFont defaultFont;
|
||||
private int defaultFontSize;
|
||||
|
||||
/**
|
||||
* Create a wrapper around a PDF page.
|
||||
*
|
||||
* @param pdDoc Document
|
||||
* @param pdPage Page
|
||||
* @param margin Margin
|
||||
* @param defaultFont Default font
|
||||
* @param defaultFontSize Default fond size
|
||||
* @throws IOException
|
||||
*/
|
||||
public PdfPage(PDDocument pdDoc, PDPage pdPage, float margin, PDFont defaultFont, int defaultFontSize) throws IOException {
|
||||
this.pdPage = pdPage;
|
||||
this.pdContent = new PDPageContentStream(pdDoc, pdPage);
|
||||
this.margin = margin;
|
||||
this.defaultFont = defaultFont;
|
||||
this.defaultFontSize = defaultFontSize;
|
||||
|
||||
pdContent.beginText();
|
||||
pdContent.newLineAtOffset(margin, pdPage.getMediaBox().getHeight() - margin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a text with default font.
|
||||
*
|
||||
* @param text Text
|
||||
* @throws IOException
|
||||
*/
|
||||
public PdfPage addText(String text) throws IOException {
|
||||
drawText(pdPage.getMediaBox().getWidth() - 2 * margin, defaultFont, defaultFontSize, text, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a text with default font.
|
||||
*
|
||||
* @param text Text
|
||||
* @param centered If true, the text will be centered in the page
|
||||
* @throws IOException
|
||||
*/
|
||||
public PdfPage addText(String text, boolean centered) throws IOException {
|
||||
drawText(pdPage.getMediaBox().getWidth() - 2 * margin, defaultFont, defaultFontSize, text, centered);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a text in the page.
|
||||
*
|
||||
* @param text Text
|
||||
* @param centered If true, the text will be centered in the page
|
||||
* @param font Font
|
||||
* @param fontSize Font size
|
||||
* @throws IOException
|
||||
*/
|
||||
public PdfPage addText(String text, boolean centered, PDFont font, int fontSize) throws IOException {
|
||||
drawText(pdPage.getMediaBox().getWidth() - 2 * margin, font, fontSize, text, centered);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new line.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public PdfPage newLine() throws IOException {
|
||||
pdContent.newLineAtOffset(0, - defaultFont.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * defaultFontSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a text with low level PDFBox API.
|
||||
*
|
||||
* @param paragraphWidth Paragraph width
|
||||
* @param font Font
|
||||
* @param fontSize Font size
|
||||
* @param text Text
|
||||
* @param centered If true, the text will be centered in the paragraph
|
||||
* @throws IOException
|
||||
*/
|
||||
private void drawText(float paragraphWidth, PDFont font, int fontSize, String text, boolean centered) throws IOException {
|
||||
pdContent.setFont(font, fontSize);
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
float height = font.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize;
|
||||
for (int i : possibleWrapPoints(text)) {
|
||||
float width = font.getStringWidth(text.substring(start, i)) / 1000 * fontSize;
|
||||
if (start < end && width > paragraphWidth) {
|
||||
// Draw partial text and increase height
|
||||
pdContent.newLineAtOffset(0, - height);
|
||||
String line = text.substring(start, end);
|
||||
float lineWidth = font.getStringWidth(line) / 1000 * fontSize;
|
||||
float offset = (paragraphWidth - lineWidth) / 2;
|
||||
if (centered) pdContent.newLineAtOffset(offset, 0);
|
||||
pdContent.showText(line);
|
||||
if (centered) pdContent.newLineAtOffset(- offset, 0);
|
||||
start = end;
|
||||
}
|
||||
end = i;
|
||||
}
|
||||
|
||||
// Last piece of text
|
||||
String line = text.substring(start);
|
||||
float lineWidth = font.getStringWidth(line) / 1000 * fontSize;
|
||||
float offset = (paragraphWidth - lineWidth) / 2;
|
||||
pdContent.newLineAtOffset(0, - height);
|
||||
if (centered) pdContent.newLineAtOffset(offset, 0);
|
||||
pdContent.showText(line);
|
||||
if (centered) pdContent.newLineAtOffset(- offset, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns wrap points for a given piece of text.
|
||||
*
|
||||
* @param text Text
|
||||
* @return Wrap points
|
||||
*/
|
||||
private int[] possibleWrapPoints(String text) {
|
||||
String[] split = text.split("(?<=\\W)");
|
||||
int[] ret = new int[split.length];
|
||||
ret[0] = split[0].length();
|
||||
for (int i = 1 ; i < split.length ; i++) {
|
||||
ret[i] = ret[i-1] + split[i].length();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
pdContent.endText();
|
||||
pdContent.close();
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package com.sismics.util.log4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.log4j.Level;
|
||||
|
||||
/**
|
||||
* Log search criteria.
|
||||
@ -10,9 +11,9 @@ import org.apache.commons.lang.StringUtils;
|
||||
public class LogCriteria {
|
||||
|
||||
/**
|
||||
* Logging level (DEBUG, WARN)...
|
||||
* Minimum logging level (DEBUG, WARN)...
|
||||
*/
|
||||
private String level;
|
||||
private Level minLevel;
|
||||
|
||||
/**
|
||||
* Logger name / tag.
|
||||
@ -24,57 +25,30 @@ public class LogCriteria {
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* Getter of level.
|
||||
*
|
||||
* @return level
|
||||
*/
|
||||
public String getLevel() {
|
||||
return level;
|
||||
public Level getMinLevel() {
|
||||
return minLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of level.
|
||||
*
|
||||
* @param level level
|
||||
*/
|
||||
public void setLevel(String level) {
|
||||
this.level = StringUtils.lowerCase(level);
|
||||
public LogCriteria setMinLevel(Level level) {
|
||||
this.minLevel = level;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of tag.
|
||||
*
|
||||
* @return tag
|
||||
*/
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of tag.
|
||||
*
|
||||
* @param tag tag
|
||||
*/
|
||||
public void setTag(String tag) {
|
||||
public LogCriteria setTag(String tag) {
|
||||
this.tag = StringUtils.lowerCase(tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of message.
|
||||
*
|
||||
* @return message
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of message.
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
public void setMessage(String message) {
|
||||
public LogCriteria setMessage(String message) {
|
||||
this.message = StringUtils.lowerCase(message);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.sismics.util.log4j;
|
||||
|
||||
import org.apache.log4j.Level;
|
||||
|
||||
/**
|
||||
* Log entry.
|
||||
*
|
||||
@ -14,7 +16,7 @@ public class LogEntry {
|
||||
/**
|
||||
* Logging level (DEBUG, WARN)...
|
||||
*/
|
||||
private String level;
|
||||
private Level level;
|
||||
|
||||
/**
|
||||
* Logger name / tag.
|
||||
@ -34,45 +36,25 @@ public class LogEntry {
|
||||
* @param tag Logger name / tag
|
||||
* @param message Message logged
|
||||
*/
|
||||
public LogEntry(long timestamp, String level, String tag, String message) {
|
||||
public LogEntry(long timestamp, Level level, String tag, String message) {
|
||||
this.timestamp = timestamp;
|
||||
this.level = level;
|
||||
this.tag = tag;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of timestamp.
|
||||
*
|
||||
* @return timestamp
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of level.
|
||||
*
|
||||
* @return level
|
||||
*/
|
||||
public String getLevel() {
|
||||
public Level getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of tag.
|
||||
*
|
||||
* @return tag
|
||||
*/
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of message.
|
||||
*
|
||||
* @return message
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
@ -1,17 +1,19 @@
|
||||
package com.sismics.util.log4j;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
import org.apache.log4j.AppenderSkeleton;
|
||||
import org.apache.log4j.helpers.LogLog;
|
||||
import org.apache.log4j.spi.LoggingEvent;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.apache.log4j.AppenderSkeleton;
|
||||
import org.apache.log4j.Level;
|
||||
import org.apache.log4j.helpers.LogLog;
|
||||
import org.apache.log4j.spi.LoggingEvent;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
|
||||
/**
|
||||
* Memory appender for Log4J.
|
||||
*
|
||||
@ -54,7 +56,7 @@ public class MemoryAppender extends AppenderSkeleton {
|
||||
|
||||
String loggerName = getLoggerName(event);
|
||||
|
||||
LogEntry logEntry = new LogEntry(System.currentTimeMillis(), event.getLevel().toString(), loggerName, event.getMessage().toString());
|
||||
LogEntry logEntry = new LogEntry(System.currentTimeMillis(), event.getLevel(), loggerName, event.getMessage().toString());
|
||||
logQueue.add(logEntry);
|
||||
}
|
||||
|
||||
@ -98,13 +100,13 @@ public class MemoryAppender extends AppenderSkeleton {
|
||||
*/
|
||||
public void find(LogCriteria criteria, PaginatedList<LogEntry> list) {
|
||||
List<LogEntry> logEntryList = new LinkedList<LogEntry>();
|
||||
final String level = criteria.getLevel();
|
||||
final Level minLevel = criteria.getMinLevel();
|
||||
final String tag = criteria.getTag();
|
||||
final String message = criteria.getMessage();
|
||||
int resultCount = 0;
|
||||
for (Iterator<LogEntry> it = logQueue.iterator(); it.hasNext();) {
|
||||
LogEntry logEntry = it.next();
|
||||
if ((level == null || logEntry.getLevel().toLowerCase().equals(level)) &&
|
||||
if ((minLevel == null || Integer.compare(logEntry.getLevel().toInt(), minLevel.toInt()) >= 0) &&
|
||||
(tag == null || logEntry.getTag().toLowerCase().equals(tag)) &&
|
||||
(message == null || logEntry.getMessage().toLowerCase().contains(message))) {
|
||||
logEntryList.add(logEntry);
|
||||
|
@ -1 +1 @@
|
||||
db.version=6
|
||||
db.version=8
|
@ -0,0 +1,3 @@
|
||||
create cached table T_RELATION ( REL_ID_C varchar(36) not null, REL_IDDOCFROM_C varchar(36) not null, REL_IDDOCTO_C varchar(36) not null, REL_DELETEDATE_D datetime, primary key (REL_ID_C) );
|
||||
|
||||
update T_CONFIG set CFG_VALUE_C = '7' where CFG_ID_C = 'DB_VERSION';
|
@ -0,0 +1,7 @@
|
||||
create memory table T_GROUP ( GRP_ID_C varchar(36) not null, GRP_IDPARENT_C varchar(36), GRP_NAME_C varchar(50) not null, GRP_IDROLE_C varchar(36), GRP_DELETEDATE_D datetime, primary key (GRP_ID_C) );
|
||||
create memory table T_USER_GROUP ( UGP_ID_C varchar(36) not null, UGP_IDUSER_C varchar(36) not null, UGP_IDGROUP_C varchar(36) not null, UGP_DELETEDATE_D datetime, primary key (UGP_ID_C) );
|
||||
|
||||
insert into T_GROUP(GRP_ID_C, GRP_NAME_C, GRP_IDROLE_C) values('administrators', 'administrators', 'admin');
|
||||
insert into T_USER_GROUP(UGP_ID_C, UGP_IDUSER_C, UGP_IDGROUP_C) values('admin-administrators', 'admin', 'administrators');
|
||||
|
||||
update T_CONFIG set CFG_VALUE_C = '8' where CFG_ID_C = 'DB_VERSION';
|
@ -3,16 +3,18 @@ package com.sismics.docs.core.util;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.Resources;
|
||||
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.util.mime.MimeType;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
/**
|
||||
* Test of the file entity utilities.
|
||||
*
|
||||
@ -50,6 +52,21 @@ public class TestFileUtil {
|
||||
InputStream inputStream2 = Resources.getResource("file/udhr_encrypted.pdf").openStream();
|
||||
InputStream inputStream3 = Resources.getResource("file/document.docx").openStream();
|
||||
InputStream inputStream4 = Resources.getResource("file/document.odt").openStream()) {
|
||||
// Document
|
||||
DocumentDto documentDto = new DocumentDto();
|
||||
documentDto.setTitle("My super document 1");
|
||||
documentDto.setDescription("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis id turpis iaculis, commodo est ac, efficitur quam. Nam accumsan magna in orci vulputate ultricies. Sed vulputate neque magna, at laoreet leo ultricies vel. Proin eu hendrerit felis. Quisque sit amet arcu efficitur, pulvinar orci sed, imperdiet elit. Nunc posuere ex sed fermentum congue. Aliquam ultrices convallis finibus. Praesent iaculis justo vitae dictum auctor. Praesent suscipit imperdiet erat ac maximus. Aenean pharetra quam sed fermentum commodo. Donec sagittis ipsum nibh, id congue dolor venenatis quis. In tincidunt nisl non ex sollicitudin, a imperdiet neque scelerisque. Nullam lacinia ac orci sed faucibus. Donec tincidunt venenatis justo, nec fermentum justo rutrum a.");
|
||||
documentDto.setSubject("A set of random picture");
|
||||
documentDto.setIdentifier("ID-2016-08-00001");
|
||||
documentDto.setPublisher("My Publisher, Inc.");
|
||||
documentDto.setFormat("A4 standard ISO format");
|
||||
documentDto.setType("Image");
|
||||
documentDto.setCoverage("France");
|
||||
documentDto.setRights("Public Domain");
|
||||
documentDto.setLanguage("en");
|
||||
documentDto.setCreator("user1");
|
||||
documentDto.setCreateTimestamp(new Date().getTime());
|
||||
|
||||
// First file
|
||||
Files.copy(inputStream0, DirectoryUtil.getStorageDirectory().resolve("apollo_landscape"), StandardCopyOption.REPLACE_EXISTING);
|
||||
File file0 = new File();
|
||||
@ -81,7 +98,9 @@ public class TestFileUtil {
|
||||
file4.setId("document_odt");
|
||||
file4.setMimeType(MimeType.OPEN_DOCUMENT_TEXT);
|
||||
|
||||
PdfUtil.convertToPdf(Lists.newArrayList(file0, file1, file2, file3, file4), true, 10).close();
|
||||
try (InputStream pdfInputStream = PdfUtil.convertToPdf(documentDto, Lists.newArrayList(file0, file1, file2, file3, file4), true, true, 10)) {
|
||||
ByteStreams.copy(pdfInputStream, System.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,14 +26,14 @@
|
||||
<org.slf4j.jcl-over-slf4j.version>1.6.6</org.slf4j.jcl-over-slf4j.version>
|
||||
<junit.junit.version>4.12</junit.junit.version>
|
||||
<com.h2database.h2.version>1.4.191</com.h2database.h2.version>
|
||||
<org.glassfish.jersey.version>2.22.1</org.glassfish.jersey.version>
|
||||
<org.glassfish.jersey.version>2.22.2</org.glassfish.jersey.version>
|
||||
<org.mindrot.jbcrypt>0.3m</org.mindrot.jbcrypt>
|
||||
<org.apache.lucene.version>5.5.0</org.apache.lucene.version>
|
||||
<org.imgscalr.imgscalr-lib.version>4.2</org.imgscalr.imgscalr-lib.version>
|
||||
<org.apache.pdfbox.pdfbox.version>2.0.0-RC3</org.apache.pdfbox.pdfbox.version>
|
||||
<org.bouncycastle.bcprov-jdk15on.version>1.54</org.bouncycastle.bcprov-jdk15on.version>
|
||||
<joda-time.joda-time.version>2.9.1</joda-time.joda-time.version>
|
||||
<org.hibernate.hibernate.version>5.0.7.Final</org.hibernate.hibernate.version>
|
||||
<joda-time.joda-time.version>2.9.2</joda-time.joda-time.version>
|
||||
<org.hibernate.hibernate.version>5.1.0.Final</org.hibernate.hibernate.version>
|
||||
<javax.servlet.javax.servlet-api.version>3.1.0</javax.servlet.javax.servlet-api.version>
|
||||
<fr.opensagres.xdocreport.version>1.0.5</fr.opensagres.xdocreport.version>
|
||||
<net.java.dev.jna.jna.version>4.2.1</net.java.dev.jna.jna.version>
|
||||
|
@ -1,7 +1,11 @@
|
||||
package com.sismics.security;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import jersey.repackaged.com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Anonymous principal.
|
||||
*
|
||||
@ -47,12 +51,12 @@ public class AnonymousPrincipal implements IPrincipal {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of dateTimeZone.
|
||||
*
|
||||
* @param dateTimeZone dateTimeZone
|
||||
*/
|
||||
public void setDateTimeZone(DateTimeZone dateTimeZone) {
|
||||
this.dateTimeZone = dateTimeZone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getGroupIdSet() {
|
||||
return Sets.newHashSet();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.sismics.security;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
@ -24,6 +25,14 @@ public interface IPrincipal extends Principal {
|
||||
*/
|
||||
public String getId();
|
||||
|
||||
/**
|
||||
* Returns the list of group ID of the connected user,
|
||||
* or an empty list if the user is anonymous.
|
||||
*
|
||||
* @return List of group ID
|
||||
*/
|
||||
public Set<String> getGroupIdSet();
|
||||
|
||||
/**
|
||||
* Returns the timezone of the principal.
|
||||
*
|
||||
|
@ -35,6 +35,11 @@ public class UserPrincipal implements IPrincipal {
|
||||
*/
|
||||
private Set<String> baseFunctionSet;
|
||||
|
||||
/**
|
||||
* User groups.
|
||||
*/
|
||||
private Set<String> groupIdSet;
|
||||
|
||||
/**
|
||||
* Constructor of UserPrincipal.
|
||||
*
|
||||
@ -56,11 +61,6 @@ public class UserPrincipal implements IPrincipal {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of id.
|
||||
*
|
||||
* @param id id
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
@ -70,11 +70,6 @@ public class UserPrincipal implements IPrincipal {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of name.
|
||||
*
|
||||
* @param name name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
@ -84,11 +79,6 @@ public class UserPrincipal implements IPrincipal {
|
||||
return dateTimeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of dateTimeZone.
|
||||
*
|
||||
* @param dateTimeZone dateTimeZone
|
||||
*/
|
||||
public void setDateTimeZone(DateTimeZone dateTimeZone) {
|
||||
this.dateTimeZone = dateTimeZone;
|
||||
}
|
||||
@ -98,31 +88,24 @@ public class UserPrincipal implements IPrincipal {
|
||||
return email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of email.
|
||||
*
|
||||
* @param email email
|
||||
*/
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of baseFunctionSet.
|
||||
*
|
||||
* @return baseFunctionSet
|
||||
*/
|
||||
public Set<String> getBaseFunctionSet() {
|
||||
return baseFunctionSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of baseFunctionSet.
|
||||
*
|
||||
* @param baseFunctionSet baseFunctionSet
|
||||
*/
|
||||
public void setBaseFunctionSet(Set<String> baseFunctionSet) {
|
||||
this.baseFunctionSet = baseFunctionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getGroupIdSet() {
|
||||
return groupIdSet;
|
||||
}
|
||||
|
||||
public void setGroupIdSet(Set<String> groupIdSet) {
|
||||
this.groupIdSet = groupIdSet;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package com.sismics.util.filter;
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
@ -20,18 +22,22 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.sismics.docs.core.constant.Constants;
|
||||
import com.sismics.docs.core.dao.jpa.AuthenticationTokenDao;
|
||||
import com.sismics.docs.core.dao.jpa.GroupDao;
|
||||
import com.sismics.docs.core.dao.jpa.RoleBaseFunctionDao;
|
||||
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.dto.GroupDto;
|
||||
import com.sismics.docs.core.model.jpa.AuthenticationToken;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
import com.sismics.docs.core.util.TransactionUtil;
|
||||
import com.sismics.security.AnonymousPrincipal;
|
||||
import com.sismics.security.UserPrincipal;
|
||||
|
||||
import jersey.repackaged.com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* This filter is used to authenticate the user having an active session via an authentication token stored in database.
|
||||
* The filter extracts the authentication token stored in a cookie.
|
||||
* If the ocokie exists and the token is valid, the filter injects a UserPrincipal into a request attribute.
|
||||
* If the cookie exists and the token is valid, the filter injects a UserPrincipal into a request attribute.
|
||||
* If not, the user is anonymous, and the filter injects a AnonymousPrincipal into the request attribute.
|
||||
*
|
||||
* @author jtremeaux
|
||||
@ -113,10 +119,6 @@ public class TokenBasedSecurityFilter implements Filter {
|
||||
User user = userDao.getById(authenticationToken.getUserId());
|
||||
if (user != null && user.getDeleteDate() == null) {
|
||||
injectAuthenticatedUser(request, user);
|
||||
|
||||
// Update the last connection date
|
||||
authenticationTokenDao.updateLastConnectionDate(authenticationToken.getId());
|
||||
TransactionUtil.commit();
|
||||
} else {
|
||||
injectAnonymousUser(request);
|
||||
}
|
||||
@ -153,9 +155,25 @@ public class TokenBasedSecurityFilter implements Filter {
|
||||
private void injectAuthenticatedUser(HttpServletRequest request, User user) {
|
||||
UserPrincipal userPrincipal = new UserPrincipal(user.getId(), user.getUsername());
|
||||
|
||||
// Add groups
|
||||
GroupDao groupDao = new GroupDao();
|
||||
Set<String> groupRoleIdSet = new HashSet<>();
|
||||
List<GroupDto> groupDtoList = groupDao.findByCriteria(new GroupCriteria()
|
||||
.setUserId(user.getId())
|
||||
.setRecursive(true), null);
|
||||
Set<String> groupIdSet = Sets.newHashSet();
|
||||
for (GroupDto groupDto : groupDtoList) {
|
||||
groupIdSet.add(groupDto.getId());
|
||||
if (groupDto.getRoleId() != null) {
|
||||
groupRoleIdSet.add(groupDto.getRoleId());
|
||||
}
|
||||
}
|
||||
userPrincipal.setGroupIdSet(groupIdSet);
|
||||
|
||||
// Add base functions
|
||||
groupRoleIdSet.add(user.getRoleId());
|
||||
RoleBaseFunctionDao userBaseFuction = new RoleBaseFunctionDao();
|
||||
Set<String> baseFunctionSet = userBaseFuction.findByRoleId(user.getRoleId());
|
||||
Set<String> baseFunctionSet = userBaseFuction.findByRoleId(groupRoleIdSet);
|
||||
userPrincipal.setBaseFunctionSet(baseFunctionSet);
|
||||
|
||||
// Add email
|
||||
|
@ -31,22 +31,59 @@ public class ClientUtil {
|
||||
*
|
||||
* @param username Username
|
||||
*/
|
||||
public void createUser(String username) {
|
||||
public void createUser(String username, String... groupNameList) {
|
||||
// Login admin to create the user
|
||||
String adminAuthenticationToken = login("admin", "admin", false);
|
||||
String adminToken = login("admin", "admin", false);
|
||||
|
||||
// Create the user
|
||||
Form form = new Form();
|
||||
form.param("username", username);
|
||||
form.param("email", username + "@docs.com");
|
||||
form.param("password", "12345678");
|
||||
form.param("storage_quota", "1000000"); // 1MB quota
|
||||
resource.path("/user").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminAuthenticationToken)
|
||||
.put(Entity.form(form), JsonObject.class);
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.put(Entity.form(new Form()
|
||||
.param("username", username)
|
||||
.param("email", username + "@docs.com")
|
||||
.param("password", "12345678")
|
||||
.param("storage_quota", "1000000")), JsonObject.class); // 1MB quota
|
||||
|
||||
// Add to groups
|
||||
for (String groupName : groupNameList) {
|
||||
resource.path("/group/" + groupName).request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.put(Entity.form(new Form()
|
||||
.param("username", username)), JsonObject.class);
|
||||
}
|
||||
|
||||
// Logout admin
|
||||
logout(adminAuthenticationToken);
|
||||
logout(adminToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a group.
|
||||
*
|
||||
* @param name Name
|
||||
*/
|
||||
public void createGroup(String name) {
|
||||
createGroup(name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a group.
|
||||
*
|
||||
* @param name Name
|
||||
* @param parent Parent
|
||||
*/
|
||||
public void createGroup(String name, String parentId) {
|
||||
// Login admin to create the group
|
||||
String adminToken = login("admin", "admin", false);
|
||||
|
||||
// Create the gorup
|
||||
resource.path("/group").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.put(Entity.form(new Form()
|
||||
.param("name", name)
|
||||
.param("parent", parentId)), JsonObject.class);
|
||||
|
||||
// Logout admin
|
||||
logout(adminToken);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
api.min_version=1.0
|
||||
db.version=6
|
||||
db.version=8
|
@ -1,6 +1,7 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonArrayBuilder;
|
||||
@ -14,18 +15,21 @@ import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sismics.docs.core.constant.AclTargetType;
|
||||
import com.sismics.docs.core.constant.PermType;
|
||||
import com.sismics.docs.core.dao.jpa.AclDao;
|
||||
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||
import com.sismics.docs.core.dao.jpa.GroupDao;
|
||||
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.dto.GroupDto;
|
||||
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||
import com.sismics.docs.core.model.jpa.Acl;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.model.jpa.Group;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.rest.exception.ClientException;
|
||||
import com.sismics.rest.exception.ForbiddenClientException;
|
||||
@ -41,12 +45,17 @@ public class AclResource extends BaseResource {
|
||||
/**
|
||||
* Add an ACL.
|
||||
*
|
||||
* @param sourceId Source ID
|
||||
* @param permStr Permission
|
||||
* @param targetName Target name
|
||||
* @param type ACL type
|
||||
* @return Response
|
||||
*/
|
||||
@PUT
|
||||
public Response add(@FormParam("source") String sourceId,
|
||||
@FormParam("perm") String permStr,
|
||||
@FormParam("username") String username) {
|
||||
@FormParam("target") String targetName,
|
||||
@FormParam("type") String typeStr) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
@ -54,18 +63,39 @@ public class AclResource extends BaseResource {
|
||||
// Validate input
|
||||
ValidationUtil.validateRequired(sourceId, "source");
|
||||
PermType perm = PermType.valueOf(ValidationUtil.validateLength(permStr, "perm", 1, 30, false));
|
||||
username = ValidationUtil.validateLength(username, "username", 1, 50, false);
|
||||
AclTargetType type = AclTargetType.valueOf(ValidationUtil.validateLength(typeStr, "type", 1, 10, false));
|
||||
targetName = ValidationUtil.validateLength(targetName, "target", 1, 50, false);
|
||||
|
||||
// Validate the target user
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(username);
|
||||
if (user == null) {
|
||||
throw new ClientException("UserNotFound", MessageFormat.format("User not found: {0}", username));
|
||||
// Search user or group
|
||||
String targetId = null;
|
||||
switch (type) {
|
||||
case USER:
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(targetName);
|
||||
if (user != null) {
|
||||
targetId = user.getId();
|
||||
}
|
||||
break;
|
||||
case GROUP:
|
||||
GroupDao groupDao = new GroupDao();
|
||||
Group group = groupDao.getActiveByName(targetName);
|
||||
if (group != null) {
|
||||
targetId = group.getId();
|
||||
}
|
||||
break;
|
||||
case SHARE:
|
||||
// Share must use the Share REST resource
|
||||
break;
|
||||
}
|
||||
|
||||
// Does a target has been found?
|
||||
if (targetId == null) {
|
||||
throw new ClientException("InvalidTarget", MessageFormat.format("This target does not exist: {0}", targetName));
|
||||
}
|
||||
|
||||
// Check permission on the source by the principal
|
||||
AclDao aclDao = new AclDao();
|
||||
if (!aclDao.checkPermission(sourceId, PermType.WRITE, principal.getId())) {
|
||||
if (!aclDao.checkPermission(sourceId, PermType.WRITE, getTargetIdList(null))) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
@ -73,18 +103,18 @@ public class AclResource extends BaseResource {
|
||||
Acl acl = new Acl();
|
||||
acl.setSourceId(sourceId);
|
||||
acl.setPerm(perm);
|
||||
acl.setTargetId(user.getId());
|
||||
acl.setTargetId(targetId);
|
||||
|
||||
// Avoid duplicates
|
||||
if (!aclDao.checkPermission(acl.getSourceId(), acl.getPerm(), acl.getTargetId())) {
|
||||
if (!aclDao.checkPermission(acl.getSourceId(), acl.getPerm(), Lists.newArrayList(acl.getTargetId()))) {
|
||||
aclDao.create(acl, principal.getId());
|
||||
|
||||
// Returns the ACL
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("perm", acl.getPerm().name())
|
||||
.add("id", acl.getTargetId())
|
||||
.add("name", user.getUsername())
|
||||
.add("type", AclTargetType.USER.name());
|
||||
.add("name", targetName)
|
||||
.add("type", type.name());
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
@ -94,7 +124,9 @@ public class AclResource extends BaseResource {
|
||||
/**
|
||||
* Deletes an ACL.
|
||||
*
|
||||
* @param id ACL ID
|
||||
* @param sourceId Source ID
|
||||
* @param permStr Permission
|
||||
* @param targetId Target ID
|
||||
* @return Response
|
||||
*/
|
||||
@DELETE
|
||||
@ -114,7 +146,7 @@ public class AclResource extends BaseResource {
|
||||
|
||||
// Check permission on the source by the principal
|
||||
AclDao aclDao = new AclDao();
|
||||
if (!aclDao.checkPermission(sourceId, PermType.WRITE, principal.getId())) {
|
||||
if (!aclDao.checkPermission(sourceId, PermType.WRITE, getTargetIdList(null))) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
@ -153,18 +185,25 @@ public class AclResource extends BaseResource {
|
||||
// Search users
|
||||
UserDao userDao = new UserDao();
|
||||
JsonArrayBuilder users = Json.createArrayBuilder();
|
||||
|
||||
PaginatedList<UserDto> paginatedList = PaginatedLists.create();
|
||||
SortCriteria sortCriteria = new SortCriteria(1, true);
|
||||
|
||||
userDao.findByCriteria(paginatedList, new UserCriteria().setSearch(search), sortCriteria);
|
||||
for (UserDto userDto : paginatedList.getResultList()) {
|
||||
List<UserDto> userDtoList = userDao.findByCriteria(new UserCriteria().setSearch(search), sortCriteria);
|
||||
for (UserDto userDto : userDtoList) {
|
||||
users.add(Json.createObjectBuilder()
|
||||
.add("username", userDto.getUsername()));
|
||||
.add("name", userDto.getUsername()));
|
||||
}
|
||||
|
||||
// Search groups
|
||||
GroupDao groupDao = new GroupDao();
|
||||
JsonArrayBuilder groups = Json.createArrayBuilder();
|
||||
List<GroupDto> groupDtoList = groupDao.findByCriteria(new GroupCriteria().setSearch(search), sortCriteria);
|
||||
for (GroupDto groupDto : groupDtoList) {
|
||||
groups.add(Json.createObjectBuilder()
|
||||
.add("name", groupDto.getName()));
|
||||
}
|
||||
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("users", users);
|
||||
.add("users", users)
|
||||
.add("groups", groups);
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.log4j.Appender;
|
||||
import org.apache.log4j.Level;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -81,7 +82,7 @@ public class AppResource extends BaseResource {
|
||||
/**
|
||||
* Retrieve the application logs.
|
||||
*
|
||||
* @param level Filter on logging level
|
||||
* @param minLevel Filter on logging level
|
||||
* @param tag Filter on logger name / tag
|
||||
* @param message Filter on message
|
||||
* @param limit Page limit
|
||||
@ -91,7 +92,7 @@ public class AppResource extends BaseResource {
|
||||
@GET
|
||||
@Path("log")
|
||||
public Response log(
|
||||
@QueryParam("level") String level,
|
||||
@QueryParam("level") String minLevel,
|
||||
@QueryParam("tag") String tag,
|
||||
@QueryParam("message") String message,
|
||||
@QueryParam("limit") Integer limit,
|
||||
@ -99,8 +100,6 @@ public class AppResource extends BaseResource {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
// TODO Change level by minLevel (returns all logs above)
|
||||
|
||||
// Get the memory appender
|
||||
org.apache.log4j.Logger logger = org.apache.log4j.Logger.getRootLogger();
|
||||
Appender appender = logger.getAppender("MEMORY");
|
||||
@ -110,10 +109,10 @@ public class AppResource extends BaseResource {
|
||||
MemoryAppender memoryAppender = (MemoryAppender) appender;
|
||||
|
||||
// Find the logs
|
||||
LogCriteria logCriteria = new LogCriteria();
|
||||
logCriteria.setLevel(StringUtils.stripToNull(level));
|
||||
logCriteria.setTag(StringUtils.stripToNull(tag));
|
||||
logCriteria.setMessage(StringUtils.stripToNull(message));
|
||||
LogCriteria logCriteria = new LogCriteria()
|
||||
.setMinLevel(Level.toLevel(StringUtils.stripToNull(minLevel)))
|
||||
.setTag(StringUtils.stripToNull(tag))
|
||||
.setMessage(StringUtils.stripToNull(message));
|
||||
|
||||
PaginatedList<LogEntry> paginatedList = PaginatedLists.create(limit, offset);
|
||||
memoryAppender.find(logCriteria, paginatedList);
|
||||
@ -121,7 +120,7 @@ public class AppResource extends BaseResource {
|
||||
for (LogEntry logEntry : paginatedList.getResultList()) {
|
||||
logs.add(Json.createObjectBuilder()
|
||||
.add("date", logEntry.getTimestamp())
|
||||
.add("level", logEntry.getLevel())
|
||||
.add("level", logEntry.getLevel().toString())
|
||||
.add("tag", logEntry.getTag())
|
||||
.add("message", logEntry.getMessage()));
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.sismics.docs.core.constant.PermType;
|
||||
import com.sismics.docs.core.dao.jpa.AclDao;
|
||||
import com.sismics.docs.core.dao.jpa.AuditLogDao;
|
||||
@ -18,7 +19,6 @@ import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.rest.exception.ForbiddenClientException;
|
||||
import com.sismics.rest.exception.ServerException;
|
||||
import com.sismics.rest.util.JsonUtil;
|
||||
|
||||
/**
|
||||
@ -43,25 +43,21 @@ public class AuditLogResource extends BaseResource {
|
||||
PaginatedList<AuditLogDto> paginatedList = PaginatedLists.create(20, 0);
|
||||
SortCriteria sortCriteria = new SortCriteria(1, false);
|
||||
AuditLogCriteria criteria = new AuditLogCriteria();
|
||||
if (documentId == null) {
|
||||
if (Strings.isNullOrEmpty(documentId)) {
|
||||
// Search logs for a user
|
||||
criteria.setUserId(principal.getId());
|
||||
} else {
|
||||
// Check ACL on the document
|
||||
AclDao aclDao = new AclDao();
|
||||
if (!aclDao.checkPermission(documentId, PermType.READ, principal.getId())) {
|
||||
if (!aclDao.checkPermission(documentId, PermType.READ, getTargetIdList(null))) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
criteria.setDocumentId(documentId);
|
||||
}
|
||||
|
||||
// Search the logs
|
||||
try {
|
||||
AuditLogDao auditLogDao = new AuditLogDao();
|
||||
auditLogDao.findByCriteria(paginatedList, criteria, sortCriteria);
|
||||
} catch (Exception e) {
|
||||
throw new ServerException("SearchError", "Error searching in logs", e);
|
||||
}
|
||||
AuditLogDao auditLogDao = new AuditLogDao();
|
||||
auditLogDao.findByCriteria(paginatedList, criteria, sortCriteria);
|
||||
|
||||
// Assemble the results
|
||||
JsonArrayBuilder logs = Json.createArrayBuilder();
|
||||
|
@ -1,12 +1,14 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sismics.docs.rest.constant.BaseFunction;
|
||||
import com.sismics.rest.exception.ForbiddenClientException;
|
||||
import com.sismics.security.IPrincipal;
|
||||
@ -77,4 +79,21 @@ public abstract class BaseResource {
|
||||
Set<String> baseFunctionSet = ((UserPrincipal) principal).getBaseFunctionSet();
|
||||
return baseFunctionSet != null && baseFunctionSet.contains(baseFunction.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ACL target ID.
|
||||
*
|
||||
* @param shareId Share ID (optional)
|
||||
* @return List of ACL target ID
|
||||
*/
|
||||
protected List<String> getTargetIdList(String shareId) {
|
||||
List<String> targetIdList = Lists.newArrayList(principal.getGroupIdSet());
|
||||
if (principal.getId() != null) {
|
||||
targetIdList.add(principal.getId());
|
||||
}
|
||||
if (shareId != null) {
|
||||
targetIdList.add(shareId);
|
||||
}
|
||||
return targetIdList;
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ public class CommentResource extends BaseResource {
|
||||
|
||||
// Read access on doc gives access to write comments
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
if (documentDao.getDocument(documentId, PermType.READ, principal.getId()) == null) {
|
||||
if (documentDao.getDocument(documentId, PermType.READ, getTargetIdList(null)) == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ public class CommentResource extends BaseResource {
|
||||
if (!comment.getUserId().equals(principal.getId())) {
|
||||
// Get the associated document
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
if (documentDao.getDocument(comment.getDocumentId(), PermType.WRITE, principal.getId()) == null) {
|
||||
if (documentDao.getDocument(comment.getDocumentId(), PermType.WRITE, getTargetIdList(null)) == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
}
|
||||
@ -125,7 +125,7 @@ public class CommentResource extends BaseResource {
|
||||
|
||||
// Read access on doc gives access to read comments
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
if (documentDao.getDocument(documentId, PermType.READ, shareId == null ? principal.getId() : shareId) == null) {
|
||||
if (documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId)) == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,6 @@ import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.ws.rs.core.StreamingOutput;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
@ -43,12 +42,14 @@ import com.sismics.docs.core.dao.jpa.AclDao;
|
||||
import com.sismics.docs.core.dao.jpa.ContributorDao;
|
||||
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||
import com.sismics.docs.core.dao.jpa.FileDao;
|
||||
import com.sismics.docs.core.dao.jpa.RelationDao;
|
||||
import com.sismics.docs.core.dao.jpa.TagDao;
|
||||
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.DocumentCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.dto.AclDto;
|
||||
import com.sismics.docs.core.dao.jpa.dto.ContributorDto;
|
||||
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
||||
import com.sismics.docs.core.dao.jpa.dto.RelationDto;
|
||||
import com.sismics.docs.core.dao.jpa.dto.TagDto;
|
||||
import com.sismics.docs.core.event.DocumentCreatedAsyncEvent;
|
||||
import com.sismics.docs.core.event.DocumentDeletedAsyncEvent;
|
||||
@ -93,16 +94,11 @@ public class DocumentResource extends BaseResource {
|
||||
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
AclDao aclDao = new AclDao();
|
||||
DocumentDto documentDto = documentDao.getDocument(documentId);
|
||||
DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId));
|
||||
if (documentDto == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
// Check document visibility
|
||||
if (!aclDao.checkPermission(documentId, PermType.READ, shareId == null ? principal.getId() : shareId)) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
JsonObjectBuilder document = Json.createObjectBuilder()
|
||||
.add("id", documentDto.getId())
|
||||
.add("title", documentDto.getTitle())
|
||||
@ -152,7 +148,8 @@ public class DocumentResource extends BaseResource {
|
||||
.add("type", aclDto.getTargetType()));
|
||||
|
||||
if (!principal.isAnonymous()
|
||||
&& aclDto.getTargetId().equals(principal.getId())
|
||||
&& (aclDto.getTargetId().equals(principal.getId())
|
||||
|| principal.getGroupIdSet().contains(aclDto.getTargetId()))
|
||||
&& aclDto.getPerm() == PermType.WRITE) {
|
||||
// The document is writable for the current user
|
||||
writable = true;
|
||||
@ -172,6 +169,18 @@ public class DocumentResource extends BaseResource {
|
||||
}
|
||||
document.add("contributors", contributorList);
|
||||
|
||||
// Add relations
|
||||
RelationDao relationDao = new RelationDao();
|
||||
List<RelationDto> relationDtoList = relationDao.getByDocumentId(documentId);
|
||||
JsonArrayBuilder relationList = Json.createArrayBuilder();
|
||||
for (RelationDto relationDto : relationDtoList) {
|
||||
relationList.add(Json.createObjectBuilder()
|
||||
.add("id", relationDto.getId())
|
||||
.add("title", relationDto.getTitle())
|
||||
.add("source", relationDto.isSource()));
|
||||
}
|
||||
document.add("relations", relationList);
|
||||
|
||||
return Response.ok().entity(document.build()).build();
|
||||
}
|
||||
|
||||
@ -186,8 +195,8 @@ public class DocumentResource extends BaseResource {
|
||||
public Response getPdf(
|
||||
@PathParam("id") String documentId,
|
||||
@QueryParam("share") String shareId,
|
||||
@QueryParam("metadata") Boolean metadata,
|
||||
@QueryParam("comments") Boolean comments,
|
||||
final @QueryParam("metadata") Boolean metadata,
|
||||
final @QueryParam("comments") Boolean comments,
|
||||
final @QueryParam("fitimagetopage") Boolean fitImageToPage,
|
||||
@QueryParam("margin") String marginStr) {
|
||||
authenticate();
|
||||
@ -197,8 +206,8 @@ public class DocumentResource extends BaseResource {
|
||||
|
||||
// Get document and check read permission
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
Document document = documentDao.getDocument(documentId, PermType.READ, shareId == null ? principal.getId() : shareId);
|
||||
if (document == null) {
|
||||
final DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId));
|
||||
if (documentDto == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
@ -217,7 +226,7 @@ public class DocumentResource extends BaseResource {
|
||||
StreamingOutput stream = new StreamingOutput() {
|
||||
@Override
|
||||
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
|
||||
try (InputStream inputStream = PdfUtil.convertToPdf(fileList, fitImageToPage, margin)) {
|
||||
try (InputStream inputStream = PdfUtil.convertToPdf(documentDto, fileList, fitImageToPage, metadata, margin)) {
|
||||
ByteStreams.copy(inputStream, outputStream);
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
@ -229,7 +238,7 @@ public class DocumentResource extends BaseResource {
|
||||
|
||||
return Response.ok(stream)
|
||||
.header("Content-Type", MimeType.APPLICATION_PDF)
|
||||
.header("Content-Disposition", "inline; filename=\"" + document.getTitle() + ".pdf\"")
|
||||
.header("Content-Disposition", "inline; filename=\"" + documentDto.getTitle() + ".pdf\"")
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -260,7 +269,7 @@ public class DocumentResource extends BaseResource {
|
||||
PaginatedList<DocumentDto> paginatedList = PaginatedLists.create(limit, offset);
|
||||
SortCriteria sortCriteria = new SortCriteria(sortColumn, asc);
|
||||
DocumentCriteria documentCriteria = parseSearchQuery(search);
|
||||
documentCriteria.setUserId(principal.getId());
|
||||
documentCriteria.setTargetIdList(getTargetIdList(null));
|
||||
try {
|
||||
documentDao.findByCriteria(paginatedList, documentCriteria, sortCriteria);
|
||||
} catch (Exception e) {
|
||||
@ -430,6 +439,7 @@ public class DocumentResource extends BaseResource {
|
||||
@FormParam("coverage") String coverage,
|
||||
@FormParam("rights") String rights,
|
||||
@FormParam("tags") List<String> tagList,
|
||||
@FormParam("relations") List<String> relationList,
|
||||
@FormParam("language") String language,
|
||||
@FormParam("create_date") String createDateStr) {
|
||||
if (!authenticate()) {
|
||||
@ -493,6 +503,9 @@ public class DocumentResource extends BaseResource {
|
||||
// Update tags
|
||||
updateTagList(documentId, tagList);
|
||||
|
||||
// Update relations
|
||||
updateRelationList(documentId, relationList);
|
||||
|
||||
// Raise a document created event
|
||||
DocumentCreatedAsyncEvent documentCreatedAsyncEvent = new DocumentCreatedAsyncEvent();
|
||||
documentCreatedAsyncEvent.setUserId(principal.getId());
|
||||
@ -526,6 +539,7 @@ public class DocumentResource extends BaseResource {
|
||||
@FormParam("coverage") String coverage,
|
||||
@FormParam("rights") String rights,
|
||||
@FormParam("tags") List<String> tagList,
|
||||
@FormParam("relations") List<String> relationList,
|
||||
@FormParam("language") String language,
|
||||
@FormParam("create_date") String createDateStr) {
|
||||
if (!authenticate()) {
|
||||
@ -533,8 +547,8 @@ public class DocumentResource extends BaseResource {
|
||||
}
|
||||
|
||||
// Validate input data
|
||||
title = ValidationUtil.validateLength(title, "title", 1, 100, true);
|
||||
language = ValidationUtil.validateLength(language, "language", 3, 3, true);
|
||||
title = ValidationUtil.validateLength(title, "title", 1, 100, false);
|
||||
language = ValidationUtil.validateLength(language, "language", 3, 3, false);
|
||||
description = ValidationUtil.validateLength(description, "description", 0, 4000, true);
|
||||
subject = ValidationUtil.validateLength(subject, "subject", 0, 500, true);
|
||||
identifier = ValidationUtil.validateLength(identifier, "identifier", 0, 500, true);
|
||||
@ -549,61 +563,49 @@ public class DocumentResource extends BaseResource {
|
||||
throw new ClientException("ValidationError", MessageFormat.format("{0} is not a supported language", language));
|
||||
}
|
||||
|
||||
// Check write permission
|
||||
AclDao aclDao = new AclDao();
|
||||
if (!aclDao.checkPermission(id, PermType.WRITE, getTargetIdList(null))) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Get the document
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
Document document;
|
||||
document = documentDao.getDocument(id, PermType.WRITE, principal.getId());
|
||||
Document document = documentDao.getById(id);
|
||||
if (document == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
// Update the document
|
||||
if (!StringUtils.isEmpty(title)) {
|
||||
document.setTitle(title);
|
||||
}
|
||||
if (!StringUtils.isEmpty(description)) {
|
||||
document.setDescription(description);
|
||||
}
|
||||
if (!StringUtils.isEmpty(subject)) {
|
||||
document.setSubject(subject);
|
||||
}
|
||||
if (!StringUtils.isEmpty(identifier)) {
|
||||
document.setIdentifier(identifier);
|
||||
}
|
||||
if (!StringUtils.isEmpty(publisher)) {
|
||||
document.setPublisher(publisher);
|
||||
}
|
||||
if (!StringUtils.isEmpty(format)) {
|
||||
document.setFormat(format);
|
||||
}
|
||||
if (!StringUtils.isEmpty(source)) {
|
||||
document.setSource(source);
|
||||
}
|
||||
if (!StringUtils.isEmpty(type)) {
|
||||
document.setType(type);
|
||||
}
|
||||
if (!StringUtils.isEmpty(coverage)) {
|
||||
document.setCoverage(coverage);
|
||||
}
|
||||
if (!StringUtils.isEmpty(rights)) {
|
||||
document.setRights(rights);
|
||||
}
|
||||
if (createDate != null) {
|
||||
document.setTitle(title);
|
||||
document.setDescription(description);
|
||||
document.setSubject(subject);
|
||||
document.setIdentifier(identifier);
|
||||
document.setPublisher(publisher);
|
||||
document.setFormat(format);
|
||||
document.setSource(source);
|
||||
document.setType(type);
|
||||
document.setCoverage(coverage);
|
||||
document.setRights(rights);
|
||||
document.setLanguage(language);
|
||||
if (createDate == null) {
|
||||
document.setCreateDate(new Date());
|
||||
} else {
|
||||
document.setCreateDate(createDate);
|
||||
}
|
||||
if (language != null) {
|
||||
document.setLanguage(language);
|
||||
}
|
||||
|
||||
document = documentDao.update(document, principal.getId());
|
||||
|
||||
// Update tags
|
||||
updateTagList(id, tagList);
|
||||
|
||||
// Raise a document updated event
|
||||
// Update relations
|
||||
updateRelationList(id, relationList);
|
||||
|
||||
// Raise a document updated event (with the document to update Lucene)
|
||||
DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent();
|
||||
documentUpdatedAsyncEvent.setUserId(principal.getId());
|
||||
documentUpdatedAsyncEvent.setDocument(document);
|
||||
documentUpdatedAsyncEvent.setDocumentId(id);
|
||||
AppContext.getInstance().getAsyncEventBus().post(documentUpdatedAsyncEvent);
|
||||
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
@ -636,6 +638,28 @@ public class DocumentResource extends BaseResource {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update relations list on a document.
|
||||
*
|
||||
* @param documentId Document ID
|
||||
* @param relationList Relation ID list
|
||||
*/
|
||||
private void updateRelationList(String documentId, List<String> relationList) {
|
||||
if (relationList != null) {
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
RelationDao relationDao = new RelationDao();
|
||||
Set<String> documentIdSet = new HashSet<>();
|
||||
for (String targetDocId : relationList) {
|
||||
// ACL are not checked, because the editing user is not forced to view the target document
|
||||
Document document = documentDao.getById(targetDocId);
|
||||
if (document != null && !documentId.equals(targetDocId)) {
|
||||
documentIdSet.add(targetDocId);
|
||||
}
|
||||
}
|
||||
relationDao.updateRelationList(documentId, documentIdSet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a document.
|
||||
*
|
||||
@ -653,14 +677,14 @@ public class DocumentResource extends BaseResource {
|
||||
// Get the document
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
FileDao fileDao = new FileDao();
|
||||
Document document = documentDao.getDocument(id, PermType.WRITE, principal.getId());
|
||||
List<File> fileList = fileDao.getByDocumentId(principal.getId(), id);
|
||||
if (document == null) {
|
||||
DocumentDto documentDto = documentDao.getDocument(id, PermType.WRITE, getTargetIdList(null));
|
||||
if (documentDto == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
List<File> fileList = fileDao.getByDocumentId(principal.getId(), id);
|
||||
|
||||
// Delete the document
|
||||
documentDao.delete(document.getId(), principal.getId());
|
||||
documentDao.delete(documentDto.getId(), principal.getId());
|
||||
|
||||
// Raise file deleted events (don't bother sending document updated event)
|
||||
for (File file : fileList) {
|
||||
@ -673,7 +697,7 @@ public class DocumentResource extends BaseResource {
|
||||
// Raise a document deleted event
|
||||
DocumentDeletedAsyncEvent documentDeletedAsyncEvent = new DocumentDeletedAsyncEvent();
|
||||
documentDeletedAsyncEvent.setUserId(principal.getId());
|
||||
documentDeletedAsyncEvent.setDocument(document);
|
||||
documentDeletedAsyncEvent.setDocumentId(documentDto.getId());
|
||||
AppContext.getInstance().getAsyncEventBus().post(documentDeletedAsyncEvent);
|
||||
|
||||
// Always return OK
|
||||
|
@ -48,7 +48,6 @@ import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent;
|
||||
import com.sismics.docs.core.event.FileCreatedAsyncEvent;
|
||||
import com.sismics.docs.core.event.FileDeletedAsyncEvent;
|
||||
import com.sismics.docs.core.model.context.AppContext;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
import com.sismics.docs.core.util.DirectoryUtil;
|
||||
@ -94,13 +93,13 @@ public class FileResource extends BaseResource {
|
||||
User user = userDao.getById(principal.getId());
|
||||
|
||||
// Get the document
|
||||
Document document = null;
|
||||
DocumentDto documentDto = null;
|
||||
if (Strings.isNullOrEmpty(documentId)) {
|
||||
documentId = null;
|
||||
} else {
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
document = documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
|
||||
if (document == null) {
|
||||
documentDto = documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null));
|
||||
if (documentDto == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
}
|
||||
@ -165,7 +164,7 @@ public class FileResource extends BaseResource {
|
||||
if (documentId != null) {
|
||||
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
|
||||
fileCreatedAsyncEvent.setUserId(principal.getId());
|
||||
fileCreatedAsyncEvent.setDocument(document);
|
||||
fileCreatedAsyncEvent.setLanguage(documentDto.getLanguage());
|
||||
fileCreatedAsyncEvent.setFile(file);
|
||||
fileCreatedAsyncEvent.setInputStream(fileInputStream);
|
||||
fileCreatedAsyncEvent.setPdfInputStream(pdfIntputStream);
|
||||
@ -173,7 +172,7 @@ public class FileResource extends BaseResource {
|
||||
|
||||
DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent();
|
||||
documentUpdatedAsyncEvent.setUserId(principal.getId());
|
||||
documentUpdatedAsyncEvent.setDocument(document);
|
||||
documentUpdatedAsyncEvent.setDocumentId(documentId);
|
||||
AppContext.getInstance().getAsyncEventBus().post(documentUpdatedAsyncEvent);
|
||||
}
|
||||
|
||||
@ -214,8 +213,8 @@ public class FileResource extends BaseResource {
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
FileDao fileDao = new FileDao();
|
||||
File file = fileDao.getFile(id, principal.getId());
|
||||
Document document = documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
|
||||
if (file == null || document == null) {
|
||||
DocumentDto documentDto = documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null));
|
||||
if (file == null || documentDto == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
@ -236,14 +235,14 @@ public class FileResource extends BaseResource {
|
||||
final InputStream responseInputStream = EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey());
|
||||
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
|
||||
fileCreatedAsyncEvent.setUserId(principal.getId());
|
||||
fileCreatedAsyncEvent.setDocument(document);
|
||||
fileCreatedAsyncEvent.setLanguage(documentDto.getLanguage());
|
||||
fileCreatedAsyncEvent.setFile(file);
|
||||
fileCreatedAsyncEvent.setInputStream(responseInputStream);
|
||||
AppContext.getInstance().getAsyncEventBus().post(fileCreatedAsyncEvent);
|
||||
|
||||
DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent();
|
||||
documentUpdatedAsyncEvent.setUserId(principal.getId());
|
||||
documentUpdatedAsyncEvent.setDocument(document);
|
||||
documentUpdatedAsyncEvent.setDocumentId(documentId);
|
||||
AppContext.getInstance().getAsyncEventBus().post(documentUpdatedAsyncEvent);
|
||||
} catch (Exception e) {
|
||||
throw new ClientException("AttachError", "Error attaching file to document", e);
|
||||
@ -277,7 +276,7 @@ public class FileResource extends BaseResource {
|
||||
|
||||
// Get the document
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
if (documentDao.getDocument(documentId, PermType.WRITE, principal.getId()) == null) {
|
||||
if (documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null)) == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
@ -313,7 +312,7 @@ public class FileResource extends BaseResource {
|
||||
// Check document visibility
|
||||
if (documentId != null) {
|
||||
AclDao aclDao = new AclDao();
|
||||
if (!aclDao.checkPermission(documentId, PermType.READ, shareId == null ? principal.getId() : shareId)) {
|
||||
if (!aclDao.checkPermission(documentId, PermType.READ, getTargetIdList(shareId))) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
} else if (!authenticated) {
|
||||
@ -364,14 +363,14 @@ public class FileResource extends BaseResource {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
Document document = null;
|
||||
DocumentDto documentDto = null;
|
||||
if (file.getDocumentId() == null) {
|
||||
// It's an orphan file
|
||||
if (!file.getUserId().equals(principal.getId())) {
|
||||
// But not ours
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
} else if ((document = documentDao.getDocument(file.getDocumentId(), PermType.WRITE, principal.getId())) == null) {
|
||||
} else if ((documentDto = documentDao.getDocument(file.getDocumentId(), PermType.WRITE, getTargetIdList(null))) == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
@ -395,11 +394,11 @@ public class FileResource extends BaseResource {
|
||||
fileDeletedAsyncEvent.setFile(file);
|
||||
AppContext.getInstance().getAsyncEventBus().post(fileDeletedAsyncEvent);
|
||||
|
||||
if (document != null) {
|
||||
if (documentDto != null) {
|
||||
// Raise a new document updated
|
||||
DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent();
|
||||
documentUpdatedAsyncEvent.setUserId(principal.getId());
|
||||
documentUpdatedAsyncEvent.setDocument(document);
|
||||
documentUpdatedAsyncEvent.setDocumentId(documentDto.getId());
|
||||
AppContext.getInstance().getAsyncEventBus().post(documentUpdatedAsyncEvent);
|
||||
}
|
||||
|
||||
@ -446,7 +445,7 @@ public class FileResource extends BaseResource {
|
||||
} else {
|
||||
// Check document accessibility
|
||||
AclDao aclDao = new AclDao();
|
||||
if (!aclDao.checkPermission(file.getDocumentId(), PermType.READ, shareId == null ? principal.getId() : shareId)) {
|
||||
if (!aclDao.checkPermission(file.getDocumentId(), PermType.READ, getTargetIdList(shareId))) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
}
|
||||
@ -520,17 +519,11 @@ public class FileResource extends BaseResource {
|
||||
|
||||
// Get the document
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
DocumentDto documentDto = documentDao.getDocument(documentId);
|
||||
DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId));
|
||||
if (documentDto == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
// Check document visibility
|
||||
AclDao aclDao = new AclDao();
|
||||
if (!aclDao.checkPermission(documentId, PermType.READ, shareId == null ? principal.getId() : shareId)) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Get files and user associated with this document
|
||||
FileDao fileDao = new FileDao();
|
||||
final UserDao userDao = new UserDao();
|
||||
|
@ -0,0 +1,342 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonArrayBuilder;
|
||||
import javax.json.JsonObjectBuilder;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.sismics.docs.core.dao.jpa.GroupDao;
|
||||
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.dto.GroupDto;
|
||||
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||
import com.sismics.docs.core.model.jpa.Group;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
import com.sismics.docs.core.model.jpa.UserGroup;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.docs.rest.constant.BaseFunction;
|
||||
import com.sismics.rest.exception.ClientException;
|
||||
import com.sismics.rest.exception.ForbiddenClientException;
|
||||
import com.sismics.rest.util.JsonUtil;
|
||||
import com.sismics.rest.util.ValidationUtil;
|
||||
|
||||
/**
|
||||
* Group REST resources.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
@Path("/group")
|
||||
public class GroupResource extends BaseResource {
|
||||
/**
|
||||
* Add a group.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
@PUT
|
||||
public Response add(@FormParam("parent") String parentName,
|
||||
@FormParam("name") String name) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
// Validate input
|
||||
name = ValidationUtil.validateLength(name, "name", 1, 50, false);
|
||||
ValidationUtil.validateAlphanumeric(name, "name");
|
||||
|
||||
// Avoid duplicates
|
||||
GroupDao groupDao = new GroupDao();
|
||||
Group existingGroup = groupDao.getActiveByName(name);
|
||||
if (existingGroup != null) {
|
||||
throw new ClientException("GroupAlreadyExists", MessageFormat.format("This group already exists: {0}", name));
|
||||
}
|
||||
|
||||
// Validate parent
|
||||
String parentId = null;
|
||||
if (!Strings.isNullOrEmpty(parentName)) {
|
||||
Group parentGroup = groupDao.getActiveByName(parentName);
|
||||
if (parentGroup == null) {
|
||||
throw new ClientException("ParentGroupNotFound", MessageFormat.format("This group does not exists: {0}", parentName));
|
||||
}
|
||||
parentId = parentGroup.getId();
|
||||
}
|
||||
|
||||
// Create the group
|
||||
groupDao.create(new Group()
|
||||
.setName(name)
|
||||
.setParentId(parentId), principal.getId());
|
||||
|
||||
// Always return OK
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("status", "ok");
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a group.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
@POST
|
||||
@Path("{groupName: [a-zA-Z0-9_]+}")
|
||||
public Response update(@PathParam("groupName") String groupName,
|
||||
@FormParam("parent") String parentName,
|
||||
@FormParam("name") String name) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
// Validate input
|
||||
name = ValidationUtil.validateLength(name, "name", 1, 50, false);
|
||||
ValidationUtil.validateAlphanumeric(name, "name");
|
||||
|
||||
// Get the group (by its old name)
|
||||
GroupDao groupDao = new GroupDao();
|
||||
Group group = groupDao.getActiveByName(groupName);
|
||||
if (group == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
// Avoid duplicates
|
||||
Group existingGroup = groupDao.getActiveByName(name);
|
||||
if (existingGroup != null && existingGroup.getId() != group.getId()) {
|
||||
throw new ClientException("GroupAlreadyExists", MessageFormat.format("This group already exists: {0}", name));
|
||||
}
|
||||
|
||||
// Validate parent
|
||||
String parentId = null;
|
||||
if (!Strings.isNullOrEmpty(parentName)) {
|
||||
Group parentGroup = groupDao.getActiveByName(parentName);
|
||||
if (parentGroup == null) {
|
||||
throw new ClientException("ParentGroupNotFound", MessageFormat.format("This group does not exists: {0}", parentName));
|
||||
}
|
||||
parentId = parentGroup.getId();
|
||||
}
|
||||
|
||||
// Update the group
|
||||
groupDao.update(group.setName(name)
|
||||
.setParentId(parentId), principal.getId());
|
||||
|
||||
// Always return OK
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("status", "ok");
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a group.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
@DELETE
|
||||
@Path("{groupName: [a-zA-Z0-9_]+}")
|
||||
public Response delete(@PathParam("groupName") String groupName) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
// Get the group
|
||||
GroupDao groupDao = new GroupDao();
|
||||
Group group = groupDao.getActiveByName(groupName);
|
||||
if (group == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
// Delete the group
|
||||
groupDao.delete(group.getId(), principal.getId());
|
||||
|
||||
// Always return OK
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("status", "ok");
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a user to a group.
|
||||
*
|
||||
* @param groupName Group name
|
||||
* @param username Username
|
||||
* @return Response
|
||||
*/
|
||||
@PUT
|
||||
@Path("{groupName: [a-zA-Z0-9_]+}")
|
||||
public Response addMember(@PathParam("groupName") String groupName,
|
||||
@FormParam("username") String username) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
// Validate input
|
||||
groupName = ValidationUtil.validateLength(groupName, "name", 1, 50, false);
|
||||
username = ValidationUtil.validateLength(username, "username", 1, 50, false);
|
||||
|
||||
// Get the group
|
||||
GroupDao groupDao = new GroupDao();
|
||||
Group group = groupDao.getActiveByName(groupName);
|
||||
if (group == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
// Get the user
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(username);
|
||||
if (user == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
// Avoid duplicates
|
||||
List<GroupDto> groupDtoList = groupDao.findByCriteria(new GroupCriteria().setUserId(user.getId()), null);
|
||||
boolean found = false;
|
||||
for (GroupDto groupDto : groupDtoList) {
|
||||
if (groupDto.getId().equals(group.getId())) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// Add the membership
|
||||
UserGroup userGroup = new UserGroup();
|
||||
userGroup.setGroupId(group.getId());
|
||||
userGroup.setUserId(user.getId());
|
||||
groupDao.addMember(userGroup);
|
||||
}
|
||||
|
||||
// Always return OK
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("status", "ok");
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an user from a group.
|
||||
*
|
||||
* @param groupName Group name
|
||||
* @param username Username
|
||||
* @return Response
|
||||
*/
|
||||
@DELETE
|
||||
@Path("{groupName: [a-zA-Z0-9_]+}/{username: [a-zA-Z0-9_]+}")
|
||||
public Response removeMember(@PathParam("groupName") String groupName,
|
||||
@PathParam("username") String username) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
// Validate input
|
||||
groupName = ValidationUtil.validateLength(groupName, "name", 1, 50, false);
|
||||
username = ValidationUtil.validateLength(username, "username", 1, 50, false);
|
||||
|
||||
// Get the group
|
||||
GroupDao groupDao = new GroupDao();
|
||||
Group group = groupDao.getActiveByName(groupName);
|
||||
if (group == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
// Get the user
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(username);
|
||||
if (user == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
// Remove the membership
|
||||
groupDao.removeMember(group.getId(), user.getId());
|
||||
|
||||
// Always return OK
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("status", "ok");
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all active groups.
|
||||
*
|
||||
* @param sortColumn Sort index
|
||||
* @param asc If true, ascending sorting, else descending
|
||||
* @return Response
|
||||
*/
|
||||
@GET
|
||||
public Response list(
|
||||
@QueryParam("sort_column") Integer sortColumn,
|
||||
@QueryParam("asc") Boolean asc) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
JsonArrayBuilder groups = Json.createArrayBuilder();
|
||||
SortCriteria sortCriteria = new SortCriteria(sortColumn, asc);
|
||||
|
||||
GroupDao groupDao = new GroupDao();
|
||||
List<GroupDto> groupDtoList = groupDao.findByCriteria(new GroupCriteria(), sortCriteria);
|
||||
for (GroupDto groupDto : groupDtoList) {
|
||||
groups.add(Json.createObjectBuilder()
|
||||
.add("name", groupDto.getName())
|
||||
.add("parent", JsonUtil.nullable(groupDto.getParentName())));
|
||||
}
|
||||
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("groups", groups);
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a group.
|
||||
*
|
||||
* @param groupName Group name
|
||||
* @return Response
|
||||
*/
|
||||
@GET
|
||||
@Path("{groupName: [a-zA-Z0-9_]+}")
|
||||
public Response get(@PathParam("groupName") String groupName) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Get the group
|
||||
GroupDao groupDao = new GroupDao();
|
||||
Group group = groupDao.getActiveByName(groupName);
|
||||
if (group == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
// Build the response
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("name", group.getName());
|
||||
|
||||
// Get the parent
|
||||
if (group.getParentId() != null) {
|
||||
Group parentGroup = groupDao.getActiveById(group.getParentId());
|
||||
response.add("parent", parentGroup.getName());
|
||||
}
|
||||
|
||||
// Members
|
||||
JsonArrayBuilder members = Json.createArrayBuilder();
|
||||
UserDao userDao = new UserDao();
|
||||
List<UserDto> userDtoList = userDao.findByCriteria(new UserCriteria().setGroupId(group.getId()), new SortCriteria(1, true));
|
||||
for (UserDto userDto : userDtoList) {
|
||||
members.add(userDto.getUsername());
|
||||
}
|
||||
response.add("members", members);
|
||||
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ public class ShareResource extends BaseResource {
|
||||
* Add a share to a document.
|
||||
*
|
||||
* @param documentId Document ID
|
||||
* @param name Share name
|
||||
* @return Response
|
||||
*/
|
||||
@PUT
|
||||
@ -53,7 +54,7 @@ public class ShareResource extends BaseResource {
|
||||
|
||||
// Get the document
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
if (documentDao.getDocument(documentId, PermType.WRITE, principal.getId()) == null) {
|
||||
if (documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null)) == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
@ -102,7 +103,7 @@ public class ShareResource extends BaseResource {
|
||||
}
|
||||
|
||||
Acl acl = aclList.get(0);
|
||||
if (!aclDao.checkPermission(acl.getSourceId(), PermType.WRITE, principal.getId())) {
|
||||
if (!aclDao.checkPermission(acl.getSourceId(), PermType.WRITE, getTargetIdList(null))) {
|
||||
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", acl.getSourceId()));
|
||||
}
|
||||
|
||||
|
@ -25,13 +25,17 @@ import javax.ws.rs.core.Response;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.sismics.docs.core.constant.Constants;
|
||||
import com.sismics.docs.core.dao.jpa.AuthenticationTokenDao;
|
||||
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||
import com.sismics.docs.core.dao.jpa.FileDao;
|
||||
import com.sismics.docs.core.dao.jpa.GroupDao;
|
||||
import com.sismics.docs.core.dao.jpa.RoleBaseFunctionDao;
|
||||
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.dto.GroupDto;
|
||||
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||
import com.sismics.docs.core.event.DocumentDeletedAsyncEvent;
|
||||
import com.sismics.docs.core.event.FileDeletedAsyncEvent;
|
||||
@ -39,10 +43,9 @@ import com.sismics.docs.core.model.context.AppContext;
|
||||
import com.sismics.docs.core.model.jpa.AuthenticationToken;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.docs.core.model.jpa.Group;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
import com.sismics.docs.core.util.EncryptionUtil;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.docs.rest.constant.BaseFunction;
|
||||
import com.sismics.rest.exception.ClientException;
|
||||
@ -299,14 +302,7 @@ public class UserResource extends BaseResource {
|
||||
}
|
||||
|
||||
// Get the value of the session token
|
||||
String authToken = null;
|
||||
if (request.getCookies() != null) {
|
||||
for (Cookie cookie : request.getCookies()) {
|
||||
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())) {
|
||||
authToken = cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
String authToken = getAuthToken();
|
||||
|
||||
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
|
||||
AuthenticationToken authenticationToken = null;
|
||||
@ -328,7 +324,7 @@ public class UserResource extends BaseResource {
|
||||
|
||||
// Deletes the client token in the HTTP response
|
||||
JsonObjectBuilder response = Json.createObjectBuilder();
|
||||
NewCookie cookie = new NewCookie(TokenBasedSecurityFilter.COOKIE_NAME, null);
|
||||
NewCookie cookie = new NewCookie(TokenBasedSecurityFilter.COOKIE_NAME, null, "/", null, 1, null, -1, new Date(1), false, false);
|
||||
return Response.ok().entity(response.build()).cookie(cookie).build();
|
||||
}
|
||||
|
||||
@ -362,7 +358,7 @@ public class UserResource extends BaseResource {
|
||||
for (Document document : documentList) {
|
||||
DocumentDeletedAsyncEvent documentDeletedAsyncEvent = new DocumentDeletedAsyncEvent();
|
||||
documentDeletedAsyncEvent.setUserId(principal.getId());
|
||||
documentDeletedAsyncEvent.setDocument(document);
|
||||
documentDeletedAsyncEvent.setDocumentId(document.getId());
|
||||
AppContext.getInstance().getAsyncEventBus().post(documentDeletedAsyncEvent);
|
||||
}
|
||||
|
||||
@ -403,7 +399,7 @@ public class UserResource extends BaseResource {
|
||||
|
||||
// Ensure that the admin user is not deleted
|
||||
RoleBaseFunctionDao userBaseFuction = new RoleBaseFunctionDao();
|
||||
Set<String> baseFunctionSet = userBaseFuction.findByRoleId(user.getRoleId());
|
||||
Set<String> baseFunctionSet = userBaseFuction.findByRoleId(Sets.newHashSet(user.getRoleId()));
|
||||
if (baseFunctionSet.contains(BaseFunction.ADMIN.name())) {
|
||||
throw new ClientException("ForbiddenError", "The admin user cannot be deleted");
|
||||
}
|
||||
@ -421,7 +417,7 @@ public class UserResource extends BaseResource {
|
||||
for (Document document : documentList) {
|
||||
DocumentDeletedAsyncEvent documentDeletedAsyncEvent = new DocumentDeletedAsyncEvent();
|
||||
documentDeletedAsyncEvent.setUserId(principal.getId());
|
||||
documentDeletedAsyncEvent.setDocument(document);
|
||||
documentDeletedAsyncEvent.setDocumentId(document.getId());
|
||||
AppContext.getInstance().getAsyncEventBus().post(documentDeletedAsyncEvent);
|
||||
}
|
||||
|
||||
@ -457,18 +453,39 @@ public class UserResource extends BaseResource {
|
||||
response.add("is_default_password", Constants.DEFAULT_ADMIN_PASSWORD.equals(adminUser.getPassword()));
|
||||
}
|
||||
} else {
|
||||
// Update the last connection date
|
||||
String authToken = getAuthToken();
|
||||
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
|
||||
authenticationTokenDao.updateLastConnectionDate(authToken);
|
||||
|
||||
// Build the response
|
||||
response.add("anonymous", false);
|
||||
UserDao userDao = new UserDao();
|
||||
GroupDao groupDao = new GroupDao();
|
||||
User user = userDao.getById(principal.getId());
|
||||
List<GroupDto> groupDtoList = groupDao.findByCriteria(new GroupCriteria()
|
||||
.setUserId(user.getId())
|
||||
.setRecursive(true), null);
|
||||
|
||||
response.add("username", user.getUsername())
|
||||
.add("email", user.getEmail())
|
||||
.add("storage_quota", user.getStorageQuota())
|
||||
.add("storage_current", user.getStorageCurrent());
|
||||
|
||||
// Base functions
|
||||
JsonArrayBuilder baseFunctions = Json.createArrayBuilder();
|
||||
for (String baseFunction : ((UserPrincipal) principal).getBaseFunctionSet()) {
|
||||
baseFunctions.add(baseFunction);
|
||||
}
|
||||
|
||||
// Groups
|
||||
JsonArrayBuilder groups = Json.createArrayBuilder();
|
||||
for (GroupDto groupDto : groupDtoList) {
|
||||
groups.add(groupDto.getName());
|
||||
}
|
||||
|
||||
response.add("base_functions", baseFunctions)
|
||||
.add("groups", groups)
|
||||
.add("is_default_password", hasBaseFunction(BaseFunction.ADMIN) && Constants.DEFAULT_ADMIN_PASSWORD.equals(user.getPassword()));
|
||||
}
|
||||
|
||||
@ -495,8 +512,19 @@ public class UserResource extends BaseResource {
|
||||
throw new ClientException("UserNotFound", "The user doesn't exist");
|
||||
}
|
||||
|
||||
// Groups
|
||||
GroupDao groupDao = new GroupDao();
|
||||
List<GroupDto> groupDtoList = groupDao.findByCriteria(
|
||||
new GroupCriteria().setUserId(user.getId()),
|
||||
new SortCriteria(1, true));
|
||||
JsonArrayBuilder groups = Json.createArrayBuilder();
|
||||
for (GroupDto groupDto : groupDtoList) {
|
||||
groups.add(groupDto.getName());
|
||||
}
|
||||
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("username", user.getUsername())
|
||||
.add("groups", groups)
|
||||
.add("email", user.getEmail())
|
||||
.add("storage_quota", user.getStorageQuota())
|
||||
.add("storage_current", user.getStorageCurrent());
|
||||
@ -506,30 +534,37 @@ public class UserResource extends BaseResource {
|
||||
/**
|
||||
* Returns all active users.
|
||||
*
|
||||
* @param limit Page limit
|
||||
* @param offset Page offset
|
||||
* @param sortColumn Sort index
|
||||
* @param asc If true, ascending sorting, else descending
|
||||
* @param groupName Only return users from this group
|
||||
* @return Response
|
||||
*/
|
||||
@GET
|
||||
@Path("list")
|
||||
public Response list(
|
||||
@QueryParam("limit") Integer limit,
|
||||
@QueryParam("offset") Integer offset,
|
||||
@QueryParam("sort_column") Integer sortColumn,
|
||||
@QueryParam("asc") Boolean asc) {
|
||||
@QueryParam("asc") Boolean asc,
|
||||
@QueryParam("group") String groupName) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
JsonArrayBuilder users = Json.createArrayBuilder();
|
||||
PaginatedList<UserDto> paginatedList = PaginatedLists.create(limit, offset);
|
||||
SortCriteria sortCriteria = new SortCriteria(sortColumn, asc);
|
||||
|
||||
// Validate the group
|
||||
String groupId = null;
|
||||
if (!Strings.isNullOrEmpty(groupName)) {
|
||||
GroupDao groupDao = new GroupDao();
|
||||
Group group = groupDao.getActiveByName(groupName);
|
||||
if (group != null) {
|
||||
groupId = group.getId();
|
||||
}
|
||||
}
|
||||
|
||||
UserDao userDao = new UserDao();
|
||||
userDao.findByCriteria(paginatedList, new UserCriteria(), sortCriteria);
|
||||
for (UserDto userDto : paginatedList.getResultList()) {
|
||||
List<UserDto> userDtoList = userDao.findByCriteria(new UserCriteria().setGroupId(groupId), sortCriteria);
|
||||
for (UserDto userDto : userDtoList) {
|
||||
users.add(Json.createObjectBuilder()
|
||||
.add("id", userDto.getId())
|
||||
.add("username", userDto.getUsername())
|
||||
@ -540,7 +575,6 @@ public class UserResource extends BaseResource {
|
||||
}
|
||||
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("total", paginatedList.getResultCount())
|
||||
.add("users", users);
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
@ -558,14 +592,7 @@ public class UserResource extends BaseResource {
|
||||
}
|
||||
|
||||
// Get the value of the session token
|
||||
String authToken = null;
|
||||
if (request.getCookies() != null) {
|
||||
for (Cookie cookie : request.getCookies()) {
|
||||
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())) {
|
||||
authToken = cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
String authToken = getAuthToken();
|
||||
|
||||
JsonArrayBuilder sessions = Json.createArrayBuilder();
|
||||
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
|
||||
@ -600,14 +627,7 @@ public class UserResource extends BaseResource {
|
||||
}
|
||||
|
||||
// Get the value of the session token
|
||||
String authToken = null;
|
||||
if (request.getCookies() != null) {
|
||||
for (Cookie cookie : request.getCookies()) {
|
||||
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())) {
|
||||
authToken = cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
String authToken = getAuthToken();
|
||||
|
||||
// Remove other tokens
|
||||
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
|
||||
@ -618,4 +638,21 @@ public class UserResource extends BaseResource {
|
||||
.add("status", "ok");
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication token value.
|
||||
*
|
||||
* @return Token value
|
||||
*/
|
||||
private String getAuthToken() {
|
||||
if (request.getCookies() != null) {
|
||||
for (Cookie cookie : request.getCookies()) {
|
||||
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())
|
||||
&& !Strings.isNullOrEmpty(cookie.getValue())) {
|
||||
return cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -106,6 +106,33 @@ angular.module('docs',
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('settings.group', {
|
||||
url: '/group',
|
||||
views: {
|
||||
'settings': {
|
||||
templateUrl: 'partial/docs/settings.group.html',
|
||||
controller: 'SettingsGroup'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('settings.group.edit', {
|
||||
url: '/edit/:name',
|
||||
views: {
|
||||
'group': {
|
||||
templateUrl: 'partial/docs/settings.group.edit.html',
|
||||
controller: 'SettingsGroupEdit'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('settings.group.add', {
|
||||
url: '/add',
|
||||
views: {
|
||||
'group': {
|
||||
templateUrl: 'partial/docs/settings.group.edit.html',
|
||||
controller: 'SettingsGroupEdit'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('settings.vocabulary', {
|
||||
url: '/vocabulary',
|
||||
views: {
|
||||
@ -221,19 +248,37 @@ angular.module('docs',
|
||||
url: '/user',
|
||||
views: {
|
||||
'page': {
|
||||
templateUrl: 'partial/docs/user.html',
|
||||
controller: 'User'
|
||||
templateUrl: 'partial/docs/usergroup.html',
|
||||
controller: 'UserGroup'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('user.profile', {
|
||||
url: '/:username',
|
||||
views: {
|
||||
'user': {
|
||||
'sub': {
|
||||
templateUrl: 'partial/docs/user.profile.html',
|
||||
controller: 'UserProfile'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('group', {
|
||||
url: '/group',
|
||||
views: {
|
||||
'page': {
|
||||
templateUrl: 'partial/docs/usergroup.html',
|
||||
controller: 'UserGroup'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('group.profile', {
|
||||
url: '/:name',
|
||||
views: {
|
||||
'sub': {
|
||||
templateUrl: 'partial/docs/group.profile.html',
|
||||
controller: 'GroupProfile'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Configuring Restangular
|
||||
|
@ -50,6 +50,7 @@ angular.module('docs').controller('DocumentEdit', function($rootScope, $scope, $
|
||||
$scope.resetForm = function() {
|
||||
$scope.document = {
|
||||
tags: [],
|
||||
relations: [],
|
||||
language: 'fra'
|
||||
};
|
||||
$scope.newFiles = [];
|
||||
@ -71,6 +72,9 @@ angular.module('docs').controller('DocumentEdit', function($rootScope, $scope, $
|
||||
|
||||
// Extract ids from tags
|
||||
document.tags = _.pluck(document.tags, 'id');
|
||||
|
||||
// Extract ids from relations (only when our document is the source)
|
||||
document.relations = _.pluck(_.where(document.relations, { source: true }), 'id');
|
||||
|
||||
if ($scope.isEdit()) {
|
||||
promise = Restangular.one('document', $stateParams.id).post('', document);
|
||||
|
@ -35,18 +35,21 @@ angular.module('docs').controller('DocumentViewPermissions', function ($scope, $
|
||||
if ($scope.acl.perm == 'READWRITE') {
|
||||
acls = [{
|
||||
source: $stateParams.id,
|
||||
username: $scope.acl.username,
|
||||
perm: 'READ'
|
||||
target: $scope.acl.target.name,
|
||||
perm: 'READ',
|
||||
type: $scope.acl.target.type
|
||||
}, {
|
||||
source: $stateParams.id,
|
||||
username: $scope.acl.username,
|
||||
perm: 'WRITE'
|
||||
target: $scope.acl.target.name,
|
||||
perm: 'WRITE',
|
||||
type: $scope.acl.target.type
|
||||
}];
|
||||
} else {
|
||||
acls = [{
|
||||
source: $stateParams.id,
|
||||
username: $scope.acl.username,
|
||||
perm: $scope.acl.perm
|
||||
target: $scope.acl.target.name,
|
||||
perm: $scope.acl.perm,
|
||||
type: $scope.acl.target.type
|
||||
}];
|
||||
}
|
||||
|
||||
@ -74,7 +77,20 @@ angular.module('docs').controller('DocumentViewPermissions', function ($scope, $
|
||||
.get({
|
||||
search: $viewValue
|
||||
}).then(function(data) {
|
||||
deferred.resolve(_.pluck(data.users, 'username'), true);
|
||||
var output = [];
|
||||
|
||||
// Add the type to use later
|
||||
output.push.apply(output, _.map(data.users, function(user) {
|
||||
user.type = 'USER';
|
||||
return user;
|
||||
}));
|
||||
output.push.apply(output, _.map(data.groups, function(group) {
|
||||
group.type = 'GROUP';
|
||||
return group;
|
||||
}));
|
||||
|
||||
// Send the data to the typeahead directive
|
||||
deferred.resolve(output, true);
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Group profile controller.
|
||||
*/
|
||||
angular.module('docs').controller('GroupProfile', function($stateParams, Restangular, $scope) {
|
||||
// Load user
|
||||
Restangular.one('group', $stateParams.name).get().then(function(data) {
|
||||
$scope.group = data;
|
||||
});
|
||||
});
|
@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Settings group page controller.
|
||||
*/
|
||||
angular.module('docs').controller('SettingsGroup', function($scope, $state, Restangular) {
|
||||
/**
|
||||
* Load groups from server.
|
||||
*/
|
||||
$scope.loadGroups = function() {
|
||||
Restangular.one('group').get().then(function(data) {
|
||||
$scope.groups = data.groups;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.loadGroups();
|
||||
|
||||
/**
|
||||
* Edit a group.
|
||||
*/
|
||||
$scope.editGroup = function(group) {
|
||||
$state.go('settings.group.edit', { name: group.name });
|
||||
};
|
||||
});
|
@ -0,0 +1,128 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Settings group edition page controller.
|
||||
*/
|
||||
angular.module('docs').controller('SettingsGroupEdit', function($scope, $dialog, $state, $stateParams, Restangular, $q) {
|
||||
/**
|
||||
* Returns true if in edit mode (false in add mode).
|
||||
*/
|
||||
$scope.isEdit = function() {
|
||||
return $stateParams.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* In edit mode, load the current group.
|
||||
*/
|
||||
if ($scope.isEdit()) {
|
||||
Restangular.one('group', $stateParams.name).get().then(function(data) {
|
||||
$scope.group = data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current group.
|
||||
*/
|
||||
$scope.edit = function() {
|
||||
var promise = null;
|
||||
var group = angular.copy($scope.group);
|
||||
|
||||
if ($scope.isEdit()) {
|
||||
promise = Restangular
|
||||
.one('group', $stateParams.name)
|
||||
.post('', group);
|
||||
} else {
|
||||
promise = Restangular
|
||||
.one('group')
|
||||
.put(group);
|
||||
}
|
||||
|
||||
promise.then(function() {
|
||||
$scope.loadGroups();
|
||||
if ($scope.isEdit()) {
|
||||
$state.go('settings.group');
|
||||
} else {
|
||||
// Go to edit this group to add members
|
||||
$state.go('settings.group.edit', { name: group.name });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete the current group.
|
||||
*/
|
||||
$scope.remove = function() {
|
||||
var title = 'Delete group';
|
||||
var msg = 'Do you really want to delete this group?';
|
||||
var btns = [{result:'cancel', label: 'Cancel'}, {result:'ok', label: 'OK', cssClass: 'btn-primary'}];
|
||||
|
||||
$dialog.messageBox(title, msg, btns, function(result) {
|
||||
if (result == 'ok') {
|
||||
Restangular.one('group', $stateParams.name).remove().then(function() {
|
||||
$scope.loadGroups();
|
||||
$state.go('settings.group');
|
||||
}, function() {
|
||||
$state.go('settings.group');
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a promise for typeahead group.
|
||||
*/
|
||||
$scope.getGroupTypeahead = function($viewValue) {
|
||||
var deferred = $q.defer();
|
||||
Restangular.one('group')
|
||||
.getList('', {
|
||||
sort_column: 1,
|
||||
asc: true
|
||||
}).then(function(data) {
|
||||
deferred.resolve(_.pluck(_.filter(data.groups, function(group) {
|
||||
return group.name.indexOf($viewValue) !== -1;
|
||||
}), 'name'));
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a promise for typeahead user.
|
||||
*/
|
||||
$scope.getUserTypeahead = function($viewValue) {
|
||||
var deferred = $q.defer();
|
||||
Restangular.one('user')
|
||||
.getList('list', {
|
||||
search: $viewValue,
|
||||
sort_column: 1,
|
||||
asc: true
|
||||
}).then(function(data) {
|
||||
deferred.resolve(_.pluck(_.filter(data.users, function(user) {
|
||||
return user.username.indexOf($viewValue) !== -1;
|
||||
}), 'username'));
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a new member.
|
||||
*/
|
||||
$scope.addMember = function(member) {
|
||||
$scope.member = '';
|
||||
Restangular.one('group/' + $stateParams.name).put({
|
||||
username: member
|
||||
}).then(function() {
|
||||
if ($scope.group.members.indexOf(member) === -1) {
|
||||
$scope.group.members.push(member);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a member.
|
||||
*/
|
||||
$scope.removeMember = function(member) {
|
||||
Restangular.one('group/' + $stateParams.name, member).remove().then(function() {
|
||||
$scope.group.members.splice($scope.group.members.indexOf(member), 1);
|
||||
});
|
||||
};
|
||||
});
|
@ -8,7 +8,10 @@ angular.module('docs').controller('SettingsUser', function($scope, $state, Resta
|
||||
* Load users from server.
|
||||
*/
|
||||
$scope.loadUsers = function() {
|
||||
Restangular.one('user/list').get({ limit: 100 }).then(function(data) {
|
||||
Restangular.one('user/list').get({
|
||||
sort_column: 1,
|
||||
asc: true
|
||||
}).then(function(data) {
|
||||
$scope.users = data.users;
|
||||
});
|
||||
};
|
||||
|