Auditing document-downloads via Document Library Record plugin

As you have been noticed, the version 6.1 adds a lot of CMS (Document Library) features. The following is a list of these features, not the full list.

  • Capability to manage different document types: basic document, image (Image Gallery is merged into Document Library), video, audio, etc.
  • Providing Dynamic Data List (DDL) and Dynamic Data Mapping (DDM) - see blogs Dynamic Data Lists I and Dynamic Data Lists II
  • Ability to mount different repositories via CMIS (see blogs Mounting Multiple CMIS Repositories on Liferay 6.1)
  • Capability to define custom document types in the runtime
  • Capability to define custom meta-data of document types in the runtime
  • Ability to make custom document types and their meta-data searchable
  • Ability to apply Workflow on document-type-level
  • Ability to set up asset-links among assets (like Blogs Entry, Comments, Message Boards Message, Web Content, Calendar Event, Document Library Document, Wiki Page)
  • And more.

This article will introduce an additiona feature: tracking who downloaded documents in Liferay portal 6.1 via Document Library Record plugin.

Use cases

The plugin (Document Library Record plugin) will consider following use cases.

  • Audit document-downloads: who downloaded the document and when the document got downloaded.
  • Define resources to be audited:  only defined resources will get audited
  • Distinguish auditing condition: trace non-signed-in required resource and trace sign-in required resource 
  • Report document-downloads: reporting by group, by user, by resource instance, by document-type, by date (weekly, monthly, yearly).

Abstracted from the book: Liferay Portal Systems Development (A Liferay cookbook for developers using version 6.1 or above)

Possible implementation

The plugin could be implemented in the following steps

  • Specify two entities for Document Library Record: DLRecord and DLRecordLog.

The entity DLRecord is defined as follows.

<!-- PK fields -->
<column name="definitionId" type="long" primary="true" />
<!-- Audit fields -->
<column name="folderId" type="long" />
<column name="documentType" type="String" />
<column name="companyId" type="long" />
<column name="groupId" type="long" />
<column name="userId" type="long" />
<column name="createDate" type="Date" />
<column name="modifiedBy" type="long" />
<column name="modifiedDate" type="Date" />
<!-- Other fields -->
<column name="name" type="String" />
<column name="title" type="String" />
<column name="signinRequired" type="boolean" />

The entity DLRecordLog is defined as follows.

<!-- PK fields -->
<column name="logId" type="long" primary="true" />
<!-- Audit fields -->
<column name="definitionId" type="long" />
<column name="documentType" type="String" />
<column name="companyId" type="long" />
<column name="groupId" type="long" />
<column name="userId" type="long" />
<column name="createDate" type="Date" />

  • Record document-downloads.

The following is sample code.

try {
DLRecordDefinition dLRecordDefinition =
DLRecordDefinitionLocalServiceUtil.findByF_N(folderId, name);
if (dLRecordDefinition != null) {

ThemeDisplay themeDisplay =
(ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);
if (dLRecordDefinition.isSigninRequired()
&& !themeDisplay.isSignedIn()) {
response.sendRedirect(themeDisplay.getURLSignIn());
}
long companyId = themeDisplay.getCompanyId();
long groupId = themeDisplay.getScopeGroupId();
long userId = themeDisplay.getUserId();

DLRecordLogLocalServiceUtil.addDLRecordLog(
dLRecordDefinition.getDefinitionId(), companyId,
groupId, userId);
}
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

Results

Here I share a few screenshots from the version 6.1 (revision 88826).

Resource search

Trace definitions

List records

When downloading a document (basic document, image, video, audio, etc.), audit it.

Summary

The plugin provided capability to audit document-downloads. It did work fine in the version 6.1. Of course, the plugin did also work well in the version 6 (like 6.0.6 CE, 6.0 EE SP1, 6.0 EE SP2, etc.). In addition, I would send a ton of thanks to Liferay development team who created the draft version of Document Library Record portlet.

Download URLs

6.0.12

6.0.11

6.0.10

6.0.6

6.1.0

Blogs
Hey Jonas, is this plugin available to download (either in binary or source code form)?
Thanks Jonas !!!
I think this is nice feature and eager to see ASAP emoticon
@James @Andres,

The download link (for both 6.0 CE, 6.0 EE ) will be added shortly. The download link would be ready for 6.1 when 6.1 was released.

Thanks

Jonas
Hi Jonas,

Thanks for sharing. Been looking forward to such on LR.
Is the plugin a "full featured" record management system?
Download URL for 6.0.12 is ready.

Download URLs for 6.0.10, 6.0.11, 6.0.6, and 6.1.0 would be ready in request.
Hi Pius,

The plugin - a "full featured" record management system - is available in the book.

Liferay Portal Systems Development (A Liferay cookbook for developers using version 6.1 or above):
http://www.amazon.com/Liferay-Portal-Systems-Development/dp/1849515980

Thanks,

Jonas
[...] Hooks are a feature to catch hold of the properties and JSP files into an instance of the portal, as if catching them with a hook. Hook plugins are more powerful plugins that come to complement... [...] Read More
mmmh, the download link 6.0.12 links back to this page,
any ideas?

c. Thomas
Hi Tomas, it seems that it is working fine after signed in. Thanks, Jonas
Hello,

I was wondering how must I do the request for the 6.1 plugin, or if it isn't ready for the moment.

Thanks a lot!

Jordi
Hi Jordi,

The same feature (plugin) is available in the 6.1. Once the 6.1 CE version is ready, you may use this plugin.

Thanks

Jonas
Hi, Jonas,

This is cool! Do you know when I can download the source code for this? I have tried sourceForge, Liferay download site and Liferay svn.
Hi Jonas,

I can't deploy it succesfully on Liferay 6.1, I got this error:

Loading file:/D:/Liferay-6.1/liferay-portal-6.1.0-ce-ga1/tomcat-7.0.23/temp/11-document-library-record-portlet/WEB-INF/classes/service.properties
11:10:19,405 ERROR [PortletApplicationContext:87] org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Bea
n class [com.liferay.dlrecord.service.impl.DLRecordDefinitionLocalServiceImpl] not found
Offending resource: ServletContext resource [/WEB-INF/classes/META-INF/portlet-spring.xml]
Bean 'com.liferay.dlrecord.service.DLRecordDefinitionLocalService'; nested exception is java.lang.ClassNotFoundException: com.liferay.dlrecord.service
.impl.DLRecordDefinitionLocalServiceImpl
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Bean class [com.liferay.dlrecord.service.impl.DLRecor
dDefinitionLocalServiceImpl] not found
Offending resource: ServletContext resource [/WEB-INF/classes/META-INF/portlet-spring.xml]
Bean 'com.liferay.dlrecord.service.DLRecordDefinitionLocalService'; nested exception is java.lang.ClassNotFoundException: com.liferay.dlrecord.service
.impl.DLRecordDefinitionLocalServiceImpl
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:68)
at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:85)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.error(BeanDefinitionParserDelegate.java:291)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseBeanDefinitionElement(BeanDefinitionParserDelegate.java:491)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseBeanDefinitionElement(BeanDefinitionParserDelegate.java:396)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseBeanDefinitionElement(BeanDefinitionParserDelegate.java:365)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.processBeanDefinition(DefaultBeanDefinitionDocumentReader.java:25
8)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseDefaultElement(DefaultBeanDefinitionDocumentReader.java:153)

at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:132
)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:
93)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
Hi Firas, the war was generated in the 6.0.12. For 6.1, the war has to be re-generated. Please let me know if you still need this plugin for 6.1.

Thanks

Jonas Yuan
Hi Jonas, thanks for your reply, really I need it but I don't know how to re-generate the plugin for Liferay6.1.

Best Regards,
Firas BD
Hi Firas, do you use this for your web site / project? if yes, I could generate free WAR for version 6.1.

Thanks

Jonas
Hi Jonas, yes it is for my Intranet portal project, I need also your help in others issues, could you please send me your email adress, this is my email adress:

zangar.firas@gmail.com

Best Regards,
Firas BD
Hi Firas,

You can drop an email to jonasliferay@gmail.com.

Thanks

Jonas
I guess the download links has been removed emoticon
I'd need to do something similar in 6.0.6 CE (well, actually a very basic version) and seeing the code would be useful.
I'd need to do something when a document is downloaded, but I guess the normal methods associated to a Model Listener don't include the simple download...