Liferay’s Document and Media out of the box does NOT offer the feature of copying a file from one location to another, it only provides move feature. In this post, I will show you how to augment the action menu of a file in Documents and Media to include the copy feature.
Recently, our client had a business requirement to include copy feature in individual files and folder in the Documents and Media in Liferay. Liferay’s Document and Media out of the box does NOT offer this feature, it only provides move feature. In this post, I will only show how to copy files from one location to another and in a later post, I will expand this topic and implement copy feature in folders as well.
To implement this requirement, we will create two hooks: Jsp Hook and Service Hook.
I am assuming you have already created a blank Liferay Plugin for a Hook.
These are the steps to follow:
- liferay-hook.xml file:We wil setup liferay-hook.xml to look like the following:
<?xml version=”1.0″?> <!DOCTYPE hook PUBLIC “-//Liferay//DTD Hook 6.2.0//EN” “http://www.liferay.com/dtd/liferay-hook_6_2_0.dtd”> <hook> <custom-jsp-dir>/META-INF/custom_jsps</custom-jsp-dir> <service> <service-type>com.liferay.portlet.documentlibrary.service.DLAppService</service-type> <service-impl>com.example.enhancement.DLAppServiceImpl</service-impl> </service> </hook> - The jsp files to include:Now we will include the following jsp files in our custom jsp folder that we specified in the xml above.
- file_entry_action.jsp
- move_file_entry.jsp
Both of the above must be located in the path: custom_jsps\html\portlet\document_library\
These files can be taken from the source code of Liferay. - We must add one more file to the custom_jsps directory: copy.jspf
This file must be located in the following path:
custom_jsps\html\portlet\document_library\action\copy.jspf
The content of this file must be the same as the content of move.jspf from the source of Liferay, for now.
- Modifying the jsp(s) to our requirements:Here, we will modify the jsp files which we included in the previous step:
- file_entry_action.jspThis is the jsp which is responsible for showing the action menu of an individual file.
The part of the jsp where we are including the .jspf(s) for the action menu, we want to include our copy.jspf that we created in the previous step:<%@ include file="/html/portlet/document_library/action/copy.jspf" %>
This should be around line 120 of the 6.2 SP14 source code. - copy.jspfCurrently, the content of this file should be the same as the content of move.jspf. We will change a few things here:
- Set parameter to moveURL:
moveURL.setParameter("enhancement1_copy", "true"); // name of the key doesn't matter as long as it's consistent throughout
- Set icon and name:Replace the ‘liferay-ui:icon’ tag with:
<liferay-ui:icon<br> iconCssClass="icon-copy"<br> message="copy"<br> url="<%= moveURL.toString() %>"<br>/>
After this step, we should be able to see ‘copy’ option under the action menu of a document:
- Set parameter to moveURL:
- move_file_entry.jspThis is the jsp which we will be taken once we click move or copy and here is where we will choose where to copy the file. In the start of this file, add the following two java statements:
String copy = ParamUtil.getString(request, "enhancement1_copy");<br>
portletURL.setParameter(“enhancement1_copy”, copy);
Now, in the <aui:form>, add the following hidden input:<aui:input name="enhancement1_copy" type="hidden" value="<%= copy %>" />
Now, locate where the submit button is, it is wrapped in a <aui:button-row> element and replace that element with the following element:<aui:button-row><br> <%if(copy.equals("true")) { %><br> <aui:button disabled="<%= fileEntry.isCheckedOut() && !fileEntry.hasLock() %>" type="submit" value="copy" /><br> <br> <%} else { %><br> <aui:button disabled="<%= fileEntry.isCheckedOut() && !fileEntry.hasLock() %>" type="submit" value="move" /><br> <%} %><br> <aui:button href="<%= redirect %>" type="cancel" /><br></aui:button-row>
With this, we have changed the jsp to either be for the copy or move commands. Also, we will be able to pass the parameters to our custome Java class which is overriding the service.
- file_entry_action.jspThis is the jsp which is responsible for showing the action menu of an individual file.
- Working on the Service part of the hook, implementing our Java class.
This class should extend the DLAppServiceWrapper. We will be overriding the moveFileEntry() method od this class. First we will check whether the user wants to copy or move. If the user wants to move, then we will use the default Liferay functionality, otherwise, we will use our logic to copy the file without deleting it from the current location. Here is the class with the required code:public class DLAppServiceImpl extends DLAppServiceWrapper {<br><br> public DLAppServiceImpl(DLAppService dlAppService) {<br> super(dlAppService);<br> }<br><br> /**<br> * Moves the file entry to the new folder.<br> *<br> * @param fileEntryId the primary key of the file entry<br> * @param newFolderId the primary key of the new folder<br> * @param serviceContext the service context to be applied<br> * @return the file entry<br> * @throws PortalException if the file entry or the new folder could not be<br> * found<br> * @throws SystemException if a system exception occurred<br> */<br> @Override<br> public FileEntry moveFileEntry(long fileEntryId, long newFolderId, ServiceContext serviceContext) throws PortalException, SystemException {<br> <br> if(_log.isDebugEnabled()) {<br> _log.debug("called moveFileEntry()");<br> }<br><br> HttpServletRequest request = serviceContext.getRequest(); <br> String copy = ParamUtil.getString(request, "enhancement1_copy");<br> <br> // if the user wants to <em>move</em>, use default functionality<br> if(!copy.equals("true")) {<br> return super.moveFileEntry(fileEntryId, newFolderId, serviceContext);<br> }<br> <br> if(_log.isDebugEnabled()) {<br> _log.debug("will copy the fileEntry");<br> }<br> <br> Repository fromRepository = getFileEntryRepository(fileEntryId);<br> Repository toRepository = getFolderRepository(<br> newFolderId, serviceContext.getScopeGroupId());<br><br> if (newFolderId != DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {<br> Folder toFolder = toRepository.getFolder(newFolderId);<br><br> if (toFolder.isMountPoint()) {<br> toRepository = getRepository(toFolder.getRepositoryId());<br> }<br> }<br><br> if (fromRepository.getRepositoryId() ==<br> toRepository.getRepositoryId()) {<br> if(_log.isDebugEnabled()) {<br> _log.debug("will copy in the same repo");<br> }<br> // Move file entries within repository<br> return copyFileWithinRepo(fileEntryId, newFolderId, serviceContext);<br> }<br><br> // Move file entries between repositories<br> return moveFileEntries(<br> fileEntryId, newFolderId, fromRepository, toRepository,<br> serviceContext);<br> }<br> <br> private FileEntry copyFileWithinRepo(long fileEntryId, long newFolderId, ServiceContext serviceContext) throws PortalException, SystemException {<br> <br> Repository fromRepository = getFileEntryRepository(fileEntryId);<br> Repository toRepository = getFolderRepository(<br> newFolderId, serviceContext.getScopeGroupId());<br> if(_log.isDebugEnabled()) {<br> _log.debug("calling fromRepository.copyFileEntry(...)");<br> }<br> FileEntry fileEntry = fromRepository.copyFileEntry(serviceContext.getScopeGroupId(), fileEntryId, newFolderId, serviceContext);<br> <br> if(_log.isDebugEnabled()) {<br> _log.debug("copied the fileEntry. Updating the fileEntry");<br> }<br> FileEntry srcFileEntry = DLAppLocalServiceUtil.getFileEntry(fileEntryId);<br><br> DLAppHelperLocalServiceUtil.updateFileEntry(serviceContext.getUserId(), fileEntry, srcFileEntry.getLatestFileVersion(), <br> fileEntry.getFileVersion(), serviceContext);<br> <br> Map<String, Serializable> workflowContext = new HashMap<String, Serializable>();<br> workflowContext.put("event", DLSyncConstants.EVENT_ADD);<br> <br> DLFileEntry dlFileEntry = DLFileEntryLocalServiceUtil.getDLFileEntry(fileEntry.getFileEntryId());<br> if(_log.isDebugEnabled()) {<br> _log.debug("calling toRepository.updateFileEntry()");<br> }<br> toRepository.updateFileEntry(fileEntry.getFileEntryId(), dlFileEntry.getName(), fileEntry.getMimeType(), fileEntry.getTitle(), fileEntry.getDescription(), null, false, null, fileEntry.getSize(), serviceContext);<br> if(_log.isDebugEnabled()) {<br> _log.debug("updated the file entry. returning...");<br> }<br> return fileEntry;<br> }<br><br> protected Repository getFileEntryRepository(long fileEntryId)<br> throws PortalException, SystemException {<br><br> try {<br> return RepositoryServiceUtil.getRepositoryImpl(0, fileEntryId, 0);<br> }<br> catch (InvalidRepositoryIdException irie) {<br> StringBundler sb = new StringBundler(3);<br><br> sb.append("No FileEntry exists with the key {fileEntryId=");<br> sb.append(fileEntryId);<br> sb.append("}");<br><br> throw new NoSuchFileEntryException(sb.toString(), irie);<br> }<br> }<br> <br> protected Repository getFolderRepository(long folderId)<br> throws PortalException, SystemException {<br><br> try {<br> return RepositoryServiceUtil.getRepositoryImpl(folderId, 0, 0);<br> }<br> catch (InvalidRepositoryIdException irie) {<br> StringBundler sb = new StringBundler(3);<br><br> sb.append("No Folder exists with the key {folderId=");<br> sb.append(folderId);<br> sb.append("}");<br><br> throw new NoSuchFolderException(sb.toString(), irie);<br> }<br> }<br> <br> protected Repository getFolderRepository(long folderId, long groupId)<br> throws PortalException, SystemException {<br><br> Repository repository = null;<br><br> if (folderId == DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {<br> repository = getRepository(groupId);<br> }<br> else {<br> repository = getFolderRepository(folderId);<br> }<br><br> return repository;<br> }<br> <br> protected Repository getRepository(long repositoryId)<br> throws PortalException, SystemException {<br><br> try {<br> return RepositoryServiceUtil.getRepositoryImpl(repositoryId);<br> }<br> catch (InvalidRepositoryIdException irie) {<br> StringBundler sb = new StringBundler(3);<br><br> sb.append("No Group exists with the key {repositoryId=");<br> sb.append(repositoryId);<br> sb.append("}");<br><br> throw new NoSuchGroupException(sb.toString(), irie);<br> }<br> }<br> <br> protected FileEntry moveFileEntries(<br> long fileEntryId, long newFolderId, Repository fromRepository,<br> Repository toRepository, ServiceContext serviceContext)<br> throws PortalException, SystemException {<br><br> FileEntry sourceFileEntry = fromRepository.getFileEntry(fileEntryId);<br><br> FileEntry destinationFileEntry = copyFileEntry(<br> toRepository, sourceFileEntry, newFolderId, serviceContext);<br><br> return destinationFileEntry;<br> }<br> <br> protected FileEntry copyFileEntry(<br> Repository toRepository, FileEntry fileEntry, long newFolderId,<br> ServiceContext serviceContext)<br> throws PortalException, SystemException {<br><br> List<FileVersion> fileVersions = fileEntry.getFileVersions(<br> WorkflowConstants.STATUS_ANY);<br><br> FileVersion latestFileVersion = fileVersions.get(<br> fileVersions.size() - 1);<br><br> FileEntry destinationFileEntry = toRepository.addFileEntry(<br> newFolderId, fileEntry.getTitle(), latestFileVersion.getMimeType(),<br> latestFileVersion.getTitle(), latestFileVersion.getDescription(),<br> StringPool.BLANK, latestFileVersion.getContentStream(false),<br> latestFileVersion.getSize(), serviceContext);<br><br> FileVersion oldDestinationFileVersion =<br> destinationFileEntry.getFileVersion();<br> <br> DLAppHelperLocalServiceUtil.addFileEntry(<br> serviceContext.getUserId(), destinationFileEntry, oldDestinationFileVersion,<br> serviceContext);<br><br> for (int i = fileVersions.size() - 2; i >= 0; i--) {<br> FileVersion fileVersion = fileVersions.get(i);<br><br> FileVersion previousFileVersion = fileVersions.get(i + 1);<br><br> try {<br> destinationFileEntry = toRepository.updateFileEntry(<br> destinationFileEntry.getFileEntryId(),<br> fileVersion.getTitle(), fileVersion.getMimeType(),<br> fileVersion.getTitle(), fileVersion.getDescription(),<br> StringPool.BLANK,<br> isMajorVersion(previousFileVersion, fileVersion),<br> fileVersion.getContentStream(false), fileVersion.getSize(),<br> serviceContext);<br><br> FileVersion destinationFileVersion =<br> destinationFileEntry.getFileVersion();<br><br> DLAppHelperLocalServiceUtil.updateFileEntry(<br> serviceContext.getUserId(), destinationFileEntry,<br> oldDestinationFileVersion, destinationFileVersion,<br> serviceContext);<br><br> oldDestinationFileVersion = destinationFileVersion;<br> }<br> catch (PortalException pe) {<br> toRepository.deleteFileEntry(<br> destinationFileEntry.getFileEntryId());<br><br> throw pe;<br> }<br> }<br><br> return destinationFileEntry;<br> }<br> <br> protected boolean isMajorVersion(FileVersion previousFileVersion, FileVersion currentFileVersion) {<br><br> long currentVersion = GetterUtil.getLong(currentFileVersion.getVersion());<br> long previousVersion = GetterUtil.getLong(previousFileVersion.getVersion());<br><br> return (currentVersion - previousVersion) >= 1;<br> }<br>}
This wraps up the tutorial on implementing this Liferay hook. Thank you for visiting, please comment on any mistakes I may have made!
2 Comments
minecraft · March 25, 2019 at 4:56 am
Wow! Finally I got a weblog from where I be able to really take helpful data regarding my study and knowledge.
minecraft · March 29, 2019 at 1:23 pm
Hi there to all, it’s truly a pleasant for me to pay a visit this website, it consists of useful Information.