Dave Ward 5245ec7316 Merged V4.0-BUG-FIX to HEAD
36604: ALF-13745: Merged V3.4-BUG-FIX (3.4.10) to V4.0-BUG-FIX (4.0.3)
      36602: ALF-13667 Additional OpenOffice mimetypes to be added to the mime-type maps
         - xls to pdf limit had been commented out
         - failover.transformer.PdfRenderer.PdfToImage and failover.transformer.PdfBox.PdfToImage should not have been
           been registered. Introduced an unregisteredBaseContentTransformer. In the case of these two transforms this would
           not have been a problem, as they were disable anyway due to EXPLICIT setting elsewhere.
   36608: First part of ALF-14209 SOLR - does not support query for all stores
   - dynamic SOLR cores to track a store
   - NEW, DELETE, and change properties and reload
   36635: ALF-13404 for documentlibrary-v2 APIs
   36669: Fix issue with rendering multi-valued properties in JSON from server-side JS (from DaveD)
   36676: Fix for ALF-14216 Solr Exception when you try to sort folders or files by size.
   36692: Fix ALF-12966 - Comments doesn't work on iOS Safari. Anywhere TinyMCE is present does not work correctly e.g. comments, blogs, wiki, HTML content creation.
   36693: ALF-14138: Prevent default Surf CMIS content WebScript clashing with Alfreco version (updated Surf libs r1081)
   36714: ALF-14224: WorkflowTaskDefinition form-key fetching fixed for multi-instance UserTasks
   36721: Fix for ALF-8374 - Simple view: incorrect file type icon for *.page and *.eps files
   36726: Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/V4.0-BUG-FIX
      36724: Fix for ALF-14207 - cm:link not correctly handled in Share's doclist when users don't have permission
   36728: Fix for ALF-14002: "No dashlets listed when customizing Site dashboard for the "Web Site Design Project" sample Share Site "
   36732: Merged V3.4-BUG-FIX to V4.0-BUG-FIX
      36637: ALF-6162 Thumbnail is not produced for PDFs encoded with JBIG2Decode
         << Reported problem still exists, but have made improvements see https://issues.alfresco.com/jira/browse/ALF-6162?focusedCommentId=162936&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-162936 >>
         - PDFRenderer now fails with an exception in more cases, rather than just silently continuing. Specific case was not handling JBig2 images.
           previously just resulted in a black box)
         - Handle the font family CIDFontType0 which includes HiddenHorzOCR
      36640: ALF-13769: Merged BELARUS/DEV/V3.4-BUG-FIX-2012_05_04 to V3.4-BUG-FIX (3.4.10)
         36440: ALF-11956: WCM accessibility
            Assignment of TinyMCE accessibility hotkeys only to activated RTE between multiple instances is added.
            Headings and some other related labels within the context of 'Create Web Content Wizard' were shortened and clarified.
            Fixed some issues related to hidden text for accessibility support in context of IE, which doesn't calculate styles and dimensions that are applied to elements before adding to DOM
         36443: ALF-11956: WCM accessibility
            Added some changes missed in r36440
      36645: ALF-13769: Merged DEV/V3.4-BUG-FIX-2012_01_10 to V3.4-BUG-FIX (3.4.10)
         33381: ALF-10162: Web Form validation can be bypassed
            1. New method was introduced to XFormsBean which allows it to validate the last inserted values.
            2. CreateWebContentWizard finds XFormsBean in session and invokes isXformValid. If any validation
               errors exist it goes back and show the JSF error.
            3. xform.js - contains code that removes JSF error as well as the XForms error. This is necessary because we need to clear
               the JSF error on the Javascript actions
            4. container.jsp - includes a div wrapper with the id to be able unambiguously find the JSF error block.
      36700: Merged DEV to V3.4-BUG-FIX (Reviewed by Erik)
         36450: ALF-12261 : IE8 Specific: It's impossible add relationship type "Parent/Child"
            The type of the submit button should be set to 'button' in 'new-rmreference.get.html.ftl'.
      36702: Merged DEV to V3.4-BUG-FIX (Reviewed by Erik)
         36670: ALF-12825: Impossible choose the "Publication Date", the calendar isn't displayed
            The DatePicker component register a validation handler for the date entry field so that the submit button disables when an invalid date is entered. This handler register with the forms runtime instance. We should create this instance in FormUI_consructor, not is onReady method (form.js), because constructors always in order will be invoked. 
      36703: Fix for Mac Office 2011 Powerpoint save fails on CIFS. ALF-13615.
   36733: Merged V4.0 to V4.0-BUG-FIX
      36628: Added ${NOW} variable option for schema bootstrap scripts (see ALF-14174)
      36632: Fix last part of ALF-14174: The patch adding timestamps to acl_change_set breaks SOLR tracking
       - Added ${NOW} to ACL change set timestamp when upgrading: commit_time_ms = ${NOW}
      36647: ALF-14190: FSTR is not working
      - Due to someone updating dependencies without updating the build scripts!
   36734: Merged V4.0 to V4.0-BUG-FIX (RECORD ONLY)
      36605: ALF-13745: Merged V4.0-BUG-FIX (4.0.3) to V4.0 (4.0.2)
         36604: ALF-13745: Merged V3.4-BUG-FIX (3.4.10) to V4.0-BUG-FIX (4.0.3)
            36602: ALF-13667 Additional OpenOffice mimetypes to be added to the mime-type maps
      36678: Merged BRANCHES/DEV/V4.0-BUG-FIX to BRANCHES/V4.0
          36675: Fix for ALF-14216 Solr Exception when you try to sort folders or files by size.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@36737 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2012-05-22 16:51:44 +00:00

882 lines
28 KiB
Java

/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.filesys.repo;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.jlan.server.SrvSession;
import org.alfresco.jlan.server.filesys.AccessDeniedException;
import org.alfresco.jlan.server.filesys.DiskFullException;
import org.alfresco.jlan.server.filesys.FileAttribute;
import org.alfresco.jlan.server.filesys.FileInfo;
import org.alfresco.jlan.server.filesys.FileOpenParams;
import org.alfresco.jlan.server.filesys.NetworkFile;
import org.alfresco.jlan.smb.SeekType;
import org.alfresco.jlan.smb.server.SMBSrvSession;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.AbstractContentReader;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.filestore.FileContentReader;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionListenerAdapter;
import org.alfresco.service.cmr.repository.ContentAccessor;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.usage.ContentQuotaException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* Implementation of the <tt>NetworkFile</tt> for direct interaction
* with the channel repository.
* <p>
* This provides the interaction with the Alfresco Content Model file/folder structure.
*
* @author Derek Hulley
*/
public class ContentNetworkFile extends NodeRefNetworkFile
{
private static final Log logger = LogFactory.getLog(ContentNetworkFile.class);
// Services
private NodeService nodeService;
private ContentService contentService;
private MimetypeService mimetypeService;
private FileChannel channel; // File channel to file content
private ContentAccessor content; // content
private String preUpdateContentURL;
// Indicate if file has been written to or truncated/resized
private boolean modified;
// Flag to indicate if the file channel is writable
private boolean writableChannel;
/**
* Helper method to create a {@link NetworkFile network file} given a node reference.
*/
public static ContentNetworkFile createFile( NodeService nodeService, ContentService contentService, MimetypeService mimetypeService,
CifsHelper cifsHelper, NodeRef nodeRef, String path, boolean readOnly, boolean attributesOnly, SrvSession sess)
{
// Create the file
ContentNetworkFile netFile = null;
if ( isMSOfficeSpecialFile(path, sess, nodeService, nodeRef)) {
// Create a file for special processing for Excel
netFile = new MSOfficeContentNetworkFile( nodeService, contentService, mimetypeService, nodeRef, path);
}
else if ( isOpenOfficeSpecialFile( path, sess, nodeService, nodeRef)) {
// Create a file for special processing
netFile = new OpenOfficeContentNetworkFile( nodeService, contentService, mimetypeService, nodeRef, path);
}
else {
// Create a normal content file
netFile = new ContentNetworkFile(nodeService, contentService, mimetypeService, nodeRef, path);
}
// Set relevant parameters
if (attributesOnly) {
netFile.setGrantedAccess( NetworkFile.ATTRIBUTESONLY);
}
else if (readOnly) {
netFile.setGrantedAccess(NetworkFile.READONLY);
}
else {
netFile.setGrantedAccess(NetworkFile.READWRITE);
}
// Check the type
FileInfo fileInfo;
try
{
fileInfo = cifsHelper.getFileInformation(nodeRef, "", false, false);
}
catch (FileNotFoundException e)
{
throw new AlfrescoRuntimeException("File not found when creating network file: " + nodeRef, e);
}
if (fileInfo.isDirectory())
{
netFile.setAttributes(FileAttribute.Directory);
}
else
{
// Set the current size
netFile.setFileSize(fileInfo.getSize());
}
// Set the file timestamps
if ( fileInfo.hasCreationDateTime())
netFile.setCreationDate( fileInfo.getCreationDateTime());
if ( fileInfo.hasModifyDateTime() && fileInfo.getModifyDateTime() > 0L)
netFile.setModifyDate(fileInfo.getModifyDateTime());
else
netFile.setModifyDate(fileInfo.getCreationDateTime());
if ( fileInfo.hasAccessDateTime() && fileInfo.getAccessDateTime() > 0L)
netFile.setAccessDate(fileInfo.getAccessDateTime());
else
netFile.setAccessDate(fileInfo.getCreationDateTime());
// Set the file attributes
netFile.setAttributes(fileInfo.getFileAttributes());
// Set the owner process id
//
//netFile.setProcessId( params.getProcessId());
// If the file is read-only then only allow read access
if ( netFile.isReadOnly())
netFile.setGrantedAccess(NetworkFile.READONLY);
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Create file node=" + nodeRef + ", path=" + path + ", netfile=" + netFile);
// Return the network file
return netFile;
}
/**
* Class constructor
*
* @param transactionService TransactionService
* @param nodeService NodeService
* @param contentService ContentService
* @param nodeRef NodeRef
* @param name String
*/
protected ContentNetworkFile(
NodeService nodeService,
ContentService contentService,
MimetypeService mimetypeService,
NodeRef nodeRef,
String name)
{
super(name, nodeRef);
setFullName(name);
this.nodeService = nodeService;
this.contentService = contentService;
this.mimetypeService = mimetypeService;
}
/**
* Return the file details as a string
*
* @return String
*/
public String toString()
{
StringBuilder str = new StringBuilder();
str.append( "[");
str.append(getFullName());
str.append(",");
str.append( getNodeRef().getId());
str.append( ",channel=");
str.append( channel);
if ( channel != null)
str.append( writableChannel ? "(Write)" : "(Read)");
if ( modified)
str.append( ",modified");
str.append( "]");
return str.toString();
}
/**
* @return Returns true if the channel should be writable
*
* @see NetworkFile#getGrantedAccess()
* @see NetworkFile#READONLY
* @see NetworkFile#WRITEONLY
* @see NetworkFile#READWRITE
*/
private boolean isWritable()
{
// Check that we are allowed to write
int access = getGrantedAccess();
return (access == NetworkFile.READWRITE || access == NetworkFile.WRITEONLY);
}
/**
* Determine if the file content data has been opened
*
* @return boolean
*/
public final boolean hasContent()
{
return content != null ? true : false;
}
/**
* Opens the channel for reading or writing depending on the access mode.
* <p>
* Side effect: sets fileSize
* <p>
* If the channel is already open, it is left.
*
* @param write true if the channel must be writable
* @param trunc true if the writable channel does not require the previous content data
* @throws AccessDeniedException if this network file is read only
* @throws AlfrescoRuntimeException if this network file represents a directory
*
* @see NetworkFile#getGrantedAccess()
* @see NetworkFile#READONLY
* @see NetworkFile#WRITEONLY
* @see NetworkFile#READWRITE
*/
public void openContent(boolean write, boolean trunc)
throws AccessDeniedException, AlfrescoRuntimeException
{
// Check if the file is a directory
if (isDirectory())
{
throw new AlfrescoRuntimeException("Unable to open content for a directory network file: " + this);
}
if(channel != null && !write)
{
if(logger.isDebugEnabled())
{
logger.debug("channel is already open for read-only");
}
return;
}
// Check if write access is required and the current channel is read-only
// update of channel and content member variables need to be serialized
synchronized(this)
{
if ( write && writableChannel == false && channel != null)
{
// Close the existing read-only channel
try
{
channel.close();
channel = null;
content = null;
}
catch (IOException ex)
{
logger.error("Error closing read-only channel", ex);
}
// Debug
if ( logger.isDebugEnabled())
{
logger.debug("Switching to writable channel for " + getName());
}
}
else if (channel != null)
{
// Already have read/write channel open
return;
}
// We need to create the channel
if (write && !isWritable())
{
throw new AccessDeniedException("The network file was created for read-only: " + this);
}
preUpdateContentURL = null;
// Need to open content for write
if (write)
{
// Get a writeable channel to the content, along with the original content
if(logger.isDebugEnabled())
{
logger.debug("open writer for content property");
}
content = contentService.getWriter( getNodeRef(), ContentModel.PROP_CONTENT, false);
// Keep the original content for later comparison
ContentData preUpdateContentData = (ContentData) nodeService.getProperty( getNodeRef(), ContentModel.PROP_CONTENT);
if (preUpdateContentData != null)
{
preUpdateContentURL = preUpdateContentData.getContentUrl();
}
// Indicate that we have a writable channel to the file
writableChannel = true;
// Get the writable channel, do not copy existing content data if the file is to be truncated
channel = ((ContentWriter) content).getFileChannel( trunc);
}
else
{
// Get a read-only channel to the content
if(logger.isDebugEnabled())
{
logger.debug("open reader for content property");
}
content = contentService.getReader( getNodeRef(), ContentModel.PROP_CONTENT);
// Ensure that the content we are going to read is valid
content = FileContentReader.getSafeContentReader(
(ContentReader) content,
I18NUtil.getMessage(FileContentReader.MSG_MISSING_CONTENT),
getNodeRef(), content);
// Indicate that we only have a read-only channel to the data
writableChannel = false;
// Get the read-only channel
channel = ((ContentReader) content).getFileChannel();
}
// Update the current file size
if ( channel != null)
{
try
{
setFileSize(channel.size());
}
catch (IOException ex)
{
logger.error( ex);
}
// Indicate that the file is open
setClosed( false);
}
} // release lock
}
/**
* Close the file
*
* @exception IOException
*/
public void closeFile()
throws IOException
{
// Check if this is a directory
if(logger.isDebugEnabled())
{
logger.debug("closeFile");
}
if (isDirectory())
{
// Nothing to do
if(logger.isDebugEnabled())
{
logger.debug("file is a directory - nothing to do");
}
setClosed( true);
return;
}
else if (!hasContent()) {
// File was not read/written so content was not opened
if(logger.isDebugEnabled())
{
logger.debug("no content to write - nothing to do");
}
setClosed( true);
return;
}
// Check if the file has been modified
// update of channel and content member variables need to be serialized
synchronized(this)
{
if (modified)
{
if(logger.isDebugEnabled())
{
logger.debug("content has been modified");
}
NodeRef contentNodeRef = getNodeRef();
ContentWriter writer = (ContentWriter)content;
// We may be in a retry block, in which case this section will already have executed and channel will be null
if (channel != null)
{
// Close the channel
channel.close();
channel = null;
}
// Do we need the mimetype guessing for us when we're done?
if (content.getMimetype() == null || content.getMimetype().equals(MimetypeMap.MIMETYPE_BINARY) )
{
String filename = (String) nodeService.getProperty(contentNodeRef, ContentModel.PROP_NAME);
writer.guessMimetype(filename);
}
// We always want the encoding guessing
writer.guessEncoding();
// Retrieve the content data and stop the content URL from being 'eagerly deleted', in case we need to
// retry the transaction
final ContentData contentData = content.getContentData();
// Update node properties, but only if the binary has changed (ETHREEOH-1861)
ContentReader postUpdateContentReader = writer.getReader();
RunAsWork<ContentReader> getReader = new RunAsWork<ContentReader>()
{
public ContentReader doWork() throws Exception
{
return preUpdateContentURL == null ? null : contentService.getRawReader(preUpdateContentURL);
}
};
ContentReader preUpdateContentReader = AuthenticationUtil.runAs(getReader, AuthenticationUtil.getSystemUserName());
boolean contentChanged = preUpdateContentURL == null
|| !AbstractContentReader.compareContentReaders(preUpdateContentReader,
postUpdateContentReader);
if (contentChanged)
{
if(logger.isDebugEnabled())
{
logger.debug("content has changed - remove ASPECT_NO_CONTENT");
}
nodeService.removeAspect(contentNodeRef, ContentModel.ASPECT_NO_CONTENT);
try
{
nodeService.setProperty( contentNodeRef, ContentModel.PROP_CONTENT, contentData);
}
catch (ContentQuotaException qe)
{
content = null;
setClosed( true);
throw new DiskFullException(qe.getMessage());
}
}
// Tidy up after ourselves after a successful commit. Otherwise leave things to allow a retry.
AlfrescoTransactionSupport.bindListener(new TransactionListenerAdapter()
{
@Override
public void afterCommit()
{
synchronized(this)
{
if(channel == null)
{
content = null;
preUpdateContentURL = null;
setClosed( true);
}
}
}
});
}
else if (channel != null)
{
// Close it - it was not modified
if(logger.isDebugEnabled())
{
logger.debug("content not modified - simply close the channel");
}
channel.close();
channel = null;
content = null;
setClosed(true);
}
}
}
/**
* Truncate or extend the file to the specified length
*
* @param size long
* @exception IOException
*/
public void truncateFile(long size)
throws IOException
{
logger.debug("truncate file");
try
{
// If the content data channel has not been opened yet and the requested size is zero
// then this is an open for overwrite so the existing content data is not copied
if ( hasContent() == false && size == 0L)
{
// Open content for overwrite, no need to copy existing content data
openContent(true, true);
}
else
{
// Normal open for write
openContent(true, false);
// Truncate or extend the channel
channel.truncate(size);
}
}
catch ( ContentIOException ex) {
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Error opening file " + getFullName() + " for write", ex);
// Convert to a file server I/O error
throw new DiskFullException("Failed to open " + getFullName() + " for write");
}
// Set modification flag
modified = true;
incrementWriteCount();
// Set the new file size
setFileSize( size);
// Update the modification date/time and file size
if ( getFileState() != null) {
getFileState().updateModifyDateTime();
getFileState().setFileSize( size);
getFileState().setAllocationSize( size);
}
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Truncate file=" + this + ", size=" + size);
}
/**
* Write a block of data to the file.
*
* @param buf byte[]
* @param len int
* @param pos int
* @param fileOff long
* @exception IOException
*/
public void writeFile(byte[] buffer, int length, int position, long fileOffset)
throws IOException
{
try
{
// Open the channel for writing
openContent(true, false);
}
catch ( ContentIOException ex) {
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Error opening file " + getFullName() + " for write", ex);
// Convert to a file server I/O error
throw new DiskFullException("Failed to open " + getFullName() + " for write");
}
// Write to the channel
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, position, length);
int count = channel.write(byteBuffer, fileOffset);
// Set modification flag
modified = true;
incrementWriteCount();
// Update the current file size
setFileSize(channel.size());
// Update the modification date/time and live file size
if ( getFileState() != null) {
getFileState().updateModifyDateTime();
getFileState().setFileSize( getFileSize());
}
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Write file=" + this + ", size=" + count);
}
/**
* Read from the file.
*
* @param buf byte[]
* @param len int
* @param pos int
* @param fileOff long
* @return Length of data read.
* @exception IOException
*/
public int readFile(byte[] buffer, int length, int position, long fileOffset)
throws IOException
{
// Open the channel for reading
openContent(false, false);
// Read from the channel
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, position, length);
int count = channel.read(byteBuffer, fileOffset);
if (count < 0)
{
count = 0; // doesn't obey the same rules, i.e. just returns the bytes read
}
// Update the access date/time
if ( getFileState() != null)
getFileState().updateAccessDateTime();
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Read file=" + this + " read=" + count);
// Return the actual count of bytes read
return count;
}
/**
* Open the file
*
* @param createFlag boolean
* @exception IOException
*/
@Override
public void openFile(boolean createFlag)
throws IOException
{
// Wait for read/write before opening the content channel
}
/**
* Seek to a new position in the file
*
* @param pos long
* @param typ int
* @return long
*/
@Override
public long seekFile(long pos, int typ)
throws IOException
{
// Open the file, if not already open
openContent( false, false);
// Check if the current file position is the required file position
long curPos = channel.position();
switch (typ) {
// From start of file
case SeekType.StartOfFile :
if (curPos != pos)
channel.position( pos);
break;
// From current position
case SeekType.CurrentPos :
channel.position( curPos + pos);
break;
// From end of file
case SeekType.EndOfFile :
{
long newPos = channel.size() + pos;
channel.position(newPos);
}
break;
}
// Update the access date/time
if ( getFileState() != null)
getFileState().updateAccessDateTime();
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Seek file=" + this + ", pos=" + pos + ", type=" + typ);
// Return the new file position
return channel.position();
}
/**
* Flush and buffered data for this file
*
* @exception IOException
*/
@Override
public void flushFile()
throws IOException
{
// Open the channel for writing
openContent(true, false);
// Flush the channel - metadata flushing is not important
channel.force(false);
// Update the access date/time
if ( getFileState() != null)
getFileState().updateAccessDateTime();
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Flush file=" + this);
}
/**
* Return the modified status
*
* @return boolean
*/
public final boolean isModified() {
return modified;
}
/**
* Check if the file is an MS Office document type that needs special processing
*
* @param path String
* @param sess SrvSession
* @param nodeService NodeService
* @param nodeRef NodeRef
* @return boolean
*/
private static final boolean isMSOfficeSpecialFile( String path, SrvSession sess, NodeService nodeService, NodeRef nodeRef) {
// Check if the file extension indicates a problem MS Office format
path = path.toLowerCase();
if ( path.endsWith( ".xls") && sess instanceof SMBSrvSession) {
// Check if the file is versionable
if ( nodeService.hasAspect( nodeRef, ContentModel.ASPECT_VERSIONABLE))
return true;
}
return false;
}
/**
* Check if the file is an OpenOffice document type that needs special processing
*
* @param path String
* @param sess SrvSession
* @param nodeService NodeService
* @param nodeRef NodeRef
* @return boolean
*/
private static final boolean isOpenOfficeSpecialFile( String path, SrvSession sess, NodeService nodeService, NodeRef nodeRef) {
// Check if the file extension indicates a problem OpenOffice format
path = path.toLowerCase();
if ( path.endsWith( ".odt") && sess instanceof SMBSrvSession) {
// Check if the file is versionable
if ( nodeService.hasAspect( nodeRef, ContentModel.ASPECT_VERSIONABLE))
return true;
}
return false;
}
}