Merge pull request #86 from sismics/master

Push to production
This commit is contained in:
Benjamin Gamard 2016-03-21 01:01:14 +01:00
commit 6aef7246a0
138 changed files with 3949 additions and 727 deletions

View File

@ -28,6 +28,7 @@ Features
- 256-bit AES encryption - 256-bit AES encryption
- Tag system with relations - Tag system with relations
- Multi-users ACL system - Multi-users ACL system
- Hierarchical groups
- Audit log - Audit log
- Comments - Comments
- Storage quota per user - Storage quota per user

View File

@ -77,10 +77,13 @@
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" /> <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/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" /> <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/bundles" />
<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/animated-vector-drawable/23.2.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.android.support/appcompat-v7/23.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.shamanland/fab/0.0.6/jars" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/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.easing/android-easing/1.0.3/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/it.sephiroth.android.library.imagezoom/imagezoom/1.0.5/jars" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/it.sephiroth.android.library.imagezoom/imagezoom/1.0.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
@ -92,19 +95,21 @@
</content> </content>
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" /> <orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <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="android-easing-1.0.3" level="project" />
<orderEntry type="library" exported="" name="imagezoom-1.0.5" level="project" /> <orderEntry type="library" exported="" name="imagezoom-1.0.5" level="project" />
<orderEntry type="library" exported="" name="design-23.2.1" level="project" />
<orderEntry type="library" exported="" name="okhttp-urlconnection-3.1.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="picasso-2.5.2" level="project" />
<orderEntry type="library" exported="" name="recyclerview-v7-23.1.1" level="project" /> <orderEntry type="library" exported="" name="animated-vector-drawable-23.2.1" level="project" />
<orderEntry type="library" exported="" name="support-v4-23.1.1" level="project" /> <orderEntry type="library" exported="" name="recyclerview-v7-23.2.1" level="project" />
<orderEntry type="library" exported="" name="fab-0.0.6" level="project" /> <orderEntry type="library" exported="" name="support-annotations-23.2.1" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-23.1.1" level="project" />
<orderEntry type="library" exported="" name="okhttp-3.1.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="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="picasso2-okhttp3-downloader-1.0.2" level="project" />
<orderEntry type="library" exported="" name="tokenautocomplete-1.2.1" level="project" /> <orderEntry type="library" exported="" name="tokenautocomplete-1.2.1" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-23.2.1" level="project" />
<orderEntry type="library" exported="" name="eventbus-3.0.0" level="project" /> <orderEntry type="library" exported="" name="eventbus-3.0.0" level="project" />
</component> </component>
</module> </module>

View File

@ -3,7 +3,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { 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' apply plugin: 'com.android.application'
@ -50,11 +50,11 @@ android {
dependencies { dependencies {
compile fileTree(dir: 'libs', include: '*.jar') compile fileTree(dir: 'libs', include: '*.jar')
compile 'com.android.support:appcompat-v7:23.1.1' compile 'com.android.support:appcompat-v7:23.2.1'
compile 'com.android.support:recyclerview-v7:23.1.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 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
compile 'org.greenrobot:eventbus:3.0.0' 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.picasso:picasso:2.5.2'
compile 'com.squareup.okhttp3:okhttp:3.1.1' compile 'com.squareup.okhttp3:okhttp:3.1.1'
compile "com.squareup.okhttp3:okhttp-urlconnection:3.1.1" compile "com.squareup.okhttp3:okhttp-urlconnection:3.1.1"

View File

@ -47,6 +47,16 @@
android:name=".activity.DocumentEditActivity" android:name=".activity.DocumentEditActivity"
android:label="@string/new_document"> android:label="@string/new_document">
</activity> </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 <activity
android:name=".activity.SettingsActivity" android:name=".activity.SettingsActivity"
android:label="@string/settings"> android:label="@string/settings">

View File

@ -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);
}
}

View File

@ -154,7 +154,7 @@ public class DocumentViewActivity extends AppCompatActivity {
* *
* @param document Document in JSON format * @param document Document in JSON format
*/ */
private void refreshDocument(JSONObject document) { private void refreshDocument(final JSONObject document) {
this.document = document; this.document = document;
String title = document.optString("title"); String title = document.optString("title");
@ -249,7 +249,7 @@ public class DocumentViewActivity extends AppCompatActivity {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
DialogFragment dialog = DocExportPdfFragment.newInstance( DialogFragment dialog = DocExportPdfFragment.newInstance(
DocumentViewActivity.this.document.optString("id"), DocumentViewActivity.this.document.optString("title")); document.optString("id"), document.optString("title"));
dialog.show(getSupportFragmentManager(), "DocExportPdfFragment"); dialog.show(getSupportFragmentManager(), "DocExportPdfFragment");
} }
}); });
@ -259,11 +259,22 @@ public class DocumentViewActivity extends AppCompatActivity {
button.setOnClickListener(new View.OnClickListener() { button.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { 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"); 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 // Button add a comment
ImageButton imageButton = (ImageButton) findViewById(R.id.addCommentBtn); ImageButton imageButton = (ImageButton) findViewById(R.id.addCommentBtn);
imageButton.setOnClickListener(new View.OnClickListener() { imageButton.setOnClickListener(new View.OnClickListener() {
@ -300,7 +311,7 @@ public class DocumentViewActivity extends AppCompatActivity {
// Grab the attached files // Grab the attached files
updateFiles(); updateFiles();
// Grab the full document (used for ACLs and writable status) // Grab the full document (used for ACLs, remaining metadata and writable status)
updateDocument(); updateDocument();
} }
@ -630,6 +641,7 @@ public class DocumentViewActivity extends AppCompatActivity {
menu.findItem(R.id.delete_file).setVisible(writable); 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.actionEditDocument).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
findViewById(R.id.actionUploadFile).setVisibility(writable ? View.VISIBLE : View.INVISIBLE); findViewById(R.id.actionUploadFile).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
findViewById(R.id.actionSharing).setVisibility(writable ? View.VISIBLE : View.INVISIBLE); findViewById(R.id.actionSharing).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
@ -637,7 +649,36 @@ public class DocumentViewActivity extends AppCompatActivity {
// ACLs // ACLs
ListView aclListView = (ListView) findViewById(R.id.aclListView); 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);
}
});
} }
}); });
} }

View File

@ -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);
}
}

View File

@ -30,7 +30,6 @@ import org.json.JSONObject;
* @author bgamard * @author bgamard
*/ */
public class LoginActivity extends AppCompatActivity { public class LoginActivity extends AppCompatActivity {
/** /**
* User interface. * User interface.
*/ */

View File

@ -42,7 +42,6 @@ import org.json.JSONObject;
*/ */
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
private ActionBarDrawerToggle drawerToggle; private ActionBarDrawerToggle drawerToggle;
private MenuItem searchItem; private MenuItem searchItem;
private DrawerLayout drawerLayout; private DrawerLayout drawerLayout;
@ -72,7 +71,7 @@ public class MainActivity extends AppCompatActivity {
// between the sliding drawer and the action bar app icon // between the sliding drawer and the action bar app icon
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
R.string.drawer_open, R.string.drawer_close); R.string.drawer_open, R.string.drawer_close);
drawerLayout.setDrawerListener(drawerToggle); drawerLayout.addDrawerListener(drawerToggle);
// Fill the drawer user info // Fill the drawer user info
JSONObject userInfo = ApplicationContext.getInstance().getUserInfo(); 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()); handleIntent(getIntent());
EventBus.getDefault().register(this); EventBus.getDefault().register(this);

View File

@ -12,7 +12,6 @@ import com.sismics.docs.fragment.SettingsFragment;
* @author bgamard. * @author bgamard.
*/ */
public class SettingsActivity extends AppCompatActivity { public class SettingsActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);

View File

@ -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);
}
}

View File

@ -98,11 +98,19 @@ public class AclListAdapter extends BaseAdapter {
* An ACL item in the list. * An ACL item in the list.
* Permissions are grouped together. * Permissions are grouped together.
*/ */
private static class AclItem { public static class AclItem {
private String type; private String type;
private String name; private String name;
private List<String> permList = new ArrayList<>(); private List<String> permList = new ArrayList<>();
public String getType() {
return type;
}
public String getName() {
return name;
}
@Override @Override
public int hashCode() { public int hashCode() {
return (type + name).hashCode(); return (type + name).hashCode();

View File

@ -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;
}
}

View File

@ -55,6 +55,7 @@ public class SearchFragment extends DialogFragment {
View view = inflater.inflate(R.layout.search_dialog, null); View view = inflater.inflate(R.layout.search_dialog, null);
final EditText searchEditText = (EditText) view.findViewById(R.id.searchEditText); final EditText searchEditText = (EditText) view.findViewById(R.id.searchEditText);
final EditText fulltextEditText = (EditText) view.findViewById(R.id.fulltextEditText); 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 CheckBox sharedCheckbox = (CheckBox) view.findViewById(R.id.sharedCheckbox);
final Spinner languageSpinner = (Spinner) view.findViewById(R.id.languageSpinner); final Spinner languageSpinner = (Spinner) view.findViewById(R.id.languageSpinner);
final DatePickerView beforeDatePicker = (DatePickerView) view.findViewById(R.id.beforeDatePicker); final DatePickerView beforeDatePicker = (DatePickerView) view.findViewById(R.id.beforeDatePicker);
@ -89,6 +90,7 @@ public class SearchFragment extends DialogFragment {
// Build the simple criterias // Build the simple criterias
SearchQueryBuilder queryBuilder = new SearchQueryBuilder() SearchQueryBuilder queryBuilder = new SearchQueryBuilder()
.simpleSearch(searchEditText.getText().toString()) .simpleSearch(searchEditText.getText().toString())
.creator(creatorEditText.getText().toString())
.shared(sharedCheckbox.isChecked()) .shared(sharedCheckbox.isChecked())
.language(((LanguageAdapter.Language) languageSpinner.getSelectedItem()).getId()) .language(((LanguageAdapter.Language) languageSpinner.getSelectedItem()).getId())
.before(beforeDatePicker.getDate()) .before(beforeDatePicker.getDate())

View File

@ -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));
}
}

View File

@ -53,6 +53,23 @@ public class UserResource extends BaseResource {
.newCall(request) .newCall(request)
.enqueue(HttpCallback.buildOkHttpCallback(callback)); .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. * POST /user/logout.

View File

@ -59,6 +59,21 @@ public class SearchQueryBuilder {
return this; 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. * Add a language criteria.
* *

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View 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>

View 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>

View File

@ -37,7 +37,7 @@
android:textSize="16sp" android:textSize="16sp"
android:layout_centerInParent="true"/> android:layout_centerInParent="true"/>
<com.shamanland.fab.FloatingActionButton <android.support.design.widget.FloatingActionButton
android:id="@+id/addDocumentButton" android:id="@+id/addDocumentButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -48,6 +48,6 @@
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginBottom="20dp" android:layout_marginBottom="20dp"
android:src="@drawable/ic_add_white_24dp" android:src="@drawable/ic_add_white_24dp"
app:floatingActionButtonColor="#263238"/> app:fabSize="normal"/>
</RelativeLayout> </RelativeLayout>

View File

@ -13,6 +13,7 @@
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_marginRight="12dp" android:layout_marginRight="12dp"
android:layout_marginEnd="12dp"
android:id="@+id/folderImageView" android:id="@+id/folderImageView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -22,7 +23,9 @@
android:id="@+id/titleTextView" android:id="@+id/titleTextView"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_toRightOf="@+id/folderImageView" android:layout_toRightOf="@+id/folderImageView"
android:layout_toEndOf="@+id/folderImageView"
android:layout_toLeftOf="@+id/dateTextView" android:layout_toLeftOf="@+id/dateTextView"
android:layout_toStartOf="@+id/dateTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="sans-serif-light" android:fontFamily="sans-serif-light"

View File

@ -52,14 +52,14 @@
<!-- Comments --> <!-- Comments -->
<TextView <TextView
android:drawableStart="@drawable/ic_comment_black_24dp" android:drawableStart="@drawable/ic_comment_grey600_24dp"
android:drawableLeft="@drawable/ic_comment_black_24dp" android:drawableLeft="@drawable/ic_comment_grey600_24dp"
android:drawablePadding="6dp" android:drawablePadding="6dp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="16sp" android:textSize="16sp"
android:gravity="center" android:gravity="center"
android:textColor="@color/primary_text_default_material_light" android:textColor="#de000000"
android:text="@string/comments" android:text="@string/comments"
android:layout_margin="12dp"/> android:layout_margin="12dp"/>
@ -173,7 +173,7 @@
android:drawableTop="@drawable/ic_create_grey600_24dp" android:drawableTop="@drawable/ic_create_grey600_24dp"
style="?android:buttonBarButtonStyle" style="?android:buttonBarButtonStyle"
android:text="@string/edit_document" android:text="@string/edit_document"
android:textColor="@color/button_material_dark" android:textColor="#ff5a595b"
android:textAllCaps="false" android:textAllCaps="false"
android:layout_margin="8dp"/> android:layout_margin="8dp"/>
@ -184,7 +184,7 @@
android:drawableTop="@drawable/ic_file_upload_grey600_24dp" android:drawableTop="@drawable/ic_file_upload_grey600_24dp"
style="?android:buttonBarButtonStyle" style="?android:buttonBarButtonStyle"
android:text="@string/upload_file" android:text="@string/upload_file"
android:textColor="@color/button_material_dark" android:textColor="#ff5a595b"
android:textAllCaps="false" android:textAllCaps="false"
android:layout_margin="8dp"/> android:layout_margin="8dp"/>
@ -195,7 +195,7 @@
android:drawableTop="@drawable/ic_file_download_grey600_24dp" android:drawableTop="@drawable/ic_file_download_grey600_24dp"
style="?android:buttonBarButtonStyle" style="?android:buttonBarButtonStyle"
android:text="@string/download_document" android:text="@string/download_document"
android:textColor="@color/button_material_dark" android:textColor="#ff5a595b"
android:textAllCaps="false" android:textAllCaps="false"
android:layout_margin="8dp"/> android:layout_margin="8dp"/>
@ -214,9 +214,9 @@
android:drawableTop="@drawable/ic_description_grey600_24dp" android:drawableTop="@drawable/ic_description_grey600_24dp"
style="?android:buttonBarButtonStyle" style="?android:buttonBarButtonStyle"
android:text="@string/export_pdf" android:text="@string/export_pdf"
android:textColor="@color/button_material_dark" android:textColor="#ff5a595b"
android:textAllCaps="false" android:textAllCaps="false"
android:layout_margin="8dp"/> android:layout_margin="0dp"/>
<Button <Button
android:id="@+id/actionSharing" android:id="@+id/actionSharing"
@ -225,9 +225,20 @@
android:drawableTop="@drawable/ic_share_grey600_24dp" android:drawableTop="@drawable/ic_share_grey600_24dp"
style="?android:buttonBarButtonStyle" style="?android:buttonBarButtonStyle"
android:text="@string/share" android:text="@string/share"
android:textColor="@color/button_material_dark" android:textColor="#ff5a595b"
android:textAllCaps="false" 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 <Button
android:id="@+id/actionDelete" android:id="@+id/actionDelete"
@ -236,9 +247,9 @@
android:drawableTop="@drawable/ic_delete_grey600_24dp" android:drawableTop="@drawable/ic_delete_grey600_24dp"
style="?android:buttonBarButtonStyle" style="?android:buttonBarButtonStyle"
android:text="@string/delete_document" android:text="@string/delete_document"
android:textColor="@color/button_material_dark" android:textColor="#ff5a595b"
android:textAllCaps="false" android:textAllCaps="false"
android:layout_margin="8dp"/> android:layout_margin="0dp"/>
</LinearLayout> </LinearLayout>
@ -280,12 +291,33 @@
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:fontFamily="sans-serif-light"/> 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 <TextView
android:id="@+id/tagTextView" android:id="@+id/tagTextView"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/createdDateLabel" android:layout_below="@id/creatorLabel"
android:layout_marginRight="8dp" android:layout_marginRight="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:maxLines="1" android:maxLines="1"
@ -333,7 +365,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="16sp" android:textSize="16sp"
android:textColor="@color/primary_text_default_material_light" android:textColor="#de000000"
android:text="@string/who_can_access" android:text="@string/who_can_access"
android:layout_margin="12dp"/> android:layout_margin="12dp"/>

View 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: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>

View File

@ -117,6 +117,40 @@
</RelativeLayout> </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 --> <!-- Separator -->
<View <View

View File

@ -27,6 +27,15 @@
android:textSize="18sp" android:textSize="18sp"
android:hint="@string/fulltext_search"/> 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 --> <!-- Language -->
<Spinner <Spinner
android:id="@+id/languageSpinner" android:id="@+id/languageSpinner"

View File

@ -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>

View File

@ -99,6 +99,7 @@
<string name="title">Title</string> <string name="title">Title</string>
<string name="simple_search">Simple search</string> <string name="simple_search">Simple search</string>
<string name="fulltext_search">Fulltext search</string> <string name="fulltext_search">Fulltext search</string>
<string name="creator">Creator</string>
<string name="after_date">After date</string> <string name="after_date">After date</string>
<string name="before_date">Before date</string> <string name="before_date">Before date</string>
<string name="search_tags">Search tags</string> <string name="search_tags">Search tags</string>
@ -115,7 +116,7 @@
<string name="comment_delete">Delete comment</string> <string name="comment_delete">Delete comment</string>
<string name="deleting_comment">Deleting comment</string> <string name="deleting_comment">Deleting comment</string>
<string name="error_deleting_comment">Error 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="download">Download</string>
<string name="margin">Margin</string> <string name="margin">Margin</string>
<string name="fit_image_to_page">Fit image to page</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_file_title">Sismics Docs file export</string>
<string name="download_document_title">Sismics Docs document export</string> <string name="download_document_title">Sismics Docs document export</string>
<string name="download_pdf_title">Sismics Docs PDF 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> </resources>

View File

@ -68,10 +68,12 @@ public class AclDao {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public List<AclDto> getBySourceId(String sourceId) { public List<AclDto> getBySourceId(String sourceId) {
EntityManager em = ThreadLocalContext.get().getEntityManager(); 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(" 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_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_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 "); sb.append(" where a.ACL_DELETEDATE_D is null and a.ACL_SOURCEID_C = :sourceId ");
// Perform the query // Perform the query
@ -88,10 +90,21 @@ public class AclDao {
aclDto.setPerm(PermType.valueOf((String) o[i++])); aclDto.setPerm(PermType.valueOf((String) o[i++]));
aclDto.setTargetId((String) o[i++]); aclDto.setTargetId((String) o[i++]);
String userName = (String) o[i++]; String userName = (String) o[i++];
String shareId = (String) o[i++];
String shareName = (String) o[i++]; String shareName = (String) o[i++];
aclDto.setTargetName(userName == null ? shareName : userName); String groupName = (String) o[i++];
aclDto.setTargetType(userName == null ? if (userName != null) {
AclTargetType.SHARE.name() : AclTargetType.USER.name()); 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); aclDtoList.add(aclDto);
} }
return aclDtoList; return aclDtoList;
@ -105,12 +118,12 @@ public class AclDao {
* @param targetId ACL target entity ID * @param targetId ACL target entity ID
* @return True if the document is accessible * @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(); 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("sourceId", sourceId);
q.setParameter("perm", perm); q.setParameter("perm", perm);
q.setParameter("targetId", targetId); q.setParameter("targetIdList", targetIdList);
// We have a matching permission // We have a matching permission
if (q.getResultList().size() > 0) { if (q.getResultList().size() > 0) {

View File

@ -54,9 +54,8 @@ public class AuditLogDao {
* @param criteria Search criteria * @param criteria Search criteria
* @param sortCriteria Sort criteria * @param sortCriteria Sort criteria
* @return List of audit logs * @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>(); 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 "); 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 ");

View File

@ -57,7 +57,7 @@ public class DocumentDao {
} }
/** /**
* Returns the list of all documents. * Returns the list of all active documents.
* *
* @return List of 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 * @param userId User ID
* @return List of documents * @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 id Document ID
* @param perm Permission needed
* @param userId User ID
* @return Document * @return Document
*/ */
public DocumentDto getDocument(String id) { public DocumentDto getDocument(String id, PermType perm, List<String> targetIdList) {
EntityManager em = ThreadLocalContext.get().getEntityManager(); 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(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(" (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(" u.USE_USERNAME_C ");
sb.append(" from T_DOCUMENT d, T_USER u "); sb.append(" from T_DOCUMENT d ");
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(" 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()); Query q = em.createNativeQuery(sb.toString());
q.setParameter("id", id); q.setParameter("id", id);
q.setParameter("perm", perm.name());
q.setParameter("targetIdList", targetIdList);
Object[] o = null; Object[] o = null;
try { try {
o = (Object[]) q.getSingleResult(); o = (Object[]) q.getSingleResult();
@ -126,30 +134,6 @@ public class DocumentDao {
return documentDto; 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. * Deletes a document.
* *
@ -184,20 +168,27 @@ public class DocumentDao {
q.setParameter("dateNow", dateNow); q.setParameter("dateNow", dateNow);
q.executeUpdate(); 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 // Create audit log
AuditLogUtil.create(documentDb, AuditLogType.DELETE, userId); AuditLogUtil.create(documentDb, AuditLogType.DELETE, userId);
} }
/** /**
* Gets a document by its ID. * Gets an active document by its ID.
* *
* @param id Document ID * @param id Document ID
* @return Document * @return Document
*/ */
public Document getById(String id) { public Document getById(String id) {
EntityManager em = ThreadLocalContext.get().getEntityManager(); 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 { try {
return em.find(Document.class, id); return (Document) q.getSingleResult();
} catch (NoResultException e) { } catch (NoResultException e) {
return null; return null;
} }
@ -216,16 +207,16 @@ public class DocumentDao {
Map<String, Object> parameterMap = new HashMap<String, Object>(); Map<String, Object> parameterMap = new HashMap<String, Object>();
List<String> criteriaList = new ArrayList<String>(); List<String> criteriaList = new ArrayList<String>();
StringBuilder sb = new StringBuilder("select 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(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(" (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 "); sb.append(" from T_DOCUMENT d ");
// Adds search criteria // Adds search criteria
if (criteria.getUserId() != null) { if (criteria.getTargetIdList() != null) {
// Read permission is enough for searching // 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 "); 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("userId", criteria.getUserId()); parameterMap.put("targetIdList", criteria.getTargetIdList());
} }
if (!Strings.isNullOrEmpty(criteria.getSearch()) || !Strings.isNullOrEmpty(criteria.getFullSearch())) { if (!Strings.isNullOrEmpty(criteria.getSearch()) || !Strings.isNullOrEmpty(criteria.getFullSearch())) {
LuceneDao luceneDao = new LuceneDao(); LuceneDao luceneDao = new LuceneDao();

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -16,17 +16,17 @@ public class RoleBaseFunctionDao {
/** /**
* Find the set of base functions of a role. * Find the set of base functions of a role.
* *
* @param roleId Role ID * @param roleIdSet Set of role ID
* @return Set of base functions * @return Set of base functions
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Set<String> findByRoleId(String roleId) { public Set<String> findByRoleId(Set<String> roleIdSet) {
EntityManager em = ThreadLocalContext.get().getEntityManager(); EntityManager em = ThreadLocalContext.get().getEntityManager();
StringBuilder sb = new StringBuilder("select rbf.RBF_IDBASEFUNCTION_C from T_ROLE_BASE_FUNCTION rbf, T_ROLE r"); 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"); sb.append(" and r.ROL_ID_C = rbf.RBF_IDROLE_C and r.ROL_DELETEDATE_D is null");
Query q = em.createNativeQuery(sb.toString()); Query q = em.createNativeQuery(sb.toString());
q.setParameter("roleId", roleId); q.setParameter("roleIdSet", roleIdSet);
return Sets.newHashSet(q.getResultList()); return Sets.newHashSet(q.getResultList());
} }
} }

View File

@ -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.dao.jpa.dto.UserDto;
import com.sismics.docs.core.model.jpa.User; import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.AuditLogUtil; 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.QueryParam;
import com.sismics.docs.core.util.jpa.QueryUtil;
import com.sismics.docs.core.util.jpa.SortCriteria; import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.util.context.ThreadLocalContext; import com.sismics.util.context.ThreadLocalContext;
@ -265,10 +264,11 @@ public class UserDao {
/** /**
* Returns the list of all users. * Returns the list of all users.
* *
* @param paginatedList List of users (updated by side effects) * @param criteria Search criteria
* @param sortCriteria Sort 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>(); Map<String, Object> parameterMap = new HashMap<String, Object>();
List<String> criteriaList = new ArrayList<String>(); List<String> criteriaList = new ArrayList<String>();
@ -281,6 +281,11 @@ public class UserDao {
parameterMap.put("search", "%" + criteria.getSearch() + "%"); 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"); criteriaList.add("u.USE_DELETEDATE_D is null");
if (!criteriaList.isEmpty()) { if (!criteriaList.isEmpty()) {
@ -289,8 +294,9 @@ public class UserDao {
} }
// Perform the search // Perform the search
QueryParam queryParam = new QueryParam(sb.toString(), parameterMap); QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria);
List<Object[]> l = PaginatedLists.executePaginatedQuery(paginatedList, queryParam, sortCriteria); @SuppressWarnings("unchecked")
List<Object[]> l = QueryUtil.getNativeQuery(queryParam).getResultList();
// Assemble results // Assemble results
List<UserDto> userDtoList = new ArrayList<UserDto>(); List<UserDto> userDtoList = new ArrayList<UserDto>();
@ -305,6 +311,6 @@ public class UserDao {
userDto.setStorageQuota(((Number) o[i++]).longValue()); userDto.setStorageQuota(((Number) o[i++]).longValue());
userDtoList.add(userDto); userDtoList.add(userDto);
} }
paginatedList.setResultList(userDtoList); return userDtoList;
} }
} }

View File

@ -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 * @param name Name
* @return Vocabulary entries * @return Vocabulary entries

View File

@ -11,9 +11,9 @@ import java.util.List;
*/ */
public class DocumentCriteria { public class DocumentCriteria {
/** /**
* User ID. * ACL target ID list.
*/ */
private String userId; private List<String> targetIdList;
/** /**
* Search query. * Search query.
@ -55,12 +55,12 @@ public class DocumentCriteria {
*/ */
private String creatorId; private String creatorId;
public String getUserId() { public List<String> getTargetIdList() {
return userId; return targetIdList;
} }
public void setUserId(String userId) { public void setTargetIdList(List<String> targetIdList) {
this.userId = userId; this.targetIdList = targetIdList;
} }
public String getSearch() { public String getSearch() {

View File

@ -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;
}
}

View File

@ -1,7 +1,5 @@
package com.sismics.docs.core.dao.jpa.criteria; package com.sismics.docs.core.dao.jpa.criteria;
/** /**
* User criteria. * User criteria.
* *
@ -12,6 +10,11 @@ public class UserCriteria {
* Search query. * Search query.
*/ */
private String search; private String search;
/**
* Group ID.
*/
private String groupId;
public String getSearch() { public String getSearch() {
return search; return search;
@ -21,4 +24,13 @@ public class UserCriteria {
this.search = search; this.search = search;
return this; return this;
} }
public String getGroupId() {
return groupId;
}
public UserCriteria setGroupId(String groupId) {
this.groupId = groupId;
return this;
}
} }

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -26,74 +26,34 @@ public class TagDto {
*/ */
private String parentId; private String parentId;
/**
* Getter of id.
*
* @return the id
*/
public String getId() { public String getId() {
return id; return id;
} }
/**
* Setter of id.
*
* @param id id
*/
public void setId(String id) { public void setId(String id) {
this.id = id; this.id = id;
} }
/**
* Getter of name.
*
* @return the name
*/
public String getName() { public String getName() {
return name; return name;
} }
/**
* Setter of name.
*
* @param name name
*/
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
/**
* Getter of color.
*
* @return the color
*/
public String getColor() { public String getColor() {
return color; return color;
} }
/**
* Setter of color.
*
* @param color color
*/
public void setColor(String color) { public void setColor(String color) {
this.color = color; this.color = color;
} }
/**
* Getter of parentId.
*
* @return the parentId
*/
public String getParentId() { public String getParentId() {
return parentId; return parentId;
} }
/**
* Setter of parentId.
*
* @param color parentId
*/
public void setParentId(String parentId) { public void setParentId(String parentId) {
this.parentId = parentId; this.parentId = parentId;
} }

View File

@ -1,6 +1,5 @@
package com.sismics.docs.core.dao.jpa.dto; package com.sismics.docs.core.dao.jpa.dto;
/** /**
* User DTO. * User DTO.
* *

View File

@ -1,9 +1,7 @@
package com.sismics.docs.core.dao.lucene; package com.sismics.docs.core.dao.lucene;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer;
@ -46,16 +44,14 @@ public class LuceneDao {
indexWriter.deleteAll(); indexWriter.deleteAll();
// Add all documents // Add all documents
Map<String, Document> documentMap = new HashMap<>();
for (Document document : documentList) { for (Document document : documentList) {
org.apache.lucene.document.Document luceneDocument = getDocumentFromDocument(document); org.apache.lucene.document.Document luceneDocument = getDocumentFromDocument(document);
indexWriter.addDocument(luceneDocument); indexWriter.addDocument(luceneDocument);
documentMap.put(document.getId(), document);
} }
// Add all files // Add all files
for (File file : fileList) { 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); indexWriter.addDocument(luceneDocument);
} }
} }
@ -81,13 +77,12 @@ public class LuceneDao {
* Add file to the index. * Add file to the index.
* *
* @param file File to add * @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() { LuceneUtil.handle(new LuceneRunnable() {
@Override @Override
public void run(IndexWriter indexWriter) throws Exception { 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); indexWriter.addDocument(luceneDocument);
} }
}); });
@ -112,13 +107,12 @@ public class LuceneDao {
* Update file index. * Update file index.
* *
* @param file Updated file * @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() { LuceneUtil.handle(new LuceneRunnable() {
@Override @Override
public void run(IndexWriter indexWriter) throws Exception { 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); indexWriter.updateDocument(new Term("id", file.getId()), luceneDocument);
} }
}); });
@ -200,7 +194,7 @@ public class LuceneDao {
/** /**
* Build Lucene document from database document. * Build Lucene document from database document.
* *
* @param document Document * @param documentDto Document
* @return Document * @return Document
*/ */
private org.apache.lucene.document.Document getDocumentFromDocument(Document document) { private org.apache.lucene.document.Document getDocumentFromDocument(Document document) {
@ -243,10 +237,9 @@ public class LuceneDao {
* Build Lucene document from file. * Build Lucene document from file.
* *
* @param file File * @param file File
* @param document Document linked to the file
* @return Document * @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(); 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("id", file.getId(), Field.Store.YES));
luceneDocument.add(new StringField("doctype", "file", Field.Store.YES)); luceneDocument.add(new StringField("doctype", "file", Field.Store.YES));

View File

@ -1,7 +1,6 @@
package com.sismics.docs.core.event; package com.sismics.docs.core.event;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.sismics.docs.core.model.jpa.Document;
/** /**
* Document deleted event. * Document deleted event.
@ -10,32 +9,22 @@ import com.sismics.docs.core.model.jpa.Document;
*/ */
public class DocumentDeletedAsyncEvent extends UserEvent { public class DocumentDeletedAsyncEvent extends UserEvent {
/** /**
* Created document. * Document ID.
*/ */
private Document document; private String documentId;
/** public String getDocumentId() {
* Getter of document. return documentId;
*
* @return the document
*/
public Document getDocument() {
return document;
} }
/** public void setDocumentId(String documentId) {
* Setter of document. this.documentId = documentId;
*
* @param document document
*/
public void setDocument(Document document) {
this.document = document;
} }
@Override @Override
public String toString() { public String toString() {
return MoreObjects.toStringHelper(this) return MoreObjects.toStringHelper(this)
.add("document", document) .add("documentId", documentId)
.toString(); .toString();
} }
} }

View File

@ -1,7 +1,6 @@
package com.sismics.docs.core.event; package com.sismics.docs.core.event;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.sismics.docs.core.model.jpa.Document;
/** /**
* Document updated event. * Document updated event.
@ -10,32 +9,22 @@ import com.sismics.docs.core.model.jpa.Document;
*/ */
public class DocumentUpdatedAsyncEvent extends UserEvent { public class DocumentUpdatedAsyncEvent extends UserEvent {
/** /**
* Created document. * Document ID.
*/ */
private Document document; private String documentId;
/** public String getDocumentId() {
* Getter of document. return documentId;
*
* @return the document
*/
public Document getDocument() {
return document;
} }
/** public void setDocumentId(String documentId) {
* Setter of document. this.documentId = documentId;
*
* @param document document
*/
public void setDocument(Document document) {
this.document = document;
} }
@Override @Override
public String toString() { public String toString() {
return MoreObjects.toStringHelper(this) return MoreObjects.toStringHelper(this)
.add("document", document) .add("documentId", documentId)
.toString(); .toString();
} }
} }

View File

@ -3,7 +3,6 @@ package com.sismics.docs.core.event;
import java.io.InputStream; import java.io.InputStream;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.sismics.docs.core.model.jpa.Document;
import com.sismics.docs.core.model.jpa.File; import com.sismics.docs.core.model.jpa.File;
/** /**
@ -18,9 +17,9 @@ public class FileCreatedAsyncEvent extends UserEvent {
private File file; private File file;
/** /**
* Document linked to the file. * Language of the file.
*/ */
private Document document; private String language;
/** /**
* Unencrypted input stream containing the file. * Unencrypted input stream containing the file.
@ -42,12 +41,12 @@ public class FileCreatedAsyncEvent extends UserEvent {
this.file = file; this.file = file;
} }
public Document getDocument() { public String getLanguage() {
return document; return language;
} }
public void setDocument(Document document) { public void setLanguage(String language) {
this.document = document; this.language = language;
} }
public InputStream getInputStream() { public InputStream getInputStream() {
@ -70,7 +69,7 @@ public class FileCreatedAsyncEvent extends UserEvent {
public String toString() { public String toString() {
return MoreObjects.toStringHelper(this) return MoreObjects.toStringHelper(this)
.add("file", file) .add("file", file)
.add("document", document) .add("language", language)
.toString(); .toString();
} }
} }

View File

@ -14,20 +14,10 @@ public class FileDeletedAsyncEvent extends UserEvent {
*/ */
private File file; private File file;
/**
* Getter of file.
*
* @return the file
*/
public File getFile() { public File getFile() {
return file; return file;
} }
/**
* Setter of file.
*
* @param file file
*/
public void setFile(File file) { public void setFile(File file) {
this.file = file; this.file = file;
} }

View File

@ -32,6 +32,6 @@ public class DocumentDeletedAsyncListener {
// Update Lucene index // Update Lucene index
LuceneDao luceneDao = new LuceneDao(); LuceneDao luceneDao = new LuceneDao();
luceneDao.deleteDocument(documentDeletedAsyncEvent.getDocument().getId()); luceneDao.deleteDocument(documentDeletedAsyncEvent.getDocumentId());
} }
} }

View File

@ -7,6 +7,7 @@ import org.slf4j.LoggerFactory;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import com.sismics.docs.core.dao.jpa.ContributorDao; 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.dao.lucene.LuceneDao;
import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent; import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent;
import com.sismics.docs.core.model.jpa.Contributor; import com.sismics.docs.core.model.jpa.Contributor;
@ -35,12 +36,17 @@ public class DocumentUpdatedAsyncListener {
log.info("Document updated event: " + event.toString()); log.info("Document updated event: " + event.toString());
} }
// Update contributors list
TransactionUtil.handle(new Runnable() { TransactionUtil.handle(new Runnable() {
@Override @Override
public void run() { 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(); 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 // Check if the user firing this event is not already a contributor
for (Contributor contributor : contributorList) { for (Contributor contributor : contributorList) {
@ -52,14 +58,10 @@ public class DocumentUpdatedAsyncListener {
// Add a new contributor // Add a new contributor
Contributor contributor = new Contributor(); Contributor contributor = new Contributor();
contributor.setDocumentId(event.getDocument().getId()); contributor.setDocumentId(event.getDocumentId());
contributor.setUserId(event.getUserId()); contributor.setUserId(event.getUserId());
contributorDao.create(contributor); contributorDao.create(contributor);
} }
}); });
// Update Lucene index
LuceneDao luceneDao = new LuceneDao();
luceneDao.updateDocument(event.getDocument());
} }
} }

View File

@ -41,7 +41,7 @@ public class FileCreatedAsyncListener {
// Extract text content from the file // Extract text content from the file
long startTime = System.currentTimeMillis(); 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(), fileCreatedAsyncEvent.getPdfInputStream());
fileCreatedAsyncEvent.getInputStream().close(); fileCreatedAsyncEvent.getInputStream().close();
if (fileCreatedAsyncEvent.getPdfInputStream() != null) { if (fileCreatedAsyncEvent.getPdfInputStream() != null) {
@ -66,6 +66,6 @@ public class FileCreatedAsyncListener {
// Update Lucene index // Update Lucene index
LuceneDao luceneDao = new LuceneDao(); LuceneDao luceneDao = new LuceneDao();
luceneDao.createFile(fileCreatedAsyncEvent.getFile(), fileCreatedAsyncEvent.getDocument()); luceneDao.createFile(fileCreatedAsyncEvent.getFile());
} }
} }

View File

@ -30,20 +30,20 @@ public class Acl implements Loggable {
/** /**
* ACL permission. * ACL permission.
*/ */
@Column(name = "ACL_PERM_C", length = 30) @Column(name = "ACL_PERM_C", length = 30, nullable = false)
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private PermType perm; private PermType perm;
/** /**
* ACL source ID. * ACL source ID.
*/ */
@Column(name = "ACL_SOURCEID_C", length = 36) @Column(name = "ACL_SOURCEID_C", length = 36, nullable = false)
private String sourceId; private String sourceId;
/** /**
* ACL target ID. * ACL target ID.
*/ */
@Column(name = "ACL_TARGETID_C", length = 36) @Column(name = "ACL_TARGETID_C", length = 36, nullable = false)
private String targetId; private String targetId;
/** /**

View File

@ -13,7 +13,7 @@ import com.google.common.base.MoreObjects;
* @author bgamard * @author bgamard
*/ */
@Entity @Entity
@Table(name = "T_Contributor") @Table(name = "T_CONTRIBUTOR")
public class Contributor { public class Contributor {
/** /**
* Contributor ID. * Contributor ID.

View File

@ -33,13 +33,13 @@ public class DocumentTag implements Serializable {
/** /**
* Document ID. * Document ID.
*/ */
@Column(name = "DOT_IDDOCUMENT_C", length = 36) @Column(name = "DOT_IDDOCUMENT_C", nullable = false, length = 36)
private String documentId; private String documentId;
/** /**
* Tag ID. * Tag ID.
*/ */
@Column(name = "DOT_IDTAG_C", length = 36) @Column(name = "DOT_IDTAG_C", nullable = false, length = 36)
private String tagId; private String tagId;
/** /**
@ -83,6 +83,7 @@ public class DocumentTag implements Serializable {
@Override @Override
public String toString() { public String toString() {
return MoreObjects.toStringHelper(this) return MoreObjects.toStringHelper(this)
.add("id", id)
.add("documentId", documentId) .add("documentId", documentId)
.add("tagId", tagId) .add("tagId", tagId)
.toString(); .toString();

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -18,7 +18,6 @@ import org.imgscalr.Scalr.Mode;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.sismics.docs.core.model.jpa.Document;
import com.sismics.docs.core.model.jpa.File; import com.sismics.docs.core.model.jpa.File;
import com.sismics.tess4j.Tesseract; import com.sismics.tess4j.Tesseract;
import com.sismics.util.ImageUtil; import com.sismics.util.ImageUtil;
@ -37,17 +36,17 @@ public class FileUtil {
/** /**
* Extract content from a file. * Extract content from a file.
* *
* @param document Document linked to the file * @param language Language to extract
* @param file File to extract * @param file File to extract
* @param inputStream Unencrypted input stream * @param inputStream Unencrypted input stream
* @param pdfInputStream Unencrypted PDF input stream * @param pdfInputStream Unencrypted PDF input stream
* @return Content extract * @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; String content = null;
if (ImageUtil.isImage(file.getMimeType())) { if (ImageUtil.isImage(file.getMimeType())) {
content = ocrFile(inputStream, document); content = ocrFile(inputStream, language);
} else if (pdfInputStream != null) { } else if (pdfInputStream != null) {
content = PdfUtil.extractPdf(pdfInputStream); content = PdfUtil.extractPdf(pdfInputStream);
} }
@ -59,10 +58,10 @@ public class FileUtil {
* Optical character recognition on a stream. * Optical character recognition on a stream.
* *
* @param inputStream Unencrypted input stream * @param inputStream Unencrypted input stream
* @param document Document linked to the file * @param language Language to OCR
* @return Content extracted * @return Content extracted
*/ */
private static String ocrFile(InputStream inputStream, Document document) { private static String ocrFile(InputStream inputStream, String language) {
Tesseract instance = Tesseract.getInstance(); Tesseract instance = Tesseract.getInstance();
String content = null; String content = null;
BufferedImage image = null; BufferedImage image = null;
@ -80,7 +79,7 @@ public class FileUtil {
// OCR the file // OCR the file
try { try {
log.info("Starting OCR with TESSDATA_PREFIX=" + System.getenv("TESSDATA_PREFIX") + ";LC_NUMERIC=" + System.getenv("LC_NUMERIC")); 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); content = instance.doOCR(image);
} catch (Throwable e) { } catch (Throwable e) {
log.error("Error while OCR-izing the image", e); log.error("Error while OCR-izing the image", e);

View File

@ -7,6 +7,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List; import java.util.List;
import javax.imageio.ImageIO; 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.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle; 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.JPEGFactory;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.common.base.Strings;
import com.google.common.io.Closer; 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.model.jpa.File;
import com.sismics.docs.core.util.pdf.PdfPage;
import com.sismics.util.ImageUtil; import com.sismics.util.ImageUtil;
import com.sismics.util.mime.MimeType; import com.sismics.util.mime.MimeType;
@ -141,25 +147,67 @@ public class PdfUtil {
/** /**
* Convert a document and its files to a merged PDF file. * Convert a document and its files to a merged PDF file.
* *
* @param documentDto Document DTO
* @param fileList List of files * @param fileList List of files
* @param fitImageToPage Fit images to the page * @param fitImageToPage Fit images to the page
* @param metadata Add a page with metadata
* @param margin Margins in millimeters * @param margin Margins in millimeters
* @return PDF input stream * @return PDF input stream
* @throws IOException * @throws IOException
*/ */
public static InputStream convertToPdf(List<File> fileList, boolean fitImageToPage, int margin) throws Exception { public static InputStream convertToPdf(DocumentDto documentDto, List<File> fileList,
// TODO PDF Export: Option to add a front page with: boolean fitImageToPage, boolean metadata, int margin) throws Exception {
// document title, document description, creator, date created, language, // Setup PDFBox
// 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
Closer closer = Closer.create(); Closer closer = Closer.create();
MemoryUsageSetting memUsageSettings = MemoryUsageSetting.setupMixed(1000000); // 1MB max memory usage MemoryUsageSetting memUsageSettings = MemoryUsageSetting.setupMixed(1000000); // 1MB max memory usage
memUsageSettings.setTempDir(new java.io.File(System.getProperty("java.io.tmpdir"))); // To OS temp memUsageSettings.setTempDir(new java.io.File(System.getProperty("java.io.tmpdir"))); // To OS temp
float mmPerInch = 1 / (10 * 2.54f) * 72f; float mmPerInch = 1 / (10 * 2.54f) * 72f;
// Create a blank PDF
try (PDDocument doc = new PDDocument(memUsageSettings)) { 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 // Add files
for (File file : fileList) { for (File file : fileList) {
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId()); Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());

View File

@ -105,13 +105,7 @@ public class PaginatedLists {
* @return List of results * @return List of results
*/ */
public static <E> List<Object[]> executePaginatedQuery(PaginatedList<E> paginatedList, QueryParam queryParam, SortCriteria sortCriteria) { public static <E> List<Object[]> executePaginatedQuery(PaginatedList<E> paginatedList, QueryParam queryParam, SortCriteria sortCriteria) {
StringBuilder sb = new StringBuilder(queryParam.getQueryString()); QueryParam sortedQueryParam = QueryUtil.getSortedQueryParam(queryParam, sortCriteria);
sb.append(" order by c");
sb.append(sortCriteria.getColumn());
sb.append(sortCriteria.isAsc() ? " asc" : " desc");
QueryParam sortedQueryParam = new QueryParam(sb.toString(), queryParam.getParameterMap());
executeCountQuery(paginatedList, sortedQueryParam); executeCountQuery(paginatedList, sortedQueryParam);
return executeResultQuery(paginatedList, sortedQueryParam); return executeResultQuery(paginatedList, sortedQueryParam);
} }

View File

@ -1,10 +1,11 @@
package com.sismics.docs.core.util.jpa; 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.EntityManager;
import javax.persistence.Query; import javax.persistence.Query;
import java.util.Map.Entry;
import com.sismics.util.context.ThreadLocalContext;
/** /**
* Query utilities. * Query utilities.
@ -27,4 +28,22 @@ public class QueryUtil {
} }
return query; 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());
}
} }

View File

@ -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();
}
}

View File

@ -1,6 +1,7 @@
package com.sismics.util.log4j; package com.sismics.util.log4j;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Level;
/** /**
* Log search criteria. * Log search criteria.
@ -10,9 +11,9 @@ import org.apache.commons.lang.StringUtils;
public class LogCriteria { public class LogCriteria {
/** /**
* Logging level (DEBUG, WARN)... * Minimum logging level (DEBUG, WARN)...
*/ */
private String level; private Level minLevel;
/** /**
* Logger name / tag. * Logger name / tag.
@ -24,57 +25,30 @@ public class LogCriteria {
*/ */
private String message; private String message;
/** public Level getMinLevel() {
* Getter of level. return minLevel;
*
* @return level
*/
public String getLevel() {
return level;
} }
/** public LogCriteria setMinLevel(Level level) {
* Setter of level. this.minLevel = level;
* return this;
* @param level level
*/
public void setLevel(String level) {
this.level = StringUtils.lowerCase(level);
} }
/**
* Getter of tag.
*
* @return tag
*/
public String getTag() { public String getTag() {
return tag; return tag;
} }
/** public LogCriteria setTag(String tag) {
* Setter of tag.
*
* @param tag tag
*/
public void setTag(String tag) {
this.tag = StringUtils.lowerCase(tag); this.tag = StringUtils.lowerCase(tag);
return this;
} }
/**
* Getter of message.
*
* @return message
*/
public String getMessage() { public String getMessage() {
return message; return message;
} }
/** public LogCriteria setMessage(String message) {
* Setter of message.
*
* @param message message
*/
public void setMessage(String message) {
this.message = StringUtils.lowerCase(message); this.message = StringUtils.lowerCase(message);
return this;
} }
} }

View File

@ -1,5 +1,7 @@
package com.sismics.util.log4j; package com.sismics.util.log4j;
import org.apache.log4j.Level;
/** /**
* Log entry. * Log entry.
* *
@ -14,7 +16,7 @@ public class LogEntry {
/** /**
* Logging level (DEBUG, WARN)... * Logging level (DEBUG, WARN)...
*/ */
private String level; private Level level;
/** /**
* Logger name / tag. * Logger name / tag.
@ -34,45 +36,25 @@ public class LogEntry {
* @param tag Logger name / tag * @param tag Logger name / tag
* @param message Message logged * @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.timestamp = timestamp;
this.level = level; this.level = level;
this.tag = tag; this.tag = tag;
this.message = message; this.message = message;
} }
/**
* Getter of timestamp.
*
* @return timestamp
*/
public long getTimestamp() { public long getTimestamp() {
return timestamp; return timestamp;
} }
/** public Level getLevel() {
* Getter of level.
*
* @return level
*/
public String getLevel() {
return level; return level;
} }
/**
* Getter of tag.
*
* @return tag
*/
public String getTag() { public String getTag() {
return tag; return tag;
} }
/**
* Getter of message.
*
* @return message
*/
public String getMessage() { public String getMessage() {
return message; return message;
} }

View File

@ -1,17 +1,19 @@
package com.sismics.util.log4j; 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.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; 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. * Memory appender for Log4J.
* *
@ -54,7 +56,7 @@ public class MemoryAppender extends AppenderSkeleton {
String loggerName = getLoggerName(event); 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); logQueue.add(logEntry);
} }
@ -98,13 +100,13 @@ public class MemoryAppender extends AppenderSkeleton {
*/ */
public void find(LogCriteria criteria, PaginatedList<LogEntry> list) { public void find(LogCriteria criteria, PaginatedList<LogEntry> list) {
List<LogEntry> logEntryList = new LinkedList<LogEntry>(); List<LogEntry> logEntryList = new LinkedList<LogEntry>();
final String level = criteria.getLevel(); final Level minLevel = criteria.getMinLevel();
final String tag = criteria.getTag(); final String tag = criteria.getTag();
final String message = criteria.getMessage(); final String message = criteria.getMessage();
int resultCount = 0; int resultCount = 0;
for (Iterator<LogEntry> it = logQueue.iterator(); it.hasNext();) { for (Iterator<LogEntry> it = logQueue.iterator(); it.hasNext();) {
LogEntry logEntry = it.next(); 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)) && (tag == null || logEntry.getTag().toLowerCase().equals(tag)) &&
(message == null || logEntry.getMessage().toLowerCase().contains(message))) { (message == null || logEntry.getMessage().toLowerCase().contains(message))) {
logEntryList.add(logEntry); logEntryList.add(logEntry);

View File

@ -1 +1 @@
db.version=6 db.version=8

View File

@ -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';

View File

@ -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';

View File

@ -3,16 +3,18 @@ package com.sismics.docs.core.util;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.Date;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
import com.google.common.io.Resources; 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.docs.core.model.jpa.File;
import com.sismics.util.mime.MimeType; import com.sismics.util.mime.MimeType;
import org.junit.Assert;
/** /**
* Test of the file entity utilities. * Test of the file entity utilities.
* *
@ -50,6 +52,21 @@ public class TestFileUtil {
InputStream inputStream2 = Resources.getResource("file/udhr_encrypted.pdf").openStream(); InputStream inputStream2 = Resources.getResource("file/udhr_encrypted.pdf").openStream();
InputStream inputStream3 = Resources.getResource("file/document.docx").openStream(); InputStream inputStream3 = Resources.getResource("file/document.docx").openStream();
InputStream inputStream4 = Resources.getResource("file/document.odt").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 // First file
Files.copy(inputStream0, DirectoryUtil.getStorageDirectory().resolve("apollo_landscape"), StandardCopyOption.REPLACE_EXISTING); Files.copy(inputStream0, DirectoryUtil.getStorageDirectory().resolve("apollo_landscape"), StandardCopyOption.REPLACE_EXISTING);
File file0 = new File(); File file0 = new File();
@ -81,7 +98,9 @@ public class TestFileUtil {
file4.setId("document_odt"); file4.setId("document_odt");
file4.setMimeType(MimeType.OPEN_DOCUMENT_TEXT); 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);
}
} }
} }
} }

View File

@ -26,14 +26,14 @@
<org.slf4j.jcl-over-slf4j.version>1.6.6</org.slf4j.jcl-over-slf4j.version> <org.slf4j.jcl-over-slf4j.version>1.6.6</org.slf4j.jcl-over-slf4j.version>
<junit.junit.version>4.12</junit.junit.version> <junit.junit.version>4.12</junit.junit.version>
<com.h2database.h2.version>1.4.191</com.h2database.h2.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.mindrot.jbcrypt>0.3m</org.mindrot.jbcrypt>
<org.apache.lucene.version>5.5.0</org.apache.lucene.version> <org.apache.lucene.version>5.5.0</org.apache.lucene.version>
<org.imgscalr.imgscalr-lib.version>4.2</org.imgscalr.imgscalr-lib.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.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> <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> <joda-time.joda-time.version>2.9.2</joda-time.joda-time.version>
<org.hibernate.hibernate.version>5.0.7.Final</org.hibernate.hibernate.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> <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> <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> <net.java.dev.jna.jna.version>4.2.1</net.java.dev.jna.jna.version>

View File

@ -1,7 +1,11 @@
package com.sismics.security; package com.sismics.security;
import java.util.Set;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import jersey.repackaged.com.google.common.collect.Sets;
/** /**
* Anonymous principal. * Anonymous principal.
* *
@ -47,12 +51,12 @@ public class AnonymousPrincipal implements IPrincipal {
return null; return null;
} }
/**
* Setter of dateTimeZone.
*
* @param dateTimeZone dateTimeZone
*/
public void setDateTimeZone(DateTimeZone dateTimeZone) { public void setDateTimeZone(DateTimeZone dateTimeZone) {
this.dateTimeZone = dateTimeZone; this.dateTimeZone = dateTimeZone;
} }
@Override
public Set<String> getGroupIdSet() {
return Sets.newHashSet();
}
} }

View File

@ -1,6 +1,7 @@
package com.sismics.security; package com.sismics.security;
import java.security.Principal; import java.security.Principal;
import java.util.Set;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
@ -24,6 +25,14 @@ public interface IPrincipal extends Principal {
*/ */
public String getId(); 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. * Returns the timezone of the principal.
* *

View File

@ -35,6 +35,11 @@ public class UserPrincipal implements IPrincipal {
*/ */
private Set<String> baseFunctionSet; private Set<String> baseFunctionSet;
/**
* User groups.
*/
private Set<String> groupIdSet;
/** /**
* Constructor of UserPrincipal. * Constructor of UserPrincipal.
* *
@ -56,11 +61,6 @@ public class UserPrincipal implements IPrincipal {
return id; return id;
} }
/**
* Setter of id.
*
* @param id id
*/
public void setId(String id) { public void setId(String id) {
this.id = id; this.id = id;
} }
@ -70,11 +70,6 @@ public class UserPrincipal implements IPrincipal {
return name; return name;
} }
/**
* Setter of name.
*
* @param name name
*/
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
@ -84,11 +79,6 @@ public class UserPrincipal implements IPrincipal {
return dateTimeZone; return dateTimeZone;
} }
/**
* Setter of dateTimeZone.
*
* @param dateTimeZone dateTimeZone
*/
public void setDateTimeZone(DateTimeZone dateTimeZone) { public void setDateTimeZone(DateTimeZone dateTimeZone) {
this.dateTimeZone = dateTimeZone; this.dateTimeZone = dateTimeZone;
} }
@ -98,31 +88,24 @@ public class UserPrincipal implements IPrincipal {
return email; return email;
} }
/**
* Setter of email.
*
* @param email email
*/
public void setEmail(String email) { public void setEmail(String email) {
this.email = email; this.email = email;
} }
/**
* Getter of baseFunctionSet.
*
* @return baseFunctionSet
*/
public Set<String> getBaseFunctionSet() { public Set<String> getBaseFunctionSet() {
return baseFunctionSet; return baseFunctionSet;
} }
/**
* Setter of baseFunctionSet.
*
* @param baseFunctionSet baseFunctionSet
*/
public void setBaseFunctionSet(Set<String> baseFunctionSet) { public void setBaseFunctionSet(Set<String> baseFunctionSet) {
this.baseFunctionSet = baseFunctionSet; this.baseFunctionSet = baseFunctionSet;
} }
@Override
public Set<String> getGroupIdSet() {
return groupIdSet;
}
public void setGroupIdSet(Set<String> groupIdSet) {
this.groupIdSet = groupIdSet;
}
} }

View File

@ -3,6 +3,8 @@ package com.sismics.util.filter;
import java.io.IOException; import java.io.IOException;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import javax.servlet.Filter; import javax.servlet.Filter;
@ -20,18 +22,22 @@ import org.slf4j.LoggerFactory;
import com.sismics.docs.core.constant.Constants; import com.sismics.docs.core.constant.Constants;
import com.sismics.docs.core.dao.jpa.AuthenticationTokenDao; 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.RoleBaseFunctionDao;
import com.sismics.docs.core.dao.jpa.UserDao; 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.AuthenticationToken;
import com.sismics.docs.core.model.jpa.User; import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.TransactionUtil;
import com.sismics.security.AnonymousPrincipal; import com.sismics.security.AnonymousPrincipal;
import com.sismics.security.UserPrincipal; 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. * 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. * 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. * If not, the user is anonymous, and the filter injects a AnonymousPrincipal into the request attribute.
* *
* @author jtremeaux * @author jtremeaux
@ -113,10 +119,6 @@ public class TokenBasedSecurityFilter implements Filter {
User user = userDao.getById(authenticationToken.getUserId()); User user = userDao.getById(authenticationToken.getUserId());
if (user != null && user.getDeleteDate() == null) { if (user != null && user.getDeleteDate() == null) {
injectAuthenticatedUser(request, user); injectAuthenticatedUser(request, user);
// Update the last connection date
authenticationTokenDao.updateLastConnectionDate(authenticationToken.getId());
TransactionUtil.commit();
} else { } else {
injectAnonymousUser(request); injectAnonymousUser(request);
} }
@ -153,9 +155,25 @@ public class TokenBasedSecurityFilter implements Filter {
private void injectAuthenticatedUser(HttpServletRequest request, User user) { private void injectAuthenticatedUser(HttpServletRequest request, User user) {
UserPrincipal userPrincipal = new UserPrincipal(user.getId(), user.getUsername()); 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 // Add base functions
groupRoleIdSet.add(user.getRoleId());
RoleBaseFunctionDao userBaseFuction = new RoleBaseFunctionDao(); RoleBaseFunctionDao userBaseFuction = new RoleBaseFunctionDao();
Set<String> baseFunctionSet = userBaseFuction.findByRoleId(user.getRoleId()); Set<String> baseFunctionSet = userBaseFuction.findByRoleId(groupRoleIdSet);
userPrincipal.setBaseFunctionSet(baseFunctionSet); userPrincipal.setBaseFunctionSet(baseFunctionSet);
// Add email // Add email

View File

@ -31,22 +31,59 @@ public class ClientUtil {
* *
* @param username Username * @param username Username
*/ */
public void createUser(String username) { public void createUser(String username, String... groupNameList) {
// Login admin to create the user // Login admin to create the user
String adminAuthenticationToken = login("admin", "admin", false); String adminToken = login("admin", "admin", false);
// Create the user // 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() resource.path("/user").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminAuthenticationToken) .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
.put(Entity.form(form), JsonObject.class); .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 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);
} }
/** /**

View File

@ -1,3 +1,3 @@
api.current_version=${project.version} api.current_version=${project.version}
api.min_version=1.0 api.min_version=1.0
db.version=6 db.version=8

View File

@ -1,6 +1,7 @@
package com.sismics.docs.rest.resource; package com.sismics.docs.rest.resource;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.List;
import javax.json.Json; import javax.json.Json;
import javax.json.JsonArrayBuilder; import javax.json.JsonArrayBuilder;
@ -14,18 +15,21 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response; 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.AclTargetType;
import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.constant.PermType;
import com.sismics.docs.core.dao.jpa.AclDao; import com.sismics.docs.core.dao.jpa.AclDao;
import com.sismics.docs.core.dao.jpa.DocumentDao; 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.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.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.dao.jpa.dto.UserDto;
import com.sismics.docs.core.model.jpa.Acl; import com.sismics.docs.core.model.jpa.Acl;
import com.sismics.docs.core.model.jpa.Document; 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.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.docs.core.util.jpa.SortCriteria;
import com.sismics.rest.exception.ClientException; import com.sismics.rest.exception.ClientException;
import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.exception.ForbiddenClientException;
@ -41,12 +45,17 @@ public class AclResource extends BaseResource {
/** /**
* Add an ACL. * Add an ACL.
* *
* @param sourceId Source ID
* @param permStr Permission
* @param targetName Target name
* @param type ACL type
* @return Response * @return Response
*/ */
@PUT @PUT
public Response add(@FormParam("source") String sourceId, public Response add(@FormParam("source") String sourceId,
@FormParam("perm") String permStr, @FormParam("perm") String permStr,
@FormParam("username") String username) { @FormParam("target") String targetName,
@FormParam("type") String typeStr) {
if (!authenticate()) { if (!authenticate()) {
throw new ForbiddenClientException(); throw new ForbiddenClientException();
} }
@ -54,18 +63,39 @@ public class AclResource extends BaseResource {
// Validate input // Validate input
ValidationUtil.validateRequired(sourceId, "source"); ValidationUtil.validateRequired(sourceId, "source");
PermType perm = PermType.valueOf(ValidationUtil.validateLength(permStr, "perm", 1, 30, false)); 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 // Search user or group
UserDao userDao = new UserDao(); String targetId = null;
User user = userDao.getActiveByUsername(username); switch (type) {
if (user == null) { case USER:
throw new ClientException("UserNotFound", MessageFormat.format("User not found: {0}", username)); 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 // Check permission on the source by the principal
AclDao aclDao = new AclDao(); AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(sourceId, PermType.WRITE, principal.getId())) { if (!aclDao.checkPermission(sourceId, PermType.WRITE, getTargetIdList(null))) {
throw new ForbiddenClientException(); throw new ForbiddenClientException();
} }
@ -73,18 +103,18 @@ public class AclResource extends BaseResource {
Acl acl = new Acl(); Acl acl = new Acl();
acl.setSourceId(sourceId); acl.setSourceId(sourceId);
acl.setPerm(perm); acl.setPerm(perm);
acl.setTargetId(user.getId()); acl.setTargetId(targetId);
// Avoid duplicates // 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()); aclDao.create(acl, principal.getId());
// Returns the ACL // Returns the ACL
JsonObjectBuilder response = Json.createObjectBuilder() JsonObjectBuilder response = Json.createObjectBuilder()
.add("perm", acl.getPerm().name()) .add("perm", acl.getPerm().name())
.add("id", acl.getTargetId()) .add("id", acl.getTargetId())
.add("name", user.getUsername()) .add("name", targetName)
.add("type", AclTargetType.USER.name()); .add("type", type.name());
return Response.ok().entity(response.build()).build(); return Response.ok().entity(response.build()).build();
} }
@ -94,7 +124,9 @@ public class AclResource extends BaseResource {
/** /**
* Deletes an ACL. * Deletes an ACL.
* *
* @param id ACL ID * @param sourceId Source ID
* @param permStr Permission
* @param targetId Target ID
* @return Response * @return Response
*/ */
@DELETE @DELETE
@ -114,7 +146,7 @@ public class AclResource extends BaseResource {
// Check permission on the source by the principal // Check permission on the source by the principal
AclDao aclDao = new AclDao(); AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(sourceId, PermType.WRITE, principal.getId())) { if (!aclDao.checkPermission(sourceId, PermType.WRITE, getTargetIdList(null))) {
throw new ForbiddenClientException(); throw new ForbiddenClientException();
} }
@ -153,18 +185,25 @@ public class AclResource extends BaseResource {
// Search users // Search users
UserDao userDao = new UserDao(); UserDao userDao = new UserDao();
JsonArrayBuilder users = Json.createArrayBuilder(); JsonArrayBuilder users = Json.createArrayBuilder();
PaginatedList<UserDto> paginatedList = PaginatedLists.create();
SortCriteria sortCriteria = new SortCriteria(1, true); SortCriteria sortCriteria = new SortCriteria(1, true);
List<UserDto> userDtoList = userDao.findByCriteria(new UserCriteria().setSearch(search), sortCriteria);
userDao.findByCriteria(paginatedList, new UserCriteria().setSearch(search), sortCriteria); for (UserDto userDto : userDtoList) {
for (UserDto userDto : paginatedList.getResultList()) {
users.add(Json.createObjectBuilder() 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() JsonObjectBuilder response = Json.createObjectBuilder()
.add("users", users); .add("users", users)
.add("groups", groups);
return Response.ok().entity(response.build()).build(); return Response.ok().entity(response.build()).build();
} }
} }

View File

@ -22,6 +22,7 @@ import javax.ws.rs.core.Response;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Appender; import org.apache.log4j.Appender;
import org.apache.log4j.Level;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -81,7 +82,7 @@ public class AppResource extends BaseResource {
/** /**
* Retrieve the application logs. * Retrieve the application logs.
* *
* @param level Filter on logging level * @param minLevel Filter on logging level
* @param tag Filter on logger name / tag * @param tag Filter on logger name / tag
* @param message Filter on message * @param message Filter on message
* @param limit Page limit * @param limit Page limit
@ -91,7 +92,7 @@ public class AppResource extends BaseResource {
@GET @GET
@Path("log") @Path("log")
public Response log( public Response log(
@QueryParam("level") String level, @QueryParam("level") String minLevel,
@QueryParam("tag") String tag, @QueryParam("tag") String tag,
@QueryParam("message") String message, @QueryParam("message") String message,
@QueryParam("limit") Integer limit, @QueryParam("limit") Integer limit,
@ -99,8 +100,6 @@ public class AppResource extends BaseResource {
if (!authenticate()) { if (!authenticate()) {
throw new ForbiddenClientException(); throw new ForbiddenClientException();
} }
// TODO Change level by minLevel (returns all logs above)
// Get the memory appender // Get the memory appender
org.apache.log4j.Logger logger = org.apache.log4j.Logger.getRootLogger(); org.apache.log4j.Logger logger = org.apache.log4j.Logger.getRootLogger();
Appender appender = logger.getAppender("MEMORY"); Appender appender = logger.getAppender("MEMORY");
@ -110,10 +109,10 @@ public class AppResource extends BaseResource {
MemoryAppender memoryAppender = (MemoryAppender) appender; MemoryAppender memoryAppender = (MemoryAppender) appender;
// Find the logs // Find the logs
LogCriteria logCriteria = new LogCriteria(); LogCriteria logCriteria = new LogCriteria()
logCriteria.setLevel(StringUtils.stripToNull(level)); .setMinLevel(Level.toLevel(StringUtils.stripToNull(minLevel)))
logCriteria.setTag(StringUtils.stripToNull(tag)); .setTag(StringUtils.stripToNull(tag))
logCriteria.setMessage(StringUtils.stripToNull(message)); .setMessage(StringUtils.stripToNull(message));
PaginatedList<LogEntry> paginatedList = PaginatedLists.create(limit, offset); PaginatedList<LogEntry> paginatedList = PaginatedLists.create(limit, offset);
memoryAppender.find(logCriteria, paginatedList); memoryAppender.find(logCriteria, paginatedList);
@ -121,7 +120,7 @@ public class AppResource extends BaseResource {
for (LogEntry logEntry : paginatedList.getResultList()) { for (LogEntry logEntry : paginatedList.getResultList()) {
logs.add(Json.createObjectBuilder() logs.add(Json.createObjectBuilder()
.add("date", logEntry.getTimestamp()) .add("date", logEntry.getTimestamp())
.add("level", logEntry.getLevel()) .add("level", logEntry.getLevel().toString())
.add("tag", logEntry.getTag()) .add("tag", logEntry.getTag())
.add("message", logEntry.getMessage())); .add("message", logEntry.getMessage()));
} }

View File

@ -9,6 +9,7 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; 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.constant.PermType;
import com.sismics.docs.core.dao.jpa.AclDao; import com.sismics.docs.core.dao.jpa.AclDao;
import com.sismics.docs.core.dao.jpa.AuditLogDao; 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.PaginatedLists;
import com.sismics.docs.core.util.jpa.SortCriteria; import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.exception.ServerException;
import com.sismics.rest.util.JsonUtil; import com.sismics.rest.util.JsonUtil;
/** /**
@ -43,25 +43,21 @@ public class AuditLogResource extends BaseResource {
PaginatedList<AuditLogDto> paginatedList = PaginatedLists.create(20, 0); PaginatedList<AuditLogDto> paginatedList = PaginatedLists.create(20, 0);
SortCriteria sortCriteria = new SortCriteria(1, false); SortCriteria sortCriteria = new SortCriteria(1, false);
AuditLogCriteria criteria = new AuditLogCriteria(); AuditLogCriteria criteria = new AuditLogCriteria();
if (documentId == null) { if (Strings.isNullOrEmpty(documentId)) {
// Search logs for a user // Search logs for a user
criteria.setUserId(principal.getId()); criteria.setUserId(principal.getId());
} else { } else {
// Check ACL on the document // Check ACL on the document
AclDao aclDao = new AclDao(); 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(); return Response.status(Status.NOT_FOUND).build();
} }
criteria.setDocumentId(documentId); criteria.setDocumentId(documentId);
} }
// Search the logs // Search the logs
try { AuditLogDao auditLogDao = new AuditLogDao();
AuditLogDao auditLogDao = new AuditLogDao(); auditLogDao.findByCriteria(paginatedList, criteria, sortCriteria);
auditLogDao.findByCriteria(paginatedList, criteria, sortCriteria);
} catch (Exception e) {
throw new ServerException("SearchError", "Error searching in logs", e);
}
// Assemble the results // Assemble the results
JsonArrayBuilder logs = Json.createArrayBuilder(); JsonArrayBuilder logs = Json.createArrayBuilder();

View File

@ -1,12 +1,14 @@
package com.sismics.docs.rest.resource; package com.sismics.docs.rest.resource;
import java.security.Principal; import java.security.Principal;
import java.util.List;
import java.util.Set; import java.util.Set;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import com.google.common.collect.Lists;
import com.sismics.docs.rest.constant.BaseFunction; import com.sismics.docs.rest.constant.BaseFunction;
import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.security.IPrincipal; import com.sismics.security.IPrincipal;
@ -77,4 +79,21 @@ public abstract class BaseResource {
Set<String> baseFunctionSet = ((UserPrincipal) principal).getBaseFunctionSet(); Set<String> baseFunctionSet = ((UserPrincipal) principal).getBaseFunctionSet();
return baseFunctionSet != null && baseFunctionSet.contains(baseFunction.name()); 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;
}
} }

View File

@ -51,7 +51,7 @@ public class CommentResource extends BaseResource {
// Read access on doc gives access to write comments // Read access on doc gives access to write comments
DocumentDao documentDao = new DocumentDao(); 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(); return Response.status(Status.NOT_FOUND).build();
} }
@ -97,7 +97,7 @@ public class CommentResource extends BaseResource {
if (!comment.getUserId().equals(principal.getId())) { if (!comment.getUserId().equals(principal.getId())) {
// Get the associated document // Get the associated document
DocumentDao documentDao = new DocumentDao(); 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(); 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 // Read access on doc gives access to read comments
DocumentDao documentDao = new DocumentDao(); 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(); return Response.status(Status.NOT_FOUND).build();
} }

View File

@ -27,7 +27,6 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.StreamingOutput; import javax.ws.rs.core.StreamingOutput;
import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter; 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.ContributorDao;
import com.sismics.docs.core.dao.jpa.DocumentDao; import com.sismics.docs.core.dao.jpa.DocumentDao;
import com.sismics.docs.core.dao.jpa.FileDao; 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.TagDao;
import com.sismics.docs.core.dao.jpa.UserDao; import com.sismics.docs.core.dao.jpa.UserDao;
import com.sismics.docs.core.dao.jpa.criteria.DocumentCriteria; 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.AclDto;
import com.sismics.docs.core.dao.jpa.dto.ContributorDto; 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.DocumentDto;
import com.sismics.docs.core.dao.jpa.dto.RelationDto;
import com.sismics.docs.core.dao.jpa.dto.TagDto; import com.sismics.docs.core.dao.jpa.dto.TagDto;
import com.sismics.docs.core.event.DocumentCreatedAsyncEvent; import com.sismics.docs.core.event.DocumentCreatedAsyncEvent;
import com.sismics.docs.core.event.DocumentDeletedAsyncEvent; import com.sismics.docs.core.event.DocumentDeletedAsyncEvent;
@ -93,16 +94,11 @@ public class DocumentResource extends BaseResource {
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
AclDao aclDao = new AclDao(); AclDao aclDao = new AclDao();
DocumentDto documentDto = documentDao.getDocument(documentId); DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId));
if (documentDto == null) { if (documentDto == null) {
return Response.status(Status.NOT_FOUND).build(); 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() JsonObjectBuilder document = Json.createObjectBuilder()
.add("id", documentDto.getId()) .add("id", documentDto.getId())
.add("title", documentDto.getTitle()) .add("title", documentDto.getTitle())
@ -152,7 +148,8 @@ public class DocumentResource extends BaseResource {
.add("type", aclDto.getTargetType())); .add("type", aclDto.getTargetType()));
if (!principal.isAnonymous() if (!principal.isAnonymous()
&& aclDto.getTargetId().equals(principal.getId()) && (aclDto.getTargetId().equals(principal.getId())
|| principal.getGroupIdSet().contains(aclDto.getTargetId()))
&& aclDto.getPerm() == PermType.WRITE) { && aclDto.getPerm() == PermType.WRITE) {
// The document is writable for the current user // The document is writable for the current user
writable = true; writable = true;
@ -172,6 +169,18 @@ public class DocumentResource extends BaseResource {
} }
document.add("contributors", contributorList); 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(); return Response.ok().entity(document.build()).build();
} }
@ -186,8 +195,8 @@ public class DocumentResource extends BaseResource {
public Response getPdf( public Response getPdf(
@PathParam("id") String documentId, @PathParam("id") String documentId,
@QueryParam("share") String shareId, @QueryParam("share") String shareId,
@QueryParam("metadata") Boolean metadata, final @QueryParam("metadata") Boolean metadata,
@QueryParam("comments") Boolean comments, final @QueryParam("comments") Boolean comments,
final @QueryParam("fitimagetopage") Boolean fitImageToPage, final @QueryParam("fitimagetopage") Boolean fitImageToPage,
@QueryParam("margin") String marginStr) { @QueryParam("margin") String marginStr) {
authenticate(); authenticate();
@ -197,8 +206,8 @@ public class DocumentResource extends BaseResource {
// Get document and check read permission // Get document and check read permission
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
Document document = documentDao.getDocument(documentId, PermType.READ, shareId == null ? principal.getId() : shareId); final DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId));
if (document == null) { if (documentDto == null) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
@ -217,7 +226,7 @@ public class DocumentResource extends BaseResource {
StreamingOutput stream = new StreamingOutput() { StreamingOutput stream = new StreamingOutput() {
@Override @Override
public void write(OutputStream outputStream) throws IOException, WebApplicationException { 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); ByteStreams.copy(inputStream, outputStream);
} catch (Exception e) { } catch (Exception e) {
throw new IOException(e); throw new IOException(e);
@ -229,7 +238,7 @@ public class DocumentResource extends BaseResource {
return Response.ok(stream) return Response.ok(stream)
.header("Content-Type", MimeType.APPLICATION_PDF) .header("Content-Type", MimeType.APPLICATION_PDF)
.header("Content-Disposition", "inline; filename=\"" + document.getTitle() + ".pdf\"") .header("Content-Disposition", "inline; filename=\"" + documentDto.getTitle() + ".pdf\"")
.build(); .build();
} }
@ -260,7 +269,7 @@ public class DocumentResource extends BaseResource {
PaginatedList<DocumentDto> paginatedList = PaginatedLists.create(limit, offset); PaginatedList<DocumentDto> paginatedList = PaginatedLists.create(limit, offset);
SortCriteria sortCriteria = new SortCriteria(sortColumn, asc); SortCriteria sortCriteria = new SortCriteria(sortColumn, asc);
DocumentCriteria documentCriteria = parseSearchQuery(search); DocumentCriteria documentCriteria = parseSearchQuery(search);
documentCriteria.setUserId(principal.getId()); documentCriteria.setTargetIdList(getTargetIdList(null));
try { try {
documentDao.findByCriteria(paginatedList, documentCriteria, sortCriteria); documentDao.findByCriteria(paginatedList, documentCriteria, sortCriteria);
} catch (Exception e) { } catch (Exception e) {
@ -430,6 +439,7 @@ public class DocumentResource extends BaseResource {
@FormParam("coverage") String coverage, @FormParam("coverage") String coverage,
@FormParam("rights") String rights, @FormParam("rights") String rights,
@FormParam("tags") List<String> tagList, @FormParam("tags") List<String> tagList,
@FormParam("relations") List<String> relationList,
@FormParam("language") String language, @FormParam("language") String language,
@FormParam("create_date") String createDateStr) { @FormParam("create_date") String createDateStr) {
if (!authenticate()) { if (!authenticate()) {
@ -493,6 +503,9 @@ public class DocumentResource extends BaseResource {
// Update tags // Update tags
updateTagList(documentId, tagList); updateTagList(documentId, tagList);
// Update relations
updateRelationList(documentId, relationList);
// Raise a document created event // Raise a document created event
DocumentCreatedAsyncEvent documentCreatedAsyncEvent = new DocumentCreatedAsyncEvent(); DocumentCreatedAsyncEvent documentCreatedAsyncEvent = new DocumentCreatedAsyncEvent();
documentCreatedAsyncEvent.setUserId(principal.getId()); documentCreatedAsyncEvent.setUserId(principal.getId());
@ -526,6 +539,7 @@ public class DocumentResource extends BaseResource {
@FormParam("coverage") String coverage, @FormParam("coverage") String coverage,
@FormParam("rights") String rights, @FormParam("rights") String rights,
@FormParam("tags") List<String> tagList, @FormParam("tags") List<String> tagList,
@FormParam("relations") List<String> relationList,
@FormParam("language") String language, @FormParam("language") String language,
@FormParam("create_date") String createDateStr) { @FormParam("create_date") String createDateStr) {
if (!authenticate()) { if (!authenticate()) {
@ -533,8 +547,8 @@ public class DocumentResource extends BaseResource {
} }
// Validate input data // Validate input data
title = ValidationUtil.validateLength(title, "title", 1, 100, true); title = ValidationUtil.validateLength(title, "title", 1, 100, false);
language = ValidationUtil.validateLength(language, "language", 3, 3, true); language = ValidationUtil.validateLength(language, "language", 3, 3, false);
description = ValidationUtil.validateLength(description, "description", 0, 4000, true); description = ValidationUtil.validateLength(description, "description", 0, 4000, true);
subject = ValidationUtil.validateLength(subject, "subject", 0, 500, true); subject = ValidationUtil.validateLength(subject, "subject", 0, 500, true);
identifier = ValidationUtil.validateLength(identifier, "identifier", 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)); 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 // Get the document
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
Document document; Document document = documentDao.getById(id);
document = documentDao.getDocument(id, PermType.WRITE, principal.getId());
if (document == null) { if (document == null) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
// Update the document // Update the document
if (!StringUtils.isEmpty(title)) { document.setTitle(title);
document.setTitle(title); document.setDescription(description);
} document.setSubject(subject);
if (!StringUtils.isEmpty(description)) { document.setIdentifier(identifier);
document.setDescription(description); document.setPublisher(publisher);
} document.setFormat(format);
if (!StringUtils.isEmpty(subject)) { document.setSource(source);
document.setSubject(subject); document.setType(type);
} document.setCoverage(coverage);
if (!StringUtils.isEmpty(identifier)) { document.setRights(rights);
document.setIdentifier(identifier); document.setLanguage(language);
} if (createDate == null) {
if (!StringUtils.isEmpty(publisher)) { document.setCreateDate(new Date());
document.setPublisher(publisher); } else {
}
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.setCreateDate(createDate); document.setCreateDate(createDate);
} }
if (language != null) {
document.setLanguage(language);
}
document = documentDao.update(document, principal.getId()); document = documentDao.update(document, principal.getId());
// Update tags // Update tags
updateTagList(id, tagList); 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 documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent();
documentUpdatedAsyncEvent.setUserId(principal.getId()); documentUpdatedAsyncEvent.setUserId(principal.getId());
documentUpdatedAsyncEvent.setDocument(document); documentUpdatedAsyncEvent.setDocumentId(id);
AppContext.getInstance().getAsyncEventBus().post(documentUpdatedAsyncEvent); AppContext.getInstance().getAsyncEventBus().post(documentUpdatedAsyncEvent);
JsonObjectBuilder response = Json.createObjectBuilder() 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. * Deletes a document.
* *
@ -653,14 +677,14 @@ public class DocumentResource extends BaseResource {
// Get the document // Get the document
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
FileDao fileDao = new FileDao(); FileDao fileDao = new FileDao();
Document document = documentDao.getDocument(id, PermType.WRITE, principal.getId()); DocumentDto documentDto = documentDao.getDocument(id, PermType.WRITE, getTargetIdList(null));
List<File> fileList = fileDao.getByDocumentId(principal.getId(), id); if (documentDto == null) {
if (document == null) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
List<File> fileList = fileDao.getByDocumentId(principal.getId(), id);
// Delete the document // 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) // Raise file deleted events (don't bother sending document updated event)
for (File file : fileList) { for (File file : fileList) {
@ -673,7 +697,7 @@ public class DocumentResource extends BaseResource {
// Raise a document deleted event // Raise a document deleted event
DocumentDeletedAsyncEvent documentDeletedAsyncEvent = new DocumentDeletedAsyncEvent(); DocumentDeletedAsyncEvent documentDeletedAsyncEvent = new DocumentDeletedAsyncEvent();
documentDeletedAsyncEvent.setUserId(principal.getId()); documentDeletedAsyncEvent.setUserId(principal.getId());
documentDeletedAsyncEvent.setDocument(document); documentDeletedAsyncEvent.setDocumentId(documentDto.getId());
AppContext.getInstance().getAsyncEventBus().post(documentDeletedAsyncEvent); AppContext.getInstance().getAsyncEventBus().post(documentDeletedAsyncEvent);
// Always return OK // Always return OK

View File

@ -48,7 +48,6 @@ import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent;
import com.sismics.docs.core.event.FileCreatedAsyncEvent; import com.sismics.docs.core.event.FileCreatedAsyncEvent;
import com.sismics.docs.core.event.FileDeletedAsyncEvent; import com.sismics.docs.core.event.FileDeletedAsyncEvent;
import com.sismics.docs.core.model.context.AppContext; 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.File;
import com.sismics.docs.core.model.jpa.User; import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.DirectoryUtil; import com.sismics.docs.core.util.DirectoryUtil;
@ -94,13 +93,13 @@ public class FileResource extends BaseResource {
User user = userDao.getById(principal.getId()); User user = userDao.getById(principal.getId());
// Get the document // Get the document
Document document = null; DocumentDto documentDto = null;
if (Strings.isNullOrEmpty(documentId)) { if (Strings.isNullOrEmpty(documentId)) {
documentId = null; documentId = null;
} else { } else {
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
document = documentDao.getDocument(documentId, PermType.WRITE, principal.getId()); documentDto = documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null));
if (document == null) { if (documentDto == null) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
} }
@ -165,7 +164,7 @@ public class FileResource extends BaseResource {
if (documentId != null) { if (documentId != null) {
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent(); FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
fileCreatedAsyncEvent.setUserId(principal.getId()); fileCreatedAsyncEvent.setUserId(principal.getId());
fileCreatedAsyncEvent.setDocument(document); fileCreatedAsyncEvent.setLanguage(documentDto.getLanguage());
fileCreatedAsyncEvent.setFile(file); fileCreatedAsyncEvent.setFile(file);
fileCreatedAsyncEvent.setInputStream(fileInputStream); fileCreatedAsyncEvent.setInputStream(fileInputStream);
fileCreatedAsyncEvent.setPdfInputStream(pdfIntputStream); fileCreatedAsyncEvent.setPdfInputStream(pdfIntputStream);
@ -173,7 +172,7 @@ public class FileResource extends BaseResource {
DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent(); DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent();
documentUpdatedAsyncEvent.setUserId(principal.getId()); documentUpdatedAsyncEvent.setUserId(principal.getId());
documentUpdatedAsyncEvent.setDocument(document); documentUpdatedAsyncEvent.setDocumentId(documentId);
AppContext.getInstance().getAsyncEventBus().post(documentUpdatedAsyncEvent); AppContext.getInstance().getAsyncEventBus().post(documentUpdatedAsyncEvent);
} }
@ -214,8 +213,8 @@ public class FileResource extends BaseResource {
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
FileDao fileDao = new FileDao(); FileDao fileDao = new FileDao();
File file = fileDao.getFile(id, principal.getId()); File file = fileDao.getFile(id, principal.getId());
Document document = documentDao.getDocument(documentId, PermType.WRITE, principal.getId()); DocumentDto documentDto = documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null));
if (file == null || document == null) { if (file == null || documentDto == null) {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
@ -236,14 +235,14 @@ public class FileResource extends BaseResource {
final InputStream responseInputStream = EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey()); final InputStream responseInputStream = EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey());
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent(); FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
fileCreatedAsyncEvent.setUserId(principal.getId()); fileCreatedAsyncEvent.setUserId(principal.getId());
fileCreatedAsyncEvent.setDocument(document); fileCreatedAsyncEvent.setLanguage(documentDto.getLanguage());
fileCreatedAsyncEvent.setFile(file); fileCreatedAsyncEvent.setFile(file);
fileCreatedAsyncEvent.setInputStream(responseInputStream); fileCreatedAsyncEvent.setInputStream(responseInputStream);
AppContext.getInstance().getAsyncEventBus().post(fileCreatedAsyncEvent); AppContext.getInstance().getAsyncEventBus().post(fileCreatedAsyncEvent);
DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent(); DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent();
documentUpdatedAsyncEvent.setUserId(principal.getId()); documentUpdatedAsyncEvent.setUserId(principal.getId());
documentUpdatedAsyncEvent.setDocument(document); documentUpdatedAsyncEvent.setDocumentId(documentId);
AppContext.getInstance().getAsyncEventBus().post(documentUpdatedAsyncEvent); AppContext.getInstance().getAsyncEventBus().post(documentUpdatedAsyncEvent);
} catch (Exception e) { } catch (Exception e) {
throw new ClientException("AttachError", "Error attaching file to document", e); throw new ClientException("AttachError", "Error attaching file to document", e);
@ -277,7 +276,7 @@ public class FileResource extends BaseResource {
// Get the document // Get the document
DocumentDao documentDao = new DocumentDao(); 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(); return Response.status(Status.NOT_FOUND).build();
} }
@ -313,7 +312,7 @@ public class FileResource extends BaseResource {
// Check document visibility // Check document visibility
if (documentId != null) { if (documentId != null) {
AclDao aclDao = new AclDao(); 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(); return Response.status(Status.NOT_FOUND).build();
} }
} else if (!authenticated) { } else if (!authenticated) {
@ -364,14 +363,14 @@ public class FileResource extends BaseResource {
return Response.status(Status.NOT_FOUND).build(); return Response.status(Status.NOT_FOUND).build();
} }
Document document = null; DocumentDto documentDto = null;
if (file.getDocumentId() == null) { if (file.getDocumentId() == null) {
// It's an orphan file // It's an orphan file
if (!file.getUserId().equals(principal.getId())) { if (!file.getUserId().equals(principal.getId())) {
// But not ours // But not ours
throw new ForbiddenClientException(); 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(); return Response.status(Status.NOT_FOUND).build();
} }
@ -395,11 +394,11 @@ public class FileResource extends BaseResource {
fileDeletedAsyncEvent.setFile(file); fileDeletedAsyncEvent.setFile(file);
AppContext.getInstance().getAsyncEventBus().post(fileDeletedAsyncEvent); AppContext.getInstance().getAsyncEventBus().post(fileDeletedAsyncEvent);
if (document != null) { if (documentDto != null) {
// Raise a new document updated // Raise a new document updated
DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent(); DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent();
documentUpdatedAsyncEvent.setUserId(principal.getId()); documentUpdatedAsyncEvent.setUserId(principal.getId());
documentUpdatedAsyncEvent.setDocument(document); documentUpdatedAsyncEvent.setDocumentId(documentDto.getId());
AppContext.getInstance().getAsyncEventBus().post(documentUpdatedAsyncEvent); AppContext.getInstance().getAsyncEventBus().post(documentUpdatedAsyncEvent);
} }
@ -446,7 +445,7 @@ public class FileResource extends BaseResource {
} else { } else {
// Check document accessibility // Check document accessibility
AclDao aclDao = new AclDao(); 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(); throw new ForbiddenClientException();
} }
} }
@ -520,17 +519,11 @@ public class FileResource extends BaseResource {
// Get the document // Get the document
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
DocumentDto documentDto = documentDao.getDocument(documentId); DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId));
if (documentDto == null) { if (documentDto == null) {
return Response.status(Status.NOT_FOUND).build(); 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 // Get files and user associated with this document
FileDao fileDao = new FileDao(); FileDao fileDao = new FileDao();
final UserDao userDao = new UserDao(); final UserDao userDao = new UserDao();

View File

@ -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();
}
}

View File

@ -37,6 +37,7 @@ public class ShareResource extends BaseResource {
* Add a share to a document. * Add a share to a document.
* *
* @param documentId Document ID * @param documentId Document ID
* @param name Share name
* @return Response * @return Response
*/ */
@PUT @PUT
@ -53,7 +54,7 @@ public class ShareResource extends BaseResource {
// Get the document // Get the document
DocumentDao documentDao = new DocumentDao(); 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(); return Response.status(Status.NOT_FOUND).build();
} }
@ -102,7 +103,7 @@ public class ShareResource extends BaseResource {
} }
Acl acl = aclList.get(0); 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())); throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", acl.getSourceId()));
} }

View File

@ -25,13 +25,17 @@ import javax.ws.rs.core.Response;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.sismics.docs.core.constant.Constants; import com.sismics.docs.core.constant.Constants;
import com.sismics.docs.core.dao.jpa.AuthenticationTokenDao; import com.sismics.docs.core.dao.jpa.AuthenticationTokenDao;
import com.sismics.docs.core.dao.jpa.DocumentDao; import com.sismics.docs.core.dao.jpa.DocumentDao;
import com.sismics.docs.core.dao.jpa.FileDao; 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.RoleBaseFunctionDao;
import com.sismics.docs.core.dao.jpa.UserDao; 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.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.dao.jpa.dto.UserDto;
import com.sismics.docs.core.event.DocumentDeletedAsyncEvent; import com.sismics.docs.core.event.DocumentDeletedAsyncEvent;
import com.sismics.docs.core.event.FileDeletedAsyncEvent; 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.AuthenticationToken;
import com.sismics.docs.core.model.jpa.Document; import com.sismics.docs.core.model.jpa.Document;
import com.sismics.docs.core.model.jpa.File; 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.model.jpa.User;
import com.sismics.docs.core.util.EncryptionUtil; 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.core.util.jpa.SortCriteria;
import com.sismics.docs.rest.constant.BaseFunction; import com.sismics.docs.rest.constant.BaseFunction;
import com.sismics.rest.exception.ClientException; import com.sismics.rest.exception.ClientException;
@ -299,14 +302,7 @@ public class UserResource extends BaseResource {
} }
// Get the value of the session token // Get the value of the session token
String authToken = null; String authToken = getAuthToken();
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())) {
authToken = cookie.getValue();
}
}
}
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao(); AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
AuthenticationToken authenticationToken = null; AuthenticationToken authenticationToken = null;
@ -328,7 +324,7 @@ public class UserResource extends BaseResource {
// Deletes the client token in the HTTP response // Deletes the client token in the HTTP response
JsonObjectBuilder response = Json.createObjectBuilder(); 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(); return Response.ok().entity(response.build()).cookie(cookie).build();
} }
@ -362,7 +358,7 @@ public class UserResource extends BaseResource {
for (Document document : documentList) { for (Document document : documentList) {
DocumentDeletedAsyncEvent documentDeletedAsyncEvent = new DocumentDeletedAsyncEvent(); DocumentDeletedAsyncEvent documentDeletedAsyncEvent = new DocumentDeletedAsyncEvent();
documentDeletedAsyncEvent.setUserId(principal.getId()); documentDeletedAsyncEvent.setUserId(principal.getId());
documentDeletedAsyncEvent.setDocument(document); documentDeletedAsyncEvent.setDocumentId(document.getId());
AppContext.getInstance().getAsyncEventBus().post(documentDeletedAsyncEvent); AppContext.getInstance().getAsyncEventBus().post(documentDeletedAsyncEvent);
} }
@ -403,7 +399,7 @@ public class UserResource extends BaseResource {
// Ensure that the admin user is not deleted // Ensure that the admin user is not deleted
RoleBaseFunctionDao userBaseFuction = new RoleBaseFunctionDao(); 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())) { if (baseFunctionSet.contains(BaseFunction.ADMIN.name())) {
throw new ClientException("ForbiddenError", "The admin user cannot be deleted"); throw new ClientException("ForbiddenError", "The admin user cannot be deleted");
} }
@ -421,7 +417,7 @@ public class UserResource extends BaseResource {
for (Document document : documentList) { for (Document document : documentList) {
DocumentDeletedAsyncEvent documentDeletedAsyncEvent = new DocumentDeletedAsyncEvent(); DocumentDeletedAsyncEvent documentDeletedAsyncEvent = new DocumentDeletedAsyncEvent();
documentDeletedAsyncEvent.setUserId(principal.getId()); documentDeletedAsyncEvent.setUserId(principal.getId());
documentDeletedAsyncEvent.setDocument(document); documentDeletedAsyncEvent.setDocumentId(document.getId());
AppContext.getInstance().getAsyncEventBus().post(documentDeletedAsyncEvent); 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())); response.add("is_default_password", Constants.DEFAULT_ADMIN_PASSWORD.equals(adminUser.getPassword()));
} }
} else { } else {
// Update the last connection date
String authToken = getAuthToken();
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
authenticationTokenDao.updateLastConnectionDate(authToken);
// Build the response
response.add("anonymous", false); response.add("anonymous", false);
UserDao userDao = new UserDao(); UserDao userDao = new UserDao();
GroupDao groupDao = new GroupDao();
User user = userDao.getById(principal.getId()); User user = userDao.getById(principal.getId());
List<GroupDto> groupDtoList = groupDao.findByCriteria(new GroupCriteria()
.setUserId(user.getId())
.setRecursive(true), null);
response.add("username", user.getUsername()) response.add("username", user.getUsername())
.add("email", user.getEmail()) .add("email", user.getEmail())
.add("storage_quota", user.getStorageQuota()) .add("storage_quota", user.getStorageQuota())
.add("storage_current", user.getStorageCurrent()); .add("storage_current", user.getStorageCurrent());
// Base functions
JsonArrayBuilder baseFunctions = Json.createArrayBuilder(); JsonArrayBuilder baseFunctions = Json.createArrayBuilder();
for (String baseFunction : ((UserPrincipal) principal).getBaseFunctionSet()) { for (String baseFunction : ((UserPrincipal) principal).getBaseFunctionSet()) {
baseFunctions.add(baseFunction); baseFunctions.add(baseFunction);
} }
// Groups
JsonArrayBuilder groups = Json.createArrayBuilder();
for (GroupDto groupDto : groupDtoList) {
groups.add(groupDto.getName());
}
response.add("base_functions", baseFunctions) response.add("base_functions", baseFunctions)
.add("groups", groups)
.add("is_default_password", hasBaseFunction(BaseFunction.ADMIN) && Constants.DEFAULT_ADMIN_PASSWORD.equals(user.getPassword())); .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"); 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() JsonObjectBuilder response = Json.createObjectBuilder()
.add("username", user.getUsername()) .add("username", user.getUsername())
.add("groups", groups)
.add("email", user.getEmail()) .add("email", user.getEmail())
.add("storage_quota", user.getStorageQuota()) .add("storage_quota", user.getStorageQuota())
.add("storage_current", user.getStorageCurrent()); .add("storage_current", user.getStorageCurrent());
@ -506,30 +534,37 @@ public class UserResource extends BaseResource {
/** /**
* Returns all active users. * Returns all active users.
* *
* @param limit Page limit
* @param offset Page offset
* @param sortColumn Sort index * @param sortColumn Sort index
* @param asc If true, ascending sorting, else descending * @param asc If true, ascending sorting, else descending
* @param groupName Only return users from this group
* @return Response * @return Response
*/ */
@GET @GET
@Path("list") @Path("list")
public Response list( public Response list(
@QueryParam("limit") Integer limit,
@QueryParam("offset") Integer offset,
@QueryParam("sort_column") Integer sortColumn, @QueryParam("sort_column") Integer sortColumn,
@QueryParam("asc") Boolean asc) { @QueryParam("asc") Boolean asc,
@QueryParam("group") String groupName) {
if (!authenticate()) { if (!authenticate()) {
throw new ForbiddenClientException(); throw new ForbiddenClientException();
} }
JsonArrayBuilder users = Json.createArrayBuilder(); JsonArrayBuilder users = Json.createArrayBuilder();
PaginatedList<UserDto> paginatedList = PaginatedLists.create(limit, offset);
SortCriteria sortCriteria = new SortCriteria(sortColumn, asc); 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 userDao = new UserDao();
userDao.findByCriteria(paginatedList, new UserCriteria(), sortCriteria); List<UserDto> userDtoList = userDao.findByCriteria(new UserCriteria().setGroupId(groupId), sortCriteria);
for (UserDto userDto : paginatedList.getResultList()) { for (UserDto userDto : userDtoList) {
users.add(Json.createObjectBuilder() users.add(Json.createObjectBuilder()
.add("id", userDto.getId()) .add("id", userDto.getId())
.add("username", userDto.getUsername()) .add("username", userDto.getUsername())
@ -540,7 +575,6 @@ public class UserResource extends BaseResource {
} }
JsonObjectBuilder response = Json.createObjectBuilder() JsonObjectBuilder response = Json.createObjectBuilder()
.add("total", paginatedList.getResultCount())
.add("users", users); .add("users", users);
return Response.ok().entity(response.build()).build(); return Response.ok().entity(response.build()).build();
} }
@ -558,14 +592,7 @@ public class UserResource extends BaseResource {
} }
// Get the value of the session token // Get the value of the session token
String authToken = null; String authToken = getAuthToken();
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())) {
authToken = cookie.getValue();
}
}
}
JsonArrayBuilder sessions = Json.createArrayBuilder(); JsonArrayBuilder sessions = Json.createArrayBuilder();
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao(); AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
@ -600,14 +627,7 @@ public class UserResource extends BaseResource {
} }
// Get the value of the session token // Get the value of the session token
String authToken = null; String authToken = getAuthToken();
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())) {
authToken = cookie.getValue();
}
}
}
// Remove other tokens // Remove other tokens
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao(); AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
@ -618,4 +638,21 @@ public class UserResource extends BaseResource {
.add("status", "ok"); .add("status", "ok");
return Response.ok().entity(response.build()).build(); 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;
}
} }

View File

@ -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', { .state('settings.vocabulary', {
url: '/vocabulary', url: '/vocabulary',
views: { views: {
@ -221,19 +248,37 @@ angular.module('docs',
url: '/user', url: '/user',
views: { views: {
'page': { 'page': {
templateUrl: 'partial/docs/user.html', templateUrl: 'partial/docs/usergroup.html',
controller: 'User' controller: 'UserGroup'
} }
} }
}) })
.state('user.profile', { .state('user.profile', {
url: '/:username', url: '/:username',
views: { views: {
'user': { 'sub': {
templateUrl: 'partial/docs/user.profile.html', templateUrl: 'partial/docs/user.profile.html',
controller: 'UserProfile' 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 // Configuring Restangular

View File

@ -50,6 +50,7 @@ angular.module('docs').controller('DocumentEdit', function($rootScope, $scope, $
$scope.resetForm = function() { $scope.resetForm = function() {
$scope.document = { $scope.document = {
tags: [], tags: [],
relations: [],
language: 'fra' language: 'fra'
}; };
$scope.newFiles = []; $scope.newFiles = [];
@ -71,6 +72,9 @@ angular.module('docs').controller('DocumentEdit', function($rootScope, $scope, $
// Extract ids from tags // Extract ids from tags
document.tags = _.pluck(document.tags, 'id'); 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()) { if ($scope.isEdit()) {
promise = Restangular.one('document', $stateParams.id).post('', document); promise = Restangular.one('document', $stateParams.id).post('', document);

View File

@ -35,18 +35,21 @@ angular.module('docs').controller('DocumentViewPermissions', function ($scope, $
if ($scope.acl.perm == 'READWRITE') { if ($scope.acl.perm == 'READWRITE') {
acls = [{ acls = [{
source: $stateParams.id, source: $stateParams.id,
username: $scope.acl.username, target: $scope.acl.target.name,
perm: 'READ' perm: 'READ',
type: $scope.acl.target.type
}, { }, {
source: $stateParams.id, source: $stateParams.id,
username: $scope.acl.username, target: $scope.acl.target.name,
perm: 'WRITE' perm: 'WRITE',
type: $scope.acl.target.type
}]; }];
} else { } else {
acls = [{ acls = [{
source: $stateParams.id, source: $stateParams.id,
username: $scope.acl.username, target: $scope.acl.target.name,
perm: $scope.acl.perm perm: $scope.acl.perm,
type: $scope.acl.target.type
}]; }];
} }
@ -74,7 +77,20 @@ angular.module('docs').controller('DocumentViewPermissions', function ($scope, $
.get({ .get({
search: $viewValue search: $viewValue
}).then(function(data) { }).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; return deferred.promise;
}; };

View File

@ -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;
});
});

View File

@ -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 });
};
});

View File

@ -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);
});
};
});

View File

@ -8,7 +8,10 @@ angular.module('docs').controller('SettingsUser', function($scope, $state, Resta
* Load users from server. * Load users from server.
*/ */
$scope.loadUsers = function() { $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; $scope.users = data.users;
}); });
}; };

Some files were not shown because too many files have changed in this diff Show More