Close #72: Android: Audit log

This commit is contained in:
jendib 2016-03-12 23:25:31 +01:00
parent 5e2bd76e10
commit 24713f54e2
24 changed files with 424 additions and 29 deletions

View File

@ -77,6 +77,7 @@
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />

View File

@ -47,6 +47,10 @@
android:name=".activity.DocumentEditActivity"
android:label="@string/new_document">
</activity>
<activity
android:name=".activity.AuditLogActivity"
android:label="@string/latest_activity">
</activity>
<activity
android:name=".activity.SettingsActivity"
android:label="@string/settings">

View File

@ -0,0 +1,105 @@
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.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);
}
});
// 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
*/
private void refreshDocument(JSONObject document) {
private void refreshDocument(final JSONObject document) {
this.document = document;
String title = document.optString("title");
@ -249,7 +249,7 @@ public class DocumentViewActivity extends AppCompatActivity {
@Override
public void onClick(View view) {
DialogFragment dialog = DocExportPdfFragment.newInstance(
DocumentViewActivity.this.document.optString("id"), DocumentViewActivity.this.document.optString("title"));
document.optString("id"), document.optString("title"));
dialog.show(getSupportFragmentManager(), "DocExportPdfFragment");
}
});
@ -259,11 +259,22 @@ public class DocumentViewActivity extends AppCompatActivity {
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
DialogFragment dialog = DocShareFragment.newInstance(DocumentViewActivity.this.document.optString("id"));
DialogFragment dialog = DocShareFragment.newInstance(document.optString("id"));
dialog.show(getSupportFragmentManager(), "DocShareFragment");
}
});
// Action audit log
button = (Button) findViewById(R.id.actionAuditLog);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(DocumentViewActivity.this, AuditLogActivity.class);
intent.putExtra("documentId", document.optString("id"));
startActivity(intent);
}
});
// Button add a comment
ImageButton imageButton = (ImageButton) findViewById(R.id.addCommentBtn);
imageButton.setOnClickListener(new View.OnClickListener() {

View File

@ -72,7 +72,7 @@ public class MainActivity extends AppCompatActivity {
// between the sliding drawer and the action bar app icon
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
R.string.drawer_open, R.string.drawer_close);
drawerLayout.setDrawerListener(drawerToggle);
drawerLayout.addDrawerListener(drawerToggle);
// Fill the drawer user info
JSONObject userInfo = ApplicationContext.getInstance().getUserInfo();
@ -137,6 +137,15 @@ public class MainActivity extends AppCompatActivity {
}
});
// Click on Latest activity
View auditLogLayout = findViewById(R.id.auditLogLayout);
auditLogLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, AuditLogActivity.class));
}
});
handleIntent(getIntent());
EventBus.getDefault().register(this);

View File

@ -0,0 +1,92 @@
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":
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

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

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,28 @@
<?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: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,62 @@
<?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:clickable="true"
android:focusable="true"
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

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

View File

@ -52,14 +52,14 @@
<!-- Comments -->
<TextView
android:drawableStart="@drawable/ic_comment_black_24dp"
android:drawableLeft="@drawable/ic_comment_black_24dp"
android:drawableStart="@drawable/ic_comment_grey600_24dp"
android:drawableLeft="@drawable/ic_comment_grey600_24dp"
android:drawablePadding="6dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:gravity="center"
android:textColor="@color/primary_text_default_material_light"
android:textColor="#de000000"
android:text="@string/comments"
android:layout_margin="12dp"/>
@ -173,7 +173,7 @@
android:drawableTop="@drawable/ic_create_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/edit_document"
android:textColor="@color/button_material_dark"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="8dp"/>
@ -184,7 +184,7 @@
android:drawableTop="@drawable/ic_file_upload_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/upload_file"
android:textColor="@color/button_material_dark"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="8dp"/>
@ -195,7 +195,7 @@
android:drawableTop="@drawable/ic_file_download_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/download_document"
android:textColor="@color/button_material_dark"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="8dp"/>
@ -214,9 +214,9 @@
android:drawableTop="@drawable/ic_description_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/export_pdf"
android:textColor="@color/button_material_dark"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="8dp"/>
android:layout_margin="0dp"/>
<Button
android:id="@+id/actionSharing"
@ -225,9 +225,20 @@
android:drawableTop="@drawable/ic_share_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/share"
android:textColor="@color/button_material_dark"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="8dp"/>
android:layout_margin="0dp"/>
<Button
android:id="@+id/actionAuditLog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="@drawable/ic_assignment_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/activity"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="0dp"/>
<Button
android:id="@+id/actionDelete"
@ -236,9 +247,9 @@
android:drawableTop="@drawable/ic_delete_grey600_24dp"
style="?android:buttonBarButtonStyle"
android:text="@string/delete_document"
android:textColor="@color/button_material_dark"
android:textColor="#ff5a595b"
android:textAllCaps="false"
android:layout_margin="8dp"/>
android:layout_margin="0dp"/>
</LinearLayout>
@ -333,7 +344,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="@color/primary_text_default_material_light"
android:textColor="#de000000"
android:text="@string/who_can_access"
android:layout_margin="12dp"/>

View File

@ -117,6 +117,40 @@
</RelativeLayout>
<!-- Audit log -->
<RelativeLayout
android:id="@+id/auditLogLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:clickable="true"
android:background="?android:attr/selectableItemBackground">
<ImageView
android:id="@+id/auditLogImageView"
android:layout_centerVertical="true"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_marginRight="12dp"
android:layout_marginEnd="12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_assignment_grey600_24dp"/>
<TextView
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/auditLogImageView"
android:layout_toEndOf="@+id/auditLogImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:textColor="#212121"
android:text="@string/latest_activity"
android:textSize="14sp"/>
</RelativeLayout>
<!-- Separator -->
<View

View File

@ -116,7 +116,7 @@
<string name="comment_delete">Delete comment</string>
<string name="deleting_comment">Deleting comment</string>
<string name="error_deleting_comment">Error deleting comment</string>
<string name="export_pdf">Export PDF</string>
<string name="export_pdf">PDF</string>
<string name="download">Download</string>
<string name="margin">Margin</string>
<string name="fit_image_to_page">Fit image to page</string>
@ -126,5 +126,7 @@
<string name="download_file_title">Sismics Docs file export</string>
<string name="download_document_title">Sismics Docs document export</string>
<string name="download_pdf_title">Sismics Docs PDF export</string>
<string name="latest_activity">Latest activity</string>
<string name="activity">Activity</string>
</resources>

View File

@ -54,9 +54,8 @@ public class AuditLogDao {
* @param criteria Search criteria
* @param sortCriteria Sort criteria
* @return List of audit logs
* @throws Exception
*/
public void findByCriteria(PaginatedList<AuditLogDto> paginatedList, AuditLogCriteria criteria, SortCriteria sortCriteria) throws Exception {
public void findByCriteria(PaginatedList<AuditLogDto> paginatedList, AuditLogCriteria criteria, SortCriteria sortCriteria) {
Map<String, Object> parameterMap = new HashMap<String, Object>();
StringBuilder baseQuery = new StringBuilder("select l.LOG_ID_C c0, l.LOG_CREATEDATE_D c1, u.USE_USERNAME_C c2, l.LOG_IDENTITY_C c3, l.LOG_CLASSENTITY_C c4, l.LOG_TYPE_C c5, l.LOG_MESSAGE_C c6 from T_AUDIT_LOG l ");

View File

@ -9,6 +9,7 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.google.common.base.Strings;
import com.sismics.docs.core.constant.PermType;
import com.sismics.docs.core.dao.jpa.AclDao;
import com.sismics.docs.core.dao.jpa.AuditLogDao;
@ -18,7 +19,6 @@ import com.sismics.docs.core.util.jpa.PaginatedList;
import com.sismics.docs.core.util.jpa.PaginatedLists;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.exception.ServerException;
import com.sismics.rest.util.JsonUtil;
/**
@ -43,7 +43,7 @@ public class AuditLogResource extends BaseResource {
PaginatedList<AuditLogDto> paginatedList = PaginatedLists.create(20, 0);
SortCriteria sortCriteria = new SortCriteria(1, false);
AuditLogCriteria criteria = new AuditLogCriteria();
if (documentId == null) {
if (Strings.isNullOrEmpty(documentId)) {
// Search logs for a user
criteria.setUserId(principal.getId());
} else {
@ -56,12 +56,8 @@ public class AuditLogResource extends BaseResource {
}
// Search the logs
try {
AuditLogDao auditLogDao = new AuditLogDao();
auditLogDao.findByCriteria(paginatedList, criteria, sortCriteria);
} catch (Exception e) {
throw new ServerException("SearchError", "Error searching in logs", e);
}
AuditLogDao auditLogDao = new AuditLogDao();
auditLogDao.findByCriteria(paginatedList, criteria, sortCriteria);
// Assemble the results
JsonArrayBuilder logs = Json.createArrayBuilder();

View File

@ -34,7 +34,7 @@
<a href="#/tag">{{ log.message }}</a>
</span>
<span ng-switch-when="User">
<a href="#/settings/account">{{ log.message }}</a>
<a href="#/user/{{ log.message }}">{{ log.message }}</a>
</span>
</span>
</td>